diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e60b7be3..7da4cd6d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,31 @@ # Changelog -## (unreleased) +## v3.0.3988 (2018-11-23) + + + +### **New Features** + +- Added Sonarr v3 #2359. [TidusJar] + +### **Fixes** + +- Fixed a potential security vulnerability. [Jamie] + +- Sorted out some of the settings pages, trying to make it consistent. [Jamie] + +- #2669 Fixed missing translations. [TidusJar] + +- Maps alias email variable for welcome emails. [Victor Usoltsev] + +- Increased the logo size on the landing page to match the container below it. [Jamie] + +- Think the request queue is done! [Jamie] + +- Finished off the job. [TidusJar] + + +## v3.0.3988 (2018-11-23) ### **New Features** @@ -8,8 +33,13 @@ - Added the ability to get the ombi user via a Plex Token #2591. [Jamie] +- Update CHANGELOG.md. [Jamie] + ### **Fixes** + +- Fixed #2601 [TidusJar] + - Made the subscribe/unsubscribe button more obvious on the UI #2309. [Jamie] - Fixed #2603. [Jamie] diff --git a/README.md b/README.md index cfc0ad23c..6347a8a5f 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ ___ # Features Here are some of the features Ombi V3 has: * Now working without crashes on Linux. -* Lets users request Movies and TV Shows (whether it being the entire series, an entire season, or even single episodes.) +* Lets users request Movies, Music, and TV Shows (whether it being the entire series, an entire season, or even single episodes.) * Easily manage your requests * User management system (supports plex.tv, Emby and local accounts) * A landing page that will give you the availability of your Plex/Emby server and also add custom notification text to inform your users of downtime. @@ -50,6 +50,7 @@ We integrate with the following applications: * Emby * Sonarr * Radarr +* Lidarr * DogNzb * Couch Potato @@ -87,6 +88,7 @@ We are planning to bring back these features in V3 but for now you can find a li | DogNzb | Yes | No | | Issues | Yes | Yes | | Headphones | No | Yes | +| Lidarr | Yes | No | # Feature Requests Feature requests are handled on FeatHub. diff --git a/src/Ombi.Api.Github/GithubApi.cs b/src/Ombi.Api.Github/GithubApi.cs index f7b3fbcbf..6865b33fe 100644 --- a/src/Ombi.Api.Github/GithubApi.cs +++ b/src/Ombi.Api.Github/GithubApi.cs @@ -24,16 +24,5 @@ namespace Ombi.Api.Github request.AddHeader("User-Agent", "Ombi"); return await _api.Request>(request); } - - public async Task GetThemesRawContent(string url) - { - var sections = url.Split('/'); - var lastPart = sections.Last(); - url = url.Replace(lastPart, string.Empty); - var request = new Request(lastPart, url, HttpMethod.Get); - request.AddHeader("Accept", "application/vnd.github.v3+json"); - request.AddHeader("User-Agent", "Ombi"); - return await _api.RequestContent(request); - } } } diff --git a/src/Ombi.Api.Github/IGithubApi.cs b/src/Ombi.Api.Github/IGithubApi.cs index 307158b72..1cca37f02 100644 --- a/src/Ombi.Api.Github/IGithubApi.cs +++ b/src/Ombi.Api.Github/IGithubApi.cs @@ -7,6 +7,5 @@ namespace Ombi.Api.Github public interface IGithubApi { Task> GetCakeThemes(); - Task GetThemesRawContent(string url); } } \ No newline at end of file diff --git a/src/Ombi.Core/Engine/VoteEngine.cs b/src/Ombi.Core/Engine/VoteEngine.cs index 7c4d48a6f..5c73e03d9 100644 --- a/src/Ombi.Core/Engine/VoteEngine.cs +++ b/src/Ombi.Core/Engine/VoteEngine.cs @@ -11,7 +11,7 @@ using Ombi.Core.Models; using Ombi.Core.Models.UI; using Ombi.Core.Rule.Interfaces; using Ombi.Core.Settings; -using Ombi.Schedule.Jobs.Ombi; +using Ombi.Helpers; using Ombi.Settings.Settings.Models; using Ombi.Store.Entities; using Ombi.Store.Repository; @@ -114,7 +114,7 @@ namespace Ombi.Core.Engine foreach (var epInformation in childRequests.SeasonRequests.OrderBy(x => x.SeasonNumber)) { var orderedEpisodes = epInformation.Episodes.OrderBy(x => x.EpisodeNumber).ToList(); - var episodeString = NewsletterJob.BuildEpisodeList(orderedEpisodes.Select(x => x.EpisodeNumber)); + var episodeString = StringHelper.BuildEpisodeList(orderedEpisodes.Select(x => x.EpisodeNumber)); finalsb.Append($"Season: {epInformation.SeasonNumber} - Episodes: {episodeString}"); finalsb.Append("
"); } diff --git a/src/Ombi.Core/Ombi.Core.csproj b/src/Ombi.Core/Ombi.Core.csproj index 1748fcc2c..3dad3eb5f 100644 --- a/src/Ombi.Core/Ombi.Core.csproj +++ b/src/Ombi.Core/Ombi.Core.csproj @@ -20,16 +20,18 @@ + + + - diff --git a/src/Ombi.Core/Senders/MovieSender.cs b/src/Ombi.Core/Senders/MovieSender.cs index 66978f758..9124eb9c9 100644 --- a/src/Ombi.Core/Senders/MovieSender.cs +++ b/src/Ombi.Core/Senders/MovieSender.cs @@ -20,7 +20,7 @@ namespace Ombi.Core.Senders { public MovieSender(ISettingsService radarrSettings, IRadarrApi api, ILogger log, ISettingsService dogSettings, IDogNzbApi dogApi, ISettingsService cpSettings, - ICouchPotatoApi cpApi, IRepository userProfiles) + ICouchPotatoApi cpApi, IRepository userProfiles, IRepository requestQueue, INotificationHelper notify) { RadarrSettings = radarrSettings; RadarrApi = api; @@ -30,6 +30,8 @@ namespace Ombi.Core.Senders CouchPotatoSettings = cpSettings; CouchPotatoApi = cpApi; _userProfiles = userProfiles; + _requestQueuRepository = requestQueue; + _notificationHelper = notify; } private ISettingsService RadarrSettings { get; } @@ -40,38 +42,63 @@ namespace Ombi.Core.Senders private ISettingsService CouchPotatoSettings { get; } private ICouchPotatoApi CouchPotatoApi { get; } private readonly IRepository _userProfiles; + private readonly IRepository _requestQueuRepository; + private readonly INotificationHelper _notificationHelper; public async Task Send(MovieRequests model) { - var cpSettings = await CouchPotatoSettings.GetSettingsAsync(); - //var watcherSettings = await WatcherSettings.GetSettingsAsync(); - var radarrSettings = await RadarrSettings.GetSettingsAsync(); - if (radarrSettings.Enabled) + try { - return await SendToRadarr(model, radarrSettings); - } - var dogSettings = await DogNzbSettings.GetSettingsAsync(); - if (dogSettings.Enabled) - { - await SendToDogNzb(model, dogSettings); - return new SenderResult + var cpSettings = await CouchPotatoSettings.GetSettingsAsync(); + //var watcherSettings = await WatcherSettings.GetSettingsAsync(); + var radarrSettings = await RadarrSettings.GetSettingsAsync(); + if (radarrSettings.Enabled) { - Success = true, - Sent = true, - }; - } + return await SendToRadarr(model, radarrSettings); + } - if (cpSettings.Enabled) - { - return await SendToCp(model, cpSettings, cpSettings.DefaultProfileId); - } + var dogSettings = await DogNzbSettings.GetSettingsAsync(); + if (dogSettings.Enabled) + { + await SendToDogNzb(model, dogSettings); + return new SenderResult + { + Success = true, + Sent = true, + }; + } - //if (watcherSettings.Enabled) - //{ - // return SendToWatcher(model, watcherSettings); - //} + if (cpSettings.Enabled) + { + return await SendToCp(model, cpSettings, cpSettings.DefaultProfileId); + } + } + catch (Exception e) + { + Log.LogError(e, "Error when seing movie to DVR app, added to the request queue"); + // Check if already in request quee + var existingQueue = await _requestQueuRepository.FirstOrDefaultAsync(x => x.RequestId == model.Id); + if (existingQueue != null) + { + existingQueue.RetryCount++; + existingQueue.Error = e.Message; + await _requestQueuRepository.SaveChangesAsync(); + } + else + { + await _requestQueuRepository.Add(new RequestQueue + { + Dts = DateTime.UtcNow, + Error = e.Message, + RequestId = model.Id, + Type = RequestType.Movie, + RetryCount = 0 + }); + _notificationHelper.Notify(model, NotificationType.ItemAddedToFaultQueue); + } + } return new SenderResult { @@ -94,9 +121,9 @@ namespace Ombi.Core.Senders private async Task SendToRadarr(MovieRequests model, RadarrSettings settings) { - + var qualityToUse = int.Parse(settings.DefaultQualityProfile); - + var rootFolderPath = settings.DefaultRootPath; var profiles = await _userProfiles.GetAll().FirstOrDefaultAsync(x => x.UserId == model.RequestedUserId); @@ -155,7 +182,7 @@ namespace Ombi.Core.Senders // Search for it if (!settings.AddOnly) { - await RadarrApi.MovieSearch(new[] {existingMovie.id}, settings.ApiKey, settings.FullUri); + await RadarrApi.MovieSearch(new[] { existingMovie.id }, settings.ApiKey, settings.FullUri); } return new SenderResult { Success = true, Sent = true }; diff --git a/src/Ombi.Core/Senders/MusicSender.cs b/src/Ombi.Core/Senders/MusicSender.cs index 60e4ca6ee..76a9fc14c 100644 --- a/src/Ombi.Core/Senders/MusicSender.cs +++ b/src/Ombi.Core/Senders/MusicSender.cs @@ -1,37 +1,75 @@ using System; using System.Linq; using System.Threading.Tasks; +using Microsoft.Extensions.Logging; using Ombi.Api.Lidarr; using Ombi.Api.Lidarr.Models; -using Ombi.Api.Radarr; using Ombi.Core.Settings; using Ombi.Helpers; using Ombi.Settings.Settings.Models.External; +using Ombi.Store.Entities; using Ombi.Store.Entities.Requests; -using Serilog; +using Ombi.Store.Repository; +using ILogger = Microsoft.Extensions.Logging.ILogger; namespace Ombi.Core.Senders { public class MusicSender : IMusicSender { - public MusicSender(ISettingsService lidarr, ILidarrApi lidarrApi) + public MusicSender(ISettingsService lidarr, ILidarrApi lidarrApi, ILogger log, + IRepository requestQueue, INotificationHelper notify) { _lidarrSettings = lidarr; _lidarrApi = lidarrApi; + _log = log; + _requestQueueRepository = requestQueue; + _notificationHelper = notify; } private readonly ISettingsService _lidarrSettings; private readonly ILidarrApi _lidarrApi; + private readonly ILogger _log; + private readonly IRepository _requestQueueRepository; + private readonly INotificationHelper _notificationHelper; public async Task Send(AlbumRequest model) { - var settings = await _lidarrSettings.GetSettingsAsync(); - if (settings.Enabled) + try { - return await SendToLidarr(model, settings); + var settings = await _lidarrSettings.GetSettingsAsync(); + if (settings.Enabled) + { + return await SendToLidarr(model, settings); + } + + return new SenderResult { Success = false, Sent = false, Message = "Lidarr is not enabled" }; } + catch (Exception e) + { + _log.LogError(e, "Exception thrown when sending a music to DVR app, added to the request queue"); + var existingQueue = await _requestQueueRepository.FirstOrDefaultAsync(x => x.RequestId == model.Id); + if (existingQueue != null) + { + existingQueue.RetryCount++; + existingQueue.Error = e.Message; + await _requestQueueRepository.SaveChangesAsync(); + } + else + { + await _requestQueueRepository.Add(new RequestQueue + { + Dts = DateTime.UtcNow, + Error = e.Message, + RequestId = model.Id, + Type = RequestType.Album, + RetryCount = 0 + }); + _notificationHelper.Notify(model, NotificationType.ItemAddedToFaultQueue); + } + } + - return new SenderResult { Success = false, Sent = false, Message = "Lidarr is not enabled" }; + return new SenderResult { Success = false, Sent = false, Message = "Something went wrong!" }; } private async Task SendToLidarr(AlbumRequest model, LidarrSettings settings) diff --git a/src/Ombi.Core/Senders/TvSender.cs b/src/Ombi.Core/Senders/TvSender.cs index aadb893d0..d0946b6f4 100644 --- a/src/Ombi.Core/Senders/TvSender.cs +++ b/src/Ombi.Core/Senders/TvSender.cs @@ -23,7 +23,7 @@ namespace Ombi.Core.Senders { public TvSender(ISonarrApi sonarrApi, ISonarrV3Api sonarrV3Api, ILogger log, ISettingsService sonarrSettings, ISettingsService dog, IDogNzbApi dogApi, ISettingsService srSettings, - ISickRageApi srApi, IRepository userProfiles) + ISickRageApi srApi, IRepository userProfiles, IRepository requestQueue, INotificationHelper notify) { SonarrApi = sonarrApi; SonarrV3Api = sonarrV3Api; @@ -34,6 +34,8 @@ namespace Ombi.Core.Senders SickRageSettings = srSettings; SickRageApi = srApi; UserQualityProfiles = userProfiles; + _requestQueueRepository = requestQueue; + _notificationHelper = notify; } private ISonarrApi SonarrApi { get; } @@ -45,59 +47,94 @@ namespace Ombi.Core.Senders private ISettingsService DogNzbSettings { get; } private ISettingsService SickRageSettings { get; } private IRepository UserQualityProfiles { get; } + private readonly IRepository _requestQueueRepository; + private readonly INotificationHelper _notificationHelper; public async Task Send(ChildRequests model) { - var sonarr = await SonarrSettings.GetSettingsAsync(); - if (sonarr.Enabled) + try { - var result = await SendToSonarr(model); - if (result != null) + var sonarr = await SonarrSettings.GetSettingsAsync(); + if (sonarr.Enabled) { + var result = await SendToSonarr(model); + if (result != null) + { + return new SenderResult + { + Sent = true, + Success = true + }; + } + } + var dog = await DogNzbSettings.GetSettingsAsync(); + if (dog.Enabled) + { + var result = await SendToDogNzb(model, dog); + if (!result.Failure) + { + return new SenderResult + { + Sent = true, + Success = true + }; + } return new SenderResult { - Sent = true, - Success = true + Message = result.ErrorMessage }; } - } - var dog = await DogNzbSettings.GetSettingsAsync(); - if (dog.Enabled) - { - var result = await SendToDogNzb(model, dog); - if (!result.Failure) + var sr = await SickRageSettings.GetSettingsAsync(); + if (sr.Enabled) { + var result = await SendToSickRage(model, sr); + if (result) + { + return new SenderResult + { + Sent = true, + Success = true + }; + } return new SenderResult { - Sent = true, - Success = true + Message = "Could not send to SickRage!" }; } return new SenderResult { - Message = result.ErrorMessage + Success = true }; } - var sr = await SickRageSettings.GetSettingsAsync(); - if (sr.Enabled) + catch (Exception e) { - var result = await SendToSickRage(model, sr); - if (result) + Logger.LogError(e, "Exception thrown when sending a movie to DVR app, added to the request queue"); + // Check if already in request quee + var existingQueue = await _requestQueueRepository.FirstOrDefaultAsync(x => x.RequestId == model.Id); + if (existingQueue != null) { - return new SenderResult - { - Sent = true, - Success = true - }; + existingQueue.RetryCount++; + existingQueue.Error = e.Message; + await _requestQueueRepository.SaveChangesAsync(); } - return new SenderResult + else { - Message = "Could not send to SickRage!" - }; + await _requestQueueRepository.Add(new RequestQueue + { + Dts = DateTime.UtcNow, + Error = e.Message, + RequestId = model.Id, + Type = RequestType.TvShow, + RetryCount = 0 + }); + _notificationHelper.Notify(model, NotificationType.ItemAddedToFaultQueue); + } } + return new SenderResult { - Success = true + Success = false, + Message = "Something wen't wrong!" }; } @@ -145,7 +182,7 @@ namespace Ombi.Core.Senders } if (profiles.SonarrQualityProfileAnime > 0) { - qualityToUse = profiles.SonarrQualityProfileAnime; + qualityToUse = profiles.SonarrQualityProfileAnime; } } seriesType = "anime"; @@ -165,7 +202,7 @@ namespace Ombi.Core.Senders } if (profiles.SonarrQualityProfile > 0) { - qualityToUse = profiles.SonarrQualityProfile; + qualityToUse = profiles.SonarrQualityProfile; } } seriesType = "standard"; @@ -176,8 +213,7 @@ namespace Ombi.Core.Senders { qualityToUse = model.ParentRequest.QualityOverride.Value; } - - + // Are we using v3 sonarr? var sonarrV3 = s.V3; var languageProfileId = s.LanguageProfile; @@ -280,7 +316,7 @@ namespace Ombi.Core.Senders } } var seriesChanges = false; - + foreach (var season in model.SeasonRequests) { var sonarrSeason = sonarrEpList.Where(x => x.seasonNumber == season.SeasonNumber); diff --git a/src/Ombi.DependencyInjection/IocExtensions.cs b/src/Ombi.DependencyInjection/IocExtensions.cs index c33f31c10..190e79203 100644 --- a/src/Ombi.DependencyInjection/IocExtensions.cs +++ b/src/Ombi.DependencyInjection/IocExtensions.cs @@ -199,6 +199,7 @@ namespace Ombi.DependencyInjection services.AddTransient(); services.AddTransient(); services.AddTransient(); + services.AddTransient(); } } } diff --git a/src/Ombi.Helpers/StringHelper.cs b/src/Ombi.Helpers/StringHelper.cs index c198301fc..68a29e848 100644 --- a/src/Ombi.Helpers/StringHelper.cs +++ b/src/Ombi.Helpers/StringHelper.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Security; @@ -76,6 +77,49 @@ namespace Ombi.Helpers return -1; } + public static string BuildEpisodeList(IEnumerable orderedEpisodes) + { + var epSb = new StringBuilder(); + var previousEpisodes = new List(); + var previousEpisode = -1; + foreach (var ep in orderedEpisodes) + { + if (ep - 1 == previousEpisode) + { + // This is the next one + previousEpisodes.Add(ep); + } + else + { + if (previousEpisodes.Count > 1) + { + // End it + epSb.Append($"{previousEpisodes.First()}-{previousEpisodes.Last()}, "); + } + else if (previousEpisodes.Count == 1) + { + epSb.Append($"{previousEpisodes.FirstOrDefault()}, "); + } + // New one + previousEpisodes.Clear(); + previousEpisodes.Add(ep); + } + previousEpisode = ep; + } + + if (previousEpisodes.Count > 1) + { + // Got some left over + epSb.Append($"{previousEpisodes.First()}-{previousEpisodes.Last()}"); + } + else if (previousEpisodes.Count == 1) + { + epSb.Append(previousEpisodes.FirstOrDefault()); + } + + return epSb.ToString(); + } + public static string RemoveSpaces(this string str) { return str.Replace(" ", ""); diff --git a/src/Ombi.Notifications/Agents/DiscordNotification.cs b/src/Ombi.Notifications/Agents/DiscordNotification.cs index 84e907053..c0671fc90 100644 --- a/src/Ombi.Notifications/Agents/DiscordNotification.cs +++ b/src/Ombi.Notifications/Agents/DiscordNotification.cs @@ -6,7 +6,6 @@ using Ombi.Api.Discord; using Ombi.Api.Discord.Models; using Ombi.Core.Settings; using Ombi.Helpers; -using Ombi.Notifications.Interfaces; using Ombi.Notifications.Models; using Ombi.Settings.Settings.Models; using Ombi.Settings.Settings.Models.Notifications; diff --git a/src/Ombi.Notifications/Agents/EmailNotification.cs b/src/Ombi.Notifications/Agents/EmailNotification.cs index cff1f3b80..9621fed43 100644 --- a/src/Ombi.Notifications/Agents/EmailNotification.cs +++ b/src/Ombi.Notifications/Agents/EmailNotification.cs @@ -8,7 +8,6 @@ using Microsoft.Extensions.Logging; using MimeKit; using Ombi.Core.Settings; using Ombi.Helpers; -using Ombi.Notifications.Interfaces; using Ombi.Notifications.Models; using Ombi.Notifications.Templates; using Ombi.Settings.Settings.Models; diff --git a/src/Ombi.Notifications/Agents/MattermostNotification.cs b/src/Ombi.Notifications/Agents/MattermostNotification.cs index 37e597854..62c5505c5 100644 --- a/src/Ombi.Notifications/Agents/MattermostNotification.cs +++ b/src/Ombi.Notifications/Agents/MattermostNotification.cs @@ -2,13 +2,10 @@ using System.Collections.Generic; using System.Threading.Tasks; using Microsoft.Extensions.Logging; -using Ombi.Api.Discord; -using Ombi.Api.Discord.Models; using Ombi.Api.Mattermost; using Ombi.Api.Mattermost.Models; using Ombi.Core.Settings; using Ombi.Helpers; -using Ombi.Notifications.Interfaces; using Ombi.Notifications.Models; using Ombi.Settings.Settings.Models; using Ombi.Settings.Settings.Models.Notifications; diff --git a/src/Ombi.Notifications/Agents/MobileNotification.cs b/src/Ombi.Notifications/Agents/MobileNotification.cs index a16785909..de904ecfd 100644 --- a/src/Ombi.Notifications/Agents/MobileNotification.cs +++ b/src/Ombi.Notifications/Agents/MobileNotification.cs @@ -8,7 +8,6 @@ using Microsoft.Extensions.Logging; using Ombi.Api.Notifications; using Ombi.Core.Settings; using Ombi.Helpers; -using Ombi.Notifications.Interfaces; using Ombi.Notifications.Models; using Ombi.Settings.Settings.Models; using Ombi.Settings.Settings.Models.Notifications; diff --git a/src/Ombi.Notifications/Agents/PushbulletNotification.cs b/src/Ombi.Notifications/Agents/PushbulletNotification.cs index 6c6b1f789..8440ad319 100644 --- a/src/Ombi.Notifications/Agents/PushbulletNotification.cs +++ b/src/Ombi.Notifications/Agents/PushbulletNotification.cs @@ -4,7 +4,6 @@ using Microsoft.Extensions.Logging; using Ombi.Api.Pushbullet; using Ombi.Core.Settings; using Ombi.Helpers; -using Ombi.Notifications.Interfaces; using Ombi.Notifications.Models; using Ombi.Settings.Settings.Models; using Ombi.Settings.Settings.Models.Notifications; diff --git a/src/Ombi.Notifications/Agents/PushoverNotification.cs b/src/Ombi.Notifications/Agents/PushoverNotification.cs index 86f91dbaa..0d1675f82 100644 --- a/src/Ombi.Notifications/Agents/PushoverNotification.cs +++ b/src/Ombi.Notifications/Agents/PushoverNotification.cs @@ -5,7 +5,6 @@ using Ombi.Api.Pushbullet; using Ombi.Api.Pushover; using Ombi.Core.Settings; using Ombi.Helpers; -using Ombi.Notifications.Interfaces; using Ombi.Notifications.Models; using Ombi.Settings.Settings.Models; using Ombi.Settings.Settings.Models.Notifications; diff --git a/src/Ombi.Notifications/Agents/SlackNotification.cs b/src/Ombi.Notifications/Agents/SlackNotification.cs index ee81e9729..fdbd7ba64 100644 --- a/src/Ombi.Notifications/Agents/SlackNotification.cs +++ b/src/Ombi.Notifications/Agents/SlackNotification.cs @@ -5,7 +5,6 @@ using Ombi.Api.Slack; using Ombi.Api.Slack.Models; using Ombi.Core.Settings; using Ombi.Helpers; -using Ombi.Notifications.Interfaces; using Ombi.Notifications.Models; using Ombi.Settings.Settings.Models; using Ombi.Settings.Settings.Models.Notifications; diff --git a/src/Ombi.Notifications/Agents/TelegramNotification.cs b/src/Ombi.Notifications/Agents/TelegramNotification.cs index cf463bf99..8e4c4e898 100644 --- a/src/Ombi.Notifications/Agents/TelegramNotification.cs +++ b/src/Ombi.Notifications/Agents/TelegramNotification.cs @@ -3,7 +3,6 @@ using System.Threading.Tasks; using Microsoft.Extensions.Logging; using Ombi.Core.Settings; using Ombi.Helpers; -using Ombi.Notifications.Interfaces; using Ombi.Notifications.Models; using Ombi.Settings.Settings.Models; using Ombi.Settings.Settings.Models.Notifications; diff --git a/src/Ombi.Notifications/BaseNotification.cs b/src/Ombi.Notifications/BaseNotification.cs index 53d6d5d9d..57eafedd9 100644 --- a/src/Ombi.Notifications/BaseNotification.cs +++ b/src/Ombi.Notifications/BaseNotification.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; @@ -14,7 +13,7 @@ using Ombi.Store.Entities.Requests; using Ombi.Store.Repository; using Ombi.Store.Repository.Requests; -namespace Ombi.Notifications.Interfaces +namespace Ombi.Notifications { public abstract class BaseNotification : INotification where T : Settings.Settings.Models.Settings, new() { diff --git a/src/Ombi.Notifications/NotificationService.cs b/src/Ombi.Notifications/NotificationService.cs index d3651871f..c2985a21b 100644 --- a/src/Ombi.Notifications/NotificationService.cs +++ b/src/Ombi.Notifications/NotificationService.cs @@ -6,7 +6,6 @@ using System.Threading.Tasks; using Microsoft.Extensions.Logging; using Ombi.Core.Notifications; using Ombi.Helpers; -using Ombi.Notifications.Interfaces; using Ombi.Notifications.Models; namespace Ombi.Notifications diff --git a/src/Ombi.Schedule.Tests/NewsletterTests.cs b/src/Ombi.Schedule.Tests/NewsletterTests.cs index 146cd97cf..f729e8899 100644 --- a/src/Ombi.Schedule.Tests/NewsletterTests.cs +++ b/src/Ombi.Schedule.Tests/NewsletterTests.cs @@ -1,5 +1,7 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using NUnit.Framework; +using Ombi.Helpers; using static Ombi.Schedule.Jobs.Ombi.NewsletterJob; namespace Ombi.Schedule.Tests @@ -15,7 +17,7 @@ namespace Ombi.Schedule.Tests { ep.Add(i); } - var result = BuildEpisodeList(ep); + var result = StringHelper.BuildEpisodeList(ep); return result; } diff --git a/src/Ombi.Schedule/JobSetup.cs b/src/Ombi.Schedule/JobSetup.cs index 7ce32de04..fa111d5f4 100644 --- a/src/Ombi.Schedule/JobSetup.cs +++ b/src/Ombi.Schedule/JobSetup.cs @@ -21,7 +21,7 @@ namespace Ombi.Schedule IEmbyUserImporter embyUserImporter, ISonarrSync cache, ICouchPotatoSync cpCache, ISettingsService jobsettings, ISickRageSync srSync, IRefreshMetadata refresh, INewsletterJob newsletter, IPlexRecentlyAddedSync recentlyAddedPlex, ILidarrArtistSync artist, - IIssuesPurge purge) + IIssuesPurge purge, IResendFailedRequests resender) { _plexContentSync = plexContentSync; _radarrSync = radarrSync; @@ -38,6 +38,7 @@ namespace Ombi.Schedule _plexRecentlyAddedSync = recentlyAddedPlex; _lidarrArtistSync = artist; _issuesPurge = purge; + _resender = resender; } private readonly IPlexContentSync _plexContentSync; @@ -55,6 +56,7 @@ namespace Ombi.Schedule private readonly INewsletterJob _newsletter; private readonly ILidarrArtistSync _lidarrArtistSync; private readonly IIssuesPurge _issuesPurge; + private readonly IResendFailedRequests _resender; public void Setup() { @@ -76,6 +78,8 @@ namespace Ombi.Schedule RecurringJob.AddOrUpdate(() => _embyUserImporter.Start(), JobSettingsHelper.UserImporter(s)); RecurringJob.AddOrUpdate(() => _plexUserImporter.Start(), JobSettingsHelper.UserImporter(s)); RecurringJob.AddOrUpdate(() => _newsletter.Start(), JobSettingsHelper.Newsletter(s)); + RecurringJob.AddOrUpdate(() => _newsletter.Start(), JobSettingsHelper.Newsletter(s)); + RecurringJob.AddOrUpdate(() => _resender.Start(), JobSettingsHelper.ResendFailedRequests(s)); } diff --git a/src/Ombi.Schedule/Jobs/Emby/EmbyEpisodeSync.cs b/src/Ombi.Schedule/Jobs/Emby/EmbyEpisodeSync.cs index 23ad24268..2bb417d3d 100644 --- a/src/Ombi.Schedule/Jobs/Emby/EmbyEpisodeSync.cs +++ b/src/Ombi.Schedule/Jobs/Emby/EmbyEpisodeSync.cs @@ -83,7 +83,7 @@ namespace Ombi.Schedule.Jobs.Emby { processed++; - if (ep.LocationType.Equals("Virtual", StringComparison.InvariantCultureIgnoreCase)) + if (ep.LocationType?.Equals("Virtual", StringComparison.InvariantCultureIgnoreCase) ?? false) { // For some reason Emby is not respecting the `IsVirtualItem` field. continue; @@ -154,4 +154,4 @@ namespace Ombi.Schedule.Jobs.Emby GC.SuppressFinalize(this); } } -} \ No newline at end of file +} diff --git a/src/Ombi.Schedule/Jobs/Ombi/IResendFailedRequests.cs b/src/Ombi.Schedule/Jobs/Ombi/IResendFailedRequests.cs new file mode 100644 index 000000000..b55c0f69b --- /dev/null +++ b/src/Ombi.Schedule/Jobs/Ombi/IResendFailedRequests.cs @@ -0,0 +1,9 @@ +using System.Threading.Tasks; + +namespace Ombi.Schedule.Jobs.Ombi +{ + public interface IResendFailedRequests + { + Task Start(); + } +} \ No newline at end of file diff --git a/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs b/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs index 472cd325b..ab0ef9578 100644 --- a/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs +++ b/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs @@ -677,7 +677,7 @@ namespace Ombi.Schedule.Jobs.Ombi foreach (var epInformation in results.OrderBy(x => x.SeasonNumber)) { var orderedEpisodes = epInformation.Episodes.OrderBy(x => x.EpisodeNumber).ToList(); - var episodeString = BuildEpisodeList(orderedEpisodes.Select(x => x.EpisodeNumber)); + var episodeString = StringHelper.BuildEpisodeList(orderedEpisodes.Select(x => x.EpisodeNumber)); finalsb.Append($"Season: {epInformation.SeasonNumber} - Episodes: {episodeString}"); finalsb.Append("
"); } @@ -715,48 +715,7 @@ namespace Ombi.Schedule.Jobs.Ombi } } - public static string BuildEpisodeList(IEnumerable orderedEpisodes) - { - var epSb = new StringBuilder(); - var previousEpisodes = new List(); - var previousEpisode = -1; - foreach (var ep in orderedEpisodes) - { - if (ep - 1 == previousEpisode) - { - // This is the next one - previousEpisodes.Add(ep); - } - else - { - if (previousEpisodes.Count > 1) - { - // End it - epSb.Append($"{previousEpisodes.First()}-{previousEpisodes.Last()}, "); - } - else if (previousEpisodes.Count == 1) - { - epSb.Append($"{previousEpisodes.FirstOrDefault()}, "); - } - // New one - previousEpisodes.Clear(); - previousEpisodes.Add(ep); - } - previousEpisode = ep; - } - - if (previousEpisodes.Count > 1) - { - // Got some left over - epSb.Append($"{previousEpisodes.First()}-{previousEpisodes.Last()}"); - } - else if(previousEpisodes.Count == 1) - { - epSb.Append(previousEpisodes.FirstOrDefault()); - } - - return epSb.ToString(); - } + private async Task ProcessEmbyTv(HashSet embyContent, StringBuilder sb) { @@ -841,7 +800,7 @@ namespace Ombi.Schedule.Jobs.Ombi foreach (var epInformation in results.OrderBy(x => x.SeasonNumber)) { var orderedEpisodes = epInformation.Episodes.OrderBy(x => x.EpisodeNumber).ToList(); - var episodeString = BuildEpisodeList(orderedEpisodes.Select(x => x.EpisodeNumber)); + var episodeString = StringHelper.BuildEpisodeList(orderedEpisodes.Select(x => x.EpisodeNumber)); finalsb.Append($"Season: {epInformation.SeasonNumber} - Episodes: {episodeString}"); finalsb.Append("
"); } diff --git a/src/Ombi.Schedule/Jobs/Ombi/ResendFailedRequests.cs b/src/Ombi.Schedule/Jobs/Ombi/ResendFailedRequests.cs new file mode 100644 index 000000000..bc8f93a97 --- /dev/null +++ b/src/Ombi.Schedule/Jobs/Ombi/ResendFailedRequests.cs @@ -0,0 +1,75 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Ombi.Core; +using Ombi.Core.Senders; +using Ombi.Store.Entities; +using Ombi.Store.Repository; +using Ombi.Store.Repository.Requests; + +namespace Ombi.Schedule.Jobs.Ombi +{ + public class ResendFailedRequests : IResendFailedRequests + { + public ResendFailedRequests(IRepository queue, IMovieSender movieSender, ITvSender tvSender, IMusicSender musicSender, + IMovieRequestRepository movieRepo, ITvRequestRepository tvRepo, IMusicRequestRepository music) + { + _requestQueue = queue; + _movieSender = movieSender; + _tvSender = tvSender; + _musicSender = musicSender; + _movieRequestRepository = movieRepo; + _tvRequestRepository = tvRepo; + _musicRequestRepository = music; + } + + private readonly IRepository _requestQueue; + private readonly IMovieSender _movieSender; + private readonly ITvSender _tvSender; + private readonly IMusicSender _musicSender; + private readonly IMovieRequestRepository _movieRequestRepository; + private readonly ITvRequestRepository _tvRequestRepository; + private readonly IMusicRequestRepository _musicRequestRepository; + + public async Task Start() + { + // Get all the failed ones! + var failedRequests = _requestQueue.GetAll().Where(x => !x.Completed.HasValue); + + foreach (var request in failedRequests) + { + if (request.Type == RequestType.Movie) + { + var movieRequest = await _movieRequestRepository.GetAll().FirstOrDefaultAsync(x => x.Id == request.RequestId); + var result = await _movieSender.Send(movieRequest); + if (result.Success) + { + request.Completed = DateTime.UtcNow; + await _requestQueue.SaveChangesAsync(); + } + } + if (request.Type == RequestType.TvShow) + { + var tvRequest = await _tvRequestRepository.GetChild().FirstOrDefaultAsync(x => x.Id == request.RequestId); + var result = await _tvSender.Send(tvRequest); + if (result.Success) + { + request.Completed = DateTime.UtcNow; + await _requestQueue.SaveChangesAsync(); + } + } + if (request.Type == RequestType.Album) + { + var musicRequest = await _musicRequestRepository.GetAll().FirstOrDefaultAsync(x => x.Id == request.RequestId); + var result = await _musicSender.Send(musicRequest); + if (result.Success) + { + request.Completed = DateTime.UtcNow; + await _requestQueue.SaveChangesAsync(); + } + } + } + } + } +} \ No newline at end of file diff --git a/src/Ombi.Schedule/Ombi.Schedule.csproj b/src/Ombi.Schedule/Ombi.Schedule.csproj index b262b79d2..ff3a17115 100644 --- a/src/Ombi.Schedule/Ombi.Schedule.csproj +++ b/src/Ombi.Schedule/Ombi.Schedule.csproj @@ -34,6 +34,7 @@ + diff --git a/src/Ombi.Settings/Settings/Models/CustomizationSettings.cs b/src/Ombi.Settings/Settings/Models/CustomizationSettings.cs index 120c14bd8..6c18c712f 100644 --- a/src/Ombi.Settings/Settings/Models/CustomizationSettings.cs +++ b/src/Ombi.Settings/Settings/Models/CustomizationSettings.cs @@ -1,55 +1,16 @@ -using System; -using System.ComponentModel.DataAnnotations.Schema; -using Newtonsoft.Json; -using Ombi.Helpers; - -namespace Ombi.Settings.Settings.Models +namespace Ombi.Settings.Settings.Models { public class CustomizationSettings : Settings { public string ApplicationName { get; set; } public string ApplicationUrl { get; set; } - public string CustomCssLink { get; set; } + public string CustomCss { get; set; } public bool EnableCustomDonations { get; set; } public string CustomDonationUrl { get; set; } public string CustomDonationMessage { get; set; } public string Logo { get; set; } - - public string PresetThemeName { get; set; } - public string PresetThemeContent { get; set; } public bool RecentlyAddedPage { get; set; } - [NotMapped] - public string PresetThemeVersion - { - get - { - if (HasPresetTheme) - { - var parts = PresetThemeName.Split('-'); - return parts[3].Replace(".css", string.Empty); - } - return string.Empty; - } - } - - [NotMapped] - public string PresetThemeDisplayName - { - get - { - if (HasPresetTheme) - { - var parts = PresetThemeName.Split('-'); - return parts[1]; - } - return string.Empty; - } - } - - [NotMapped] - public bool HasPresetTheme => PresetThemeName.HasValue() || PresetThemeContent.HasValue(); - public void AddToUrl(string part) { if (string.IsNullOrEmpty(ApplicationUrl)) diff --git a/src/Ombi.Settings/Settings/Models/JobSettings.cs b/src/Ombi.Settings/Settings/Models/JobSettings.cs index 8b283cdf7..46a950185 100644 --- a/src/Ombi.Settings/Settings/Models/JobSettings.cs +++ b/src/Ombi.Settings/Settings/Models/JobSettings.cs @@ -15,5 +15,6 @@ public string Newsletter { get; set; } public string LidarrArtistSync { get; set; } public string IssuesPurge { get; set; } + public string RetryRequests { get; set; } } } \ No newline at end of file diff --git a/src/Ombi.Settings/Settings/Models/JobSettingsHelper.cs b/src/Ombi.Settings/Settings/Models/JobSettingsHelper.cs index 4491ca27a..1aca72344 100644 --- a/src/Ombi.Settings/Settings/Models/JobSettingsHelper.cs +++ b/src/Ombi.Settings/Settings/Models/JobSettingsHelper.cs @@ -61,6 +61,10 @@ namespace Ombi.Settings.Settings.Models { return Get(s.IssuesPurge, Cron.Daily()); } + public static string ResendFailedRequests(JobSettings s) + { + return Get(s.RetryRequests, Cron.Daily(6)); + } private static string Get(string settings, string defaultCron) { diff --git a/src/Ombi.Store/Context/OmbiContext.cs b/src/Ombi.Store/Context/OmbiContext.cs index 16f26c65a..3d8c05daa 100644 --- a/src/Ombi.Store/Context/OmbiContext.cs +++ b/src/Ombi.Store/Context/OmbiContext.cs @@ -56,6 +56,7 @@ namespace Ombi.Store.Context public DbSet RequestSubscription { get; set; } public DbSet UserNotificationPreferences { get; set; } public DbSet UserQualityProfileses { get; set; } + public DbSet RequestQueue { get; set; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { diff --git a/src/Ombi.Store/Entities/RequestQueue.cs b/src/Ombi.Store/Entities/RequestQueue.cs new file mode 100644 index 000000000..85f73e04b --- /dev/null +++ b/src/Ombi.Store/Entities/RequestQueue.cs @@ -0,0 +1,16 @@ +using System; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Ombi.Store.Entities +{ + [Table("RequestQueue")] + public class RequestQueue : Entity + { + public int RequestId { get; set; } + public RequestType Type { get; set; } + public DateTime Dts { get; set; } + public string Error { get; set; } + public DateTime? Completed { get; set; } + public int RetryCount { get; set; } + } +} \ No newline at end of file diff --git a/src/Ombi.Store/Entities/RequestType.cs b/src/Ombi.Store/Entities/RequestType.cs index 151453bdd..4d2d20ac4 100644 --- a/src/Ombi.Store/Entities/RequestType.cs +++ b/src/Ombi.Store/Entities/RequestType.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Ombi.Store.Entities +namespace Ombi.Store.Entities { public enum RequestType { diff --git a/src/Ombi.Store/Migrations/20181204084915_RequestQueue.Designer.cs b/src/Ombi.Store/Migrations/20181204084915_RequestQueue.Designer.cs new file mode 100644 index 000000000..468abb2c7 --- /dev/null +++ b/src/Ombi.Store/Migrations/20181204084915_RequestQueue.Designer.cs @@ -0,0 +1,1204 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Ombi.Store.Context; + +namespace Ombi.Store.Migrations +{ + [DbContext(typeof(OmbiContext))] + [Migration("20181204084915_RequestQueue")] + partial class RequestQueue + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.1.4-rtm-31024"); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Name") + .HasMaxLength(256); + + b.Property("NormalizedName") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasName("RoleNameIndex"); + + b.ToTable("AspNetRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("RoleId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider"); + + b.Property("ProviderKey"); + + b.Property("ProviderDisplayName"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId"); + + b.Property("RoleId"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId"); + + b.Property("LoginProvider"); + + b.Property("Name"); + + b.Property("Value"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.ApplicationConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Type"); + + b.Property("Value"); + + b.HasKey("Id"); + + b.ToTable("ApplicationConfiguration"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Audit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AuditArea"); + + b.Property("AuditType"); + + b.Property("DateTime"); + + b.Property("Description"); + + b.Property("User"); + + b.HasKey("Id"); + + b.ToTable("Audit"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.CouchPotatoCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TheMovieDbId"); + + b.HasKey("Id"); + + b.ToTable("CouchPotatoCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("EmbyId") + .IsRequired(); + + b.Property("ImdbId"); + + b.Property("ProviderId"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.Property("Type"); + + b.Property("Url"); + + b.HasKey("Id"); + + b.ToTable("EmbyContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("EmbyId"); + + b.Property("EpisodeNumber"); + + b.Property("ImdbId"); + + b.Property("ParentId"); + + b.Property("ProviderId"); + + b.Property("SeasonNumber"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.ToTable("EmbyEpisode"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.GlobalSettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Content"); + + b.Property("SettingsName"); + + b.HasKey("Id"); + + b.ToTable("GlobalSettings"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.LidarrAlbumCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("ArtistId"); + + b.Property("ForeignAlbumId"); + + b.Property("Monitored"); + + b.Property("PercentOfTracks"); + + b.Property("ReleaseDate"); + + b.Property("Title"); + + b.Property("TrackCount"); + + b.HasKey("Id"); + + b.ToTable("LidarrAlbumCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.LidarrArtistCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ArtistId"); + + b.Property("ArtistName"); + + b.Property("ForeignArtistId"); + + b.Property("Monitored"); + + b.HasKey("Id"); + + b.ToTable("LidarrArtistCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationTemplates", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Agent"); + + b.Property("Enabled"); + + b.Property("Message"); + + b.Property("NotificationType"); + + b.Property("Subject"); + + b.HasKey("Id"); + + b.ToTable("NotificationTemplates"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationUserId", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("PlayerId"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("NotificationUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.OmbiUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AccessFailedCount"); + + b.Property("Alias"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Email") + .HasMaxLength(256); + + b.Property("EmailConfirmed"); + + b.Property("EmbyConnectUserId"); + + b.Property("EpisodeRequestLimit"); + + b.Property("LastLoggedIn"); + + b.Property("LockoutEnabled"); + + b.Property("LockoutEnd"); + + b.Property("MovieRequestLimit"); + + b.Property("MusicRequestLimit"); + + b.Property("NormalizedEmail") + .HasMaxLength(256); + + b.Property("NormalizedUserName") + .HasMaxLength(256); + + b.Property("PasswordHash"); + + b.Property("PhoneNumber"); + + b.Property("PhoneNumberConfirmed"); + + b.Property("ProviderUserId"); + + b.Property("SecurityStamp"); + + b.Property("TwoFactorEnabled"); + + b.Property("UserAccessToken"); + + b.Property("UserName") + .HasMaxLength(256); + + b.Property("UserType"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasName("UserNameIndex"); + + b.ToTable("AspNetUsers"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeNumber"); + + b.Property("GrandparentKey"); + + b.Property("Key"); + + b.Property("ParentKey"); + + b.Property("SeasonNumber"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("GrandparentKey"); + + b.ToTable("PlexEpisode"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ParentKey"); + + b.Property("PlexContentId"); + + b.Property("PlexServerContentId"); + + b.Property("SeasonKey"); + + b.Property("SeasonNumber"); + + b.HasKey("Id"); + + b.HasIndex("PlexServerContentId"); + + b.ToTable("PlexSeasonsContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexServerContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("ImdbId"); + + b.Property("Key"); + + b.Property("Quality"); + + b.Property("ReleaseYear"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.Property("Type"); + + b.Property("Url"); + + b.HasKey("Id"); + + b.ToTable("PlexServerContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RadarrCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("HasFile"); + + b.Property("TheMovieDbId"); + + b.HasKey("Id"); + + b.ToTable("RadarrCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RecentlyAddedLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("AlbumId"); + + b.Property("ContentId"); + + b.Property("ContentType"); + + b.Property("EpisodeNumber"); + + b.Property("SeasonNumber"); + + b.Property("Type"); + + b.HasKey("Id"); + + b.ToTable("RecentlyAddedLog"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RequestQueue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Completed"); + + b.Property("Dts"); + + b.Property("Error"); + + b.Property("RequestId"); + + b.Property("RetryCount"); + + b.Property("Type"); + + b.HasKey("Id"); + + b.ToTable("RequestQueue"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.AlbumRequest", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Approved"); + + b.Property("ArtistName"); + + b.Property("Available"); + + b.Property("Cover"); + + b.Property("Denied"); + + b.Property("DeniedReason"); + + b.Property("Disk"); + + b.Property("ForeignAlbumId"); + + b.Property("ForeignArtistId"); + + b.Property("MarkedAsApproved"); + + b.Property("MarkedAsAvailable"); + + b.Property("MarkedAsDenied"); + + b.Property("Rating"); + + b.Property("ReleaseDate"); + + b.Property("RequestType"); + + b.Property("RequestedDate"); + + b.Property("RequestedUserId"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("AlbumRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Approved"); + + b.Property("Available"); + + b.Property("Denied"); + + b.Property("DeniedReason"); + + b.Property("IssueId"); + + b.Property("MarkedAsApproved"); + + b.Property("MarkedAsAvailable"); + + b.Property("MarkedAsDenied"); + + b.Property("ParentRequestId"); + + b.Property("RequestType"); + + b.Property("RequestedDate"); + + b.Property("RequestedUserId"); + + b.Property("SeriesType"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("ParentRequestId"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("ChildRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueCategory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Value"); + + b.HasKey("Id"); + + b.ToTable("IssueCategory"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueComments", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Comment"); + + b.Property("Date"); + + b.Property("IssuesId"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("IssuesId"); + + b.HasIndex("UserId"); + + b.ToTable("IssueComments"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Description"); + + b.Property("IssueCategoryId"); + + b.Property("IssueId"); + + b.Property("ProviderId"); + + b.Property("RequestId"); + + b.Property("RequestType"); + + b.Property("ResovledDate"); + + b.Property("Status"); + + b.Property("Subject"); + + b.Property("Title"); + + b.Property("UserReportedId"); + + b.HasKey("Id"); + + b.HasIndex("IssueCategoryId"); + + b.HasIndex("IssueId"); + + b.HasIndex("UserReportedId"); + + b.ToTable("Issues"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Approved"); + + b.Property("Available"); + + b.Property("Background"); + + b.Property("Denied"); + + b.Property("DeniedReason"); + + b.Property("DigitalReleaseDate"); + + b.Property("ImdbId"); + + b.Property("IssueId"); + + b.Property("MarkedAsApproved"); + + b.Property("MarkedAsAvailable"); + + b.Property("MarkedAsDenied"); + + b.Property("Overview"); + + b.Property("PosterPath"); + + b.Property("QualityOverride"); + + b.Property("ReleaseDate"); + + b.Property("RequestType"); + + b.Property("RequestedDate"); + + b.Property("RequestedUserId"); + + b.Property("RootPathOverride"); + + b.Property("Status"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("MovieRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.RequestLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeCount"); + + b.Property("RequestDate"); + + b.Property("RequestId"); + + b.Property("RequestType"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("RequestLog"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.TvRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Background"); + + b.Property("ImdbId"); + + b.Property("Overview"); + + b.Property("PosterPath"); + + b.Property("QualityOverride"); + + b.Property("ReleaseDate"); + + b.Property("RootFolder"); + + b.Property("Status"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("TvRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RequestSubscription", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("RequestId"); + + b.Property("RequestType"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("RequestSubscription"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SickRageCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SickRageCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SickRageEpisodeCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeNumber"); + + b.Property("SeasonNumber"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SickRageEpisodeCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SonarrCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SonarrCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SonarrEpisodeCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeNumber"); + + b.Property("HasFile"); + + b.Property("SeasonNumber"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SonarrEpisodeCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Tokens", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Token"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Tokens"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.UserNotificationPreferences", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Agent"); + + b.Property("Enabled"); + + b.Property("UserId"); + + b.Property("Value"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("UserNotificationPreferences"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.UserQualityProfiles", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("RadarrQualityProfile"); + + b.Property("RadarrRootPath"); + + b.Property("SonarrQualityProfile"); + + b.Property("SonarrQualityProfileAnime"); + + b.Property("SonarrRootPath"); + + b.Property("SonarrRootPathAnime"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("UserQualityProfiles"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Votes", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Date"); + + b.Property("Deleted"); + + b.Property("RequestId"); + + b.Property("RequestType"); + + b.Property("UserId"); + + b.Property("VoteType"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Votes"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AirDate"); + + b.Property("Approved"); + + b.Property("Available"); + + b.Property("EpisodeNumber"); + + b.Property("Requested"); + + b.Property("SeasonId"); + + b.Property("Title"); + + b.Property("Url"); + + b.HasKey("Id"); + + b.HasIndex("SeasonId"); + + b.ToTable("EpisodeRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChildRequestId"); + + b.Property("SeasonNumber"); + + b.HasKey("Id"); + + b.HasIndex("ChildRequestId"); + + b.ToTable("SeasonRequests"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b => + { + b.HasOne("Ombi.Store.Entities.EmbyContent", "Series") + .WithMany("Episodes") + .HasForeignKey("ParentId") + .HasPrincipalKey("EmbyId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationUserId", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany("NotificationUserIds") + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b => + { + b.HasOne("Ombi.Store.Entities.PlexServerContent", "Series") + .WithMany("Episodes") + .HasForeignKey("GrandparentKey") + .HasPrincipalKey("Key") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b => + { + b.HasOne("Ombi.Store.Entities.PlexServerContent") + .WithMany("Seasons") + .HasForeignKey("PlexServerContentId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.AlbumRequest", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b => + { + b.HasOne("Ombi.Store.Entities.Requests.TvRequests", "ParentRequest") + .WithMany("ChildRequests") + .HasForeignKey("ParentRequestId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueComments", b => + { + b.HasOne("Ombi.Store.Entities.Requests.Issues", "Issues") + .WithMany("Comments") + .HasForeignKey("IssuesId"); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b => + { + b.HasOne("Ombi.Store.Entities.Requests.IssueCategory", "IssueCategory") + .WithMany() + .HasForeignKey("IssueCategoryId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ombi.Store.Entities.Requests.ChildRequests") + .WithMany("Issues") + .HasForeignKey("IssueId"); + + b.HasOne("Ombi.Store.Entities.Requests.MovieRequests") + .WithMany("Issues") + .HasForeignKey("IssueId"); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "UserReported") + .WithMany() + .HasForeignKey("UserReportedId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.RequestLog", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RequestSubscription", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Tokens", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.UserNotificationPreferences", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany("UserNotificationPreferences") + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.UserQualityProfiles", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Votes", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b => + { + b.HasOne("Ombi.Store.Repository.Requests.SeasonRequests", "Season") + .WithMany("Episodes") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b => + { + b.HasOne("Ombi.Store.Entities.Requests.ChildRequests", "ChildRequest") + .WithMany("SeasonRequests") + .HasForeignKey("ChildRequestId") + .OnDelete(DeleteBehavior.Cascade); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Ombi.Store/Migrations/20181204084915_RequestQueue.cs b/src/Ombi.Store/Migrations/20181204084915_RequestQueue.cs new file mode 100644 index 000000000..820ce50af --- /dev/null +++ b/src/Ombi.Store/Migrations/20181204084915_RequestQueue.cs @@ -0,0 +1,35 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Ombi.Store.Migrations +{ + public partial class RequestQueue : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "RequestQueue", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + RequestId = table.Column(nullable: false), + Type = table.Column(nullable: false), + Dts = table.Column(nullable: false), + Error = table.Column(nullable: true), + Completed = table.Column(nullable: true), + RetryCount = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_RequestQueue", x => x.Id); + }); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "RequestQueue"); + } + } +} diff --git a/src/Ombi.Store/Migrations/OmbiContextModelSnapshot.cs b/src/Ombi.Store/Migrations/OmbiContextModelSnapshot.cs index 60927b1ed..5a009d5ef 100644 --- a/src/Ombi.Store/Migrations/OmbiContextModelSnapshot.cs +++ b/src/Ombi.Store/Migrations/OmbiContextModelSnapshot.cs @@ -14,7 +14,7 @@ namespace Ombi.Store.Migrations { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "2.1.3-rtm-32065"); + .HasAnnotation("ProductVersion", "2.1.4-rtm-31024"); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => { @@ -508,6 +508,28 @@ namespace Ombi.Store.Migrations b.ToTable("RecentlyAddedLog"); }); + modelBuilder.Entity("Ombi.Store.Entities.RequestQueue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Completed"); + + b.Property("Dts"); + + b.Property("Error"); + + b.Property("RequestId"); + + b.Property("RetryCount"); + + b.Property("Type"); + + b.HasKey("Id"); + + b.ToTable("RequestQueue"); + }); + modelBuilder.Entity("Ombi.Store.Entities.Requests.AlbumRequest", b => { b.Property("Id") diff --git a/src/Ombi/ClientApp/app/interfaces/IFailedRequests.ts b/src/Ombi/ClientApp/app/interfaces/IFailedRequests.ts new file mode 100644 index 000000000..51a924db5 --- /dev/null +++ b/src/Ombi/ClientApp/app/interfaces/IFailedRequests.ts @@ -0,0 +1,11 @@ +import { RequestType } from "."; +export interface IFailedRequestsViewModel { + failedId: number; + title: string; + releaseYear: Date; + requestId: number; + type: RequestType; + dts: Date; + error: string; + retryCount: number; +} diff --git a/src/Ombi/ClientApp/app/interfaces/IRequestModel.ts b/src/Ombi/ClientApp/app/interfaces/IRequestModel.ts index 310aaa6fd..b083ca088 100644 --- a/src/Ombi/ClientApp/app/interfaces/IRequestModel.ts +++ b/src/Ombi/ClientApp/app/interfaces/IRequestModel.ts @@ -1,9 +1,9 @@ import { IUser } from "./IUser"; export enum RequestType { + tvShow = 0, movie = 1, - tvShow = 2, - + album = 2, } // NEW WORLD diff --git a/src/Ombi/ClientApp/app/interfaces/ISettings.ts b/src/Ombi/ClientApp/app/interfaces/ISettings.ts index 99662088e..e74e10883 100644 --- a/src/Ombi/ClientApp/app/interfaces/ISettings.ts +++ b/src/Ombi/ClientApp/app/interfaces/ISettings.ts @@ -114,25 +114,13 @@ export interface ICustomizationSettings extends ISettings { applicationName: string; applicationUrl: string; logo: string; - customCssLink: string; + customCss: string; enableCustomDonations: boolean; customDonationUrl: string; customDonationMessage: string; - hasPresetTheme: boolean; - presetThemeName: string; - presetThemeContent: string; - presetThemeDisplayName: string; - presetThemeVersion: string; recentlyAddedPage: boolean; } -export interface IThemes { - fullName: string; - displayName: string; - version: string; - url: string; -} - export interface IJobSettings { embyContentSync: string; sonarrSync: string; @@ -147,6 +135,7 @@ export interface IJobSettings { plexRecentlyAddedSync: string; lidarrArtistSync: string; issuesPurge: string; + retryRequests: string; } export interface IIssueSettings extends ISettings { diff --git a/src/Ombi/ClientApp/app/interfaces/index.ts b/src/Ombi/ClientApp/app/interfaces/index.ts index bfc89edc6..e1cf823e8 100644 --- a/src/Ombi/ClientApp/app/interfaces/index.ts +++ b/src/Ombi/ClientApp/app/interfaces/index.ts @@ -17,3 +17,4 @@ export * from "./IRecentlyAdded"; export * from "./ILidarr"; export * from "./ISearchMusicResult"; export * from "./IVote"; +export * from "./IFailedRequests"; diff --git a/src/Ombi/ClientApp/app/search/moviesearch.component.html b/src/Ombi/ClientApp/app/search/moviesearch.component.html index a31bed3b5..64ba43450 100644 --- a/src/Ombi/ClientApp/app/search/moviesearch.component.html +++ b/src/Ombi/ClientApp/app/search/moviesearch.component.html @@ -116,7 +116,7 @@ - -
- -
- -
-
-
@@ -63,7 +54,7 @@
-
+
-
+
-
+
- -
- -
+
-
- +
+
- Preset themes are powered by - layer#Cake. -
diff --git a/src/Ombi/ClientApp/app/settings/customization/customization.component.ts b/src/Ombi/ClientApp/app/settings/customization/customization.component.ts index 098a6f5cc..0daf9ffad 100644 --- a/src/Ombi/ClientApp/app/settings/customization/customization.component.ts +++ b/src/Ombi/ClientApp/app/settings/customization/customization.component.ts @@ -1,6 +1,6 @@ import { Component, OnInit } from "@angular/core"; -import { ICustomizationSettings, IThemes } from "../../interfaces"; +import { ICustomizationSettings } from "../../interfaces"; import { NotificationService } from "../../services"; import { SettingsService } from "../../services"; @@ -10,7 +10,6 @@ import { SettingsService } from "../../services"; export class CustomizationComponent implements OnInit { public settings: ICustomizationSettings; - public themes: IThemes[]; public advanced: boolean; constructor(private settingsService: SettingsService, private notificationService: NotificationService) { } @@ -18,26 +17,6 @@ export class CustomizationComponent implements OnInit { public ngOnInit() { this.settingsService.getCustomization().subscribe(x => { this.settings = x; - this.settingsService.getThemes().subscribe(t => { - this.themes = t; - - const existingTheme = this.themes.filter((item) => { - return item.fullName === this.settings.presetThemeName; - })[0]; - - if (existingTheme) { - const index = this.themes.indexOf(existingTheme, 0); - if (index > -1) { - this.themes.splice(index, 1); - } - } - if (x.hasPresetTheme) { - this.themes.unshift({displayName: x.presetThemeDisplayName, fullName: x.presetThemeName, url: existingTheme.url, version: x.presetThemeVersion}); - this.themes.unshift({displayName: "None", fullName: "None", url: "", version: ""}); - } else { - this.themes.unshift({displayName: "Please Select", fullName: "-1", url: "-1", version: ""}); - } - }); }); } @@ -62,26 +41,4 @@ export class CustomizationComponent implements OnInit { }); } - - public dropDownChange(event: any): void { - const selectedThemeFullName = event.target.value; - const selectedTheme = this.themes.filter((val) => { - return val.fullName === selectedThemeFullName; - })[0]; - - if (selectedTheme.fullName === this.settings.presetThemeName) { - return; - } - - if (selectedTheme.fullName === "None" || selectedTheme.fullName === "-1") { - this.settings.presetThemeName = ""; - this.settings.presetThemeContent = ""; - return; - } - - this.settings.presetThemeName = selectedThemeFullName; - this.settingsService.getThemeContent(selectedTheme.url).subscribe(x => { - this.settings.presetThemeContent = x; - }); - } } diff --git a/src/Ombi/ClientApp/app/settings/failedrequests/failedrequest.component.html b/src/Ombi/ClientApp/app/settings/failedrequests/failedrequest.component.html new file mode 100644 index 000000000..898710199 --- /dev/null +++ b/src/Ombi/ClientApp/app/settings/failedrequests/failedrequest.component.html @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + +
TitleTypeRetry CountError DescriptionDelete
+ {{v.title}} + {{RequestType[v.type] | humanize}}{{v.retryCount}}
diff --git a/src/Ombi/ClientApp/app/settings/failedrequests/failedrequests.component.ts b/src/Ombi/ClientApp/app/settings/failedrequests/failedrequests.component.ts new file mode 100644 index 000000000..a303ac713 --- /dev/null +++ b/src/Ombi/ClientApp/app/settings/failedrequests/failedrequests.component.ts @@ -0,0 +1,27 @@ +import { Component, OnInit } from "@angular/core"; +import { IFailedRequestsViewModel, RequestType } from "../../interfaces"; +import { RequestRetryService } from "../../services"; + +@Component({ + templateUrl: "./failedrequest.component.html", +}) +export class FailedRequestsComponent implements OnInit { + + public vm: IFailedRequestsViewModel[]; + public RequestType = RequestType; + + constructor(private retry: RequestRetryService) { } + + public ngOnInit() { + this.retry.getFailedRequests().subscribe(x => this.vm = x); + } + + public remove(failed: IFailedRequestsViewModel) { + this.retry.deleteFailedRequest(failed.failedId).subscribe(x => { + if(x) { + const index = this.vm.indexOf(failed); + this.vm.splice(index,1); + } + }); + } +} diff --git a/src/Ombi/ClientApp/app/settings/jobs/jobs.component.html b/src/Ombi/ClientApp/app/settings/jobs/jobs.component.html index 1365710f0..e889c0fca 100644 --- a/src/Ombi/ClientApp/app/settings/jobs/jobs.component.html +++ b/src/Ombi/ClientApp/app/settings/jobs/jobs.component.html @@ -48,6 +48,13 @@ The Automatic Update is required
+ +
+ + + The Retry Requests is required + +
diff --git a/src/Ombi/ClientApp/app/settings/jobs/jobs.component.ts b/src/Ombi/ClientApp/app/settings/jobs/jobs.component.ts index d8ce106ae..853005185 100644 --- a/src/Ombi/ClientApp/app/settings/jobs/jobs.component.ts +++ b/src/Ombi/ClientApp/app/settings/jobs/jobs.component.ts @@ -36,6 +36,7 @@ export class JobsComponent implements OnInit { plexRecentlyAddedSync: [x.plexRecentlyAddedSync, Validators.required], lidarrArtistSync: [x.lidarrArtistSync, Validators.required], issuesPurge: [x.issuesPurge, Validators.required], + retryRequests: [x.retryRequests, Validators.required], }); }); } diff --git a/src/Ombi/ClientApp/app/settings/lidarr/lidarr.component.html b/src/Ombi/ClientApp/app/settings/lidarr/lidarr.component.html index 50938412f..5e898e719 100644 --- a/src/Ombi/ClientApp/app/settings/lidarr/lidarr.component.html +++ b/src/Ombi/ClientApp/app/settings/lidarr/lidarr.component.html @@ -19,25 +19,28 @@
- + - The IP/Hostname is required
- + - The Port is required +
- + - The API Key is required +
@@ -56,19 +59,22 @@
- +
- - A Default Quality Profile is required
- +
@@ -89,12 +97,14 @@
- A Language profile is required
- +
- A Metadata profile is required
diff --git a/src/Ombi/ClientApp/app/settings/radarr/radarr.component.html b/src/Ombi/ClientApp/app/settings/radarr/radarr.component.html index 909a64226..08a8035c2 100644 --- a/src/Ombi/ClientApp/app/settings/radarr/radarr.component.html +++ b/src/Ombi/ClientApp/app/settings/radarr/radarr.component.html @@ -1,5 +1,4 @@ - - +
Radarr Settings @@ -19,25 +18,34 @@
- + - - The IP/Hostname is required +
- + - - The Port is required +
- - - - The API Key is required + + +
@@ -49,63 +57,73 @@
- +
+
-
- -
-
-
- +
- +
- A Default Quality Profile is required
-
-
- - -
-
- +
- + -
- A Default Root Path is required + + +
+ +
- +
-
- - A Default Minimum Availability is required
- +
+
+
- +
@@ -118,4 +136,4 @@
-
+
\ No newline at end of file diff --git a/src/Ombi/ClientApp/app/settings/settings.module.ts b/src/Ombi/ClientApp/app/settings/settings.module.ts index 060d42b19..fb1a10abe 100644 --- a/src/Ombi/ClientApp/app/settings/settings.module.ts +++ b/src/Ombi/ClientApp/app/settings/settings.module.ts @@ -9,7 +9,7 @@ import { AuthGuard } from "../auth/auth.guard"; import { AuthService } from "../auth/auth.service"; import { CouchPotatoService, EmbyService, IssuesService, JobService, LidarrService, MobileService, NotificationMessageService, PlexService, RadarrService, - SonarrService, TesterService, ValidationService, + RequestRetryService, SonarrService, TesterService, ValidationService, } from "../services"; import { PipeModule } from "../pipes/pipe.module"; @@ -19,6 +19,7 @@ import { CouchPotatoComponent } from "./couchpotato/couchpotato.component"; import { CustomizationComponent } from "./customization/customization.component"; import { DogNzbComponent } from "./dognzb/dognzb.component"; import { EmbyComponent } from "./emby/emby.component"; +import { FailedRequestsComponent } from "./failedrequests/failedrequests.component"; import { IssuesComponent } from "./issues/issues.component"; import { JobsComponent } from "./jobs/jobs.component"; import { LandingPageComponent } from "./landingpage/landingpage.component"; @@ -77,6 +78,7 @@ const routes: Routes = [ { path: "Newsletter", component: NewsletterComponent, canActivate: [AuthGuard] }, { path: "Lidarr", component: LidarrComponent, canActivate: [AuthGuard] }, { path: "Vote", component: VoteComponent, canActivate: [AuthGuard] }, + { path: "FailedRequests", component: FailedRequestsComponent, canActivate: [AuthGuard] }, ]; @NgModule({ @@ -130,6 +132,7 @@ const routes: Routes = [ NewsletterComponent, LidarrComponent, VoteComponent, + FailedRequestsComponent, ], exports: [ RouterModule, @@ -149,6 +152,7 @@ const routes: Routes = [ MobileService, NotificationMessageService, LidarrService, + RequestRetryService, ], }) diff --git a/src/Ombi/ClientApp/app/settings/settingsmenu.component.html b/src/Ombi/ClientApp/app/settings/settingsmenu.component.html index 59ce35c32..91405d1c5 100644 --- a/src/Ombi/ClientApp/app/settings/settingsmenu.component.html +++ b/src/Ombi/ClientApp/app/settings/settingsmenu.component.html @@ -54,7 +54,7 @@ Music @@ -84,6 +84,7 @@