From 412063c9828083182d98a372ff02d21fbac727bd Mon Sep 17 00:00:00 2001 From: Victor Usoltsev Date: Sat, 27 Mar 2021 00:51:36 +1300 Subject: [PATCH 01/39] Capitalizes V for tv show notification type. Refactors notification message curlys class. --- src/Ombi.Notifications/BaseNotification.cs | 1 - .../NotificationMessageCurlys.cs | 349 ++++++++---------- 2 files changed, 144 insertions(+), 206 deletions(-) diff --git a/src/Ombi.Notifications/BaseNotification.cs b/src/Ombi.Notifications/BaseNotification.cs index c9404eb2c..9d8bbb776 100644 --- a/src/Ombi.Notifications/BaseNotification.cs +++ b/src/Ombi.Notifications/BaseNotification.cs @@ -209,7 +209,6 @@ namespace Ombi.Notifications if (model.RequestType == RequestType.Movie) { _log.LogDebug("Notification options: {@model}, Req: {@MovieRequest}, Settings: {@Customization}", model, MovieRequest, Customization); - curlys.Setup(model, MovieRequest, Customization, preference); } else if (model.RequestType == RequestType.TvShow) diff --git a/src/Ombi.Notifications/NotificationMessageCurlys.cs b/src/Ombi.Notifications/NotificationMessageCurlys.cs index 54e85080b..4c4aa01ef 100644 --- a/src/Ombi.Notifications/NotificationMessageCurlys.cs +++ b/src/Ombi.Notifications/NotificationMessageCurlys.cs @@ -14,228 +14,164 @@ namespace Ombi.Notifications { public class NotificationMessageCurlys { - public void Setup(NotificationOptions opts, MovieRequests req, CustomizationSettings s, UserNotificationPreferences pref) + public void SetupNewsletter(CustomizationSettings s) { - LoadIssues(opts); + ApplicationName = string.IsNullOrEmpty(s?.ApplicationName) ? "Ombi" : s.ApplicationName; + ApplicationUrl = s?.ApplicationUrl.HasValue() ?? false ? s.ApplicationUrl : string.Empty; + } - RequestId = req?.Id.ToString(); + public void Setup(OmbiUser user, CustomizationSettings s) + { + ApplicationName = string.IsNullOrEmpty(s?.ApplicationName) ? "Ombi" : s.ApplicationName; + ApplicationUrl = s?.ApplicationUrl.HasValue() ?? false ? s.ApplicationUrl : string.Empty; + RequestedUser = user.UserName; + Alias = user.UserAlias; + UserName = user.UserName; + } + + public void Setup(NotificationOptions opts, MovieRequests req, CustomizationSettings s, + UserNotificationPreferences pref) + { + LoadIssues(opts); + LoadCommon(req, s, pref); + LoadTitle(opts, req); ProviderId = req?.TheMovieDbId.ToString() ?? string.Empty; - string title; - if (req == null) - { - opts.Substitutes.TryGetValue("Title", out title); - } - else - { - title = req?.Title; - } - ApplicationUrl = (s?.ApplicationUrl.HasValue() ?? false) ? s.ApplicationUrl : string.Empty; - ApplicationName = string.IsNullOrEmpty(s?.ApplicationName) ? "Ombi" : s?.ApplicationName; - RequestedUser = req?.RequestedUser?.UserName; - if (UserName.IsNullOrEmpty()) - { - // Can be set if it's an issue - UserName = req?.RequestedUser?.UserName; - } + Year = req?.ReleaseDate.Year.ToString(); + Overview = req?.Overview; + AdditionalInformation = opts?.AdditionalInformation ?? string.Empty; - if (Alias.IsNullOrEmpty()) + if (!string.IsNullOrEmpty(req?.PosterPath)) { - // Can be set if it's an issue - Alias = (req?.RequestedUser?.Alias.HasValue() ?? false) ? req?.RequestedUser?.Alias : req?.RequestedUser?.UserName; + PosterImage = $"https://image.tmdb.org/t/p/w300/{req.PosterPath.TrimStart('/')}"; } - if (pref != null) - { - UserPreference = pref.Value.HasValue() ? pref.Value : Alias; - } - Title = title; - RequestedDate = req?.RequestedDate.ToString("D"); - if (Type.IsNullOrEmpty()) - { - Type = req?.RequestType.Humanize(); - } - Overview = req?.Overview; - Year = req?.ReleaseDate.Year.ToString(); - DenyReason = req?.DeniedReason; - AvailableDate = req?.MarkedAsAvailable?.ToString("D") ?? string.Empty; - if (req?.RequestType == RequestType.Movie) - { - PosterImage = string.Format((req?.PosterPath ?? string.Empty).StartsWith("/", StringComparison.InvariantCultureIgnoreCase) - ? "https://image.tmdb.org/t/p/w300{0}" : "https://image.tmdb.org/t/p/w300/{0}", req?.PosterPath); - } - else + CalculateRequestStatus(req); + } + + public void Setup(NotificationOptions opts, ChildRequests req, CustomizationSettings s, + UserNotificationPreferences pref) + { + LoadIssues(opts); + LoadCommon(req, s, pref); + LoadTitle(opts, req); + ProviderId = req?.ParentRequest?.ExternalProviderId.ToString() ?? string.Empty; + Year = req?.ParentRequest?.ReleaseDate.Year.ToString(); + Overview = req?.ParentRequest?.Overview; + AdditionalInformation = opts.AdditionalInformation; + + if (!string.IsNullOrEmpty(req?.ParentRequest?.PosterPath)) { - PosterImage = req?.PosterPath; + PosterImage = $"https://image.tmdb.org/t/p/w300/{req.ParentRequest?.PosterPath.TrimStart('/')}"; } - AdditionalInformation = opts?.AdditionalInformation ?? string.Empty; + // Generate episode list. + StringBuilder epSb = new StringBuilder(); + IEnumerable episodes = req?.SeasonRequests? + .SelectMany(x => x.Episodes) ?? new List(); + episodes + .OrderBy(x => x.EpisodeNumber) + .ToList() + .ForEach(ep => epSb.Append($"{ep.EpisodeNumber},")); + if (epSb.Length > 0) epSb.Remove(epSb.Length - 1, 1); + EpisodesList = epSb.ToString(); + // Generate season list. + StringBuilder seasonSb = new StringBuilder(); + List seasons = req?.SeasonRequests ?? new List(); + seasons + .OrderBy(x => x.SeasonNumber) + .ToList() + .ForEach(ep => seasonSb.Append($"{ep.SeasonNumber},")); + if (seasonSb.Length > 0) seasonSb.Remove(seasonSb.Length - 1, 1); + SeasonsList = seasonSb.ToString(); CalculateRequestStatus(req); } - public void Setup(NotificationOptions opts, AlbumRequest req, CustomizationSettings s, UserNotificationPreferences pref) + public void Setup(NotificationOptions opts, AlbumRequest req, CustomizationSettings s, + UserNotificationPreferences pref) { LoadIssues(opts); - - RequestId = req?.Id.ToString(); + LoadCommon(req, s, pref); + LoadTitle(opts, req); ProviderId = req?.ForeignArtistId ?? string.Empty; - - string title; - if (req == null) - { - opts.Substitutes.TryGetValue("Title", out title); - } - else - { - title = req?.Title; - } - ApplicationUrl = (s?.ApplicationUrl.HasValue() ?? false) ? s.ApplicationUrl : string.Empty; - ApplicationName = string.IsNullOrEmpty(s?.ApplicationName) ? "Ombi" : s?.ApplicationName; - RequestedUser = req?.RequestedUser?.UserName; - if (UserName.IsNullOrEmpty()) - { - // Can be set if it's an issue - UserName = req?.RequestedUser?.UserName; - } - - AvailableDate = req?.MarkedAsAvailable?.ToString("D") ?? string.Empty; - DenyReason = req?.DeniedReason; - if (Alias.IsNullOrEmpty()) - { - Alias = (req?.RequestedUser?.Alias.HasValue() ?? false) ? req?.RequestedUser?.Alias : req?.RequestedUser?.UserName; - } - if (pref != null) - { - UserPreference = pref.Value.HasValue() ? pref.Value : Alias; - } - Title = title; - RequestedDate = req?.RequestedDate.ToString("D"); - if (Type.IsNullOrEmpty()) - { - Type = req?.RequestType.Humanize(); - } Year = req?.ReleaseDate.Year.ToString(); - PosterImage = (req?.Cover.HasValue() ?? false) ? req.Cover : req?.Disk ?? string.Empty; - AdditionalInformation = opts?.AdditionalInformation ?? string.Empty; + PosterImage = req?.Cover.HasValue() ?? false ? req.Cover : req?.Disk ?? string.Empty; CalculateRequestStatus(req); } - public void SetupNewsletter(CustomizationSettings s) + private void LoadIssues(NotificationOptions opts) { - ApplicationUrl = (s?.ApplicationUrl.HasValue() ?? false) ? s.ApplicationUrl : string.Empty; - ApplicationName = string.IsNullOrEmpty(s?.ApplicationName) ? "Ombi" : s?.ApplicationName; + IssueDescription = opts.Substitutes.TryGetValue("IssueDescription", out string val) ? val : string.Empty; + IssueCategory = opts.Substitutes.TryGetValue("IssueCategory", out val) ? val : string.Empty; + IssueStatus = opts.Substitutes.TryGetValue("IssueStatus", out val) ? val : string.Empty; + IssueSubject = opts.Substitutes.TryGetValue("IssueSubject", out val) ? val : string.Empty; + NewIssueComment = opts.Substitutes.TryGetValue("NewIssueComment", out val) ? val : string.Empty; + UserName = opts.Substitutes.TryGetValue("IssueUser", out val) ? val : string.Empty; + Alias = opts.Substitutes.TryGetValue("IssueUserAlias", out val) ? val : string.Empty; + Type = opts.Substitutes.TryGetValue("RequestType", out val) && Enum.TryParse(val, out RequestType type) + ? HumanizeReturnType(type) + : string.Empty; } - public void Setup(NotificationOptions opts, ChildRequests req, CustomizationSettings s, UserNotificationPreferences pref) + private void LoadCommon(BaseRequest req, CustomizationSettings s, UserNotificationPreferences pref) { - LoadIssues(opts); - RequestId = req?.Id.ToString(); - ProviderId = req?.ParentRequest?.ExternalProviderId.ToString() ?? string.Empty; - string title; - if (req == null) - { - opts.Substitutes.TryGetValue("Title", out title); - } - else - { - title = req?.ParentRequest.Title; - } + ApplicationName = string.IsNullOrEmpty(s?.ApplicationName) ? "Ombi" : s.ApplicationName; + ApplicationUrl = s?.ApplicationUrl.HasValue() ?? false ? s.ApplicationUrl : string.Empty; + AvailableDate = req?.MarkedAsAvailable?.ToString("D") ?? string.Empty; DenyReason = req?.DeniedReason; - ApplicationUrl = (s?.ApplicationUrl.HasValue() ?? false) ? s.ApplicationUrl : string.Empty; - ApplicationName = string.IsNullOrEmpty(s?.ApplicationName) ? "Ombi" : s?.ApplicationName; + RequestId = req?.Id.ToString(); RequestedUser = req?.RequestedUser?.UserName; - if (UserName.IsNullOrEmpty()) - { - // Can be set if it's an issue - UserName = req?.RequestedUser?.UserName; - } - AvailableDate = req?.MarkedAsAvailable?.ToString("D") ?? string.Empty; - if (Alias.IsNullOrEmpty()) - { - Alias = (req?.RequestedUser?.Alias.HasValue() ?? false) ? req?.RequestedUser?.Alias : req?.RequestedUser?.UserName; - } - if (pref != null) - { - UserPreference = pref.Value.HasValue() ? pref.Value : Alias; - } - Title = title; RequestedDate = req?.RequestedDate.ToString("D"); + if (Type.IsNullOrEmpty()) { - Type = req?.RequestType.Humanize(); + Type = HumanizeReturnType(req?.RequestType); } - Overview = req?.ParentRequest.Overview; - Year = req?.ParentRequest.ReleaseDate.Year.ToString(); - if (req?.RequestType == RequestType.Movie) - { - PosterImage = string.Format((req?.ParentRequest.PosterPath ?? string.Empty).StartsWith("/", StringComparison.InvariantCultureIgnoreCase) - ? "https://image.tmdb.org/t/p/w300{0}" : "https://image.tmdb.org/t/p/w300/{0}", req?.ParentRequest.PosterPath); - } - else + if (UserName.IsNullOrEmpty()) { - PosterImage = req?.ParentRequest.PosterPath; + UserName = req?.RequestedUser?.UserName; } - AdditionalInformation = opts.AdditionalInformation; - // DO Episode and Season Lists - var episodes = req?.SeasonRequests?.SelectMany(x => x.Episodes) ?? new List(); - var seasons = req?.SeasonRequests?.OrderBy(x => x.SeasonNumber).ToList() ?? new List(); - var orderedEpisodes = episodes.OrderBy(x => x.EpisodeNumber).ToList(); - var epSb = new StringBuilder(); - var seasonSb = new StringBuilder(); - for (var i = 0; i < orderedEpisodes.Count; i++) + if (Alias.IsNullOrEmpty()) { - var ep = orderedEpisodes[i]; - if (i < orderedEpisodes.Count - 1) - { - epSb.Append($"{ep.EpisodeNumber},"); - } - else - { - epSb.Append($"{ep.EpisodeNumber}"); - } + Alias = req?.RequestedUser?.Alias.HasValue() ?? false + ? req.RequestedUser?.Alias + : req?.RequestedUser?.UserName; } - for (var i = 0; i < seasons.Count; i++) + if (pref != null) { - var ep = seasons[i]; - if (i < seasons.Count - 1) - { - seasonSb.Append($"{ep.SeasonNumber},"); - } - else - { - seasonSb.Append($"{ep.SeasonNumber}"); - } + UserPreference = pref.Value.HasValue() ? pref.Value : Alias; } - - EpisodesList = epSb.ToString(); - SeasonsList = seasonSb.ToString(); - CalculateRequestStatus(req); } - public void Setup(OmbiUser user, CustomizationSettings s) + private static string HumanizeReturnType(RequestType? requestType) { - ApplicationUrl = (s?.ApplicationUrl.HasValue() ?? false) ? s.ApplicationUrl : string.Empty; - ApplicationName = string.IsNullOrEmpty(s?.ApplicationName) ? "Ombi" : s?.ApplicationName; - RequestedUser = user.UserName; - Alias = user.UserAlias; - UserName = user.UserName; + return requestType switch + { + null => string.Empty, + RequestType.TvShow => "TV Show", + _ => requestType.Humanize() + }; } - private void LoadIssues(NotificationOptions opts) + private void LoadTitle(NotificationOptions opts, BaseRequest req) { - var val = string.Empty; - IssueDescription = opts.Substitutes.TryGetValue("IssueDescription", out val) ? val : string.Empty; - IssueCategory = opts.Substitutes.TryGetValue("IssueCategory", out val) ? val : string.Empty; - IssueStatus = opts.Substitutes.TryGetValue("IssueStatus", out val) ? val : string.Empty; - IssueSubject = opts.Substitutes.TryGetValue("IssueSubject", out val) ? val : string.Empty; - NewIssueComment = opts.Substitutes.TryGetValue("NewIssueComment", out val) ? val : string.Empty; - UserName = opts.Substitutes.TryGetValue("IssueUser", out val) ? val : string.Empty; - Alias = opts.Substitutes.TryGetValue("IssueUserAlias", out val) ? val : string.Empty; - Type = opts.Substitutes.TryGetValue("RequestType", out val) ? val.Humanize() : string.Empty; + switch (req) + { + case null: + opts.Substitutes.TryGetValue("Title", out string title); + Title = title; + break; + case ChildRequests tvShowRequest: + Title = tvShowRequest.ParentRequest?.Title; + break; + default: + Title = req.Title; + break; + } } private void CalculateRequestStatus(BaseRequest req) @@ -248,16 +184,19 @@ namespace Ombi.Notifications RequestStatus = "Available"; return; } + if (req.Denied ?? false) { RequestStatus = "Denied"; return; } + if (!req.Available && req.Approved) { RequestStatus = "Processing Request"; return; } + RequestStatus = "Pending Approval"; } } @@ -298,36 +237,36 @@ namespace Ombi.Notifications public Dictionary Curlys => new Dictionary { - {nameof(RequestId), RequestId }, - {nameof(RequestedUser), RequestedUser }, - {nameof(Title), Title }, - {nameof(RequestedDate), RequestedDate }, - {nameof(Type), Type }, - {nameof(AdditionalInformation), AdditionalInformation }, - {nameof(LongDate),LongDate}, - {nameof(ShortDate),ShortDate}, - {nameof(LongTime),LongTime}, - {nameof(ShortTime),ShortTime}, - {nameof(Overview),Overview}, - {nameof(Year),Year}, - {nameof(EpisodesList),EpisodesList}, - {nameof(SeasonsList),SeasonsList}, - {nameof(PosterImage),PosterImage}, - {nameof(ApplicationName),ApplicationName}, - {nameof(ApplicationUrl),ApplicationUrl}, - {nameof(IssueDescription),IssueDescription}, - {nameof(IssueCategory),IssueCategory}, - {nameof(IssueStatus),IssueStatus}, - {nameof(IssueSubject),IssueSubject}, - {nameof(NewIssueComment),NewIssueComment}, - {nameof(IssueUser),IssueUser}, - {nameof(UserName),UserName}, - {nameof(Alias),Alias}, - {nameof(UserPreference),UserPreference}, - {nameof(DenyReason),DenyReason}, - {nameof(AvailableDate),AvailableDate}, - {nameof(RequestStatus),RequestStatus}, - {nameof(ProviderId),ProviderId}, + { nameof(RequestId), RequestId }, + { nameof(RequestedUser), RequestedUser }, + { nameof(Title), Title }, + { nameof(RequestedDate), RequestedDate }, + { nameof(Type), Type }, + { nameof(AdditionalInformation), AdditionalInformation }, + { nameof(LongDate), LongDate }, + { nameof(ShortDate), ShortDate }, + { nameof(LongTime), LongTime }, + { nameof(ShortTime), ShortTime }, + { nameof(Overview), Overview }, + { nameof(Year), Year }, + { nameof(EpisodesList), EpisodesList }, + { nameof(SeasonsList), SeasonsList }, + { nameof(PosterImage), PosterImage }, + { nameof(ApplicationName), ApplicationName }, + { nameof(ApplicationUrl), ApplicationUrl }, + { nameof(IssueDescription), IssueDescription }, + { nameof(IssueCategory), IssueCategory }, + { nameof(IssueStatus), IssueStatus }, + { nameof(IssueSubject), IssueSubject }, + { nameof(NewIssueComment), NewIssueComment }, + { nameof(IssueUser), IssueUser }, + { nameof(UserName), UserName }, + { nameof(Alias), Alias }, + { nameof(UserPreference), UserPreference }, + { nameof(DenyReason), DenyReason }, + { nameof(AvailableDate), AvailableDate }, + { nameof(RequestStatus), RequestStatus }, + { nameof(ProviderId), ProviderId }, }; } } \ No newline at end of file From ea54f52325f058e3f57c74e9606b5aa3b72b2f9b Mon Sep 17 00:00:00 2001 From: Emil Kitti Date: Wed, 31 Mar 2021 19:49:16 +0200 Subject: [PATCH 02/39] fixes movie keywords --- src/Ombi.TheMovieDbApi/Models/FullMovieInfo.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Ombi.TheMovieDbApi/Models/FullMovieInfo.cs b/src/Ombi.TheMovieDbApi/Models/FullMovieInfo.cs index edc0c7e23..1ab33990e 100644 --- a/src/Ombi.TheMovieDbApi/Models/FullMovieInfo.cs +++ b/src/Ombi.TheMovieDbApi/Models/FullMovieInfo.cs @@ -97,6 +97,8 @@ namespace Ombi.Api.TheMovieDb.Models { [JsonProperty("results")] public List KeywordsValue { get; set; } + [JsonProperty("keywords")] + private List _movieKeywordValue { set { KeywordsValue = value; }} } public class KeywordsValue From b59a792fdf722854200cce52d11eaa87a970c02f Mon Sep 17 00:00:00 2001 From: tidusjar Date: Thu, 1 Apr 2021 23:21:13 +0100 Subject: [PATCH 03/39] Added unit tests around the notification subsuitution --- .../NotificationMessageCurlysTests.cs | 342 ++++++++++++++++++ .../Ombi.Notifications.Tests.csproj | 1 + 2 files changed, 343 insertions(+) create mode 100644 src/Ombi.Notifications.Tests/NotificationMessageCurlysTests.cs diff --git a/src/Ombi.Notifications.Tests/NotificationMessageCurlysTests.cs b/src/Ombi.Notifications.Tests/NotificationMessageCurlysTests.cs new file mode 100644 index 000000000..655ffeb23 --- /dev/null +++ b/src/Ombi.Notifications.Tests/NotificationMessageCurlysTests.cs @@ -0,0 +1,342 @@ +using AutoFixture; +using NUnit.Framework; +using Ombi.Notifications.Models; +using Ombi.Settings.Settings.Models; +using Ombi.Store.Entities; +using Ombi.Store.Entities.Requests; +using Ombi.Store.Repository.Requests; +using System.Collections.Generic; +using System.Linq; + +namespace Ombi.Notifications.Tests +{ + public class NotificationMessageCurlysTests + { + private NotificationMessageCurlys sut { get; set; } + private Fixture F { get; set; } + + [SetUp] + public void Setup() + { + F = new Fixture(); + F.Behaviors.OfType().ToList() + .ForEach(b => F.Behaviors.Remove(b)); + F.Behaviors.Add(new OmitOnRecursionBehavior()); + sut = new NotificationMessageCurlys(); + } + + [Test] + public void MovieNotificationTests() + { + var notificationOptions = new NotificationOptions(); + var req = F.Build() + .With(x => x.RequestType, RequestType.Movie) + .With(x => x.Available, true) + .Create(); + var customization = new CustomizationSettings + { + ApplicationUrl = "url", + ApplicationName = "name" + }; + var userPrefs = new UserNotificationPreferences(); + sut.Setup(notificationOptions, req, customization, userPrefs); + + Assert.That(req.Id.ToString(), Is.EqualTo(sut.RequestId)); + Assert.That(req.TheMovieDbId.ToString(), Is.EqualTo(sut.ProviderId)); + Assert.That(req.Title.ToString(), Is.EqualTo(sut.Title)); + Assert.That(req.RequestedUser.UserName, Is.EqualTo(sut.RequestedUser)); + Assert.That(req.RequestedUser.Alias, Is.EqualTo(sut.Alias)); + Assert.That(req.RequestedDate.ToString("D"), Is.EqualTo(sut.RequestedDate)); + Assert.That("Movie", Is.EqualTo(sut.Type)); + Assert.That(req.Overview, Is.EqualTo(sut.Overview)); + Assert.That(req.ReleaseDate.Year.ToString(), Is.EqualTo(sut.Year)); + Assert.That(req.DeniedReason, Is.EqualTo(sut.DenyReason)); + Assert.That(req.MarkedAsAvailable?.ToString("D"), Is.EqualTo(sut.AvailableDate)); + Assert.That("https://image.tmdb.org/t/p/w300/" + req.PosterPath, Is.EqualTo(sut.PosterImage)); + Assert.That(req.DeniedReason, Is.EqualTo(sut.DenyReason)); + Assert.That(req.RequestedUser.Alias, Is.EqualTo(sut.UserPreference)); + Assert.That(string.Empty, Is.EqualTo(sut.AdditionalInformation)); + Assert.That("Available", Is.EqualTo(sut.RequestStatus)); + Assert.That("url", Is.EqualTo(sut.ApplicationUrl)); + Assert.That("name", Is.EqualTo(sut.ApplicationName)); + } + + [Test] + public void MovieIssueNotificationTests() + { + var notificationOptions = new NotificationOptions + { + Substitutes = new Dictionary + { + { "IssueDescription", "Desc" }, + { "IssueCategory", "Cat" }, + { "IssueStatus", "state" }, + { "IssueSubject", "sub" }, + { "NewIssueComment", "a" }, + { "IssueUser", "User" }, + { "IssueUserAlias", "alias" }, + { "RequestType", "Movie" }, + } + }; + var req = F.Build() + .With(x => x.RequestType, RequestType.Movie) + .Create(); + var customization = new CustomizationSettings(); + var userPrefs = new UserNotificationPreferences(); + sut.Setup(notificationOptions, req, customization, userPrefs); + + Assert.That("Desc", Is.EqualTo(sut.IssueDescription)); + Assert.That("Cat", Is.EqualTo(sut.IssueCategory)); + Assert.That("state", Is.EqualTo(sut.IssueStatus)); + Assert.That("a", Is.EqualTo(sut.NewIssueComment)); + Assert.That("User", Is.EqualTo(sut.UserName)); + Assert.That("alias", Is.EqualTo(sut.Alias)); + Assert.That("Movie", Is.EqualTo(sut.Type)); + } + + [Test] + public void MovieNotificationUserPreferences() + { + var notificationOptions = new NotificationOptions + { + AdditionalInformation = "add" + }; + var req = F.Build() + .With(x => x.RequestType, RequestType.Movie) + .Without(x => x.MarkedAsAvailable) + .Create(); + var customization = new CustomizationSettings(); + var userPrefs = new UserNotificationPreferences + { + Value = "PrefValue" + }; + sut.Setup(notificationOptions, req, customization, userPrefs); + + Assert.That("PrefValue", Is.EqualTo(sut.UserPreference)); + Assert.That(string.Empty, Is.EqualTo(sut.AvailableDate)); + Assert.That("add", Is.EqualTo(sut.AdditionalInformation)); + } + + [TestCaseSource(nameof(RequestStatusData))] + public string MovieNotificationTests_RequestStatus(bool available, bool denied, bool approved) + { + var notificationOptions = new NotificationOptions(); + var req = F.Build() + .With(x => x.RequestType, RequestType.Movie) + .With(x => x.Available, available) + .With(x => x.Denied, denied) + .With(x => x.Approved, approved) + .Create(); + var customization = new CustomizationSettings(); + var userPrefs = new UserNotificationPreferences(); + sut.Setup(notificationOptions, req, customization, userPrefs); + return sut.RequestStatus; + } + + private static IEnumerable RequestStatusData + { + get + { + yield return new TestCaseData(true, false, false).Returns("Available"); + yield return new TestCaseData(false, true, false).Returns("Denied"); + yield return new TestCaseData(false, false, true).Returns("Processing Request"); + yield return new TestCaseData(false, false, false).Returns("Pending Approval"); + } + } + + + [Test] + public void NewsletterTests() + { + var customization = new CustomizationSettings + { + ApplicationUrl = "url", + ApplicationName = "name" + }; + sut.SetupNewsletter(customization); + + Assert.That("url", Is.EqualTo(sut.ApplicationUrl)); + Assert.That("name", Is.EqualTo(sut.ApplicationName)); + } + + [Test] + public void MusicNotificationTests() + { + var notificationOptions = new NotificationOptions(); + var req = F.Build() + .With(x => x.RequestType, RequestType.Album) + .With(x => x.Available, true) + .Create(); + var customization = new CustomizationSettings + { + ApplicationUrl = "url", + ApplicationName = "name" + }; + var userPrefs = new UserNotificationPreferences(); + sut.Setup(notificationOptions, req, customization, userPrefs); + + Assert.That(req.Id.ToString(), Is.EqualTo(sut.RequestId)); + Assert.That(req.ForeignArtistId.ToString(), Is.EqualTo(sut.ProviderId)); + Assert.That(req.Title.ToString(), Is.EqualTo(sut.Title)); + Assert.That(req.RequestedUser.UserName, Is.EqualTo(sut.RequestedUser)); + Assert.That(req.RequestedUser.Alias, Is.EqualTo(sut.Alias)); + Assert.That(req.RequestedDate.ToString("D"), Is.EqualTo(sut.RequestedDate)); + Assert.That("Album", Is.EqualTo(sut.Type)); + Assert.That(req.ReleaseDate.Year.ToString(), Is.EqualTo(sut.Year)); + Assert.That(req.DeniedReason, Is.EqualTo(sut.DenyReason)); + Assert.That(req.MarkedAsAvailable?.ToString("D"), Is.EqualTo(sut.AvailableDate)); + Assert.That(req.Cover, Is.EqualTo(sut.PosterImage)); + Assert.That(req.DeniedReason, Is.EqualTo(sut.DenyReason)); + Assert.That(req.RequestedUser.Alias, Is.EqualTo(sut.UserPreference)); + Assert.That(string.Empty, Is.EqualTo(sut.AdditionalInformation)); + Assert.That("Available", Is.EqualTo(sut.RequestStatus)); + Assert.That("url", Is.EqualTo(sut.ApplicationUrl)); + Assert.That("name", Is.EqualTo(sut.ApplicationName)); + } + + [TestCaseSource(nameof(RequestStatusData))] + public string MusicNotificationTests_RequestStatus(bool available, bool denied, bool approved) + { + var notificationOptions = new NotificationOptions(); + var req = F.Build() + .With(x => x.RequestType, RequestType.Album) + .With(x => x.Available, available) + .With(x => x.Denied, denied) + .With(x => x.Approved, approved) + .Create(); + var customization = new CustomizationSettings(); + var userPrefs = new UserNotificationPreferences(); + sut.Setup(notificationOptions, req, customization, userPrefs); + return sut.RequestStatus; + } + + [Test] + public void TvNotificationTests() + { + var notificationOptions = new NotificationOptions(); + var req = F.Build() + .With(x => x.RequestType, RequestType.TvShow) + .With(x => x.Available, true) + .Create(); + var customization = new CustomizationSettings + { + ApplicationUrl = "url", + ApplicationName = "name" + }; + var userPrefs = new UserNotificationPreferences(); + sut.Setup(notificationOptions, req, customization, userPrefs); + + Assert.That(req.Id.ToString(), Is.EqualTo(sut.RequestId)); + Assert.That(req.ParentRequest.ExternalProviderId.ToString(), Is.EqualTo(sut.ProviderId)); + Assert.That(req.ParentRequest.Title.ToString(), Is.EqualTo(sut.Title)); + Assert.That(req.RequestedUser.UserName, Is.EqualTo(sut.RequestedUser)); + Assert.That(req.RequestedUser.Alias, Is.EqualTo(sut.Alias)); + Assert.That(req.RequestedDate.ToString("D"), Is.EqualTo(sut.RequestedDate)); + Assert.That("TV Show", Is.EqualTo(sut.Type)); + Assert.That(req.ParentRequest.Overview, Is.EqualTo(sut.Overview)); + Assert.That(req.ParentRequest.ReleaseDate.Year.ToString(), Is.EqualTo(sut.Year)); + Assert.That(req.DeniedReason, Is.EqualTo(sut.DenyReason)); + Assert.That(req.MarkedAsAvailable?.ToString("D"), Is.EqualTo(sut.AvailableDate)); + Assert.That("https://image.tmdb.org/t/p/w300/" + req.ParentRequest.PosterPath, Is.EqualTo(sut.PosterImage)); + Assert.That(req.DeniedReason, Is.EqualTo(sut.DenyReason)); + Assert.That(req.RequestedUser.Alias, Is.EqualTo(sut.UserPreference)); + Assert.That(null, Is.EqualTo(sut.AdditionalInformation)); + Assert.That("Available", Is.EqualTo(sut.RequestStatus)); + Assert.That("url", Is.EqualTo(sut.ApplicationUrl)); + Assert.That("name", Is.EqualTo(sut.ApplicationName)); + } + + [Test] + public void TvNotification_EpisodeList() + { + var episodeRequests = new List + { + new EpisodeRequests + { + EpisodeNumber = 1, + }, + new EpisodeRequests + { + EpisodeNumber = 2, + }, + new EpisodeRequests + { + EpisodeNumber = 3, + } + }; + var seasonRequests = new List + { + new SeasonRequests + { + Episodes = episodeRequests, + SeasonNumber = 1 + }, + new SeasonRequests + { + Episodes = episodeRequests, + SeasonNumber = 2 + }, + new SeasonRequests + { + Episodes = episodeRequests, + SeasonNumber = 3 + } + }; + + + var notificationOptions = new NotificationOptions(); + var req = F.Build() + .With(x => x.RequestType, RequestType.TvShow) + .With(x => x.Available, true) + .With(x => x.SeasonRequests, seasonRequests) + .Create(); + var customization = new CustomizationSettings + { + ApplicationUrl = "url", + ApplicationName = "name" + }; + var userPrefs = new UserNotificationPreferences(); + sut.Setup(notificationOptions, req, customization, userPrefs); + + Assert.That(sut.EpisodesList, Is.EqualTo("1,1,1,2,2,2,3,3,3")); + Assert.That(sut.SeasonsList, Is.EqualTo("1,2,3")); + + } + + [TestCaseSource(nameof(RequestStatusData))] + public string TvShowNotificationTests_RequestStatus(bool available, bool denied, bool approved) + { + var notificationOptions = new NotificationOptions(); + var req = F.Build() + .With(x => x.RequestType, RequestType.TvShow) + .With(x => x.Available, available) + .With(x => x.Denied, denied) + .With(x => x.Approved, approved) + .Create(); + var customization = new CustomizationSettings(); + var userPrefs = new UserNotificationPreferences(); + sut.Setup(notificationOptions, req, customization, userPrefs); + return sut.RequestStatus; + } + + + [Test] + public void EmailSetupTests() + { + var user = F.Create(); + var customization = new CustomizationSettings + { + ApplicationUrl = "url", + ApplicationName = "name" + }; + sut.Setup(user, customization); + + Assert.That(user.UserName, Is.EqualTo(sut.RequestedUser)); + Assert.That(user.UserName, Is.EqualTo(sut.UserName)); + Assert.That(user.UserAlias, Is.EqualTo(sut.Alias)); + Assert.That(sut.ApplicationUrl, Is.EqualTo("url")); + Assert.That(sut.ApplicationName, Is.EqualTo("name")); + } + + } +} diff --git a/src/Ombi.Notifications.Tests/Ombi.Notifications.Tests.csproj b/src/Ombi.Notifications.Tests/Ombi.Notifications.Tests.csproj index 469611ac8..4964e8078 100644 --- a/src/Ombi.Notifications.Tests/Ombi.Notifications.Tests.csproj +++ b/src/Ombi.Notifications.Tests/Ombi.Notifications.Tests.csproj @@ -5,6 +5,7 @@ + From 9703d370561bab1b128b65084dcaf66727e2666b Mon Sep 17 00:00:00 2001 From: Victor Usoltsev Date: Mon, 5 Apr 2021 00:00:19 +1200 Subject: [PATCH 04/39] User preferences page style improvements. --- .../user-preference.component.html | 34 ++++++------------- .../user-preference.component.scss | 17 ++++++++-- 2 files changed, 25 insertions(+), 26 deletions(-) diff --git a/src/Ombi/ClientApp/src/app/user-preferences/components/user-preference/user-preference.component.html b/src/Ombi/ClientApp/src/app/user-preferences/components/user-preference/user-preference.component.html index e4c7758f6..e5fbc6c2f 100644 --- a/src/Ombi/ClientApp/src/app/user-preferences/components/user-preference/user-preference.component.html +++ b/src/Ombi/ClientApp/src/app/user-preferences/components/user-preference/user-preference.component.html @@ -1,30 +1,26 @@
-
-
- -
-
-

{{username}} ({{user.emailAddress}})

- -
+
+ +

{{username}} + ({{user.emailAddress}}) +

-
-
-
+
+
User Type:
-
+
{{UserType[user.userType]}}
-
+
{{'UserPreferences.LanguageDescription' | translate}}
@@ -38,14 +34,9 @@
-
- -
-
- -
+
{{'UserPreferences.StreamingCountryDescription' | translate}}
@@ -58,13 +49,8 @@
-
-
- - -
diff --git a/src/Ombi/ClientApp/src/app/user-preferences/components/user-preference/user-preference.component.scss b/src/Ombi/ClientApp/src/app/user-preferences/components/user-preference/user-preference.component.scss index faaef4fd5..b7085d2dc 100644 --- a/src/Ombi/ClientApp/src/app/user-preferences/components/user-preference/user-preference.component.scss +++ b/src/Ombi/ClientApp/src/app/user-preferences/components/user-preference/user-preference.component.scss @@ -5,9 +5,22 @@ } .profile-img { - border-radius: 100%; width: 75px; + height: 75px; + margin-right: 20px; + border-radius: 100%; +} + +#usernameTitle { + margin: 0; + align-self: center; + overflow-wrap: anywhere; } + +.user-type-row { + padding-bottom: 1.25em; +} + .my-auto { margin-top: auto; margin-bottom: auto; @@ -19,5 +32,5 @@ } .tab-content { - margin-top: 1%; + margin-top: 1.5em; } \ No newline at end of file From 81f410d7828ecbe17f26241aa493b02c6f50b662 Mon Sep 17 00:00:00 2001 From: tidusjar Date: Sun, 4 Apr 2021 23:35:38 +0100 Subject: [PATCH 05/39] Ombi will now tell you when there is an update available in the settings, clicking that will also provide you with the changelog and links to manually download the binaries. Also removed references to the custom HTTP client implimentation and we now use the inbuilt IHttpClientFactory, this means we have removed the "Ignore Certificate Errors" option in Ombi as it's no longer needed. --- src/Ombi.Api/Api.cs | 4 +- src/Ombi.Api/IOmbiHttpClient.cs | 14 -- src/Ombi.Api/Ombi.Api.csproj | 1 + src/Ombi.Api/OmbiHttpClient.cs | 110 ------------- src/Ombi.DependencyInjection/IocExtensions.cs | 17 +- .../Ombi.DependencyInjection.csproj | 1 + .../Ombi/Interfaces/IOmbiAutomaticUpdater.cs | 2 +- .../Jobs/Ombi/OmbiAutomaticUpdater.cs | 6 +- src/Ombi.Schedule/Processor/AppVeyor.cs | 4 +- .../Processor/ChangeLogProcessor.cs | 153 +++--------------- .../Processor/IChangeLogProcessor.cs | 2 +- .../Settings/Models/OmbiSettings.cs | 1 - .../ClientApp/src/app/interfaces/ISettings.ts | 17 +- .../src/app/services/update.service.ts | 18 +++ .../app/settings/about/about.component.html | 4 +- .../src/app/settings/about/about.component.ts | 30 ++-- .../about/update-dialog.component.html | 28 ++++ .../about/update-dialog.component.scss | 40 +++++ .../settings/about/update-dialog.component.ts | 16 ++ .../src/app/settings/ombi/ombi.component.html | 5 - .../src/app/settings/ombi/ombi.component.ts | 1 - .../src/app/settings/settings.module.ts | 8 +- src/Ombi/Controllers/V1/JobController.cs | 12 +- src/Ombi/Controllers/V1/UpdateController.cs | 6 +- src/Ombi/Ombi.csproj | 2 + src/Ombi/Program.cs | 7 +- src/Ombi/databadse.json | 14 ++ 27 files changed, 220 insertions(+), 303 deletions(-) delete mode 100644 src/Ombi.Api/IOmbiHttpClient.cs delete mode 100644 src/Ombi.Api/OmbiHttpClient.cs create mode 100644 src/Ombi/ClientApp/src/app/services/update.service.ts create mode 100644 src/Ombi/ClientApp/src/app/settings/about/update-dialog.component.html create mode 100644 src/Ombi/ClientApp/src/app/settings/about/update-dialog.component.scss create mode 100644 src/Ombi/ClientApp/src/app/settings/about/update-dialog.component.ts create mode 100644 src/Ombi/databadse.json diff --git a/src/Ombi.Api/Api.cs b/src/Ombi.Api/Api.cs index 764da5a13..7a8e7678b 100644 --- a/src/Ombi.Api/Api.cs +++ b/src/Ombi.Api/Api.cs @@ -16,14 +16,14 @@ namespace Ombi.Api { public class Api : IApi { - public Api(ILogger log, IOmbiHttpClient client) + public Api(ILogger log, HttpClient client) { Logger = log; _client = client; } private ILogger Logger { get; } - private readonly IOmbiHttpClient _client; + private readonly HttpClient _client; public static readonly JsonSerializerSettings Settings = new JsonSerializerSettings { diff --git a/src/Ombi.Api/IOmbiHttpClient.cs b/src/Ombi.Api/IOmbiHttpClient.cs deleted file mode 100644 index 1f6ff9514..000000000 --- a/src/Ombi.Api/IOmbiHttpClient.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; - -namespace Ombi.Api -{ - public interface IOmbiHttpClient - { - Task SendAsync(HttpRequestMessage request); - Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken); - Task GetStringAsync(Uri requestUri); - } -} \ No newline at end of file diff --git a/src/Ombi.Api/Ombi.Api.csproj b/src/Ombi.Api/Ombi.Api.csproj index 98da29c2a..91821c83b 100644 --- a/src/Ombi.Api/Ombi.Api.csproj +++ b/src/Ombi.Api/Ombi.Api.csproj @@ -10,6 +10,7 @@ + diff --git a/src/Ombi.Api/OmbiHttpClient.cs b/src/Ombi.Api/OmbiHttpClient.cs deleted file mode 100644 index 978476cf7..000000000 --- a/src/Ombi.Api/OmbiHttpClient.cs +++ /dev/null @@ -1,110 +0,0 @@ -#region Copyright -// /************************************************************************ -// Copyright (c) 2017 Jamie Rees -// File: OmbiHttpClient.cs -// Created By: Jamie Rees -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// ************************************************************************/ -#endregion - -using System; -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; -using Ombi.Core.Settings; -using Ombi.Helpers; -using Ombi.Settings.Settings.Models; - -namespace Ombi.Api -{ - /// - /// The purpose of this class is simple, keep one instance of the HttpClient in play. - /// There are many articles related to when using multiple HttpClient's keeping the socket in a WAIT state - /// https://blogs.msdn.microsoft.com/alazarev/2017/12/29/disposable-finalizers-and-httpclient/ - /// https://aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/ - /// - public class OmbiHttpClient : IOmbiHttpClient - { - public OmbiHttpClient(ICacheService cache, ISettingsService s) - { - _cache = cache; - _settings = s; - _runtimeVersion = AssemblyHelper.GetRuntimeVersion(); - } - - private static HttpClient _client; - private static HttpMessageHandler _handler; - - private readonly ICacheService _cache; - private readonly ISettingsService _settings; - private readonly string _runtimeVersion; - - - public async Task SendAsync(HttpRequestMessage request) - { - await Setup(); - return await _client.SendAsync(request); - } - - public async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) - { - await Setup(); - return await _client.SendAsync(request, cancellationToken); - } - - public async Task GetStringAsync(Uri requestUri) - { - await Setup(); - return await _client.GetStringAsync(requestUri); - } - - private async Task Setup() - { - if (_client == null) - { - if (_handler == null) - { - // Get the handler - _handler = await GetHandler(); - } - _client = new HttpClient(_handler); - _client.DefaultRequestHeaders.Add("User-Agent", $"Ombi/{_runtimeVersion} (https://ombi.io/)"); - } - } - - private async Task GetHandler() - { - if (_cache == null) - { - return new HttpClientHandler(); - } - var settings = await _cache.GetOrAdd(CacheKeys.OmbiSettings, async () => await _settings.GetSettingsAsync(), DateTime.Now.AddHours(1)); - if (settings.IgnoreCertificateErrors) - { - return new HttpClientHandler - { - ServerCertificateCustomValidationCallback = (message, certificate2, arg3, arg4) => true, - }; - } - return new HttpClientHandler(); - } - } -} \ No newline at end of file diff --git a/src/Ombi.DependencyInjection/IocExtensions.cs b/src/Ombi.DependencyInjection/IocExtensions.cs index ef9c9230c..73a69203a 100644 --- a/src/Ombi.DependencyInjection/IocExtensions.cs +++ b/src/Ombi.DependencyInjection/IocExtensions.cs @@ -23,7 +23,6 @@ using Ombi.Notifications; using Ombi.Schedule; using Ombi.Schedule.Jobs; using Ombi.Settings.Settings; -using Ombi.Store.Context; using Ombi.Store.Repository; using Ombi.Notifications.Agents; using Ombi.Schedule.Jobs.Radarr; @@ -68,6 +67,8 @@ using Ombi.Api.MusicBrainz; using Ombi.Api.Twilio; using Ombi.Api.CloudService; using Ombi.Api.RottenTomatoes; +using System.Net.Http; +using Microsoft.Extensions.Logging; namespace Ombi.DependencyInjection { @@ -119,14 +120,24 @@ namespace Ombi.DependencyInjection public static void RegisterHttp(this IServiceCollection services) { + var runtimeVersion = AssemblyHelper.GetRuntimeVersion(); services.AddSingleton(); services.AddScoped(sp => sp.GetService().HttpContext.User); + services.AddHttpClient("OmbiClient", client => + { + client.DefaultRequestHeaders.Add("User-Agent", $"Ombi/{runtimeVersion} (https://ombi.io/)"); + }).ConfigurePrimaryHttpMessageHandler(() => + { + var httpClientHandler = new HttpClientHandler(); + httpClientHandler.ServerCertificateCustomValidationCallback = (message, certificate2, arg3, arg4) => true; + + return httpClientHandler; + }); } public static void RegisterApi(this IServiceCollection services) { - services.AddScoped(); - services.AddScoped(); // https://blogs.msdn.microsoft.com/alazarev/2017/12/29/disposable-finalizers-and-httpclient/ + services.AddScoped(s => new Api.Api(s.GetRequiredService>(), s.GetRequiredService().CreateClient("OmbiClient"))); services.AddTransient(); services.AddTransient(); services.AddTransient(); diff --git a/src/Ombi.DependencyInjection/Ombi.DependencyInjection.csproj b/src/Ombi.DependencyInjection/Ombi.DependencyInjection.csproj index ed1e9e4a2..49d1dd194 100644 --- a/src/Ombi.DependencyInjection/Ombi.DependencyInjection.csproj +++ b/src/Ombi.DependencyInjection/Ombi.DependencyInjection.csproj @@ -13,6 +13,7 @@ + diff --git a/src/Ombi.Schedule/Jobs/Ombi/Interfaces/IOmbiAutomaticUpdater.cs b/src/Ombi.Schedule/Jobs/Ombi/Interfaces/IOmbiAutomaticUpdater.cs index 85765b6c6..4c856c9cd 100644 --- a/src/Ombi.Schedule/Jobs/Ombi/Interfaces/IOmbiAutomaticUpdater.cs +++ b/src/Ombi.Schedule/Jobs/Ombi/Interfaces/IOmbiAutomaticUpdater.cs @@ -5,6 +5,6 @@ namespace Ombi.Schedule.Jobs.Ombi public interface IOmbiAutomaticUpdater : IBaseJob { string[] GetVersion(); - Task UpdateAvailable(string branch, string currentVersion); + Task UpdateAvailable(string currentVersion); } } \ No newline at end of file diff --git a/src/Ombi.Schedule/Jobs/Ombi/OmbiAutomaticUpdater.cs b/src/Ombi.Schedule/Jobs/Ombi/OmbiAutomaticUpdater.cs index e44728698..c2cf42441 100644 --- a/src/Ombi.Schedule/Jobs/Ombi/OmbiAutomaticUpdater.cs +++ b/src/Ombi.Schedule/Jobs/Ombi/OmbiAutomaticUpdater.cs @@ -49,10 +49,10 @@ namespace Ombi.Schedule.Jobs.Ombi var productArray = productVersion.Split('-'); return productArray; } - public async Task UpdateAvailable(string branch, string currentVersion) + public async Task UpdateAvailable(string currentVersion) { - var updates = await Processor.Process(branch); + var updates = await Processor.Process(); var serverVersion = updates.UpdateVersionString; return !serverVersion.Equals(currentVersion, StringComparison.CurrentCultureIgnoreCase); @@ -88,7 +88,7 @@ namespace Ombi.Schedule.Jobs.Ombi Logger.LogDebug(LoggingEvents.Updater, "Looking for updates now"); //TODO this fails because the branch = featureupdater when it should be feature/updater - var updates = await Processor.Process(branch); + var updates = await Processor.Process(); Logger.LogDebug(LoggingEvents.Updater, "Updates: {0}", updates); diff --git a/src/Ombi.Schedule/Processor/AppVeyor.cs b/src/Ombi.Schedule/Processor/AppVeyor.cs index 6bb4e001c..5a760c50c 100644 --- a/src/Ombi.Schedule/Processor/AppVeyor.cs +++ b/src/Ombi.Schedule/Processor/AppVeyor.cs @@ -109,8 +109,8 @@ namespace Ombi.Core.Processor public string UpdateVersionString { get; set; } public int UpdateVersion { get; set; } public DateTime UpdateDate { get; set; } - - public List ChangeLogs { get; set; } + public bool UpdateAvailable { get; set; } + public string ChangeLogs { get; set; } public List Downloads { get; set; } } diff --git a/src/Ombi.Schedule/Processor/ChangeLogProcessor.cs b/src/Ombi.Schedule/Processor/ChangeLogProcessor.cs index a34bd89e1..89a6519e6 100644 --- a/src/Ombi.Schedule/Processor/ChangeLogProcessor.cs +++ b/src/Ombi.Schedule/Processor/ChangeLogProcessor.cs @@ -16,94 +16,41 @@ namespace Ombi.Schedule.Processor { public class ChangeLogProcessor : IChangeLogProcessor { - public ChangeLogProcessor(IApi api, IOmbiHttpClient client) + public ChangeLogProcessor(IApi api, IHttpClientFactory client) { _api = api; - _client = client; + _client = client.CreateClient("OmbiClient"); } private readonly IApi _api; - private readonly IOmbiHttpClient _client; + private readonly HttpClient _client; private const string _changeLogUrl = "https://raw.githubusercontent.com/tidusjar/Ombi/{0}/CHANGELOG.md"; private const string AppveyorApiUrl = "https://ci.appveyor.com/api"; private string ChangeLogUrl(string branch) => string.Format(_changeLogUrl, branch); - public async Task Process(string branch) + public async Task Process() { - var masterBranch = branch.Equals("master", StringComparison.CurrentCultureIgnoreCase); - string githubChangeLog; - - githubChangeLog = await _client.GetStringAsync(new Uri(ChangeLogUrl(branch))); - - - var html = Markdown.ToHtml(githubChangeLog); - - - var doc = new HtmlDocument(); - doc.LoadHtml(html); - - HtmlNode latestRelease; - if (masterBranch) - { - latestRelease = doc.DocumentNode.Descendants("h2") - .FirstOrDefault(x => x.InnerText != "(unreleased)"); - } - else - { - latestRelease = doc.DocumentNode.Descendants("h2") - .FirstOrDefault(x => x.InnerText == "(unreleased)"); - - if (latestRelease == null) - { - latestRelease = doc.DocumentNode.Descendants("h2") - .FirstOrDefault(x => x.InnerText != "(unreleased)"); - } - } - - var newFeatureList = latestRelease.NextSibling.NextSibling.NextSibling.NextSibling; - var featuresString = newFeatureList.ChildNodes.Where(x => x.Name != "#text").Select(x => x.InnerText.Replace("\\n", "")).ToList(); - var fixes = newFeatureList.NextSibling.NextSibling.NextSibling.NextSibling; - var fixesString = fixes.ChildNodes.Where(x => x.Name != "#text").Select(x => x.InnerText.Replace("\\n", "")).ToList(); - - // Cleanup - var featuresList = featuresString.Distinct().ToList(); - var fixesList = fixesString.Distinct().ToList(); - - // Get release var release = new Release { - Version = latestRelease.InnerText, - Features = featuresList, - Fixes = fixesList, Downloads = new List() }; - if (masterBranch) - { - var releaseTag = latestRelease.InnerText.Substring(0, 9); - await GetGitubRelease(release, releaseTag); - } - else - { - // Get AppVeyor - await GetAppVeyorRelease(release, branch); - } - - - return TransformUpdate(release,!masterBranch); - + await GetGitubRelease(release); + + return TransformUpdate(release); } - private UpdateModel TransformUpdate(Release release, bool develop) + private UpdateModel TransformUpdate(Release release) { var newUpdate = new UpdateModel { - UpdateVersionString = develop ? release.Version : release.Version.Substring(1,8), - UpdateVersion = release.Version == "(unreleased)" ? 0 : int.Parse(release.Version.Substring(1, 5).Replace(".", "")), + UpdateVersionString = release.Version, + UpdateVersion = int.Parse(release.Version.Substring(1, 5).Replace(".", "")), UpdateDate = DateTime.Now, - ChangeLogs = new List(), - Downloads = new List() - }; + ChangeLogs = release.Description, + Downloads = new List(), + UpdateAvailable = release.Version != AssemblyHelper.GetRuntimeVersion() + }; foreach (var dl in release.Downloads) { @@ -114,75 +61,16 @@ namespace Ombi.Schedule.Processor }); } - foreach (var f in release.Features) - { - var change = new ChangeLog - { - Descripion = f, - Type = "New", - }; - - newUpdate.ChangeLogs.Add(change); - } - - foreach (var f in release.Fixes) - { - var change = new ChangeLog - { - Descripion = f, - Type = "Fixed", - }; - - newUpdate.ChangeLogs.Add(change); - } - return newUpdate; } - private async Task GetAppVeyorRelease(Release release, string branch) + private async Task GetGitubRelease(Release release) { - var request = new Request($"/projects/tidusjar/requestplex/branch/{branch}", AppVeyorApi.AppveyorApiUrl, HttpMethod.Get); - request.ApplicationJsonContentType(); + var client = new GitHubClient(Octokit.ProductHeaderValue.Parse("OmbiV4")); - var builds = await _api.Request(request); - var jobId = builds.build.jobs.FirstOrDefault()?.jobId ?? string.Empty; + var releases = await client.Repository.Release.GetAll("ombi-app", "ombi"); + var latest = releases.OrderByDescending(x => x.CreatedAt).FirstOrDefault(); - if (builds.build.finished == DateTime.MinValue || builds.build.status.Equals("failed")) - { - return; - } - release.Version = builds.build.version; - // get the artifacts - request = new Request($"/buildjobs/{jobId}/artifacts", AppVeyorApi.AppveyorApiUrl, HttpMethod.Get); - request.ApplicationJsonContentType(); - - var artifacts = await _api.Request>(request); - - foreach (var item in artifacts) - { - var d = new Downloads - { - Name = item.fileName, - Url = $"{AppveyorApiUrl}/buildjobs/{jobId}/artifacts/{item.fileName}" - }; - release.Downloads.Add(d); - } - } - - private async Task GetGitubRelease(Release release, string releaseTag) - { - var client = new GitHubClient(Octokit.ProductHeaderValue.Parse("OmbiV3")); - - var releases = await client.Repository.Release.GetAll("tidusjar", "ombi"); - var latest = releases.FirstOrDefault(x => x.TagName.Equals(releaseTag, StringComparison.InvariantCultureIgnoreCase)); - if (latest.Name.Contains("V2", CompareOptions.IgnoreCase)) - { - latest = null; - } - if (latest == null) - { - latest = releases.OrderByDescending(x => x.CreatedAt).FirstOrDefault(); - } foreach (var item in latest.Assets) { var d = new Downloads @@ -192,6 +80,8 @@ namespace Ombi.Schedule.Processor }; release.Downloads.Add(d); } + release.Description = Markdown.ToHtml(latest.Body); + release.Version = latest.TagName; } } public class Release @@ -199,8 +89,7 @@ namespace Ombi.Schedule.Processor public string Version { get; set; } public string CheckinVersion { get; set; } public List Downloads { get; set; } - public List Features { get; set; } - public List Fixes { get; set; } + public string Description { get; set; } } public class Downloads diff --git a/src/Ombi.Schedule/Processor/IChangeLogProcessor.cs b/src/Ombi.Schedule/Processor/IChangeLogProcessor.cs index 97780245f..434faedd2 100644 --- a/src/Ombi.Schedule/Processor/IChangeLogProcessor.cs +++ b/src/Ombi.Schedule/Processor/IChangeLogProcessor.cs @@ -4,6 +4,6 @@ namespace Ombi.Core.Processor { public interface IChangeLogProcessor { - Task Process(string branch); + Task Process(); } } \ No newline at end of file diff --git a/src/Ombi.Settings/Settings/Models/OmbiSettings.cs b/src/Ombi.Settings/Settings/Models/OmbiSettings.cs index 36b540026..6c622f2ce 100644 --- a/src/Ombi.Settings/Settings/Models/OmbiSettings.cs +++ b/src/Ombi.Settings/Settings/Models/OmbiSettings.cs @@ -6,7 +6,6 @@ public bool CollectAnalyticData { get; set; } public bool Wizard { get; set; } public string ApiKey { get; set; } - public bool IgnoreCertificateErrors { get; set; } public bool DoNotSendNotificationsForAutoApprove { get; set; } public bool HideRequestsUsers { get; set; } public bool DisableHealthChecks { get; set; } diff --git a/src/Ombi/ClientApp/src/app/interfaces/ISettings.ts b/src/Ombi/ClientApp/src/app/interfaces/ISettings.ts index c5b29adff..7fa710248 100644 --- a/src/Ombi/ClientApp/src/app/interfaces/ISettings.ts +++ b/src/Ombi/ClientApp/src/app/interfaces/ISettings.ts @@ -12,7 +12,6 @@ export interface IOmbiSettings extends ISettings { collectAnalyticData: boolean; wizard: boolean; apiKey: string; - ignoreCertificateErrors: boolean; doNotSendNotificationsForAutoApprove: boolean; hideRequestsUsers: boolean; defaultLanguageCode: string; @@ -285,3 +284,19 @@ export interface ITheMovieDbSettings extends ISettings { showAdultMovies: boolean; excludedKeywordIds: number[]; } + +export interface IUpdateModel +{ + updateVersionString: string; + updateVersion: number; + updateDate: Date, + updateAvailable: boolean; + changeLogs: string; + downloads: IUpdateDonloads[]; +} + +export interface IUpdateDonloads +{ + name: string; + url: string +} diff --git a/src/Ombi/ClientApp/src/app/services/update.service.ts b/src/Ombi/ClientApp/src/app/services/update.service.ts new file mode 100644 index 000000000..d1e84cfaf --- /dev/null +++ b/src/Ombi/ClientApp/src/app/services/update.service.ts @@ -0,0 +1,18 @@ +import { PlatformLocation, APP_BASE_HREF } from "@angular/common"; +import { Injectable, Inject } from "@angular/core"; + +import { HttpClient } from "@angular/common/http"; +import { Observable } from "rxjs"; + +import { ServiceHelpers } from "./service.helpers"; +import { IUpdateModel } from "../interfaces"; + +@Injectable() +export class UpdateService extends ServiceHelpers { + constructor(http: HttpClient, @Inject(APP_BASE_HREF) href:string) { + super(http, "/api/v1/Update/", href); + } + public checkForUpdate(): Observable { + return this.http.get(`${this.url}`, {headers: this.headers}); + } +} diff --git a/src/Ombi/ClientApp/src/app/settings/about/about.component.html b/src/Ombi/ClientApp/src/app/settings/about/about.component.html index e6eb05cb4..f4872eda7 100644 --- a/src/Ombi/ClientApp/src/app/settings/about/about.component.html +++ b/src/Ombi/ClientApp/src/app/settings/about/about.component.html @@ -13,8 +13,8 @@
Version
-
{{about.version}} (New Update Available)
+
{{about.version}} (New Update Available)
diff --git a/src/Ombi/ClientApp/src/app/shared/admin-request-dialog/admin-request-dialog.component.ts b/src/Ombi/ClientApp/src/app/shared/admin-request-dialog/admin-request-dialog.component.ts index 8e16dd87a..acf6560b9 100644 --- a/src/Ombi/ClientApp/src/app/shared/admin-request-dialog/admin-request-dialog.component.ts +++ b/src/Ombi/ClientApp/src/app/shared/admin-request-dialog/admin-request-dialog.component.ts @@ -3,8 +3,8 @@ import { FormBuilder, FormGroup } from "@angular/forms"; import { MatDialogRef, MAT_DIALOG_DATA } from "@angular/material/dialog"; import { Observable } from "rxjs"; import { startWith, map } from "rxjs/operators"; -import { IRadarrProfile, IRadarrRootFolder, ISonarrProfile, ISonarrRootFolder, IUserDropdown, RequestType } from "../../interfaces"; -import { IdentityService, MessageService, RadarrService, RequestService, SonarrService } from "../../services"; +import { ILanguageProfiles, IRadarrProfile, IRadarrRootFolder, ISonarrProfile, ISonarrRootFolder, ISonarrSettings, IUserDropdown, RequestType } from "../../interfaces"; +import { IdentityService, MessageService, RadarrService, RequestService, SettingsService, SonarrService } from "../../services"; import { RequestServiceV2 } from "../../services/requestV2.service"; export interface IAdminRequestDialogData { @@ -23,6 +23,7 @@ export class AdminRequestDialogComponent implements OnInit { @Inject(MAT_DIALOG_DATA) public data: IAdminRequestDialogData, private identityService: IdentityService, private sonarrService: SonarrService, + private settingsService: SettingsService, private radarrService: RadarrService, private fb: FormBuilder ) {} @@ -39,6 +40,7 @@ export class AdminRequestDialogComponent implements OnInit { public sonarrProfiles: ISonarrProfile[]; public sonarrRootFolders: ISonarrRootFolder[]; + public sonarrLanguageProfiles: ILanguageProfiles[]; public radarrProfiles: IRadarrProfile[]; public radarrRootFolders: IRadarrRootFolder[]; @@ -48,6 +50,7 @@ export class AdminRequestDialogComponent implements OnInit { username: [null], sonarrPathId: [null], sonarrFolderId: [null], + sonarrLanguageId: [null], radarrPathId: [null], radarrFolderId: [null] }) @@ -62,6 +65,13 @@ export class AdminRequestDialogComponent implements OnInit { if (this.data.type === RequestType.tvShow) { this.sonarrEnabled = await this.sonarrService.isEnabled(); if (this.sonarrEnabled) { + this.settingsService.getSonarr().subscribe((settings: ISonarrSettings) => { + if (settings.v3) { + this.sonarrService.getV3LanguageProfiles(settings).subscribe((profiles: ILanguageProfiles[]) => { + this.sonarrLanguageProfiles = profiles; + }) + } + }); this.sonarrService.getQualityProfilesWithoutSettings().subscribe(c => { this.sonarrProfiles = c; }); @@ -106,6 +116,7 @@ export class AdminRequestDialogComponent implements OnInit { model.radarrRootFolderTitle = this.radarrRootFolders?.filter(x => x.id == model.radarrFolderId)[0]?.path; model.sonarrRootFolderTitle = this.sonarrRootFolders?.filter(x => x.id == model.sonarrFolderId)[0]?.path; model.sonarrQualityOverrideTitle = this.sonarrProfiles?.filter(x => x.id == model.sonarrPathId)[0]?.name; + model.sonarrLanguageProfileTitle = this.sonarrLanguageProfiles?.filter(x => x.id == model.sonarrLanguageId)[0]?.name; this.dialogRef.close(model); } } diff --git a/src/Ombi/ClientApp/src/app/shared/episode-request/episode-request.component.ts b/src/Ombi/ClientApp/src/app/shared/episode-request/episode-request.component.ts index f6d7a6e92..146f31269 100644 --- a/src/Ombi/ClientApp/src/app/shared/episode-request/episode-request.component.ts +++ b/src/Ombi/ClientApp/src/app/shared/episode-request/episode-request.component.ts @@ -66,6 +66,7 @@ export class EpisodeRequestComponent { viewModel.requestOnBehalf = result.username?.id; viewModel.qualityPathOverride = result?.sonarrPathId; viewModel.rootFolderOverride = result?.sonarrFolderId; + viewModel.languageProfile = result?.sonarrLanguageId; const requestResult = await this.requestService.requestTv(viewModel).toPromise(); this.postRequest(requestResult); diff --git a/src/Ombi/Controllers/External/TheMovieDbController.cs b/src/Ombi/Controllers/V1/External/TheMovieDbController.cs similarity index 100% rename from src/Ombi/Controllers/External/TheMovieDbController.cs rename to src/Ombi/Controllers/V1/External/TheMovieDbController.cs diff --git a/src/Ombi/Ombi.csproj b/src/Ombi/Ombi.csproj index 857d7e11b..48681e423 100644 --- a/src/Ombi/Ombi.csproj +++ b/src/Ombi/Ombi.csproj @@ -6,8 +6,8 @@ Latest - $(SemVer) - $(SemVer) + 4.0.1328.0 + 4.0.1328.0 $(FullVer) 3.1 @@ -97,6 +97,10 @@ + + + + diff --git a/src/Ombi/wwwroot/translations/en.json b/src/Ombi/wwwroot/translations/en.json index fab6213a7..eab1b18d0 100644 --- a/src/Ombi/wwwroot/translations/en.json +++ b/src/Ombi/wwwroot/translations/en.json @@ -259,6 +259,7 @@ "AutoApproveOptionsTvShort":"You can configure the request here, once requested it will be send to your DVR application! If the request is already in Sonarr, we will not change the root folder or quality profile if you set it!", "QualityProfilesSelect":"Select A Quality Profile", "RootFolderSelect":"Select A Root Folder", + "LanguageProfileSelect":"Select A Language Profile", "Status":"Status", "Availability":"Availability", "RequestStatus":"Request Status", From 560454565d82e599d11bcca1e93db2bbb18833b6 Mon Sep 17 00:00:00 2001 From: tidusjar Date: Sun, 11 Apr 2021 22:21:42 +0100 Subject: [PATCH 10/39] Added new Image posters API --- src/Ombi.Core/Engine/Interfaces/BaseEngine.cs | 5 ++ .../Engine/Interfaces/IMovieEngineV2.cs | 2 +- .../Engine/Interfaces/ITvSearchEngineV2.cs | 2 +- .../Engine/V2/MovieSearchEngineV2.cs | 4 +- src/Ombi.Core/Engine/V2/TvSearchEngineV2.cs | 4 +- .../Models/Search/SearchTvShowViewModel.cs | 3 -- src/Ombi/Controllers/V1/ImagesController.cs | 52 ++++++++++++++++++- 7 files changed, 62 insertions(+), 10 deletions(-) diff --git a/src/Ombi.Core/Engine/Interfaces/BaseEngine.cs b/src/Ombi.Core/Engine/Interfaces/BaseEngine.cs index 8ddf27656..6fd22bced 100644 --- a/src/Ombi.Core/Engine/Interfaces/BaseEngine.cs +++ b/src/Ombi.Core/Engine/Interfaces/BaseEngine.cs @@ -9,6 +9,7 @@ using Ombi.Store.Entities.Requests; using Ombi.Store.Entities; using Microsoft.EntityFrameworkCore; using Ombi.Core.Authentication; +using Ombi.Helpers; namespace Ombi.Core.Engine.Interfaces { @@ -29,6 +30,10 @@ namespace Ombi.Core.Engine.Interfaces private OmbiUser _user; protected async Task GetUser() { + if(!Username.HasValue()) + { + return null; + } var username = Username.ToUpper(); return _user ?? (_user = await UserManager.Users.FirstOrDefaultAsync(x => x.NormalizedUserName == username)); } diff --git a/src/Ombi.Core/Engine/Interfaces/IMovieEngineV2.cs b/src/Ombi.Core/Engine/Interfaces/IMovieEngineV2.cs index c4bd0aa0e..442359f0f 100644 --- a/src/Ombi.Core/Engine/Interfaces/IMovieEngineV2.cs +++ b/src/Ombi.Core/Engine/Interfaces/IMovieEngineV2.cs @@ -19,7 +19,7 @@ namespace Ombi.Core.Engine.Interfaces Task> NowPlayingMovies(int currentPosition, int amountToLoad); Task GetCollection(int collectionId, CancellationToken cancellationToken, string langCode = null); Task GetTvDbId(int theMovieDbId); - Task> PopularMovies(int currentlyLoaded, int toLoad, CancellationToken cancellationToken); + Task> PopularMovies(int currentlyLoaded, int toLoad, CancellationToken cancellationToken, string langCustomCode = null); Task> TopRatedMovies(int currentlyLoaded, int toLoad); Task> UpcomingMovies(int currentlyLoaded, int toLoad); Task GetMoviesByActor(int actorId, string langCode); diff --git a/src/Ombi.Core/Engine/Interfaces/ITvSearchEngineV2.cs b/src/Ombi.Core/Engine/Interfaces/ITvSearchEngineV2.cs index 2fc78b6bb..3e2d859c8 100644 --- a/src/Ombi.Core/Engine/Interfaces/ITvSearchEngineV2.cs +++ b/src/Ombi.Core/Engine/Interfaces/ITvSearchEngineV2.cs @@ -11,7 +11,7 @@ namespace Ombi.Core Task GetShowInformation(string tvdbid, CancellationToken token); Task GetShowByRequest(int requestId, CancellationToken token); Task> GetStreamInformation(int movieDbId, CancellationToken cancellationToken); - Task> Popular(int currentlyLoaded, int amountToLoad); + Task> Popular(int currentlyLoaded, int amountToLoad, string langCustomCode = null); Task> Anticipated(int currentlyLoaded, int amountToLoad); Task> Trending(int currentlyLoaded, int amountToLoad); } diff --git a/src/Ombi.Core/Engine/V2/MovieSearchEngineV2.cs b/src/Ombi.Core/Engine/V2/MovieSearchEngineV2.cs index 3714e58a8..e651cc719 100644 --- a/src/Ombi.Core/Engine/V2/MovieSearchEngineV2.cs +++ b/src/Ombi.Core/Engine/V2/MovieSearchEngineV2.cs @@ -124,9 +124,9 @@ namespace Ombi.Core.Engine.V2 /// Gets popular movies by paging /// /// - public async Task> PopularMovies(int currentlyLoaded, int toLoad, CancellationToken cancellationToken) + public async Task> PopularMovies(int currentlyLoaded, int toLoad, CancellationToken cancellationToken, string langCustomCode = null) { - var langCode = await DefaultLanguageCode(null); + var langCode = await DefaultLanguageCode(langCustomCode); var pages = PaginationHelper.GetNextPages(currentlyLoaded, toLoad, _theMovieDbMaxPageItems); diff --git a/src/Ombi.Core/Engine/V2/TvSearchEngineV2.cs b/src/Ombi.Core/Engine/V2/TvSearchEngineV2.cs index 9b9b40906..952c897d6 100644 --- a/src/Ombi.Core/Engine/V2/TvSearchEngineV2.cs +++ b/src/Ombi.Core/Engine/V2/TvSearchEngineV2.cs @@ -107,9 +107,9 @@ namespace Ombi.Core.Engine.V2 return await ProcessResult(mapped); } - public async Task> Popular(int currentlyLoaded, int amountToLoad) + public async Task> Popular(int currentlyLoaded, int amountToLoad, string langCustomCode = null) { - var langCode = await DefaultLanguageCode(null); + var langCode = await DefaultLanguageCode(langCustomCode); var pages = PaginationHelper.GetNextPages(currentlyLoaded, amountToLoad, ResultLimit); var results = new List(); diff --git a/src/Ombi.Core/Models/Search/SearchTvShowViewModel.cs b/src/Ombi.Core/Models/Search/SearchTvShowViewModel.cs index dfd038a91..1a3f47175 100644 --- a/src/Ombi.Core/Models/Search/SearchTvShowViewModel.cs +++ b/src/Ombi.Core/Models/Search/SearchTvShowViewModel.cs @@ -58,9 +58,6 @@ namespace Ombi.Core.Models.Search public bool PartlyAvailable { get; set; } public override RequestType Type => RequestType.TvShow; - /// - /// Only set on the images call - /// public string BackdropPath { get; set; } } } \ No newline at end of file diff --git a/src/Ombi/Controllers/V1/ImagesController.cs b/src/Ombi/Controllers/V1/ImagesController.cs index c119efe70..847cd8777 100644 --- a/src/Ombi/Controllers/V1/ImagesController.cs +++ b/src/Ombi/Controllers/V1/ImagesController.cs @@ -6,6 +6,7 @@ using Microsoft.Extensions.Options; using Ombi.Api.FanartTv; using Ombi.Config; using Ombi.Core; +using Ombi.Core.Engine.Interfaces; using Ombi.Helpers; using Ombi.Store.Repository; @@ -17,13 +18,16 @@ namespace Ombi.Controllers.V1 public class ImagesController : ControllerBase { public ImagesController(IFanartTvApi fanartTvApi, IApplicationConfigRepository config, - IOptions options, ICacheService c, IImageService imageService) + IOptions options, ICacheService c, IImageService imageService, + IMovieEngineV2 movieEngineV2, ITVSearchEngineV2 tVSearchEngineV2) { FanartTvApi = fanartTvApi; Config = config; Options = options.Value; _cache = c; _imageService = imageService; + _movieEngineV2 = movieEngineV2; + _tvSearchEngineV2 = tVSearchEngineV2; } private IFanartTvApi FanartTvApi { get; } @@ -31,6 +35,8 @@ namespace Ombi.Controllers.V1 private LandingPageBackground Options { get; } private readonly ICacheService _cache; private readonly IImageService _imageService; + private readonly IMovieEngineV2 _movieEngineV2; + private readonly ITVSearchEngineV2 _tvSearchEngineV2; [HttpGet("tv/{tvdbid}")] public async Task GetTvBanner(int tvdbid) @@ -61,6 +67,50 @@ namespace Ombi.Controllers.V1 return string.Empty; } + [HttpGet("poster")] + public async Task GetRandomPoster() + { + var key = await _cache.GetOrAdd(CacheKeys.FanartTv, async () => await Config.GetAsync(Store.Entities.ConfigurationTypes.FanartTv), DateTime.Now.AddDays(1)); + var rand = new Random(); + var val = rand.Next(1, 3); + if (val == 1) + { + var movies = (await _movieEngineV2.PopularMovies(0, 10, HttpContext.RequestAborted ,"en")).ToArray(); + var selectedMovieIndex = rand.Next(movies.Count()); + var movie = movies[selectedMovieIndex]; + + var images = await _cache.GetOrAdd($"{CacheKeys.FanartTv}movie{movie.Id}", async () => await FanartTvApi.GetMovieImages(movie.Id.ToString(), key.Value), DateTime.Now.AddDays(1)); + if (images == null) + { + return string.Empty; + } + + if (images.movieposter?.Any() ?? false) + { + var enImage = images.movieposter.Where(x => x.lang == "en").OrderByDescending(x => x.likes).Select(x => x.url).FirstOrDefault(); + if (enImage == null) + { + return images.movieposter.OrderByDescending(x => x.likes).Select(x => x.url).FirstOrDefault(); + } + return enImage; + } + + if (images.moviethumb?.Any() ?? false) + { + return images.moviethumb.OrderBy(x => x.likes).Select(x => x.url).FirstOrDefault(); + } + } + else + { + var tv = (await _tvSearchEngineV2.Popular(0, 10, "en")).ToArray(); + var selectedMovieIndex = rand.Next(tv.Count()); + var selected = tv[selectedMovieIndex]; + + return $"https://image.tmdb.org/t/p/original{selected.BackdropPath}"; + } + return ""; + } + [HttpGet("poster/movie/{movieDbId}")] public async Task GetMoviePoster(string movieDbId) { From 20c46ad027099a64114d453a6b57ff2db96f6b33 Mon Sep 17 00:00:00 2001 From: tidusjar Date: Mon, 12 Apr 2021 10:57:27 +0100 Subject: [PATCH 11/39] Fixed the bug where it stated Ombi was on the wrong version in the settings! --- src/Ombi/Ombi.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Ombi/Ombi.csproj b/src/Ombi/Ombi.csproj index 48681e423..63c4f5a87 100644 --- a/src/Ombi/Ombi.csproj +++ b/src/Ombi/Ombi.csproj @@ -6,8 +6,8 @@ Latest - 4.0.1328.0 - 4.0.1328.0 + $(SemVer) + $(SemVer) $(FullVer) 3.1 From 2a284ce75cd79ec74dbd384051c4566f7dfe38de Mon Sep 17 00:00:00 2001 From: tidusjar Date: Tue, 13 Apr 2021 10:49:20 +0100 Subject: [PATCH 12/39] Fixed the issue where TV Shows were not being hidden from the Discover section --- src/Ombi.Core/Engine/V2/TvSearchEngineV2.cs | 91 ++++++++++++------- .../Rule/Rules/Search/PlexAvailabilityRule.cs | 14 +++ src/Ombi.Mapping/Profiles/TvProfile.cs | 4 +- .../Models/MovieDbSearchResult.cs | 10 +- 4 files changed, 82 insertions(+), 37 deletions(-) diff --git a/src/Ombi.Core/Engine/V2/TvSearchEngineV2.cs b/src/Ombi.Core/Engine/V2/TvSearchEngineV2.cs index 952c897d6..a24878da7 100644 --- a/src/Ombi.Core/Engine/V2/TvSearchEngineV2.cs +++ b/src/Ombi.Core/Engine/V2/TvSearchEngineV2.cs @@ -22,6 +22,7 @@ using Microsoft.EntityFrameworkCore; using System.Threading; using Ombi.Api.TheMovieDb; using Ombi.Api.TheMovieDb.Models; +using System.Diagnostics; namespace Ombi.Core.Engine.V2 { @@ -69,39 +70,7 @@ namespace Ombi.Core.Engine.V2 { var seasonEpisodes = (await _movieApi.GetSeasonEpisodes(show.id, tvSeason.season_number, token)); - foreach (var episode in seasonEpisodes.episodes) - { - var season = mapped.SeasonRequests.FirstOrDefault(x => x.SeasonNumber == episode.season_number); - if (season == null) - { - var newSeason = new SeasonRequests - { - SeasonNumber = episode.season_number, - Overview = tvSeason.overview, - Episodes = new List() - }; - newSeason.Episodes.Add(new EpisodeRequests - { - //Url = episode...ToHttpsUrl(), - Title = episode.name, - AirDate = episode.air_date.HasValue() ? DateTime.Parse(episode.air_date) : DateTime.MinValue, - EpisodeNumber = episode.episode_number, - - }); - mapped.SeasonRequests.Add(newSeason); - } - else - { - // We already have the season, so just add the episode - season.Episodes.Add(new EpisodeRequests - { - //Url = e.url.ToHttpsUrl(), - Title = episode.name, - AirDate = episode.air_date.HasValue() ? DateTime.Parse(episode.air_date) : DateTime.MinValue, - EpisodeNumber = episode.episode_number, - }); - } - } + MapSeasons(mapped.SeasonRequests, tvSeason, seasonEpisodes); } return await ProcessResult(mapped); @@ -152,6 +121,7 @@ namespace Ombi.Core.Engine.V2 async () => await _movieApi.TopRatedTv(langCode, pagesToLoad.Page), DateTime.Now.AddHours(12)); results.AddRange(apiResult.Skip(pagesToLoad.Skip).Take(pagesToLoad.Take)); } + var processed = ProcessResults(results); return await processed; } @@ -177,22 +147,73 @@ namespace Ombi.Core.Engine.V2 return data; } - private async Task> ProcessResults(IEnumerable items) + private async Task> ProcessResults(List items) { var retVal = new List(); var settings = await _customization.GetSettingsAsync(); + foreach (var tvMazeSearch in items) { + var show = await Cache.GetOrAdd(nameof(GetShowInformation) + tvMazeSearch.Id.ToString(), + async () => await _movieApi.GetTVInfo(tvMazeSearch.Id.ToString()), DateTime.Now.AddHours(12)); + foreach (var tvSeason in show.seasons.Where(x => x.season_number != 0)) // skip the first season + { + var seasonEpisodes = await Cache.GetOrAdd("SeasonEpisodes" + show.id + tvSeason.season_number, async () => + { + return await _movieApi.GetSeasonEpisodes(show.id, tvSeason.season_number, CancellationToken.None); + }, DateTime.Now.AddHours(12)); + + MapSeasons(tvMazeSearch.SeasonRequests, tvSeason, seasonEpisodes); + } + var result = await ProcessResult(tvMazeSearch); - if (result == null || settings.HideAvailableFromDiscover && result.Available) + if (result == null || settings.HideAvailableFromDiscover && result.FullyAvailable) { continue; } retVal.Add(result); } + return retVal; } + private static void MapSeasons(List seasonRequests, Season tvSeason, SeasonDetails seasonEpisodes) + { + foreach (var episode in seasonEpisodes.episodes) + { + var season = seasonRequests.FirstOrDefault(x => x.SeasonNumber == episode.season_number); + if (season == null) + { + var newSeason = new SeasonRequests + { + SeasonNumber = episode.season_number, + Overview = tvSeason.overview, + Episodes = new List() + }; + newSeason.Episodes.Add(new EpisodeRequests + { + //Url = episode...ToHttpsUrl(), + Title = episode.name, + AirDate = episode.air_date.HasValue() ? DateTime.Parse(episode.air_date) : DateTime.MinValue, + EpisodeNumber = episode.episode_number, + + }); + seasonRequests.Add(newSeason); + } + else + { + // We already have the season, so just add the episode + season.Episodes.Add(new EpisodeRequests + { + //Url = e.url.ToHttpsUrl(), + Title = episode.name, + AirDate = episode.air_date.HasValue() ? DateTime.Parse(episode.air_date) : DateTime.MinValue, + EpisodeNumber = episode.episode_number, + }); + } + } + } + private async Task ProcessResult(T tvMazeSearch) { var item = _mapper.Map(tvMazeSearch); diff --git a/src/Ombi.Core/Rule/Rules/Search/PlexAvailabilityRule.cs b/src/Ombi.Core/Rule/Rules/Search/PlexAvailabilityRule.cs index 2a239d1d3..d0397cc15 100644 --- a/src/Ombi.Core/Rule/Rules/Search/PlexAvailabilityRule.cs +++ b/src/Ombi.Core/Rule/Rules/Search/PlexAvailabilityRule.cs @@ -25,6 +25,7 @@ namespace Ombi.Core.Rule.Rules.Search PlexServerContent item = null; var useImdb = false; var useTheMovieDb = false; + var useId = false; var useTvDb = false; if (obj.ImdbId.HasValue()) { @@ -36,6 +37,14 @@ namespace Ombi.Core.Rule.Rules.Search } if (item == null) { + if (obj.Id > 0) + { + item = await PlexContentRepository.Get(obj.Id.ToString()); + if (item != null) + { + useId = true; + } + } if (obj.TheMovieDbId.HasValue()) { item = await PlexContentRepository.Get(obj.TheMovieDbId); @@ -60,6 +69,11 @@ namespace Ombi.Core.Rule.Rules.Search if (item != null) { + if (useId) + { + obj.TheMovieDbId = obj.Id.ToString(); + useTheMovieDb = true; + } obj.Available = true; obj.PlexUrl = item.Url; obj.Quality = item.Quality; diff --git a/src/Ombi.Mapping/Profiles/TvProfile.cs b/src/Ombi.Mapping/Profiles/TvProfile.cs index 55f5ccf9e..0aceffc4f 100644 --- a/src/Ombi.Mapping/Profiles/TvProfile.cs +++ b/src/Ombi.Mapping/Profiles/TvProfile.cs @@ -81,7 +81,9 @@ namespace Ombi.Mapping.Profiles .ForMember(dest => dest.Rating, opts => opts.MapFrom(src => src.VoteAverage.ToString())) .ForMember(dest => dest.BackdropPath, opts => opts.MapFrom(src => src.PosterPath)) //.ForMember(dest => dest.Runtime, opts => opts.MapFrom(src => src.Runtime.ToString())) - .ForMember(dest => dest.Title, opts => opts.MapFrom(src => src.Title)); + .ForMember(dest => dest.Title, opts => opts.MapFrom(src => src.Title)) + .ForMember(dest => dest.SeasonRequests, opts => opts.MapFrom(src => src.SeasonRequests)) + ; //.ForMember(dest => dest.Status, opts => opts.MapFrom(src => TraktEnumHelper.GetDescription(src.Status))) //.ForMember(dest => dest.Trailer, // opts => opts.MapFrom(src => src.Trailer != null ? src.Trailer.ToString().ToHttpsUrl() : string.Empty)) diff --git a/src/Ombi.TheMovieDbApi/Models/MovieDbSearchResult.cs b/src/Ombi.TheMovieDbApi/Models/MovieDbSearchResult.cs index 0e4a8852f..e936578e9 100644 --- a/src/Ombi.TheMovieDbApi/Models/MovieDbSearchResult.cs +++ b/src/Ombi.TheMovieDbApi/Models/MovieDbSearchResult.cs @@ -1,4 +1,7 @@ -namespace Ombi.Api.TheMovieDb.Models +using Ombi.Store.Repository.Requests; +using System.Collections.Generic; + +namespace Ombi.Api.TheMovieDb.Models { public class MovieDbSearchResult { @@ -16,5 +19,10 @@ public int VoteCount { get; set; } public bool Video { get; set; } public float VoteAverage { get; set; } + + /// + /// Mapped Property and not set from the API + /// + public List SeasonRequests { get; set; } = new List(); } } \ No newline at end of file From 9c0d8f91e9144ce8ae2bfeb16502193b56512f95 Mon Sep 17 00:00:00 2001 From: tidusjar Date: Tue, 13 Apr 2021 11:00:17 +0100 Subject: [PATCH 13/39] Performance improvement coming from the previous change --- src/Ombi.Core/Engine/V2/TvSearchEngineV2.cs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/Ombi.Core/Engine/V2/TvSearchEngineV2.cs b/src/Ombi.Core/Engine/V2/TvSearchEngineV2.cs index a24878da7..56b0e8a22 100644 --- a/src/Ombi.Core/Engine/V2/TvSearchEngineV2.cs +++ b/src/Ombi.Core/Engine/V2/TvSearchEngineV2.cs @@ -154,16 +154,20 @@ namespace Ombi.Core.Engine.V2 foreach (var tvMazeSearch in items) { - var show = await Cache.GetOrAdd(nameof(GetShowInformation) + tvMazeSearch.Id.ToString(), - async () => await _movieApi.GetTVInfo(tvMazeSearch.Id.ToString()), DateTime.Now.AddHours(12)); - foreach (var tvSeason in show.seasons.Where(x => x.season_number != 0)) // skip the first season + if (settings.HideAvailableFromDiscover) { - var seasonEpisodes = await Cache.GetOrAdd("SeasonEpisodes" + show.id + tvSeason.season_number, async () => + // To hide, we need to know if it's fully available, the only way to do this is to lookup it's episodes to check if we have every episode + var show = await Cache.GetOrAdd(nameof(GetShowInformation) + tvMazeSearch.Id.ToString(), + async () => await _movieApi.GetTVInfo(tvMazeSearch.Id.ToString()), DateTime.Now.AddHours(12)); + foreach (var tvSeason in show.seasons.Where(x => x.season_number != 0)) // skip the first season { - return await _movieApi.GetSeasonEpisodes(show.id, tvSeason.season_number, CancellationToken.None); - }, DateTime.Now.AddHours(12)); + var seasonEpisodes = await Cache.GetOrAdd("SeasonEpisodes" + show.id + tvSeason.season_number, async () => + { + return await _movieApi.GetSeasonEpisodes(show.id, tvSeason.season_number, CancellationToken.None); + }, DateTime.Now.AddHours(12)); - MapSeasons(tvMazeSearch.SeasonRequests, tvSeason, seasonEpisodes); + MapSeasons(tvMazeSearch.SeasonRequests, tvSeason, seasonEpisodes); + } } var result = await ProcessResult(tvMazeSearch); From 9d8f8056bbf2c1b0a173667d8b3e96684cb405c4 Mon Sep 17 00:00:00 2001 From: Matt <927830+mattmattmatt@users.noreply.github.com> Date: Fri, 16 Apr 2021 18:51:07 -0700 Subject: [PATCH 14/39] Fix TMDB logo URL Previous value `{{baseUrl}}images/tmdb-logo.svg` leads to broken image requests, e.g. `server.com/ombiimage/tmdb-logo.svg`. We need to trail the `baseURL` with a `/` to ensure the correct path, e.g. `server.com/ombi/image/tmdb-logo.svg`. See also lines 9 and lines 14. --- .../movie/panels/movie-information-panel.component.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Ombi/ClientApp/src/app/media-details/components/movie/panels/movie-information-panel.component.html b/src/Ombi/ClientApp/src/app/media-details/components/movie/panels/movie-information-panel.component.html index 5ba86e631..6b0da8604 100644 --- a/src/Ombi/ClientApp/src/app/media-details/components/movie/panels/movie-information-panel.component.html +++ b/src/Ombi/ClientApp/src/app/media-details/components/movie/panels/movie-information-panel.component.html @@ -2,7 +2,7 @@
- {{movie.voteAverage | number:'1.0-1'}}/10 + {{movie.voteAverage | number:'1.0-1'}}/10 -
\ No newline at end of file +
From 61313ba861a60d673f9b9053636174cfbf95041d Mon Sep 17 00:00:00 2001 From: tidusjar Date: Tue, 20 Apr 2021 09:46:38 +0100 Subject: [PATCH 15/39] Added more context in the mobile notifications --- src/Ombi.Notifications/Agents/MobileNotification.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/Ombi.Notifications/Agents/MobileNotification.cs b/src/Ombi.Notifications/Agents/MobileNotification.cs index 6080acdf0..864453b71 100644 --- a/src/Ombi.Notifications/Agents/MobileNotification.cs +++ b/src/Ombi.Notifications/Agents/MobileNotification.cs @@ -57,6 +57,7 @@ namespace Ombi.Notifications.Agents var notification = new NotificationMessage { Message = parsed.Message, + Subject = "New Request", Data = GetNotificationData(parsed, NotificationType.NewRequest) }; @@ -76,6 +77,7 @@ namespace Ombi.Notifications.Agents var notification = new NotificationMessage { Message = parsed.Message, + Subject = "New Issue", Data = GetNotificationData(parsed, NotificationType.Issue) }; @@ -127,6 +129,7 @@ namespace Ombi.Notifications.Agents var notification = new NotificationMessage { Message = parsed.Message, + Subject = "Issue Resolved", Data = GetNotificationData(parsed, NotificationType.IssueResolved) }; @@ -149,6 +152,7 @@ namespace Ombi.Notifications.Agents var notification = new NotificationMessage { Message = parsed.Message, + Subject = "Request Error", Data = GetNotificationData(parsed, NotificationType.ItemAddedToFaultQueue) }; @@ -168,6 +172,7 @@ namespace Ombi.Notifications.Agents var notification = new NotificationMessage { Message = parsed.Message, + Subject = "Request Declined", Data = GetNotificationData(parsed, NotificationType.RequestDeclined) }; @@ -188,6 +193,7 @@ namespace Ombi.Notifications.Agents var notification = new NotificationMessage { Message = parsed.Message, + Subject = "Request Approved", Data = GetNotificationData(parsed, NotificationType.RequestApproved) }; @@ -212,6 +218,7 @@ namespace Ombi.Notifications.Agents var notification = new NotificationMessage { Message = parsed.Message, + Subject = "Request Available", Data = data }; // Send to user @@ -259,6 +266,7 @@ namespace Ombi.Notifications.Agents var notification = new NotificationMessage { Message = message, + Subject = "Test Notification" }; // Send to user var user = await _userManager.Users.Include(x => x.NotificationUserIds).FirstOrDefaultAsync(x => x.Id.Equals(model.UserId)); From 16b2b849d48afbd22a2f4368f5020175a0335000 Mon Sep 17 00:00:00 2001 From: tidusjar Date: Wed, 21 Apr 2021 12:28:32 +0100 Subject: [PATCH 16/39] Map the MediaServer URL for Tv Shows, so we can display the Plex button --- src/Ombi.Core/Engine/V2/MovieSearchEngineV2.cs | 1 + src/Ombi.Core/Engine/V2/TvSearchEngineV2.cs | 3 +++ 2 files changed, 4 insertions(+) diff --git a/src/Ombi.Core/Engine/V2/MovieSearchEngineV2.cs b/src/Ombi.Core/Engine/V2/MovieSearchEngineV2.cs index e651cc719..f131662be 100644 --- a/src/Ombi.Core/Engine/V2/MovieSearchEngineV2.cs +++ b/src/Ombi.Core/Engine/V2/MovieSearchEngineV2.cs @@ -371,6 +371,7 @@ namespace Ombi.Core.Engine.V2 mapped.Requested = movie.Requested; mapped.PlexUrl = movie.PlexUrl; mapped.EmbyUrl = movie.EmbyUrl; + mapped.JellyfinUrl = movie.JellyfinUrl; mapped.Subscribed = movie.Subscribed; mapped.ShowSubscribe = movie.ShowSubscribe; mapped.ReleaseDate = movie.ReleaseDate; diff --git a/src/Ombi.Core/Engine/V2/TvSearchEngineV2.cs b/src/Ombi.Core/Engine/V2/TvSearchEngineV2.cs index 56b0e8a22..9d97285df 100644 --- a/src/Ombi.Core/Engine/V2/TvSearchEngineV2.cs +++ b/src/Ombi.Core/Engine/V2/TvSearchEngineV2.cs @@ -241,6 +241,9 @@ namespace Ombi.Core.Engine.V2 item.Approved = oldModel.Approved; item.SeasonRequests = oldModel.SeasonRequests; item.RequestId = oldModel.RequestId; + item.PlexUrl = oldModel.PlexUrl; + item.EmbyUrl = oldModel.EmbyUrl; + item.JellyfinUrl = oldModel.JellyfinUrl; if (!string.IsNullOrEmpty(item.Images?.Medium)) { From acc886293dec5a746d2bd5f0bc7a033ef3176b2d Mon Sep 17 00:00:00 2001 From: Jamie Date: Mon, 26 Apr 2021 08:43:03 +0100 Subject: [PATCH 17/39] Update variables.yml --- .azuredevops/pipelines/templates/variables.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.azuredevops/pipelines/templates/variables.yml b/.azuredevops/pipelines/templates/variables.yml index 205e68eaa..e90ecc049 100644 --- a/.azuredevops/pipelines/templates/variables.yml +++ b/.azuredevops/pipelines/templates/variables.yml @@ -27,4 +27,4 @@ variables: value: "4.0.$(Build.BuildId)" - name: isMain - value: $[or(eq(variables['Build.SourceBranch'], 'refs/heads/develop'), eq(variables['Build.SourceBranch'], 'refs/heads/main'))] \ No newline at end of file + value: $[or(eq(variables['Build.SourceBranch'], 'refs/heads/develop'), eq(variables['Build.SourceBranch'], 'refs/heads/master'))] From 69923db39fb9791a0729fce6edb8eef5241f855b Mon Sep 17 00:00:00 2001 From: sirmarv <3598205+sir-marv@users.noreply.github.com> Date: Tue, 27 Apr 2021 07:50:57 -0400 Subject: [PATCH 18/39] Update WebhookApi.cs Fixes #2448. Forward slash is no longer appended to the URL. If a user wants a / at the end of the URL, they will have to enter it in the webhook baseUrl. --- src/Ombi.Api.Webhook/WebhookApi.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ombi.Api.Webhook/WebhookApi.cs b/src/Ombi.Api.Webhook/WebhookApi.cs index 8b6b35ca0..f2faaa18b 100644 --- a/src/Ombi.Api.Webhook/WebhookApi.cs +++ b/src/Ombi.Api.Webhook/WebhookApi.cs @@ -19,7 +19,7 @@ namespace Ombi.Api.Webhook public async Task PushAsync(string baseUrl, string accessToken, IDictionary parameters) { - var request = new Request("/", baseUrl, HttpMethod.Post); + var request = new Request("", baseUrl, HttpMethod.Post); if (!string.IsNullOrWhiteSpace(accessToken)) { From a578374588b4ca9df4eadf4c424e9d3d76c06874 Mon Sep 17 00:00:00 2001 From: tidusjar Date: Tue, 27 Apr 2021 20:43:08 +0100 Subject: [PATCH 19/39] TV results now respect the users set language --- src/Ombi.Core/Engine/V2/TvSearchEngineV2.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Ombi.Core/Engine/V2/TvSearchEngineV2.cs b/src/Ombi.Core/Engine/V2/TvSearchEngineV2.cs index 9d97285df..e02ae1c51 100644 --- a/src/Ombi.Core/Engine/V2/TvSearchEngineV2.cs +++ b/src/Ombi.Core/Engine/V2/TvSearchEngineV2.cs @@ -50,13 +50,14 @@ namespace Ombi.Core.Engine.V2 public async Task GetShowByRequest(int requestId, CancellationToken token) { var request = await RequestService.TvRequestService.Get().FirstOrDefaultAsync(x => x.Id == requestId); - return await GetShowInformation(request.ExternalProviderId.ToString(), token); // TODO + return await GetShowInformation(request.ExternalProviderId.ToString(), token); } public async Task GetShowInformation(string tvdbid, CancellationToken token) { - var show = await Cache.GetOrAdd(nameof(GetShowInformation) + tvdbid, - async () => await _movieApi.GetTVInfo(tvdbid), DateTime.Now.AddHours(12)); + var langCode = await DefaultLanguageCode(null); + var show = await Cache.GetOrAdd(nameof(GetShowInformation) + langCode + tvdbid, + async () => await _movieApi.GetTVInfo(tvdbid, langCode), DateTime.Now.AddHours(12)); if (show == null || show.name == null) { // We don't have enough information From 264568f3969c7fb3bd85ba3205938eeb6c54cf5e Mon Sep 17 00:00:00 2001 From: tidusjar Date: Tue, 27 Apr 2021 22:17:01 +0100 Subject: [PATCH 20/39] Added the ability to re-process requests --- .../Engine/Interfaces/IRequestEngine.cs | 2 ++ src/Ombi.Core/Engine/MovieRequestEngine.cs | 21 ++++++++++++++++++ src/Ombi.Core/Engine/TvRequestEngine.cs | 22 +++++++++++++++++++ src/Ombi.Store/Repository/BaseRepository.cs | 6 +++++ src/Ombi.Store/Repository/IRepository.cs | 2 ++ .../movie/movie-details.component.html | 1 + .../movie/movie-details.component.ts | 10 +++++++++ .../social-icons/social-icons.component.html | 4 ++++ .../social-icons/social-icons.component.ts | 5 +++++ .../tv-requests-panel.component.html | 1 + .../tv-requests-panel.component.ts | 19 +++++++++++++--- .../src/app/services/request.service.ts | 1 - .../src/app/services/requestV2.service.ts | 6 ++++- src/Ombi/Controllers/V2/RequestsController.cs | 18 +++++++++++++++ src/Ombi/wwwroot/translations/en.json | 3 ++- 15 files changed, 115 insertions(+), 6 deletions(-) diff --git a/src/Ombi.Core/Engine/Interfaces/IRequestEngine.cs b/src/Ombi.Core/Engine/Interfaces/IRequestEngine.cs index c8b7746f0..f4eeb6fc3 100644 --- a/src/Ombi.Core/Engine/Interfaces/IRequestEngine.cs +++ b/src/Ombi.Core/Engine/Interfaces/IRequestEngine.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Threading; using System.Threading.Tasks; using Ombi.Core.Models; using Ombi.Core.Models.Requests; @@ -24,5 +25,6 @@ namespace Ombi.Core.Engine.Interfaces Task UnSubscribeRequest(int requestId, RequestType type); Task SubscribeToRequest(int requestId, RequestType type); Task GetRemainingRequests(OmbiUser user = null); + Task ReProcessRequest(int requestId, CancellationToken cancellationToken); } } \ No newline at end of file diff --git a/src/Ombi.Core/Engine/MovieRequestEngine.cs b/src/Ombi.Core/Engine/MovieRequestEngine.cs index 00a97f766..8d54f0fc3 100644 --- a/src/Ombi.Core/Engine/MovieRequestEngine.cs +++ b/src/Ombi.Core/Engine/MovieRequestEngine.cs @@ -21,6 +21,7 @@ using Ombi.Settings.Settings.Models; using Ombi.Store.Entities.Requests; using Ombi.Store.Repository; using Ombi.Core.Models; +using System.Threading; namespace Ombi.Core.Engine { @@ -555,6 +556,11 @@ namespace Ombi.Core.Engine await NotificationHelper.Notify(request, NotificationType.RequestApproved); } + return await ProcessSendingMovie(request); + } + + private async Task ProcessSendingMovie(MovieRequests request) + { if (request.Approved) { var result = await Sender.Send(request); @@ -634,6 +640,21 @@ namespace Ombi.Core.Engine return await MovieRepository.GetAll().AnyAsync(x => x.RequestedUserId == userId); } + public async Task ReProcessRequest(int requestId, CancellationToken cancellationToken) + { + var request = await MovieRepository.Find(requestId); + if (request == null) + { + return new RequestEngineResult + { + Result = false, + ErrorMessage = "Request does not exist" + }; + } + + return await ProcessSendingMovie(request); + } + public async Task MarkUnavailable(int modelId) { var request = await MovieRepository.Find(modelId); diff --git a/src/Ombi.Core/Engine/TvRequestEngine.cs b/src/Ombi.Core/Engine/TvRequestEngine.cs index e332dde75..5eae0912c 100644 --- a/src/Ombi.Core/Engine/TvRequestEngine.cs +++ b/src/Ombi.Core/Engine/TvRequestEngine.cs @@ -25,6 +25,7 @@ using Ombi.Settings.Settings.Models; using Ombi.Store.Entities.Requests; using Ombi.Store.Repository; using Ombi.Core.Models; +using System.Threading; namespace Ombi.Core.Engine { @@ -896,6 +897,22 @@ namespace Ombi.Core.Engine } + public async Task ReProcessRequest(int requestId, CancellationToken cancellationToken) + { + var request = await TvRepository.GetChild().FirstOrDefaultAsync(x => x.Id == requestId, cancellationToken); + if (request == null) + { + return new RequestEngineResult + { + Result = false, + ErrorMessage = "Request does not exist" + }; + } + + return await ProcessSendingShow(request); + } + + private async Task AfterRequest(ChildRequests model, string requestOnBehalf) { var sendRuleResult = await RunSpecificRule(model, SpecificRules.CanSendNotification); @@ -913,6 +930,11 @@ namespace Ombi.Core.Engine EpisodeCount = model.SeasonRequests.Select(m => m.Episodes.Count).Sum(), }); + return await ProcessSendingShow(model); + } + + private async Task ProcessSendingShow(ChildRequests model) + { if (model.Approved) { // Autosend diff --git a/src/Ombi.Store/Repository/BaseRepository.cs b/src/Ombi.Store/Repository/BaseRepository.cs index 2908309ce..9d7d91447 100644 --- a/src/Ombi.Store/Repository/BaseRepository.cs +++ b/src/Ombi.Store/Repository/BaseRepository.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; +using System.Threading; using System.Threading.Tasks; using Microsoft.Data.Sqlite; using Microsoft.EntityFrameworkCore; @@ -28,6 +29,11 @@ namespace Ombi.Store.Repository return await _db.FindAsync(key); } + public async Task Find(object key, CancellationToken cancellationToken) + { + return await _db.FindAsync(new[] { key }, cancellationToken: cancellationToken); + } + public IQueryable GetAll() { return _db.AsQueryable(); diff --git a/src/Ombi.Store/Repository/IRepository.cs b/src/Ombi.Store/Repository/IRepository.cs index fd7dcc86d..b93b07d45 100644 --- a/src/Ombi.Store/Repository/IRepository.cs +++ b/src/Ombi.Store/Repository/IRepository.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; +using System.Threading; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Query; @@ -12,6 +13,7 @@ namespace Ombi.Store.Repository public interface IRepository where T : Entity { Task Find(object key); + Task Find(object key, CancellationToken cancellationToken); IQueryable GetAll(); Task FirstOrDefaultAsync(Expression> predicate); Task AddRange(IEnumerable content, bool save = true); diff --git a/src/Ombi/ClientApp/src/app/media-details/components/movie/movie-details.component.html b/src/Ombi/ClientApp/src/app/media-details/components/movie/movie-details.component.html index 928645f47..c0434980d 100644 --- a/src/Ombi/ClientApp/src/app/media-details/components/movie/movie-details.component.html +++ b/src/Ombi/ClientApp/src/app/media-details/components/movie/movie-details.component.html @@ -24,6 +24,7 @@ [type]="requestType" (openTrailer)="openDialog()" (onAdvancedOptions)="openAdvancedOptions()" + (onReProcessRequest)="reProcessRequest()" > diff --git a/src/Ombi/ClientApp/src/app/media-details/components/movie/movie-details.component.ts b/src/Ombi/ClientApp/src/app/media-details/components/movie/movie-details.component.ts index 3911d64ce..01c8a5783 100644 --- a/src/Ombi/ClientApp/src/app/media-details/components/movie/movie-details.component.ts +++ b/src/Ombi/ClientApp/src/app/media-details/components/movie/movie-details.component.ts @@ -190,6 +190,16 @@ export class MovieDetailsComponent { }); } + public reProcessRequest() { + this.requestService2.reprocessRequest(this.movieRequest.id, RequestType.movie).subscribe(result => { + if (result.result) { + this.messageService.send(result.message ? result.message : "Successfully Re-processed the request", "Ok"); + } else { + this.messageService.send(result.errorMessage, "Ok"); + } + }); + } + private loadBanner() { this.imageService.getMovieBanner(this.theMovidDbId.toString()).subscribe(x => { if (!this.movie.backdropPath) { diff --git a/src/Ombi/ClientApp/src/app/media-details/components/shared/social-icons/social-icons.component.html b/src/Ombi/ClientApp/src/app/media-details/components/shared/social-icons/social-icons.component.html index 60cad7bb0..f05792aa9 100644 --- a/src/Ombi/ClientApp/src/app/media-details/components/shared/social-icons/social-icons.component.html +++ b/src/Ombi/ClientApp/src/app/media-details/components/shared/social-icons/social-icons.component.html @@ -35,5 +35,9 @@ {{ 'MediaDetails.RadarrConfiguration' | translate}} {{ 'MediaDetails.SonarrConfiguration' | translate}} +
diff --git a/src/Ombi/ClientApp/src/app/media-details/components/shared/social-icons/social-icons.component.ts b/src/Ombi/ClientApp/src/app/media-details/components/shared/social-icons/social-icons.component.ts index 3a27fa723..bc504347b 100644 --- a/src/Ombi/ClientApp/src/app/media-details/components/shared/social-icons/social-icons.component.ts +++ b/src/Ombi/ClientApp/src/app/media-details/components/shared/social-icons/social-icons.component.ts @@ -26,6 +26,7 @@ export class SocialIconsComponent { @Output() openTrailer: EventEmitter = new EventEmitter(); @Output() onAdvancedOptions: EventEmitter = new EventEmitter(); + @Output() onReProcessRequest: EventEmitter = new EventEmitter(); public RequestType = RequestType; @@ -37,4 +38,8 @@ export class SocialIconsComponent { public openAdvancedOptions() { this.onAdvancedOptions.emit(); } + + public reProcessRequest() { + this.onReProcessRequest.emit(); + } } diff --git a/src/Ombi/ClientApp/src/app/media-details/components/tv/panels/tv-requests/tv-requests-panel.component.html b/src/Ombi/ClientApp/src/app/media-details/components/tv/panels/tv-requests/tv-requests-panel.component.html index 5db522024..8bca28fb5 100644 --- a/src/Ombi/ClientApp/src/app/media-details/components/tv/panels/tv-requests/tv-requests-panel.component.html +++ b/src/Ombi/ClientApp/src/app/media-details/components/tv/panels/tv-requests/tv-requests-panel.component.html @@ -70,6 +70,7 @@ +
diff --git a/src/Ombi/ClientApp/src/app/media-details/components/tv/panels/tv-requests/tv-requests-panel.component.ts b/src/Ombi/ClientApp/src/app/media-details/components/tv/panels/tv-requests/tv-requests-panel.component.ts index c7b0711b6..e51414902 100644 --- a/src/Ombi/ClientApp/src/app/media-details/components/tv/panels/tv-requests/tv-requests-panel.component.ts +++ b/src/Ombi/ClientApp/src/app/media-details/components/tv/panels/tv-requests/tv-requests-panel.component.ts @@ -4,6 +4,7 @@ import { RequestService } from "../../../../../services/request.service"; import { MessageService } from "../../../../../services"; import { MatDialog } from "@angular/material/dialog"; import { DenyDialogComponent } from "../../../shared/deny-dialog/deny-dialog.component"; +import { RequestServiceV2 } from "../../../../../services/requestV2.service"; @Component({ templateUrl: "./tv-requests-panel.component.html", @@ -16,7 +17,9 @@ export class TvRequestsPanelComponent { public displayedColumns: string[] = ['number', 'title', 'airDate', 'status']; - constructor(private requestService: RequestService, private messageService: MessageService, + constructor(private requestService: RequestService, + private requestService2: RequestServiceV2, + private messageService: MessageService, public dialog: MatDialog) { } @@ -83,9 +86,9 @@ export class TvRequestsPanelComponent { width: '250px', data: {requestId: request.id, requestType: RequestType.tvShow} }); - + dialogRef.afterClosed().subscribe(result => { - request.denied = true; + request.denied = true; request.seasonRequests.forEach((season) => { season.episodes.forEach((ep) => { ep.approved = false; @@ -93,4 +96,14 @@ export class TvRequestsPanelComponent { }); }); } + + public reProcessRequest(request: IChildRequests) { + this.requestService2.reprocessRequest(request.id, RequestType.tvShow).subscribe(result => { + if (result.result) { + this.messageService.send(result.message ? result.message : "Successfully Re-processed the request", "Ok"); + } else { + this.messageService.send(result.errorMessage, "Ok"); + } + }); + } } diff --git a/src/Ombi/ClientApp/src/app/services/request.service.ts b/src/Ombi/ClientApp/src/app/services/request.service.ts index 19a211f93..3fe60e5f3 100644 --- a/src/Ombi/ClientApp/src/app/services/request.service.ts +++ b/src/Ombi/ClientApp/src/app/services/request.service.ts @@ -187,5 +187,4 @@ export class RequestService extends ServiceHelpers { public removeAlbumRequest(request: number): any { return this.http.delete(`${this.url}music/${request}`, {headers: this.headers}); } - } diff --git a/src/Ombi/ClientApp/src/app/services/requestV2.service.ts b/src/Ombi/ClientApp/src/app/services/requestV2.service.ts index c6e155096..a7ebd6d0c 100644 --- a/src/Ombi/ClientApp/src/app/services/requestV2.service.ts +++ b/src/Ombi/ClientApp/src/app/services/requestV2.service.ts @@ -4,7 +4,7 @@ import { Injectable, Inject } from "@angular/core"; import { HttpClient } from "@angular/common/http"; import { Observable } from "rxjs"; import { ServiceHelpers } from "./service.helpers"; -import { IRequestsViewModel, IMovieRequests, IChildRequests, IMovieAdvancedOptions as IMediaAdvancedOptions, IRequestEngineResult, IAlbumRequest, ITvRequestViewModelV2 } from "../interfaces"; +import { IRequestsViewModel, IMovieRequests, IChildRequests, IMovieAdvancedOptions as IMediaAdvancedOptions, IRequestEngineResult, IAlbumRequest, ITvRequestViewModelV2, RequestType } from "../interfaces"; @Injectable() @@ -92,4 +92,8 @@ export class RequestServiceV2 extends ServiceHelpers { public requestTv(tv: ITvRequestViewModelV2): Observable { return this.http.post(`${this.url}TV/`, JSON.stringify(tv), {headers: this.headers}); } + + public reprocessRequest(requestId: number, type: RequestType): Observable { + return this.http.post(`${this.url}reprocess/${type}/${requestId}`, undefined, { headers: this.headers }); + } } diff --git a/src/Ombi/Controllers/V2/RequestsController.cs b/src/Ombi/Controllers/V2/RequestsController.cs index 0bbbd4128..67d206e0b 100644 --- a/src/Ombi/Controllers/V2/RequestsController.cs +++ b/src/Ombi/Controllers/V2/RequestsController.cs @@ -11,6 +11,7 @@ using System; using Ombi.Store.Entities; using System.Linq; using Microsoft.Extensions.Logging; +using Ombi.Attributes; namespace Ombi.Controllers.V2 { @@ -134,12 +135,14 @@ namespace Ombi.Controllers.V2 return await _tvRequestEngine.GetUnavailableRequests(count, position, sort, sortOrder); } + [PowerUser] [HttpPost("movie/advancedoptions")] public async Task UpdateAdvancedOptions([FromBody] MediaAdvancedOptions options) { return await _movieRequestEngine.UpdateAdvancedOptions(options); } + [PowerUser] [HttpPost("tv/advancedoptions")] public async Task UpdateTvAdvancedOptions([FromBody] MediaAdvancedOptions options) { @@ -198,6 +201,21 @@ namespace Ombi.Controllers.V2 return result; } + [PowerUser] + [HttpPost("reprocess/{type}/{requestId}")] + public async Task ReProcessRequest(RequestType type, int requestId) + { + switch (type) + { + case RequestType.TvShow: + return Ok(await _tvRequestEngine.ReProcessRequest(requestId, HttpContext.RequestAborted)); + case RequestType.Movie: + return Ok(await _movieRequestEngine.ReProcessRequest(requestId, HttpContext.RequestAborted)); + } + + return BadRequest(); + } + private string GetApiAlias() { // Make sure this only applies when using the API KEY diff --git a/src/Ombi/wwwroot/translations/en.json b/src/Ombi/wwwroot/translations/en.json index eab1b18d0..18e864bdd 100644 --- a/src/Ombi/wwwroot/translations/en.json +++ b/src/Ombi/wwwroot/translations/en.json @@ -288,7 +288,8 @@ "RadarrConfiguration": "Radarr Configuration", "RequestOnBehalf": "Request on behalf of", "PleaseSelectUser": "Please select a user", - "StreamingOn": "Streaming On" + "StreamingOn": "Streaming On", + "ReProcessRequest": "Re-Process Request" }, "Discovery": { "PopularTab": "Popular", From 0ad573a98a98f39d05434d28368937037045dd58 Mon Sep 17 00:00:00 2001 From: tidusjar Date: Fri, 30 Apr 2021 20:40:42 +0100 Subject: [PATCH 21/39] Fixed the issue where searching some movies may appear available if they have the same TvDb Id as the result's MovieDbId... --- .../Rule/Rules/Search/PlexAvailabilityRule.cs | 8 +++---- src/Ombi.Helpers.Tests/PlexHelperTests.cs | 1 + .../Jobs/Plex/PlexAvailabilityChecker.cs | 4 ++-- .../Repository/IPlexContentRepository.cs | 3 ++- .../Repository/PlexContentRepository.cs | 22 +++++++++++-------- 5 files changed, 22 insertions(+), 16 deletions(-) diff --git a/src/Ombi.Core/Rule/Rules/Search/PlexAvailabilityRule.cs b/src/Ombi.Core/Rule/Rules/Search/PlexAvailabilityRule.cs index d0397cc15..7b757802d 100644 --- a/src/Ombi.Core/Rule/Rules/Search/PlexAvailabilityRule.cs +++ b/src/Ombi.Core/Rule/Rules/Search/PlexAvailabilityRule.cs @@ -29,7 +29,7 @@ namespace Ombi.Core.Rule.Rules.Search var useTvDb = false; if (obj.ImdbId.HasValue()) { - item = await PlexContentRepository.Get(obj.ImdbId); + item = await PlexContentRepository.Get(obj.ImdbId, ProviderType.ImdbId); if (item != null) { useImdb = true; @@ -39,7 +39,7 @@ namespace Ombi.Core.Rule.Rules.Search { if (obj.Id > 0) { - item = await PlexContentRepository.Get(obj.Id.ToString()); + item = await PlexContentRepository.Get(obj.Id.ToString(), ProviderType.TheMovieDbId); if (item != null) { useId = true; @@ -47,7 +47,7 @@ namespace Ombi.Core.Rule.Rules.Search } if (obj.TheMovieDbId.HasValue()) { - item = await PlexContentRepository.Get(obj.TheMovieDbId); + item = await PlexContentRepository.Get(obj.TheMovieDbId, ProviderType.TheMovieDbId); if (item != null) { useTheMovieDb = true; @@ -58,7 +58,7 @@ namespace Ombi.Core.Rule.Rules.Search { if (obj.TheTvDbId.HasValue()) { - item = await PlexContentRepository.Get(obj.TheTvDbId); + item = await PlexContentRepository.Get(obj.TheTvDbId, ProviderType.TvDbId); if (item != null) { useTvDb = true; diff --git a/src/Ombi.Helpers.Tests/PlexHelperTests.cs b/src/Ombi.Helpers.Tests/PlexHelperTests.cs index 82b59fdf7..e7d35a583 100644 --- a/src/Ombi.Helpers.Tests/PlexHelperTests.cs +++ b/src/Ombi.Helpers.Tests/PlexHelperTests.cs @@ -61,6 +61,7 @@ namespace Ombi.Helpers.Tests get { yield return new TestCaseData("plex://movie/5e1632df2d4d84003e48e54e|imdb://tt9178402|tmdb://610201", new ProviderId { ImdbId = "tt9178402", TheMovieDb = "610201" }).SetName("V2 Regular Plex Id"); + yield return new TestCaseData("plex://movie/5e1632df2d4d84003e48e54e|imdb://tt9178402|tmdb://610201|thetvdb://12345", new ProviderId { ImdbId = "tt9178402", TheMovieDb = "610201", TheTvDb = "12345" }).SetName("V2 Regular Plex Id w/ tvdb"); yield return new TestCaseData("plex://movie/5d7768253c3c2a001fbcab72|imdb://tt0119567|tmdb://330", new ProviderId { ImdbId = "tt0119567", TheMovieDb = "330" }).SetName("V2 Regular Plex Id Another"); yield return new TestCaseData("plex://movie/5d7768253c3c2a001fbcab72|imdb://tt0119567", new ProviderId { ImdbId = "tt0119567" }).SetName("V2 Regular Plex Id Single Imdb"); yield return new TestCaseData("plex://movie/5d7768253c3c2a001fbcab72|tmdb://330", new ProviderId { TheMovieDb = "330" }).SetName("V2 Regular Plex Id Single Tmdb"); diff --git a/src/Ombi.Schedule/Jobs/Plex/PlexAvailabilityChecker.cs b/src/Ombi.Schedule/Jobs/Plex/PlexAvailabilityChecker.cs index d1d26d6e1..f203f297a 100644 --- a/src/Ombi.Schedule/Jobs/Plex/PlexAvailabilityChecker.cs +++ b/src/Ombi.Schedule/Jobs/Plex/PlexAvailabilityChecker.cs @@ -183,13 +183,13 @@ namespace Ombi.Schedule.Jobs.Plex PlexServerContent item = null; if (movie.ImdbId.HasValue()) { - item = await _repo.Get(movie.ImdbId); + item = await _repo.Get(movie.ImdbId, ProviderType.ImdbId); } if (item == null) { if (movie.TheMovieDbId.ToString().HasValue()) { - item = await _repo.Get(movie.TheMovieDbId.ToString()); + item = await _repo.Get(movie.TheMovieDbId.ToString(), ProviderType.TheMovieDbId); } } if (item == null) diff --git a/src/Ombi.Store/Repository/IPlexContentRepository.cs b/src/Ombi.Store/Repository/IPlexContentRepository.cs index 7bce2e75a..d1d30a630 100644 --- a/src/Ombi.Store/Repository/IPlexContentRepository.cs +++ b/src/Ombi.Store/Repository/IPlexContentRepository.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Threading.Tasks; +using Ombi.Helpers; using Ombi.Store.Entities; namespace Ombi.Store.Repository @@ -10,7 +11,7 @@ namespace Ombi.Store.Repository public interface IPlexContentRepository : IExternalRepository { Task ContentExists(string providerId); - Task Get(string providerId); + Task Get(string providerId, ProviderType type); Task GetByKey(int key); Task Update(PlexServerContent existingContent); IQueryable GetAllEpisodes(); diff --git a/src/Ombi.Store/Repository/PlexContentRepository.cs b/src/Ombi.Store/Repository/PlexContentRepository.cs index 1d53874a1..e5c31172e 100644 --- a/src/Ombi.Store/Repository/PlexContentRepository.cs +++ b/src/Ombi.Store/Repository/PlexContentRepository.cs @@ -31,6 +31,7 @@ using System.Linq; using System.Linq.Expressions; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; +using Ombi.Helpers; using Ombi.Store.Context; using Ombi.Store.Entities; @@ -61,18 +62,21 @@ namespace Ombi.Store.Repository return any; } - public async Task Get(string providerId) + public async Task Get(string providerId, ProviderType type) { - var item = await Db.PlexServerContent.FirstOrDefaultAsync(x => x.ImdbId == providerId); - if (item == null) + switch (type) { - item = await Db.PlexServerContent.FirstOrDefaultAsync(x => x.TheMovieDbId == providerId); - if (item == null) - { - item = await Db.PlexServerContent.FirstOrDefaultAsync(x => x.TvDbId == providerId); - } + case ProviderType.ImdbId: + return await Db.PlexServerContent.FirstOrDefaultAsync(x => x.ImdbId == providerId); + case ProviderType.TheMovieDbId: + return await Db.PlexServerContent.FirstOrDefaultAsync(x => x.TheMovieDbId == providerId); + case ProviderType.TvDbId: + return await Db.PlexServerContent.FirstOrDefaultAsync(x => x.TvDbId == providerId); + default: + break; } - return item; + + return null; } public async Task GetByKey(int key) From 2e316c51d26c93b3defaf9aa951a3cb27020adcf Mon Sep 17 00:00:00 2001 From: tidusjar Date: Sat, 1 May 2021 21:56:06 +0100 Subject: [PATCH 22/39] Fixed build --- src/Ombi.Schedule.Tests/PlexAvailabilityCheckerTests.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Ombi.Schedule.Tests/PlexAvailabilityCheckerTests.cs b/src/Ombi.Schedule.Tests/PlexAvailabilityCheckerTests.cs index 7704b78ac..a86afcad0 100644 --- a/src/Ombi.Schedule.Tests/PlexAvailabilityCheckerTests.cs +++ b/src/Ombi.Schedule.Tests/PlexAvailabilityCheckerTests.cs @@ -17,6 +17,7 @@ using Ombi.Store.Entities; using Ombi.Store.Entities.Requests; using Ombi.Store.Repository; using Ombi.Store.Repository.Requests; +using Ombi.Helpers; namespace Ombi.Schedule.Tests { @@ -53,7 +54,7 @@ namespace Ombi.Schedule.Tests ImdbId = "test" }; _movie.Setup(x => x.GetAll()).Returns(new List { request }.AsQueryable()); - _repo.Setup(x => x.Get("test")).ReturnsAsync(new PlexServerContent()); + _repo.Setup(x => x.Get("test", ProviderType.ImdbId)).ReturnsAsync(new PlexServerContent()); await Checker.Execute(null); From 8aec700f1194ead922a4f0b68e68f35954720fa6 Mon Sep 17 00:00:00 2001 From: tidusjar Date: Tue, 4 May 2021 09:00:27 +0100 Subject: [PATCH 23/39] Added some app store links to the new mobile app --- .../user-preference.component.html | 76 ++++++++++--------- .../user-preference.component.ts | 11 +-- src/Ombi/wwwroot/images/appstore.svg | 46 +++++++++++ 3 files changed, 87 insertions(+), 46 deletions(-) create mode 100644 src/Ombi/wwwroot/images/appstore.svg diff --git a/src/Ombi/ClientApp/src/app/user-preferences/components/user-preference/user-preference.component.html b/src/Ombi/ClientApp/src/app/user-preferences/components/user-preference/user-preference.component.html index e5fbc6c2f..b809a510d 100644 --- a/src/Ombi/ClientApp/src/app/user-preferences/components/user-preference/user-preference.component.html +++ b/src/Ombi/ClientApp/src/app/user-preferences/components/user-preference/user-preference.component.html @@ -1,7 +1,7 @@
-

{{username}} +

{{username}} ({{user.emailAddress}})

@@ -34,9 +34,7 @@
-
-
{{'UserPreferences.StreamingCountryDescription' | translate}}
@@ -50,47 +48,64 @@
+ +
+ +
+
+ +
+
+ Get it on Google Play +
+
+ Get it on Google Play +
+
- +

Change Details

-
+
You need your current password to make any changes here - - Current Password - - -
+ + Current Password + + +
- - Email Address - - -
+ + Email Address + + +
- - New Password - - -
+ + New Password + + +
- - New Password Confirm - - -
+ + New Password Confirm + + +
@@ -103,17 +118,6 @@ - -
-
- -
- - -
-
-
diff --git a/src/Ombi/ClientApp/src/app/user-preferences/components/user-preference/user-preference.component.ts b/src/Ombi/ClientApp/src/app/user-preferences/components/user-preference/user-preference.component.ts index 50753dfc8..ae68055a6 100644 --- a/src/Ombi/ClientApp/src/app/user-preferences/components/user-preference/user-preference.component.ts +++ b/src/Ombi/ClientApp/src/app/user-preferences/components/user-preference/user-preference.component.ts @@ -86,15 +86,6 @@ export class UserPreferenceComponent implements OnInit { this.identityService.updateStreamingCountry(this.selectedCountry).subscribe(x => this.notification.success(this.translate.instant("UserPreferences.Updated"))); } - public openMobileApp(event: any) { - event.preventDefault(); - - this.identityService.getAccessToken().subscribe(x => { - const url = `ombi://${this.customizationSettings.applicationUrl}_${x}`; - window.location.assign(url); - }); - } - public getProfileImage(): string { let emailHash: string|Int32Array; if (this.user.emailAddress) { @@ -131,7 +122,7 @@ export class UserPreferenceComponent implements OnInit { private welcomeText: string; - private setWelcomeText() { + private setWelcomeText() { var d = new Date(); var hour = d.getHours(); diff --git a/src/Ombi/wwwroot/images/appstore.svg b/src/Ombi/wwwroot/images/appstore.svg new file mode 100644 index 000000000..072b425a1 --- /dev/null +++ b/src/Ombi/wwwroot/images/appstore.svg @@ -0,0 +1,46 @@ + + Download_on_the_App_Store_Badge_US-UK_RGB_blk_4SVG_092917 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From f18aa778e6d376a19010e753988f30280a095ae8 Mon Sep 17 00:00:00 2001 From: tidusjar Date: Thu, 6 May 2021 19:59:06 +0100 Subject: [PATCH 24/39] Added the ability to hide the user in the discord notification --- .../Agents/DiscordNotification.cs | 22 ++++++++++--------- .../Agents/EmailNotification.cs | 4 ++-- .../Agents/LegacyMobileNotification.cs | 4 ++-- .../Agents/MobileNotification.cs | 4 ++-- src/Ombi.Notifications/BaseNotification.cs | 4 ++-- .../DiscordNotificationSettings.cs | 1 + .../app/interfaces/INotificationSettings.ts | 1 + .../notifications/discord.component.html | 3 +++ .../notifications/discord.component.ts | 3 ++- 9 files changed, 27 insertions(+), 19 deletions(-) diff --git a/src/Ombi.Notifications/Agents/DiscordNotification.cs b/src/Ombi.Notifications/Agents/DiscordNotification.cs index dda10f210..4a0c369fc 100644 --- a/src/Ombi.Notifications/Agents/DiscordNotification.cs +++ b/src/Ombi.Notifications/Agents/DiscordNotification.cs @@ -106,21 +106,23 @@ namespace Ombi.Notifications.Agents }; var fields = new List(); - - if (model.Data.TryGetValue("Alias", out var alias)) + if (!settings.HideUser) { - if (alias.HasValue()) + if (model.Data.TryGetValue("Alias", out var alias)) { - fields.Add(new DiscordField { name = "Requested By", value = alias, inline = true }); + if (alias.HasValue()) + { + fields.Add(new DiscordField { name = "Requested By", value = alias, inline = true }); + } } - } - else - { - if (model.Data.TryGetValue("RequestedUser", out var requestedUser)) + else { - if (requestedUser.HasValue()) + if (model.Data.TryGetValue("RequestedUser", out var requestedUser)) { - fields.Add(new DiscordField { name = "Requested By", value = requestedUser, inline = true }); + if (requestedUser.HasValue()) + { + fields.Add(new DiscordField { name = "Requested By", value = requestedUser, inline = true }); + } } } } diff --git a/src/Ombi.Notifications/Agents/EmailNotification.cs b/src/Ombi.Notifications/Agents/EmailNotification.cs index a7f9334fb..b6d03305b 100644 --- a/src/Ombi.Notifications/Agents/EmailNotification.cs +++ b/src/Ombi.Notifications/Agents/EmailNotification.cs @@ -240,9 +240,9 @@ namespace Ombi.Notifications.Agents private async Task SendToSubscribers(EmailNotificationSettings settings, NotificationMessage message) { - if (await SubsribedUsers.AnyAsync()) + if (await Subscribed.AnyAsync()) { - foreach (var user in SubsribedUsers) + foreach (var user in Subscribed) { if (user.Email.IsNullOrEmpty()) { diff --git a/src/Ombi.Notifications/Agents/LegacyMobileNotification.cs b/src/Ombi.Notifications/Agents/LegacyMobileNotification.cs index 5ac92d5bf..33cec783c 100644 --- a/src/Ombi.Notifications/Agents/LegacyMobileNotification.cs +++ b/src/Ombi.Notifications/Agents/LegacyMobileNotification.cs @@ -304,9 +304,9 @@ namespace Ombi.Notifications.Agents private async Task AddSubscribedUsers(List playerIds) { - if (await SubsribedUsers.AnyAsync()) + if (await Subscribed.AnyAsync()) { - foreach (var user in SubsribedUsers) + foreach (var user in Subscribed) { var notificationId = user.NotificationUserIds; if (notificationId.Any()) diff --git a/src/Ombi.Notifications/Agents/MobileNotification.cs b/src/Ombi.Notifications/Agents/MobileNotification.cs index 864453b71..bb6454bfc 100644 --- a/src/Ombi.Notifications/Agents/MobileNotification.cs +++ b/src/Ombi.Notifications/Agents/MobileNotification.cs @@ -346,9 +346,9 @@ namespace Ombi.Notifications.Agents private async Task AddSubscribedUsers(List playerIds) { - if (await SubsribedUsers.AnyAsync()) + if (await Subscribed.AnyAsync()) { - foreach (var user in SubsribedUsers) + foreach (var user in Subscribed) { var notificationIds = await _notifications.GetAll().Where(x => x.UserId == user.Id).ToListAsync(); diff --git a/src/Ombi.Notifications/BaseNotification.cs b/src/Ombi.Notifications/BaseNotification.cs index 9d8bbb776..46db5d467 100644 --- a/src/Ombi.Notifications/BaseNotification.cs +++ b/src/Ombi.Notifications/BaseNotification.cs @@ -48,7 +48,7 @@ namespace Ombi.Notifications protected ChildRequests TvRequest { get; set; } protected AlbumRequest AlbumRequest { get; set; } protected MovieRequests MovieRequest { get; set; } - protected IQueryable SubsribedUsers { get; private set; } + protected IQueryable Subscribed { get; private set; } public abstract string NotificationName { get; } @@ -75,7 +75,7 @@ namespace Ombi.Notifications if (model.RequestId > 0) { await LoadRequest(model.RequestId, model.RequestType); - SubsribedUsers = GetSubscriptions(model.RequestId, model.RequestType); + Subscribed = GetSubscriptions(model.RequestId, model.RequestType); } Customization = await CustomizationSettings.GetSettingsAsync(); diff --git a/src/Ombi.Settings/Settings/Models/Notifications/DiscordNotificationSettings.cs b/src/Ombi.Settings/Settings/Models/Notifications/DiscordNotificationSettings.cs index 45dc17ee1..0ec81a6d8 100644 --- a/src/Ombi.Settings/Settings/Models/Notifications/DiscordNotificationSettings.cs +++ b/src/Ombi.Settings/Settings/Models/Notifications/DiscordNotificationSettings.cs @@ -9,6 +9,7 @@ namespace Ombi.Settings.Settings.Models.Notifications public string WebhookUrl { get; set; } public string Username { get; set; } public string Icon { get; set; } + public bool HideUser { get; set; } [JsonIgnore] public string WebHookId => SplitWebUrl(4); diff --git a/src/Ombi/ClientApp/src/app/interfaces/INotificationSettings.ts b/src/Ombi/ClientApp/src/app/interfaces/INotificationSettings.ts index d08778e2a..e1c52103c 100644 --- a/src/Ombi/ClientApp/src/app/interfaces/INotificationSettings.ts +++ b/src/Ombi/ClientApp/src/app/interfaces/INotificationSettings.ts @@ -59,6 +59,7 @@ export interface IDiscordNotifcationSettings extends INotificationSettings { webhookUrl: string; username: string; icon: string; + hideUser: boolean; notificationTemplates: INotificationTemplates[]; } diff --git a/src/Ombi/ClientApp/src/app/settings/notifications/discord.component.html b/src/Ombi/ClientApp/src/app/settings/notifications/discord.component.html index 140198c94..9f19ae9a2 100644 --- a/src/Ombi/ClientApp/src/app/settings/notifications/discord.component.html +++ b/src/Ombi/ClientApp/src/app/settings/notifications/discord.component.html @@ -24,6 +24,8 @@ Username + Hide Users in notification +
@@ -34,6 +36,7 @@
+
diff --git a/src/Ombi/ClientApp/src/app/settings/notifications/discord.component.ts b/src/Ombi/ClientApp/src/app/settings/notifications/discord.component.ts index 73f271894..e2e92e55c 100644 --- a/src/Ombi/ClientApp/src/app/settings/notifications/discord.component.ts +++ b/src/Ombi/ClientApp/src/app/settings/notifications/discord.component.ts @@ -29,7 +29,8 @@ export class DiscordComponent implements OnInit { enabled: [x.enabled], username: [x.username], webhookUrl: [x.webhookUrl, [Validators.required]], - icon: [x.icon] + icon: [x.icon], + hideUser: [x.hideUser] }); }); From 5d8a2123120bcb79f58de4e8b882a454b05d1e98 Mon Sep 17 00:00:00 2001 From: tidusjar Date: Thu, 6 May 2021 20:15:58 +0100 Subject: [PATCH 25/39] Improved the Plex OAuth flow --- .../src/app/login/login.component.ts | 23 +++++++++++++++---- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/Ombi/ClientApp/src/app/login/login.component.ts b/src/Ombi/ClientApp/src/app/login/login.component.ts index 5245b8239..e9260d685 100644 --- a/src/Ombi/ClientApp/src/app/login/login.component.ts +++ b/src/Ombi/ClientApp/src/app/login/login.component.ts @@ -146,6 +146,9 @@ export class LoginComponent implements OnDestroy, OnInit { } public oauth() { + if (this.oAuthWindow) { + this.oAuthWindow.close(); + } this.oAuthWindow = window.open(window.location.toString(), "_blank", `toolbar=0, location=0, status=0, @@ -159,16 +162,22 @@ export class LoginComponent implements OnDestroy, OnInit { this.authService.login({ usePlexOAuth: true, password: "", rememberMe: true, username: "", plexTvPin: pin }).subscribe(x => { this.oAuthWindow!.location.replace(x.url); - this.pinTimer = setInterval(() => { + if (this.pinTimer) { + clearInterval(this.pinTimer); + } - this.oauthLoading = true; - this.getPinResult(x.pinId); - }, 4000); + this.pinTimer = setInterval(() => { + if(this.oAuthWindow.closed) { + this.oauthLoading = true; + this.getPinResult(x.pinId); + } + }, 1000); }); }); } public getPinResult(pinId: number) { + clearInterval(this.pinTimer); this.authService.oAuth(pinId).subscribe(x => { if(x.access_token) { this.store.save("id_token", x.access_token); @@ -176,7 +185,7 @@ export class LoginComponent implements OnDestroy, OnInit { if (this.authService.loggedIn()) { this.ngOnDestroy(); - if(this.oAuthWindow) { + if (this.oAuthWindow) { this.oAuthWindow.close(); } this.oauthLoading = false; @@ -184,6 +193,10 @@ export class LoginComponent implements OnDestroy, OnInit { return; } } + this.notify.open("Could not log you in!", "OK", { + duration: 3000 + }); + this.oauthLoading = false; }, err => { console.log(err); From 7c79e28d5c0a3d884ddec862dc7a87b2ff51c582 Mon Sep 17 00:00:00 2001 From: tidusjar Date: Thu, 6 May 2021 20:29:44 +0100 Subject: [PATCH 26/39] Added a message on the application url field to better explain what it does --- .../app/settings/customization/customization.component.html | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Ombi/ClientApp/src/app/settings/customization/customization.component.html b/src/Ombi/ClientApp/src/app/settings/customization/customization.component.html index c98cf65fc..3b3044780 100644 --- a/src/Ombi/ClientApp/src/app/settings/customization/customization.component.html +++ b/src/Ombi/ClientApp/src/app/settings/customization/customization.component.html @@ -12,6 +12,10 @@
+ The application url should be your Externally Accessible URL for example, your internal URL is http://192.168.1.50/ but your Externally + Accessible URL is 'https://mydomain.com/requests' Please ensure this field is correct as it drives a lot of functionality include the QR code for the + mobile app and it affects the way email notifications are sent. + Application URL From 8dd00e9e9935038754fad1202652fd5ad9575114 Mon Sep 17 00:00:00 2001 From: tidusjar Date: Thu, 6 May 2021 20:31:36 +0100 Subject: [PATCH 27/39] Improved the message on the admin request popup --- src/Ombi/wwwroot/translations/en.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Ombi/wwwroot/translations/en.json b/src/Ombi/wwwroot/translations/en.json index 18e864bdd..5e24656fe 100644 --- a/src/Ombi/wwwroot/translations/en.json +++ b/src/Ombi/wwwroot/translations/en.json @@ -254,9 +254,9 @@ "ViewCollection":"View Collection", "NotEnoughInfo": "Unfortunately there is not enough information about this show yet!", "AdvancedOptions":"Advanced Options", - "AutoApproveOptions":"You can configure the request here, once requested it will be send to your DVR application and will be auto approved!", - "AutoApproveOptionsTv":"You can configure the request here, once requested it will be send to your DVR application and will be auto approved! If the request is already in Sonarr, we will not change the root folder or quality profile if you set it!", - "AutoApproveOptionsTvShort":"You can configure the request here, once requested it will be send to your DVR application! If the request is already in Sonarr, we will not change the root folder or quality profile if you set it!", + "AutoApproveOptions":"You can configure the request here, once requested it will be send to your DVR application and will be auto approved! Please note, this is optional, just press Request to skip!", + "AutoApproveOptionsTv":"You can configure the request here, once requested it will be send to your DVR application and will be auto approved! If the request is already in Sonarr, we will not change the root folder or quality profile if you set it! Please note, this is optional, just press Request to skip!", + "AutoApproveOptionsTvShort":"You can configure the request here, once requested it will be send to your DVR application! If the request is already in Sonarr, we will not change the root folder or quality profile if you set it! Please note, this is optional, just press Request to skip!", "QualityProfilesSelect":"Select A Quality Profile", "RootFolderSelect":"Select A Root Folder", "LanguageProfileSelect":"Select A Language Profile", From 4071b2e8a215e5e5dfe60b617080c7e4c48d7e36 Mon Sep 17 00:00:00 2001 From: tidusjar Date: Fri, 7 May 2021 14:41:10 +0100 Subject: [PATCH 28/39] Added the open on mobile back in, this will be available in the next app update --- .../Rules/Search/AvailabilityRuleHelper.cs | 2 +- .../Rule/Rules/Search/PlexAvailabilityRule.cs | 4 +-- .../user-preference.component.html | 28 +++++++++++-------- .../user-preference.component.ts | 7 +++++ 4 files changed, 27 insertions(+), 14 deletions(-) diff --git a/src/Ombi.Core/Rule/Rules/Search/AvailabilityRuleHelper.cs b/src/Ombi.Core/Rule/Rules/Search/AvailabilityRuleHelper.cs index 71331c51d..c3de20798 100644 --- a/src/Ombi.Core/Rule/Rules/Search/AvailabilityRuleHelper.cs +++ b/src/Ombi.Core/Rule/Rules/Search/AvailabilityRuleHelper.cs @@ -13,7 +13,7 @@ namespace Ombi.Core.Rule.Rules.Search { public static void CheckForUnairedEpisodes(SearchTvShowViewModel search) { - foreach (var season in search.SeasonRequests) + foreach (var season in search.SeasonRequests.ToList()) { // If we have all the episodes for this season, then this season is available if (season.Episodes.All(x => x.Available)) diff --git a/src/Ombi.Core/Rule/Rules/Search/PlexAvailabilityRule.cs b/src/Ombi.Core/Rule/Rules/Search/PlexAvailabilityRule.cs index 7b757802d..68551aac4 100644 --- a/src/Ombi.Core/Rule/Rules/Search/PlexAvailabilityRule.cs +++ b/src/Ombi.Core/Rule/Rules/Search/PlexAvailabilityRule.cs @@ -85,9 +85,9 @@ namespace Ombi.Core.Rule.Rules.Search if (search.SeasonRequests.Any()) { var allEpisodes = PlexContentRepository.GetAllEpisodes(); - foreach (var season in search.SeasonRequests) + foreach (var season in search.SeasonRequests.ToList()) { - foreach (var episode in season.Episodes) + foreach (var episode in season.Episodes.ToList()) { await AvailabilityRuleHelper.SingleEpisodeCheck(useImdb, allEpisodes, episode, season, item, useTheMovieDb, useTvDb, Log); } diff --git a/src/Ombi/ClientApp/src/app/user-preferences/components/user-preference/user-preference.component.html b/src/Ombi/ClientApp/src/app/user-preferences/components/user-preference/user-preference.component.html index b809a510d..ab9f7bf0c 100644 --- a/src/Ombi/ClientApp/src/app/user-preferences/components/user-preference/user-preference.component.html +++ b/src/Ombi/ClientApp/src/app/user-preferences/components/user-preference/user-preference.component.html @@ -52,19 +52,25 @@
-
-
-
- Get it on Google Play + +
+
+ Get it on Google Play +
+
+ Get it on Google Play +
+
+ +
+
-
- Get it on Google Play -
-
+
diff --git a/src/Ombi/ClientApp/src/app/user-preferences/components/user-preference/user-preference.component.ts b/src/Ombi/ClientApp/src/app/user-preferences/components/user-preference/user-preference.component.ts index ae68055a6..eb504065c 100644 --- a/src/Ombi/ClientApp/src/app/user-preferences/components/user-preference/user-preference.component.ts +++ b/src/Ombi/ClientApp/src/app/user-preferences/components/user-preference/user-preference.component.ts @@ -120,6 +120,13 @@ export class UserPreferenceComponent implements OnInit { }) } + public openMobileApp(event: any) { + event.preventDefault(); + + const url = `ombi://${this.qrCode}`; + window.location.assign(url); + } + private welcomeText: string; private setWelcomeText() { From 50af2b84821ce39d316194c0a14e7ebb4f507d1e Mon Sep 17 00:00:00 2001 From: tidusjar Date: Fri, 7 May 2021 19:36:07 +0100 Subject: [PATCH 29/39] Fixed the issue where you couldn't remove a legacy user from the notifications --- src/Ombi/Controllers/V1/MobileController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ombi/Controllers/V1/MobileController.cs b/src/Ombi/Controllers/V1/MobileController.cs index 2ca640769..485535816 100644 --- a/src/Ombi/Controllers/V1/MobileController.cs +++ b/src/Ombi/Controllers/V1/MobileController.cs @@ -92,7 +92,7 @@ namespace Ombi.Controllers.V1 [Admin] public async Task RemoveUser([FromBody] RemoveUserModel userId) { - var user = await _userManager.Users.Include(x => x.NotificationUserIds).FirstOrDefaultAsync(x => x.Id.Equals(userId.UserId, StringComparison.InvariantCultureIgnoreCase)); + var user = await _userManager.Users.Include(x => x.NotificationUserIds).FirstOrDefaultAsync(x => x.Id == userId.UserId); try { await _notification.DeleteRange(user.NotificationUserIds); From 64c0fc17cc56c1484a33a8dc2a715843bcab82b9 Mon Sep 17 00:00:00 2001 From: tidusjar Date: Sun, 9 May 2021 19:58:36 +0100 Subject: [PATCH 30/39] Fixed #4152 --- src/Ombi.Core/Engine/Interfaces/BaseEngine.cs | 4 ++-- src/Ombi.Core/Engine/MovieRequestEngine.cs | 4 ++-- src/Ombi.Core/Rule/Interfaces/IRuleEvaluator.cs | 2 +- src/Ombi.Core/Rule/Interfaces/ISpecificRule.cs | 2 +- src/Ombi.Core/Rule/RuleEvaluator.cs | 4 ++-- .../Rule/Rules/Search/AvailabilityRuleHelper.cs | 2 +- .../Rule/Rules/Search/LidarrArtistCacheRule.cs | 3 ++- .../Rule/Rules/Specific/SendNotificationRule.cs | 11 ++++++++++- 8 files changed, 21 insertions(+), 11 deletions(-) diff --git a/src/Ombi.Core/Engine/Interfaces/BaseEngine.cs b/src/Ombi.Core/Engine/Interfaces/BaseEngine.cs index 6fd22bced..68a4335cb 100644 --- a/src/Ombi.Core/Engine/Interfaces/BaseEngine.cs +++ b/src/Ombi.Core/Engine/Interfaces/BaseEngine.cs @@ -59,9 +59,9 @@ namespace Ombi.Core.Engine.Interfaces var ruleResults = await Rules.StartSearchRules(model); return ruleResults; } - public async Task RunSpecificRule(object model, SpecificRules rule) + public async Task RunSpecificRule(object model, SpecificRules rule, string requestOnBehalf) { - var ruleResults = await Rules.StartSpecificRules(model, rule); + var ruleResults = await Rules.StartSpecificRules(model, rule, requestOnBehalf); return ruleResults; } } diff --git a/src/Ombi.Core/Engine/MovieRequestEngine.cs b/src/Ombi.Core/Engine/MovieRequestEngine.cs index 8d54f0fc3..a2380be81 100644 --- a/src/Ombi.Core/Engine/MovieRequestEngine.cs +++ b/src/Ombi.Core/Engine/MovieRequestEngine.cs @@ -71,7 +71,7 @@ namespace Ombi.Core.Engine var canRequestOnBehalf = model.RequestOnBehalf.HasValue(); var isAdmin = await UserManager.IsInRoleAsync(userDetails, OmbiRoles.PowerUser) || await UserManager.IsInRoleAsync(userDetails, OmbiRoles.Admin); - if (model.RequestOnBehalf.HasValue() && !isAdmin) + if (canRequestOnBehalf && !isAdmin) { return new RequestEngineResult { @@ -703,7 +703,7 @@ namespace Ombi.Core.Engine { await MovieRepository.Add(model); - var result = await RunSpecificRule(model, SpecificRules.CanSendNotification); + var result = await RunSpecificRule(model, SpecificRules.CanSendNotification, requestOnBehalf); if (result.Success) { await NotificationHelper.NewRequest(model); diff --git a/src/Ombi.Core/Rule/Interfaces/IRuleEvaluator.cs b/src/Ombi.Core/Rule/Interfaces/IRuleEvaluator.cs index c32342146..5b438124e 100644 --- a/src/Ombi.Core/Rule/Interfaces/IRuleEvaluator.cs +++ b/src/Ombi.Core/Rule/Interfaces/IRuleEvaluator.cs @@ -9,6 +9,6 @@ namespace Ombi.Core.Rule.Interfaces { Task> StartRequestRules(BaseRequest obj); Task> StartSearchRules(SearchViewModel obj); - Task StartSpecificRules(object obj, SpecificRules selectedRule); + Task StartSpecificRules(object obj, SpecificRules selectedRule, string requestOnBehalf); } } \ No newline at end of file diff --git a/src/Ombi.Core/Rule/Interfaces/ISpecificRule.cs b/src/Ombi.Core/Rule/Interfaces/ISpecificRule.cs index 065b2f990..f76464888 100644 --- a/src/Ombi.Core/Rule/Interfaces/ISpecificRule.cs +++ b/src/Ombi.Core/Rule/Interfaces/ISpecificRule.cs @@ -5,7 +5,7 @@ namespace Ombi.Core.Rule.Interfaces { public interface ISpecificRule where T : new() { - Task Execute(T obj); + Task Execute(T obj, string requestOnBehalf); SpecificRules Rule { get; } } } \ No newline at end of file diff --git a/src/Ombi.Core/Rule/RuleEvaluator.cs b/src/Ombi.Core/Rule/RuleEvaluator.cs index a0c272c00..712d6031e 100644 --- a/src/Ombi.Core/Rule/RuleEvaluator.cs +++ b/src/Ombi.Core/Rule/RuleEvaluator.cs @@ -58,13 +58,13 @@ namespace Ombi.Core.Rule return results; } - public async Task StartSpecificRules(object obj, SpecificRules selectedRule) + public async Task StartSpecificRules(object obj, SpecificRules selectedRule, string requestOnBehalf) { foreach (var rule in SpecificRules) { if (selectedRule == rule.Rule) { - var result = await rule.Execute(obj); + var result = await rule.Execute(obj, requestOnBehalf); return result; } } diff --git a/src/Ombi.Core/Rule/Rules/Search/AvailabilityRuleHelper.cs b/src/Ombi.Core/Rule/Rules/Search/AvailabilityRuleHelper.cs index c3de20798..fe062e851 100644 --- a/src/Ombi.Core/Rule/Rules/Search/AvailabilityRuleHelper.cs +++ b/src/Ombi.Core/Rule/Rules/Search/AvailabilityRuleHelper.cs @@ -18,7 +18,7 @@ namespace Ombi.Core.Rule.Rules.Search // If we have all the episodes for this season, then this season is available if (season.Episodes.All(x => x.Available)) { - season.SeasonAvailable = true; + season.SeasonAvailable = true; } } if (search.SeasonRequests.All(x => x.Episodes.All(e => e.Available))) diff --git a/src/Ombi.Core/Rule/Rules/Search/LidarrArtistCacheRule.cs b/src/Ombi.Core/Rule/Rules/Search/LidarrArtistCacheRule.cs index a254ea2d3..b0e9af1ec 100644 --- a/src/Ombi.Core/Rule/Rules/Search/LidarrArtistCacheRule.cs +++ b/src/Ombi.Core/Rule/Rules/Search/LidarrArtistCacheRule.cs @@ -8,7 +8,7 @@ using Ombi.Store.Repository; namespace Ombi.Core.Rule.Rules.Search { - public class LidarrArtistCacheRule : SpecificRule, ISpecificRule + public class LidarrArtistCacheRule : SpecificRule, IRules { public LidarrArtistCacheRule(IExternalRepository db) { @@ -30,6 +30,7 @@ namespace Ombi.Core.Rule.Rules.Search return Task.FromResult(Success()); } + public override SpecificRules Rule => SpecificRules.LidarrArtist; } } \ No newline at end of file diff --git a/src/Ombi.Core/Rule/Rules/Specific/SendNotificationRule.cs b/src/Ombi.Core/Rule/Rules/Specific/SendNotificationRule.cs index 30ec9b14a..af71df9ac 100644 --- a/src/Ombi.Core/Rule/Rules/Specific/SendNotificationRule.cs +++ b/src/Ombi.Core/Rule/Rules/Specific/SendNotificationRule.cs @@ -22,11 +22,20 @@ namespace Ombi.Core.Rule.Rules.Specific private OmbiUserManager UserManager { get; } private ISettingsService Settings { get; } - public async Task Execute(object obj) + public async Task Execute(object obj, string requestOnBehalf) { var req = (BaseRequest)obj; + var canRequestonBehalf = requestOnBehalf.HasValue(); var settings = await Settings.GetSettingsAsync(); var sendNotification = true; + + if (settings.DoNotSendNotificationsForAutoApprove && canRequestonBehalf) + { + return new RuleResult + { + Success = false + }; + } var requestedUser = await UserManager.Users.FirstOrDefaultAsync(x => x.Id == req.RequestedUserId); if (req.RequestType == RequestType.Movie) { From 6e32bd33f5418070d9616152e31309e84c3b4489 Mon Sep 17 00:00:00 2001 From: tidusjar Date: Sun, 9 May 2021 21:15:56 +0100 Subject: [PATCH 31/39] Fixed build --- .../Rule/Search/LidarrAlbumCacheRuleTests.cs | 10 +++++----- .../Rule/Search/LidarrArtistCacheRuleTests.cs | 6 +++--- src/Ombi.Core/Engine/MovieRequestEngine.cs | 2 +- src/Ombi.Core/Engine/MusicRequestEngine.cs | 4 ++-- src/Ombi.Core/Engine/MusicSearchEngine.cs | 8 ++++---- src/Ombi.Core/Engine/TvRequestEngine.cs | 2 +- .../Rule/Rules/Search/LidarrAlbumCacheRule.cs | 6 ++++-- .../Rule/Rules/Search/LidarrArtistCacheRule.cs | 4 ++-- 8 files changed, 22 insertions(+), 20 deletions(-) diff --git a/src/Ombi.Core.Tests/Rule/Search/LidarrAlbumCacheRuleTests.cs b/src/Ombi.Core.Tests/Rule/Search/LidarrAlbumCacheRuleTests.cs index 27dbee614..7bf84d05f 100644 --- a/src/Ombi.Core.Tests/Rule/Search/LidarrAlbumCacheRuleTests.cs +++ b/src/Ombi.Core.Tests/Rule/Search/LidarrAlbumCacheRuleTests.cs @@ -30,7 +30,7 @@ namespace Ombi.Core.Tests.Rule.Search public async Task Should_Not_Be_Monitored_Or_Available() { var request = new SearchAlbumViewModel { ForeignAlbumId = "abc" }; - var result = await Rule.Execute(request); + var result = await Rule.Execute(request, string.Empty); Assert.True(result.Success); Assert.False(request.Approved); @@ -49,7 +49,7 @@ namespace Ombi.Core.Tests.Rule.Search } }.AsQueryable()); var request = new SearchAlbumViewModel { ForeignAlbumId = "abc" }; - var result = await Rule.Execute(request); + var result = await Rule.Execute(request, string.Empty); Assert.True(result.Success); Assert.False(request.Approved); @@ -71,7 +71,7 @@ namespace Ombi.Core.Tests.Rule.Search } }.AsQueryable()); var request = new SearchAlbumViewModel { ForeignAlbumId = "abc" }; - var result = await Rule.Execute(request); + var result = await Rule.Execute(request, string.Empty); Assert.True(result.Success); Assert.False(request.Approved); @@ -93,7 +93,7 @@ namespace Ombi.Core.Tests.Rule.Search } }.AsQueryable()); var request = new SearchAlbumViewModel { ForeignAlbumId = "abc" }; - var result = await Rule.Execute(request); + var result = await Rule.Execute(request, string.Empty); Assert.True(result.Success); Assert.False(request.Approved); @@ -114,7 +114,7 @@ namespace Ombi.Core.Tests.Rule.Search } }.AsQueryable()); var request = new SearchAlbumViewModel { ForeignAlbumId = "abc" }; - var result = await Rule.Execute(request); + var result = await Rule.Execute(request, string.Empty); Assert.True(result.Success); Assert.False(request.Approved); diff --git a/src/Ombi.Core.Tests/Rule/Search/LidarrArtistCacheRuleTests.cs b/src/Ombi.Core.Tests/Rule/Search/LidarrArtistCacheRuleTests.cs index c17400acb..e13d8d1cd 100644 --- a/src/Ombi.Core.Tests/Rule/Search/LidarrArtistCacheRuleTests.cs +++ b/src/Ombi.Core.Tests/Rule/Search/LidarrArtistCacheRuleTests.cs @@ -29,7 +29,7 @@ namespace Ombi.Core.Tests.Rule.Search public async Task Should_Not_Be_Monitored() { var request = new SearchArtistViewModel { ForignArtistId = "abc" }; - var result = await Rule.Execute(request); + var result = await Rule.Execute(request, string.Empty); Assert.True(result.Success); Assert.False(request.Monitored); @@ -46,7 +46,7 @@ namespace Ombi.Core.Tests.Rule.Search } }.AsQueryable()); var request = new SearchArtistViewModel { ForignArtistId = "abc" }; - var result = await Rule.Execute(request); + var result = await Rule.Execute(request, string.Empty); Assert.True(result.Success); Assert.True(request.Monitored); @@ -64,7 +64,7 @@ namespace Ombi.Core.Tests.Rule.Search } }.AsQueryable()); var request = new SearchArtistViewModel { ForignArtistId = "abc" }; - var result = await Rule.Execute(request); + var result = await Rule.Execute(request, string.Empty); Assert.True(result.Success); Assert.True(request.Monitored); diff --git a/src/Ombi.Core/Engine/MovieRequestEngine.cs b/src/Ombi.Core/Engine/MovieRequestEngine.cs index a2380be81..d37983d21 100644 --- a/src/Ombi.Core/Engine/MovieRequestEngine.cs +++ b/src/Ombi.Core/Engine/MovieRequestEngine.cs @@ -550,7 +550,7 @@ namespace Ombi.Core.Engine request.Denied = false; await MovieRepository.Update(request); - var canNotify = await RunSpecificRule(request, SpecificRules.CanSendNotification); + var canNotify = await RunSpecificRule(request, SpecificRules.CanSendNotification, string.Empty); if (canNotify.Success) { await NotificationHelper.Notify(request, NotificationType.RequestApproved); diff --git a/src/Ombi.Core/Engine/MusicRequestEngine.cs b/src/Ombi.Core/Engine/MusicRequestEngine.cs index e473c1fb1..032b2ae7d 100644 --- a/src/Ombi.Core/Engine/MusicRequestEngine.cs +++ b/src/Ombi.Core/Engine/MusicRequestEngine.cs @@ -362,7 +362,7 @@ namespace Ombi.Core.Engine await MusicRepository.Update(request); - var canNotify = await RunSpecificRule(request, SpecificRules.CanSendNotification); + var canNotify = await RunSpecificRule(request, SpecificRules.CanSendNotification, string.Empty); if (canNotify.Success) { await NotificationHelper.Notify(request, NotificationType.RequestApproved); @@ -506,7 +506,7 @@ namespace Ombi.Core.Engine { await MusicRepository.Add(model); - var result = await RunSpecificRule(model, SpecificRules.CanSendNotification); + var result = await RunSpecificRule(model, SpecificRules.CanSendNotification, string.Empty); if (result.Success) { await NotificationHelper.NewRequest(model); diff --git a/src/Ombi.Core/Engine/MusicSearchEngine.cs b/src/Ombi.Core/Engine/MusicSearchEngine.cs index a9af03ecf..89bae7069 100644 --- a/src/Ombi.Core/Engine/MusicSearchEngine.cs +++ b/src/Ombi.Core/Engine/MusicSearchEngine.cs @@ -151,7 +151,7 @@ namespace Ombi.Core.Engine } - await Rules.StartSpecificRules(vm, SpecificRules.LidarrArtist); + await Rules.StartSpecificRules(vm, SpecificRules.LidarrArtist, string.Empty); return vm; } @@ -190,7 +190,7 @@ namespace Ombi.Core.Engine vm.Cover = a.images?.FirstOrDefault(x => x.coverType.Equals("cover"))?.url?.ToHttpsUrl(); - await Rules.StartSpecificRules(vm, SpecificRules.LidarrAlbum); + await Rules.StartSpecificRules(vm, SpecificRules.LidarrAlbum, string.Empty); await RunSearchRules(vm); @@ -230,7 +230,7 @@ namespace Ombi.Core.Engine vm.Cover = a.remoteCover; } - await Rules.StartSpecificRules(vm, SpecificRules.LidarrAlbum); + await Rules.StartSpecificRules(vm, SpecificRules.LidarrAlbum, string.Empty); await RunSearchRules(vm); @@ -258,7 +258,7 @@ namespace Ombi.Core.Engine vm.Cover = fullAlbum.remoteCover; } - await Rules.StartSpecificRules(vm, SpecificRules.LidarrAlbum); + await Rules.StartSpecificRules(vm, SpecificRules.LidarrAlbum, string.Empty); await RunSearchRules(vm); diff --git a/src/Ombi.Core/Engine/TvRequestEngine.cs b/src/Ombi.Core/Engine/TvRequestEngine.cs index 5eae0912c..00655e921 100644 --- a/src/Ombi.Core/Engine/TvRequestEngine.cs +++ b/src/Ombi.Core/Engine/TvRequestEngine.cs @@ -915,7 +915,7 @@ namespace Ombi.Core.Engine private async Task AfterRequest(ChildRequests model, string requestOnBehalf) { - var sendRuleResult = await RunSpecificRule(model, SpecificRules.CanSendNotification); + var sendRuleResult = await RunSpecificRule(model, SpecificRules.CanSendNotification, requestOnBehalf); if (sendRuleResult.Success) { await NotificationHelper.NewRequest(model); diff --git a/src/Ombi.Core/Rule/Rules/Search/LidarrAlbumCacheRule.cs b/src/Ombi.Core/Rule/Rules/Search/LidarrAlbumCacheRule.cs index 3ca57d635..ca0c0cd97 100644 --- a/src/Ombi.Core/Rule/Rules/Search/LidarrAlbumCacheRule.cs +++ b/src/Ombi.Core/Rule/Rules/Search/LidarrAlbumCacheRule.cs @@ -9,7 +9,7 @@ using Ombi.Store.Repository; namespace Ombi.Core.Rule.Rules.Search { - public class LidarrAlbumCacheRule : BaseSearchRule, IRules + public class LidarrAlbumCacheRule : SpecificRule, ISpecificRule { public LidarrAlbumCacheRule(IExternalRepository db) { @@ -18,7 +18,9 @@ namespace Ombi.Core.Rule.Rules.Search private readonly IExternalRepository _db; - public Task Execute(SearchViewModel objec) + public override SpecificRules Rule => SpecificRules.LidarrAlbum; + + public Task Execute(object objec, string requestOnBehalf) { if (objec is SearchAlbumViewModel obj) { diff --git a/src/Ombi.Core/Rule/Rules/Search/LidarrArtistCacheRule.cs b/src/Ombi.Core/Rule/Rules/Search/LidarrArtistCacheRule.cs index b0e9af1ec..06ca3f06c 100644 --- a/src/Ombi.Core/Rule/Rules/Search/LidarrArtistCacheRule.cs +++ b/src/Ombi.Core/Rule/Rules/Search/LidarrArtistCacheRule.cs @@ -8,7 +8,7 @@ using Ombi.Store.Repository; namespace Ombi.Core.Rule.Rules.Search { - public class LidarrArtistCacheRule : SpecificRule, IRules + public class LidarrArtistCacheRule : SpecificRule, ISpecificRule { public LidarrArtistCacheRule(IExternalRepository db) { @@ -17,7 +17,7 @@ namespace Ombi.Core.Rule.Rules.Search private readonly IExternalRepository _db; - public Task Execute(object objec) + public Task Execute(object objec, string requestOnBehalf) { var obj = (SearchArtistViewModel) objec; // Check if it's in Lidarr From 900ec20e42d31c0f290a04d71268065b9e6a0cc7 Mon Sep 17 00:00:00 2001 From: tidusjar Date: Sun, 9 May 2021 21:44:21 +0100 Subject: [PATCH 32/39] Fixed #4057 #4180 --- .../src/app/usermanagement/usermanagement.component.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Ombi/ClientApp/src/app/usermanagement/usermanagement.component.ts b/src/Ombi/ClientApp/src/app/usermanagement/usermanagement.component.ts index c2245957c..d119c8961 100644 --- a/src/Ombi/ClientApp/src/app/usermanagement/usermanagement.component.ts +++ b/src/Ombi/ClientApp/src/app/usermanagement/usermanagement.component.ts @@ -1,7 +1,7 @@ import { AfterViewInit, Component, OnInit, ViewChild } from "@angular/core"; - import { ICheckbox, ICustomizationSettings, IEmailNotificationSettings, IUser } from "../interfaces"; import { IdentityService, NotificationService, SettingsService } from "../services"; + import { MatSort } from "@angular/material/sort"; import { MatTableDataSource } from "@angular/material/table"; import { SelectionModel } from "@angular/cdk/collections"; @@ -90,6 +90,9 @@ export class UserManagementComponent implements OnInit { if (this.bulkMusicLimit) { x.musicRequestLimit = this.bulkMusicLimit; } + if (this.bulkStreaming) { + x.streamingCountry = this.bulkStreaming; + } this.identityService.updateUser(x).subscribe(y => { if (!y.successful) { this.notificationService.error(`Could not update user ${x.userName}. Reason ${y.errors[0]}`); @@ -102,6 +105,7 @@ export class UserManagementComponent implements OnInit { this.bulkMovieLimit = undefined; this.bulkEpisodeLimit = undefined; this.bulkMusicLimit = undefined; + this.bulkStreaming = undefined; } public isAllSelected() { From b8076e2e655958522ed4974ea1cdc88dfe63f798 Mon Sep 17 00:00:00 2001 From: Dyson Parkes Date: Mon, 10 May 2021 13:43:00 +1200 Subject: [PATCH 33/39] Adjust app store string --- .../components/user-preference/user-preference.component.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Ombi/ClientApp/src/app/user-preferences/components/user-preference/user-preference.component.html b/src/Ombi/ClientApp/src/app/user-preferences/components/user-preference/user-preference.component.html index ab9f7bf0c..6954030de 100644 --- a/src/Ombi/ClientApp/src/app/user-preferences/components/user-preference/user-preference.component.html +++ b/src/Ombi/ClientApp/src/app/user-preferences/components/user-preference/user-preference.component.html @@ -62,7 +62,7 @@
Get it on Google Play
@@ -128,4 +128,4 @@ -
\ No newline at end of file + From 21b8bf76d28b4b232f1fb74f86d2fc82da8edb24 Mon Sep 17 00:00:00 2001 From: tidusjar Date: Tue, 11 May 2021 09:51:18 +0100 Subject: [PATCH 34/39] Fixed an issue where for Available Emby content, the service ID was not being passed --- src/Ombi.Core/Rule/Rules/Search/EmbyAvailabilityRule.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ombi.Core/Rule/Rules/Search/EmbyAvailabilityRule.cs b/src/Ombi.Core/Rule/Rules/Search/EmbyAvailabilityRule.cs index 3fe11cbc4..7f6718a6b 100644 --- a/src/Ombi.Core/Rule/Rules/Search/EmbyAvailabilityRule.cs +++ b/src/Ombi.Core/Rule/Rules/Search/EmbyAvailabilityRule.cs @@ -67,7 +67,7 @@ namespace Ombi.Core.Rule.Rules.Search var s = await EmbySettings.GetSettingsAsync(); if (s.Enable) { - var server = s.Servers.FirstOrDefault(x => x.ServerHostname != null); + var server = s.Servers.FirstOrDefault(); if ((server?.ServerHostname ?? string.Empty).HasValue()) { obj.EmbyUrl = EmbyHelper.GetEmbyMediaUrl(item.EmbyId, server?.ServerId, server?.ServerHostname); From 65879acb8d2abc981409a5bd3419b064417aef98 Mon Sep 17 00:00:00 2001 From: tidusjar Date: Sun, 16 May 2021 11:52:37 +0100 Subject: [PATCH 35/39] Fixed the deseralizing issue #4181 --- src/Ombi.Api.Radarr/Models/V2/RadarrAddMovie.cs | 1 + src/Ombi.Api.Radarr/RadarrApi.cs | 3 ++- src/Ombi.Api.Radarr/RadarrV3Api.cs | 3 ++- src/Ombi/ClientApp/src/app/settings/radarr/radarr.component.ts | 2 +- 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/Ombi.Api.Radarr/Models/V2/RadarrAddMovie.cs b/src/Ombi.Api.Radarr/Models/V2/RadarrAddMovie.cs index b56049c9a..09e985f43 100644 --- a/src/Ombi.Api.Radarr/Models/V2/RadarrAddMovie.cs +++ b/src/Ombi.Api.Radarr/Models/V2/RadarrAddMovie.cs @@ -28,5 +28,6 @@ namespace Ombi.Api.Radarr.Models public string titleSlug { get; set; } public int year { get; set; } public string minimumAvailability { get; set; } + public long sizeOnDisk { get; set; } } } \ No newline at end of file diff --git a/src/Ombi.Api.Radarr/RadarrApi.cs b/src/Ombi.Api.Radarr/RadarrApi.cs index b461ccda8..e1879bba3 100644 --- a/src/Ombi.Api.Radarr/RadarrApi.cs +++ b/src/Ombi.Api.Radarr/RadarrApi.cs @@ -82,7 +82,8 @@ namespace Ombi.Api.Radarr titleSlug = title + year, monitored = true, year = year, - minimumAvailability = minimumAvailability + minimumAvailability = minimumAvailability, + sizeOnDisk = 0 }; if (searchNow) diff --git a/src/Ombi.Api.Radarr/RadarrV3Api.cs b/src/Ombi.Api.Radarr/RadarrV3Api.cs index dd1d0b279..21f95b3e1 100644 --- a/src/Ombi.Api.Radarr/RadarrV3Api.cs +++ b/src/Ombi.Api.Radarr/RadarrV3Api.cs @@ -85,7 +85,8 @@ namespace Ombi.Api.Radarr titleSlug = title + year, monitored = true, year = year, - minimumAvailability = minimumAvailability + minimumAvailability = minimumAvailability, + sizeOnDisk = 0 }; if (searchNow) diff --git a/src/Ombi/ClientApp/src/app/settings/radarr/radarr.component.ts b/src/Ombi/ClientApp/src/app/settings/radarr/radarr.component.ts index d4c5a9883..256f0a75e 100644 --- a/src/Ombi/ClientApp/src/app/settings/radarr/radarr.component.ts +++ b/src/Ombi/ClientApp/src/app/settings/radarr/radarr.component.ts @@ -43,7 +43,7 @@ export class RadarrComponent implements OnInit { addOnly: [x.addOnly], minimumAvailability: [x.minimumAvailability, [Validators.required]], scanForAvailability: [x.scanForAvailability], - v3: [x.v3] + v3: [x.v3 ?? true] }); if (x.defaultQualityProfile) { From d23c4c38594d96e28c8bf6de0e6e69b7f84276b4 Mon Sep 17 00:00:00 2001 From: tidusjar Date: Sun, 16 May 2021 12:03:39 +0100 Subject: [PATCH 36/39] removed the ripple from the navmenu #4192 --- .../ClientApp/src/app/my-nav/my-nav.component.html | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Ombi/ClientApp/src/app/my-nav/my-nav.component.html b/src/Ombi/ClientApp/src/app/my-nav/my-nav.component.html index c1b69b303..f73c9bbc8 100644 --- a/src/Ombi/ClientApp/src/app/my-nav/my-nav.component.html +++ b/src/Ombi/ClientApp/src/app/my-nav/my-nav.component.html @@ -6,12 +6,12 @@ {{applicationName}} - + -