From f03473c47ea5eeda63494bdcc0967a31c96d7184 Mon Sep 17 00:00:00 2001 From: Jamie Date: Tue, 29 May 2018 22:11:30 +0100 Subject: [PATCH 01/55] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fb2e7fd50..c83cdf157 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## (unreleased) +## v3.0.3346 (2018-05-23) ### **New Features** From 3a8f72ed0da839dfc3617e57d54e6a23397492f4 Mon Sep 17 00:00:00 2001 From: Jamie Date: Fri, 1 Jun 2018 10:43:11 +0100 Subject: [PATCH 02/55] Inital Migration --- src/Ombi.Tests/Ombi.Tests.csproj | 2 +- src/Ombi.Updater/Ombi.Updater.csproj | 2 +- src/Ombi/Ombi.csproj | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Ombi.Tests/Ombi.Tests.csproj b/src/Ombi.Tests/Ombi.Tests.csproj index 0880682f2..6296d9e8c 100644 --- a/src/Ombi.Tests/Ombi.Tests.csproj +++ b/src/Ombi.Tests/Ombi.Tests.csproj @@ -1,7 +1,7 @@ - netcoreapp2.0 + netcoreapp2.1 false diff --git a/src/Ombi.Updater/Ombi.Updater.csproj b/src/Ombi.Updater/Ombi.Updater.csproj index 3a55a7040..f7152102a 100644 --- a/src/Ombi.Updater/Ombi.Updater.csproj +++ b/src/Ombi.Updater/Ombi.Updater.csproj @@ -3,7 +3,7 @@ Exe win10-x64;win10-x86;osx-x64;ubuntu-x64;debian.8-x64;centos.7-x64;linux-x64;linux-arm;linux-arm64; - netcoreapp2.0 + netcoreapp2.1 3.0.0.0 3.0.0.0 diff --git a/src/Ombi/Ombi.csproj b/src/Ombi/Ombi.csproj index 8b9e00c45..25fd06e90 100644 --- a/src/Ombi/Ombi.csproj +++ b/src/Ombi/Ombi.csproj @@ -1,7 +1,7 @@  - netcoreapp2.0 + netcoreapp2.1 win10-x64;win10-x86;osx-x64;ubuntu-x64;debian.8-x64;centos.7-x64;linux-x64;linux-arm;linux-arm64; false Latest From 12b4bf2f5fbc30de999a436080ed04b2291ba6b9 Mon Sep 17 00:00:00 2001 From: Jamie Date: Fri, 1 Jun 2018 10:52:21 +0100 Subject: [PATCH 03/55] Upgrade packages --- src/Ombi.Api.Radarr/Ombi.Api.Radarr.csproj | 2 +- src/Ombi.Api.Service/Ombi.Api.Service.csproj | 2 +- src/Ombi.Api/Ombi.Api.csproj | 2 +- src/Ombi.Core.Tests/Ombi.Core.Tests.csproj | 2 +- src/Ombi.Core/Ombi.Core.csproj | 6 +++--- .../Ombi.DependencyInjection.csproj | 6 +++--- src/Ombi.Helpers/Ombi.Helpers.csproj | 4 ++-- .../Ombi.Notifications.Tests.csproj | 2 +- src/Ombi.Notifications/Ombi.Notifications.csproj | 2 +- .../Ombi.Schedule.Tests.csproj | 4 ++-- src/Ombi.Settings/Ombi.Settings.csproj | 2 +- src/Ombi.Store/Ombi.Store.csproj | 8 ++++---- src/Ombi.Tests/Ombi.Tests.csproj | 2 +- src/Ombi.Updater/Ombi.Updater.csproj | 16 ++++++++-------- src/Ombi/Ombi.csproj | 8 ++++---- 15 files changed, 34 insertions(+), 34 deletions(-) diff --git a/src/Ombi.Api.Radarr/Ombi.Api.Radarr.csproj b/src/Ombi.Api.Radarr/Ombi.Api.Radarr.csproj index d2b605337..ff506dc35 100644 --- a/src/Ombi.Api.Radarr/Ombi.Api.Radarr.csproj +++ b/src/Ombi.Api.Radarr/Ombi.Api.Radarr.csproj @@ -9,7 +9,7 @@ - + diff --git a/src/Ombi.Api.Service/Ombi.Api.Service.csproj b/src/Ombi.Api.Service/Ombi.Api.Service.csproj index 67f37c80d..4a7f4865e 100644 --- a/src/Ombi.Api.Service/Ombi.Api.Service.csproj +++ b/src/Ombi.Api.Service/Ombi.Api.Service.csproj @@ -11,7 +11,7 @@ - + diff --git a/src/Ombi.Api/Ombi.Api.csproj b/src/Ombi.Api/Ombi.Api.csproj index 32fc60bb6..533bcfcda 100644 --- a/src/Ombi.Api/Ombi.Api.csproj +++ b/src/Ombi.Api/Ombi.Api.csproj @@ -9,7 +9,7 @@ - + diff --git a/src/Ombi.Core.Tests/Ombi.Core.Tests.csproj b/src/Ombi.Core.Tests/Ombi.Core.Tests.csproj index d75ba3411..b9960aa7f 100644 --- a/src/Ombi.Core.Tests/Ombi.Core.Tests.csproj +++ b/src/Ombi.Core.Tests/Ombi.Core.Tests.csproj @@ -1,7 +1,7 @@  - netcoreapp2.0 + netcoreapp2.1 diff --git a/src/Ombi.Core/Ombi.Core.csproj b/src/Ombi.Core/Ombi.Core.csproj index 2037113bd..1c09c88e3 100644 --- a/src/Ombi.Core/Ombi.Core.csproj +++ b/src/Ombi.Core/Ombi.Core.csproj @@ -12,9 +12,9 @@ - - - + + + diff --git a/src/Ombi.DependencyInjection/Ombi.DependencyInjection.csproj b/src/Ombi.DependencyInjection/Ombi.DependencyInjection.csproj index 675f6461b..d2f94127f 100644 --- a/src/Ombi.DependencyInjection/Ombi.DependencyInjection.csproj +++ b/src/Ombi.DependencyInjection/Ombi.DependencyInjection.csproj @@ -9,9 +9,9 @@ - - - + + + diff --git a/src/Ombi.Helpers/Ombi.Helpers.csproj b/src/Ombi.Helpers/Ombi.Helpers.csproj index 12c6fecc4..4b104eadb 100644 --- a/src/Ombi.Helpers/Ombi.Helpers.csproj +++ b/src/Ombi.Helpers/Ombi.Helpers.csproj @@ -10,8 +10,8 @@ - - + + diff --git a/src/Ombi.Notifications.Tests/Ombi.Notifications.Tests.csproj b/src/Ombi.Notifications.Tests/Ombi.Notifications.Tests.csproj index b350b93f4..01721db3f 100644 --- a/src/Ombi.Notifications.Tests/Ombi.Notifications.Tests.csproj +++ b/src/Ombi.Notifications.Tests/Ombi.Notifications.Tests.csproj @@ -1,7 +1,7 @@  - netcoreapp2.0 + netcoreapp2.1 diff --git a/src/Ombi.Notifications/Ombi.Notifications.csproj b/src/Ombi.Notifications/Ombi.Notifications.csproj index 46a64072e..529f01357 100644 --- a/src/Ombi.Notifications/Ombi.Notifications.csproj +++ b/src/Ombi.Notifications/Ombi.Notifications.csproj @@ -10,7 +10,7 @@ - + diff --git a/src/Ombi.Schedule.Tests/Ombi.Schedule.Tests.csproj b/src/Ombi.Schedule.Tests/Ombi.Schedule.Tests.csproj index bdfbd60e6..d147af88f 100644 --- a/src/Ombi.Schedule.Tests/Ombi.Schedule.Tests.csproj +++ b/src/Ombi.Schedule.Tests/Ombi.Schedule.Tests.csproj @@ -1,11 +1,11 @@ - netcoreapp2.0 + netcoreapp2.1 - + diff --git a/src/Ombi.Settings/Ombi.Settings.csproj b/src/Ombi.Settings/Ombi.Settings.csproj index 3cb56cb07..8709fd906 100644 --- a/src/Ombi.Settings/Ombi.Settings.csproj +++ b/src/Ombi.Settings/Ombi.Settings.csproj @@ -9,7 +9,7 @@ - + diff --git a/src/Ombi.Store/Ombi.Store.csproj b/src/Ombi.Store/Ombi.Store.csproj index 703bbf672..b9ff73ef1 100644 --- a/src/Ombi.Store/Ombi.Store.csproj +++ b/src/Ombi.Store/Ombi.Store.csproj @@ -10,10 +10,10 @@ - - - - + + + + diff --git a/src/Ombi.Tests/Ombi.Tests.csproj b/src/Ombi.Tests/Ombi.Tests.csproj index 6296d9e8c..639276a75 100644 --- a/src/Ombi.Tests/Ombi.Tests.csproj +++ b/src/Ombi.Tests/Ombi.Tests.csproj @@ -7,7 +7,7 @@ - + diff --git a/src/Ombi.Updater/Ombi.Updater.csproj b/src/Ombi.Updater/Ombi.Updater.csproj index f7152102a..e46a45551 100644 --- a/src/Ombi.Updater/Ombi.Updater.csproj +++ b/src/Ombi.Updater/Ombi.Updater.csproj @@ -12,14 +12,14 @@ - - - - - - - - + + + + + + + + diff --git a/src/Ombi/Ombi.csproj b/src/Ombi/Ombi.csproj index 25fd06e90..add872a33 100644 --- a/src/Ombi/Ombi.csproj +++ b/src/Ombi/Ombi.csproj @@ -67,10 +67,10 @@ - - - - + + + + From 218b6b731d9b899a851445093fe96bb91f54985f Mon Sep 17 00:00:00 2001 From: Jamie Date: Fri, 1 Jun 2018 11:44:23 +0100 Subject: [PATCH 04/55] Fixed #2288 --- src/Ombi/ClientApp/app/requests/movierequests.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ombi/ClientApp/app/requests/movierequests.component.ts b/src/Ombi/ClientApp/app/requests/movierequests.component.ts index fe6f3b73f..f26ce763f 100644 --- a/src/Ombi/ClientApp/app/requests/movierequests.component.ts +++ b/src/Ombi/ClientApp/app/requests/movierequests.component.ts @@ -99,7 +99,7 @@ export class MovieRequestsComponent implements OnInit { public removeRequest(request: IMovieRequests) { this.requestService.removeMovieRequest(request); this.removeRequestFromUi(request); - this.loadRequests(1, this.currentlyLoaded); + this.loadRequests(this.amountToLoad, this.currentlyLoaded = 0); } public changeAvailability(request: IMovieRequests, available: boolean) { From 3a4c3823569d9524209f7a40ba331f4d278c88e4 Mon Sep 17 00:00:00 2001 From: Jamie Date: Fri, 1 Jun 2018 13:05:10 +0100 Subject: [PATCH 05/55] Fixed the sorting and filtering on the Movie Requests page, it all functions correctly now --- .../Engine/Interfaces/IMovieRequestEngine.cs | 1 - .../Engine/Interfaces/IRequestEngine.cs | 3 +- src/Ombi.Core/Engine/MovieRequestEngine.cs | 145 +++++++++++------- src/Ombi.Core/Engine/TvRequestEngine.cs | 8 +- src/Ombi.Core/Models/UI/OrderFilterModel.cs | 11 ++ src/Ombi.Core/Models/UI/OrderType.cs | 12 ++ src/Ombi.Core/Models/UI/RequestsViewModel.cs | 10 ++ .../ClientApp/app/interfaces/IRequestModel.ts | 13 +- .../app/requests/movierequests.component.html | 108 ++++++------- .../app/requests/movierequests.component.ts | 95 +++++------- .../ClientApp/app/services/request.service.ts | 11 +- src/Ombi/Controllers/RequestController.cs | 40 +++-- src/Ombi/wwwroot/translations/en.json | 10 +- 13 files changed, 259 insertions(+), 208 deletions(-) create mode 100644 src/Ombi.Core/Models/UI/OrderFilterModel.cs create mode 100644 src/Ombi.Core/Models/UI/OrderType.cs create mode 100644 src/Ombi.Core/Models/UI/RequestsViewModel.cs diff --git a/src/Ombi.Core/Engine/Interfaces/IMovieRequestEngine.cs b/src/Ombi.Core/Engine/Interfaces/IMovieRequestEngine.cs index c1ba76a7c..91cbb9e72 100644 --- a/src/Ombi.Core/Engine/Interfaces/IMovieRequestEngine.cs +++ b/src/Ombi.Core/Engine/Interfaces/IMovieRequestEngine.cs @@ -17,7 +17,6 @@ namespace Ombi.Core.Engine.Interfaces Task ApproveMovie(MovieRequests request); Task ApproveMovieById(int requestId); Task DenyMovieById(int modelId); - Task> Filter(FilterViewModel vm); } diff --git a/src/Ombi.Core/Engine/Interfaces/IRequestEngine.cs b/src/Ombi.Core/Engine/Interfaces/IRequestEngine.cs index 553ad79dd..740428ec7 100644 --- a/src/Ombi.Core/Engine/Interfaces/IRequestEngine.cs +++ b/src/Ombi.Core/Engine/Interfaces/IRequestEngine.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Threading.Tasks; using Ombi.Core.Models.Requests; +using Ombi.Core.Models.UI; using Ombi.Store.Entities; namespace Ombi.Core.Engine.Interfaces @@ -12,7 +13,7 @@ namespace Ombi.Core.Engine.Interfaces //Task> GetNewRequests(); //Task> GetAvailableRequests(); RequestCountModel RequestCount(); - Task> GetRequests(int count, int position); + Task> GetRequests(int count, int position, OrderFilterModel model); Task> GetRequests(); Task UserHasRequest(string userId); diff --git a/src/Ombi.Core/Engine/MovieRequestEngine.cs b/src/Ombi.Core/Engine/MovieRequestEngine.cs index 460752bb1..15383ed12 100644 --- a/src/Ombi.Core/Engine/MovieRequestEngine.cs +++ b/src/Ombi.Core/Engine/MovieRequestEngine.cs @@ -13,6 +13,7 @@ using Microsoft.Extensions.Logging; using Ombi.Api.TheMovieDb.Models; using Ombi.Core.Authentication; using Ombi.Core.Engine.Interfaces; +using Ombi.Core.Models.UI; using Ombi.Core.Rule.Interfaces; using Ombi.Core.Settings; using Ombi.Settings.Settings.Models; @@ -25,7 +26,8 @@ namespace Ombi.Core.Engine { public MovieRequestEngine(IMovieDbApi movieApi, IRequestServiceMain requestService, IPrincipal user, INotificationHelper helper, IRuleEvaluator r, IMovieSender sender, ILogger log, - OmbiUserManager manager, IRepository rl, ICacheService cache, ISettingsService ombiSettings, IRepository sub) + OmbiUserManager manager, IRepository rl, ICacheService cache, + ISettingsService ombiSettings, IRepository sub) : base(user, requestService, r, manager, cache, ombiSettings, sub) { MovieApi = movieApi; @@ -58,6 +60,7 @@ namespace Ombi.Core.Engine ErrorMessage = $"Please try again later" }; } + var fullMovieName = $"{movieInfo.Title}{(!string.IsNullOrEmpty(movieInfo.ReleaseDate) ? $" ({DateTime.Parse(movieInfo.ReleaseDate).Year})" : string.Empty)}"; @@ -82,7 +85,8 @@ namespace Ombi.Core.Engine }; var usDates = movieInfo.ReleaseDates?.Results?.FirstOrDefault(x => x.IsoCode == "US"); - requestModel.DigitalReleaseDate = usDates?.ReleaseDate?.FirstOrDefault(x => x.Type == ReleaseDateType.Digital)?.ReleaseDate; + requestModel.DigitalReleaseDate = usDates?.ReleaseDate + ?.FirstOrDefault(x => x.Type == ReleaseDateType.Digital)?.ReleaseDate; var ruleResults = (await RunRequestRules(requestModel)).ToList(); if (ruleResults.Any(x => !x.Success)) @@ -125,25 +129,93 @@ namespace Ombi.Core.Engine /// /// The count. /// The position. + /// The order/filter type. /// - public async Task> GetRequests(int count, int position) + public async Task> GetRequests(int count, int position, + OrderFilterModel orderFilter) { var shouldHide = await HideFromOtherUsers(); - List allRequests; + IQueryable allRequests; if (shouldHide.Hide) { - allRequests = await MovieRepository.GetWithUser(shouldHide.UserId).Skip(position).Take(count).OrderByDescending(x => x.ReleaseDate).ToListAsync(); + allRequests = + MovieRepository.GetWithUser(shouldHide + .UserId); //.Skip(position).Take(count).OrderByDescending(x => x.ReleaseDate).ToListAsync(); } else { - allRequests = await MovieRepository.GetWithUser().Skip(position).Take(count).OrderByDescending(x => x.ReleaseDate).ToListAsync(); + allRequests = + MovieRepository + .GetWithUser(); //.Skip(position).Take(count).OrderByDescending(x => x.ReleaseDate).ToListAsync(); } - allRequests.ForEach(async x => + + switch (orderFilter.AvailabilityFilter) + { + case FilterType.None: + break; + case FilterType.Available: + allRequests = allRequests.Where(x => x.Available); + break; + case FilterType.NotAvailable: + allRequests = allRequests.Where(x => !x.Available); + break; + default: + throw new ArgumentOutOfRangeException(); + } + + switch (orderFilter.StatusFilter) + { + case FilterType.None: + break; + case FilterType.Approved: + allRequests = allRequests.Where(x => x.Approved); + break; + case FilterType.Processing: + allRequests = allRequests.Where(x => x.Approved && !x.Available); + break; + case FilterType.PendingApproval: + allRequests = allRequests.Where(x => !x.Approved && !x.Available && !(x.Denied ?? false)); + break; + default: + throw new ArgumentOutOfRangeException(); + } + + var total = allRequests.Count(); + + var requests = await (OrderMovies(allRequests, orderFilter.OrderType)).Skip(position).Take(count) + .ToListAsync(); + + requests.ForEach(async x => { x.PosterPath = PosterPathHelper.FixPosterPath(x.PosterPath); await CheckForSubscription(shouldHide, x); }); - return allRequests; + return new RequestsViewModel + { + Collection = requests, + Total = total + }; + } + + private IQueryable OrderMovies(IQueryable allRequests, OrderType type) + { + switch (type) + { + case OrderType.RequestedDateAsc: + return allRequests.OrderBy(x => x.RequestedDate); + case OrderType.RequestedDateDesc: + return allRequests.OrderByDescending(x => x.RequestedDate); + case OrderType.TitleAsc: + return allRequests.OrderBy(x => x.Title); + case OrderType.TitleDesc: + return allRequests.OrderByDescending(x => x.Title); + case OrderType.StatusAsc: + return allRequests.OrderBy(x => x.Status); + case OrderType.StatusDesc: + return allRequests.OrderByDescending(x => x.Status); + default: + throw new ArgumentOutOfRangeException(nameof(type), type, null); + } } public async Task GetTotal() @@ -216,6 +288,7 @@ namespace Ombi.Core.Engine { allRequests = await MovieRepository.GetWithUser().ToListAsync(); } + var results = allRequests.Where(x => x.Title.Contains(search, CompareOptions.IgnoreCase)).ToList(); results.ForEach(async x => { @@ -241,6 +314,7 @@ namespace Ombi.Core.Engine ErrorMessage = "Request does not exist" }; } + request.Denied = true; // We are denying a request NotificationHelper.Notify(request, NotificationType.RequestDeclined); @@ -261,6 +335,7 @@ namespace Ombi.Core.Engine ErrorMessage = "Request does not exist" }; } + request.Approved = true; request.Denied = false; await MovieRepository.Update(request); @@ -281,6 +356,7 @@ namespace Ombi.Core.Engine Result = true }; } + if (!result.Success) { Logger.LogWarning("Tried auto sending movie but failed. Message: {0}", result.Message); @@ -291,6 +367,7 @@ namespace Ombi.Core.Engine Result = false }; } + // If there are no providers then it's successful but movie has not been sent } @@ -352,6 +429,7 @@ namespace Ombi.Core.Engine ErrorMessage = "Request does not exist" }; } + request.Available = false; await MovieRepository.Update(request); @@ -372,6 +450,7 @@ namespace Ombi.Core.Engine ErrorMessage = "Request does not exist" }; } + request.Available = true; NotificationHelper.Notify(request, NotificationType.RequestAvailable); await MovieRepository.Update(request); @@ -401,55 +480,7 @@ namespace Ombi.Core.Engine RequestType = RequestType.Movie, }); - return new RequestEngineResult { Result = true, Message = $"{movieName} has been successfully added!" }; - } - - public async Task> Filter(FilterViewModel vm) - { - var shouldHide = await HideFromOtherUsers(); - var requests = shouldHide.Hide - ? MovieRepository.GetWithUser(shouldHide.UserId) - : MovieRepository.GetWithUser(); - - switch (vm.AvailabilityFilter) - { - case FilterType.None: - break; - case FilterType.Available: - requests = requests.Where(x => x.Available); - break; - case FilterType.NotAvailable: - requests = requests.Where(x => !x.Available); - break; - default: - throw new ArgumentOutOfRangeException(); - } - - switch (vm.StatusFilter) - { - case FilterType.None: - break; - case FilterType.Approved: - requests = requests.Where(x => x.Approved); - break; - case FilterType.Processing: - requests = requests.Where(x => x.Approved && !x.Available); - break; - case FilterType.PendingApproval: - requests = requests.Where(x => !x.Approved && !x.Available && !(x.Denied ?? false)); - break; - default: - throw new ArgumentOutOfRangeException(); - } - - var count = await requests.CountAsync(); - requests = requests.Skip(vm.Position).Take(vm.Count); - var retVal = new FilterResult - { - Total = count, - Collection = requests - }; - return retVal; + return new RequestEngineResult {Result = true, Message = $"{movieName} has been successfully added!"}; } } } \ No newline at end of file diff --git a/src/Ombi.Core/Engine/TvRequestEngine.cs b/src/Ombi.Core/Engine/TvRequestEngine.cs index ddf84376e..8bb6d86e6 100644 --- a/src/Ombi.Core/Engine/TvRequestEngine.cs +++ b/src/Ombi.Core/Engine/TvRequestEngine.cs @@ -15,6 +15,7 @@ using Microsoft.EntityFrameworkCore; using Ombi.Core.Authentication; using Ombi.Core.Engine.Interfaces; using Ombi.Core.Helpers; +using Ombi.Core.Models.UI; using Ombi.Core.Rule; using Ombi.Core.Rule.Interfaces; using Ombi.Core.Senders; @@ -132,7 +133,7 @@ namespace Ombi.Core.Engine return await AddRequest(newRequest.NewRequest); } - public async Task> GetRequests(int count, int position) + public async Task> GetRequests(int count, int position, OrderFilterModel type) { var shouldHide = await HideFromOtherUsers(); List allRequests; @@ -161,7 +162,10 @@ namespace Ombi.Core.Engine allRequests.ForEach(async r => { await CheckForSubscription(shouldHide, r); }); - return allRequests; + return new RequestsViewModel + { + Collection = allRequests + }; } public async Task>>> GetRequestsTreeNode(int count, int position) diff --git a/src/Ombi.Core/Models/UI/OrderFilterModel.cs b/src/Ombi.Core/Models/UI/OrderFilterModel.cs new file mode 100644 index 000000000..3e3192829 --- /dev/null +++ b/src/Ombi.Core/Models/UI/OrderFilterModel.cs @@ -0,0 +1,11 @@ +using Ombi.Core.Models.Requests; + +namespace Ombi.Core.Models.UI +{ + public class OrderFilterModel + { + public FilterType AvailabilityFilter { get; set; } + public FilterType StatusFilter { get; set; } + public OrderType OrderType { get; set; } + } +} \ No newline at end of file diff --git a/src/Ombi.Core/Models/UI/OrderType.cs b/src/Ombi.Core/Models/UI/OrderType.cs new file mode 100644 index 000000000..67d20b127 --- /dev/null +++ b/src/Ombi.Core/Models/UI/OrderType.cs @@ -0,0 +1,12 @@ +namespace Ombi.Core.Models.UI +{ + public enum OrderType + { + RequestedDateAsc =1, + RequestedDateDesc =2, + TitleAsc =3, + TitleDesc=4, + StatusAsc=5, + StatusDesc=6 + } +} \ No newline at end of file diff --git a/src/Ombi.Core/Models/UI/RequestsViewModel.cs b/src/Ombi.Core/Models/UI/RequestsViewModel.cs new file mode 100644 index 000000000..9d772bc53 --- /dev/null +++ b/src/Ombi.Core/Models/UI/RequestsViewModel.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; + +namespace Ombi.Core.Models.UI +{ + public class RequestsViewModel + { + public IEnumerable Collection { get; set; } + public int Total { get; set; } + } +} \ No newline at end of file diff --git a/src/Ombi/ClientApp/app/interfaces/IRequestModel.ts b/src/Ombi/ClientApp/app/interfaces/IRequestModel.ts index 1895914c3..236e694fc 100644 --- a/src/Ombi/ClientApp/app/interfaces/IRequestModel.ts +++ b/src/Ombi/ClientApp/app/interfaces/IRequestModel.ts @@ -20,7 +20,7 @@ export interface IMovieRequests extends IFullBaseRequest { qualityOverrideTitle: string; } -export interface IFilterResult { +export interface IRequestsViewModel { total: number; collection: T[]; } @@ -87,6 +87,15 @@ export interface ITvUpdateModel { id: number; } +export enum OrderType { + RequestedDateAsc = 1, + RequestedDateDesc = 2, + TitleAsc = 3, + TitleDesc = 4, + StatusAsc = 5, + StatusDesc = 6, +} + export interface INewSeasonRequests { id: number; seasonNumber: number; @@ -112,8 +121,6 @@ export interface IMovieRequestModel { export interface IFilter { availabilityFilter: FilterType; statusFilter: FilterType; - position: number; - count: number; } export enum FilterType { diff --git a/src/Ombi/ClientApp/app/requests/movierequests.component.html b/src/Ombi/ClientApp/app/requests/movierequests.component.html index b2ea68db2..241e0af6b 100644 --- a/src/Ombi/ClientApp/app/requests/movierequests.component.html +++ b/src/Ombi/ClientApp/app/requests/movierequests.component.html @@ -1,57 +1,45 @@ - + +
@@ -59,9 +47,7 @@
-
- - +
@@ -99,7 +85,7 @@ + [translate]="'Common.PendingApproval'">
@@ -125,11 +111,15 @@
-
+
@@ -171,7 +161,7 @@
- +
- @@ -198,7 +190,7 @@
- +
diff --git a/src/Ombi/ClientApp/app/requests/movierequests.component.ts b/src/Ombi/ClientApp/app/requests/movierequests.component.ts index f26ce763f..ede242e1d 100644 --- a/src/Ombi/ClientApp/app/requests/movierequests.component.ts +++ b/src/Ombi/ClientApp/app/requests/movierequests.component.ts @@ -9,7 +9,7 @@ import { Subject } from "rxjs/Subject"; import { AuthService } from "../auth/auth.service"; import { NotificationService, RadarrService, RequestService } from "../services"; -import { FilterType, IFilter, IIssueCategory, IMovieRequests, IPagenator, IRadarrProfile, IRadarrRootFolder } from "../interfaces"; +import { FilterType, IFilter, IIssueCategory, IMovieRequests, IPagenator, IRadarrProfile, IRadarrRootFolder, OrderType } from "../interfaces"; @Component({ selector: "movie-requests", @@ -38,9 +38,9 @@ export class MovieRequestsComponent implements OnInit { public filter: IFilter; public filterType = FilterType; - public order: string = "requestedDate"; - public reverse = true; - + public orderType: OrderType = OrderType.RequestedDateDesc; + public OrderType = OrderType; + public totalMovies: number = 100; private currentlyLoaded: number; private amountToLoad: number; @@ -75,20 +75,17 @@ export class MovieRequestsComponent implements OnInit { public ngOnInit() { this.amountToLoad = 10; - this.currentlyLoaded = 10; - this.loadInit(); - this.isAdmin = this.auth.hasRole("admin") || this.auth.hasRole("poweruser"); + this.currentlyLoaded = 10; this.filter = { availabilityFilter: FilterType.None, statusFilter: FilterType.None, - count: this.amountToLoad, - position: 0, }; + this.loadInit(); + this.isAdmin = this.auth.hasRole("admin") || this.auth.hasRole("poweruser"); } public paginate(event: IPagenator) { - const skipAmount = event.first; - + const skipAmount = event.first; this.loadRequests(this.amountToLoad, skipAmount); } @@ -183,38 +180,28 @@ export class MovieRequestsComponent implements OnInit { public filterAvailability(filter: FilterType, el: any) { this.filterActiveStyle(el); this.filter.availabilityFilter = filter; - this.requestService.filterMovies(this.filter) - .subscribe(x => { - this.totalMovies = x.total; - this.setOverrides(x.collection); - this.movieRequests = x.collection; - }); + debugger; + this.loadInit(); } public filterStatus(filter: FilterType, el: any) { this.filterActiveStyle(el); this.filter.statusFilter = filter; - this.requestService.filterMovies(this.filter) - .subscribe(x => { - this.totalMovies = x.total; - this.setOverrides(x.collection); - this.movieRequests = x.collection; - }); + this.loadInit(); } - public setOrder(value: string, el: any) { + public setOrder(value: OrderType, el: any) { el = el.toElement || el.relatedTarget || el.target || el.srcElement; const parent = el.parentElement; const previousFilter = parent.querySelector(".active"); - if (this.order === value) { - this.reverse = !this.reverse; - } else { - previousFilter.className = ""; - el.className = "active"; - } - this.order = value; + previousFilter.className = ""; + el.className = "active"; + + this.orderType = value; + + this.loadInit(); } public subscribe(request: IMovieRequests) { @@ -252,26 +239,16 @@ export class MovieRequestsComponent implements OnInit { } private loadRequests(amountToLoad: number, currentlyLoaded: number) { - if(this.filter.availabilityFilter === FilterType.None && this.filter.statusFilter === FilterType.None) { - this.requestService.getMovieRequests(amountToLoad, currentlyLoaded + 1) + this.requestService.getMovieRequests(amountToLoad, currentlyLoaded, this.orderType, this.filter) .subscribe(x => { - this.setOverrides(x); + this.setOverrides(x.collection); if(!this.movieRequests) { this.movieRequests = []; } - this.movieRequests = x; + this.movieRequests = x.collection; + this.totalMovies = x.total; this.currentlyLoaded = currentlyLoaded + amountToLoad; }); - } else { - this.filter.position = currentlyLoaded; - this.requestService.filterMovies(this.filter) - .subscribe(x => { - this.setOverrides(x.collection); - this.totalMovies = x.total; - this.movieRequests = x.collection; - this.currentlyLoaded = currentlyLoaded + amountToLoad; - }); - } } private updateRequest(request: IMovieRequests) { @@ -310,23 +287,25 @@ export class MovieRequestsComponent implements OnInit { } private loadInit() { - this.requestService.getTotalMovies().subscribe(x => this.totalMovies = x); - this.requestService.getMovieRequests(this.amountToLoad, 0) + this.requestService.getMovieRequests(this.amountToLoad, 0, this.orderType, this.filter) .subscribe(x => { - this.movieRequests = x; + this.movieRequests = x.collection; + this.totalMovies = x.total; this.movieRequests.forEach((req) => { - this.setBackground(req); - this.setPoster(req); - }); - this.radarrService.getQualityProfilesFromSettings().subscribe(c => { - this.radarrProfiles = c; - this.movieRequests.forEach((req) => this.setQualityOverrides(req)); - }); - this.radarrService.getRootFoldersFromSettings().subscribe(c => { - this.radarrRootFolders = c; - this.movieRequests.forEach((req) => this.setRootFolderOverrides(req)); + this.setBackground(req); + this.setPoster(req); }); + if (this.isAdmin) { + this.radarrService.getQualityProfilesFromSettings().subscribe(c => { + this.radarrProfiles = c; + this.movieRequests.forEach((req) => this.setQualityOverrides(req)); + }); + this.radarrService.getRootFoldersFromSettings().subscribe(c => { + this.radarrRootFolders = c; + this.movieRequests.forEach((req) => this.setRootFolderOverrides(req)); + }); + } }); } diff --git a/src/Ombi/ClientApp/app/services/request.service.ts b/src/Ombi/ClientApp/app/services/request.service.ts index 2da0c1a8a..e52ab3057 100644 --- a/src/Ombi/ClientApp/app/services/request.service.ts +++ b/src/Ombi/ClientApp/app/services/request.service.ts @@ -6,7 +6,7 @@ import { Observable } from "rxjs/Rx"; import { TreeNode } from "primeng/primeng"; import { IRequestEngineResult } from "../interfaces"; -import { IChildRequests, IFilter, IFilterResult, IMovieRequestModel, IMovieRequests, IMovieUpdateModel, ITvRequests,ITvUpdateModel } from "../interfaces"; +import { IChildRequests, IFilter, IMovieRequestModel, IMovieRequests, IMovieUpdateModel, IRequestsViewModel, ITvRequests,ITvUpdateModel, OrderType } from "../interfaces"; import { ITvRequestViewModel } from "../interfaces"; import { ServiceHelpers } from "./service.helpers"; @@ -48,8 +48,8 @@ export class RequestService extends ServiceHelpers { return this.http.post(`${this.url}Movie/unavailable`, JSON.stringify(movie), {headers: this.headers}); } - public getMovieRequests(count: number, position: number): Observable { - return this.http.get(`${this.url}movie/${count}/${position}`, {headers: this.headers}); + public getMovieRequests(count: number, position: number, order: OrderType, filter: IFilter): Observable> { + return this.http.get>(`${this.url}movie/${count}/${position}/${order}/${filter.statusFilter}/${filter.availabilityFilter}`, {headers: this.headers}); } public searchMovieRequests(search: string): Observable { @@ -114,10 +114,7 @@ export class RequestService extends ServiceHelpers { public deleteChild(child: IChildRequests): Observable { return this.http.delete(`${this.url}tv/child/${child.id}`, {headers: this.headers}); } - public filterMovies(filter: IFilter): Observable> { - return this.http.post>(`${this.url}movie/filter`, JSON.stringify(filter), {headers: this.headers}); - } - + public subscribeToMovie(requestId: number): Observable { return this.http.post(`${this.url}movie/subscribe/${requestId}`, {headers: this.headers}); } diff --git a/src/Ombi/Controllers/RequestController.cs b/src/Ombi/Controllers/RequestController.cs index 0a56c32b6..47329a0ec 100644 --- a/src/Ombi/Controllers/RequestController.cs +++ b/src/Ombi/Controllers/RequestController.cs @@ -8,6 +8,7 @@ using System.Collections.Generic; using System.Threading.Tasks; using Ombi.Store.Entities.Requests; using System.Diagnostics; +using Ombi.Core.Models.UI; using Ombi.Models; using Ombi.Store.Entities; @@ -32,10 +33,18 @@ namespace Ombi.Controllers /// /// The count of items you want to return. /// The position. - [HttpGet("movie/{count:int}/{position:int}")] - public async Task> GetRequests(int count, int position) + /// The way we want to order. + /// + /// + [HttpGet("movie/{count:int}/{position:int}/{orderType:int}/{statusType:int}/{availabilityType:int}")] + public async Task> GetRequests(int count, int position, int orderType, int statusType, int availabilityType) { - return await MovieRequestEngine.GetRequests(count, position); + return await MovieRequestEngine.GetRequests(count, position, new OrderFilterModel + { + OrderType = (OrderType)orderType, + AvailabilityFilter = (FilterType)availabilityType, + StatusFilter = (FilterType)statusType, + }); } /// @@ -170,11 +179,19 @@ namespace Ombi.Controllers /// /// The count of items you want to return. /// The position. + /// + /// + /// /// - [HttpGet("tv/{count:int}/{position:int}")] - public async Task> GetTvRequests(int count, int position) + [HttpGet("tv/{count:int}/{position:int}/{orderType:int}/{statusFilterType:int}/{availabilityFilterType:int}")] + public async Task> GetTvRequests(int count, int position, int orderType, int statusType, int availabilityType) { - return await TvRequestEngine.GetRequests(count, position); + return await TvRequestEngine.GetRequests(count, position, new OrderFilterModel + { + OrderType = (OrderType)orderType, + AvailabilityFilter = (FilterType) availabilityType, + StatusFilter = (FilterType) statusType, + }); } /// @@ -347,17 +364,6 @@ namespace Ombi.Controllers return movies || tv; } - /// - /// Returns a filtered list - /// - /// - /// - [HttpPost("movie/filter")] - public async Task> Filter([FromBody] FilterViewModel vm) - { - return await MovieRequestEngine.Filter(vm); - } - /// /// Subscribes for notifications to a movie request /// diff --git a/src/Ombi/wwwroot/translations/en.json b/src/Ombi/wwwroot/translations/en.json index cb7c0fe8b..289db8be5 100644 --- a/src/Ombi/wwwroot/translations/en.json +++ b/src/Ombi/wwwroot/translations/en.json @@ -138,10 +138,12 @@ "Filter":"Filter", "Sort":"Sort", "SeasonNumberHeading":"Season: {seasonNumber}", - "SortTitle":"Title", - "SortRequestDate": "Request Date", - "SortRequestedBy":"Requested By", - "SortStatus":"Status" + "SortTitleAsc":"Title ▲", + "SortTitleDesc":"Title ▼", + "SortRequestDateAsc": "Request Date ▲", + "SortRequestDateDesc": "Request Date ▼", + "SortStatusAsc":"Status ▲", + "SortStatusDesc":"Status ▼" }, "Issues":{ "Title":"Issues", From c354efb5001e4505c9d62c947fbb96c0ce99b990 Mon Sep 17 00:00:00 2001 From: Jamie Date: Fri, 1 Jun 2018 13:06:41 +0100 Subject: [PATCH 06/55] !wip removed debugger statement --- src/Ombi/ClientApp/app/requests/movierequests.component.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Ombi/ClientApp/app/requests/movierequests.component.ts b/src/Ombi/ClientApp/app/requests/movierequests.component.ts index ede242e1d..0848daf53 100644 --- a/src/Ombi/ClientApp/app/requests/movierequests.component.ts +++ b/src/Ombi/ClientApp/app/requests/movierequests.component.ts @@ -180,7 +180,6 @@ export class MovieRequestsComponent implements OnInit { public filterAvailability(filter: FilterType, el: any) { this.filterActiveStyle(el); this.filter.availabilityFilter = filter; - debugger; this.loadInit(); } From 3be460ea3b557101dd2cb25a4636d95f73309fd2 Mon Sep 17 00:00:00 2001 From: Jamie Date: Fri, 1 Jun 2018 13:23:53 +0100 Subject: [PATCH 07/55] Fixed the SickRage/Medusa Issue where it was always being set as Skipped/Ignore #2084 --- src/Ombi.Core/Senders/TvSender.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/Ombi.Core/Senders/TvSender.cs b/src/Ombi.Core/Senders/TvSender.cs index 151ad0f7b..12124d270 100644 --- a/src/Ombi.Core/Senders/TvSender.cs +++ b/src/Ombi.Core/Senders/TvSender.cs @@ -314,9 +314,17 @@ namespace Ombi.Core.Senders foreach (var seasonRequests in model.SeasonRequests) { var srEpisodes = await SickRageApi.GetEpisodesForSeason(tvdbid, seasonRequests.SeasonNumber, settings.ApiKey, settings.FullUri); - while (srEpisodes.message.Equals("Show not found", StringComparison.CurrentCultureIgnoreCase) && srEpisodes.data.Count <= 0) + int retryTimes = 10; + var currentRetry = 0; + while (srEpisodes.message.Equals("Show not found", StringComparison.CurrentCultureIgnoreCase) || srEpisodes.message.Equals("Season not found", StringComparison.CurrentCultureIgnoreCase) && srEpisodes.data.Count <= 0) { + if (currentRetry > retryTimes) + { + Logger.LogWarning("Couldnt find the SR Season or Show, message: {0}", srEpisodes.message); + break; + } await Task.Delay(TimeSpan.FromSeconds(1)); + currentRetry++; srEpisodes = await SickRageApi.GetEpisodesForSeason(tvdbid, seasonRequests.SeasonNumber, settings.ApiKey, settings.FullUri); } From df32782eb4653cc317c538e37d4e772d6020e93d Mon Sep 17 00:00:00 2001 From: Jamie Date: Sun, 3 Jun 2018 20:53:42 +0100 Subject: [PATCH 08/55] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e8995ab76..b70c69282 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## v3.0.3346 (2018-05-23) +## v3.0.3368 (2018-06-03) ### **New Features** From af2cceb6a81f9740d154782b7249c60975de514e Mon Sep 17 00:00:00 2001 From: Jamie Rees Date: Tue, 5 Jun 2018 15:13:31 +0100 Subject: [PATCH 09/55] The UI looks at the local time to see if the JWT token has expired. Use local time to generate the token --- src/Ombi/Controllers/TokenController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ombi/Controllers/TokenController.cs b/src/Ombi/Controllers/TokenController.cs index a488ba172..b45752af4 100644 --- a/src/Ombi/Controllers/TokenController.cs +++ b/src/Ombi/Controllers/TokenController.cs @@ -126,7 +126,7 @@ namespace Ombi.Controllers var token = new JwtSecurityToken( claims: claims, - expires: rememberMe ? DateTime.UtcNow.AddDays(7) : DateTime.UtcNow.AddHours(5), + expires: rememberMe ? DateTime.Now.AddDays(7) : DateTime.Now.AddDays(1), signingCredentials: creds, audience: "Ombi", issuer: "Ombi" ); From 02f941767e9b963d91435d71282e042515d49d50 Mon Sep 17 00:00:00 2001 From: Jamie Date: Wed, 6 Jun 2018 21:10:42 +0100 Subject: [PATCH 10/55] Fixed the issue with the Recently Added Sync sometimes not working as expected --- .../Jobs/Plex/PlexContentSync.cs | 29 ++++++++++++------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/src/Ombi.Schedule/Jobs/Plex/PlexContentSync.cs b/src/Ombi.Schedule/Jobs/Plex/PlexContentSync.cs index 57804d0f3..7fdb5d19a 100644 --- a/src/Ombi.Schedule/Jobs/Plex/PlexContentSync.cs +++ b/src/Ombi.Schedule/Jobs/Plex/PlexContentSync.cs @@ -147,7 +147,7 @@ namespace Ombi.Schedule.Jobs.Plex private async Task> ProcessServer(PlexServers servers, bool recentlyAddedSearch) { - var processedContent = new HashSet(); + var processedContent = new Dictionary(); Logger.LogInformation("Getting all content from server {0}", servers.Name); var allContent = await GetAllContent(servers, recentlyAddedSearch); Logger.LogInformation("We found {0} items", allContent.Count); @@ -175,7 +175,7 @@ namespace Ombi.Schedule.Jobs.Plex continue; } - await ProcessTvShow(servers, show, contentToAdd, recentlyAddedSearch, processedContent); + await ProcessTvShow(servers, show, contentToAdd, processedContent); if (contentToAdd.Any()) { await Repo.AddRange(contentToAdd, false); @@ -183,7 +183,7 @@ namespace Ombi.Schedule.Jobs.Plex { foreach (var plexServerContent in contentToAdd) { - processedContent.Add(plexServerContent.Id); + processedContent.Add(plexServerContent.Id, plexServerContent.Key); } } contentToAdd.Clear(); @@ -208,7 +208,7 @@ namespace Ombi.Schedule.Jobs.Plex foreach (var show in content.Metadata ?? new Metadata[] { }) { count++; - await ProcessTvShow(servers, show, contentToAdd, recentlyAddedSearch, processedContent); + await ProcessTvShow(servers, show, contentToAdd, processedContent); if (contentToAdd.Any()) { @@ -217,7 +217,7 @@ namespace Ombi.Schedule.Jobs.Plex { foreach (var plexServerContent in contentToAdd) { - processedContent.Add(plexServerContent.Id); + processedContent.Add(plexServerContent.Id, plexServerContent.Key); } } contentToAdd.Clear(); @@ -299,7 +299,7 @@ namespace Ombi.Schedule.Jobs.Plex await Repo.AddRange(contentToAdd); foreach (var c in contentToAdd) { - processedContent.Add(c.Id); + processedContent.Add(c.Id, c.Key); } contentToAdd.Clear(); } @@ -310,7 +310,7 @@ namespace Ombi.Schedule.Jobs.Plex await Repo.AddRange(contentToAdd); foreach (var c in contentToAdd) { - processedContent.Add(c.Id); + processedContent.Add(c.Id, c.Key); } contentToAdd.Clear(); } @@ -321,14 +321,14 @@ namespace Ombi.Schedule.Jobs.Plex await Repo.AddRange(contentToAdd); foreach (var c in contentToAdd) { - processedContent.Add(c.Id); + processedContent.Add(c.Id, c.Key); } } - return processedContent; + return processedContent.Values; } - private async Task ProcessTvShow(PlexServers servers, Metadata show, HashSet contentToAdd, bool recentlyAdded, HashSet contentProcessed) + private async Task ProcessTvShow(PlexServers servers, Metadata show, HashSet contentToAdd, Dictionary contentProcessed) { var seasonList = await PlexApi.GetSeasons(servers.PlexAuthToken, servers.FullUri, show.ratingKey); @@ -349,7 +349,7 @@ namespace Ombi.Schedule.Jobs.Plex var existingContent = await Repo.GetFirstContentByCustom(x => x.Title == show.title && x.ReleaseYear == show.year.ToString() && x.Type == PlexMediaTypeEntity.Show); - + // Just double check the rating key, since this is our unique constraint var existingKey = await Repo.GetByKey(show.ratingKey); @@ -397,6 +397,13 @@ namespace Ombi.Schedule.Jobs.Plex } } + // Also make sure it's not already being processed... + var alreadyProcessed = contentProcessed.Select(x => x.Value).Any(x => x == show.ratingKey); + if (alreadyProcessed) + { + return; + } + // The ratingKey keeps changing... //var existingContent = await Repo.GetByKey(show.ratingKey); if (existingContent != null) From 29dc293570940702eeda240a8d6aa07dcd60e194 Mon Sep 17 00:00:00 2001 From: Jamie Date: Wed, 6 Jun 2018 21:14:58 +0100 Subject: [PATCH 11/55] Run the availability checker on finish of the recentlty added sync --- .../Jobs/Ombi/RefreshMetadata.cs | 27 ++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/src/Ombi.Schedule/Jobs/Ombi/RefreshMetadata.cs b/src/Ombi.Schedule/Jobs/Ombi/RefreshMetadata.cs index 4eae94666..d58c29ddc 100644 --- a/src/Ombi.Schedule/Jobs/Ombi/RefreshMetadata.cs +++ b/src/Ombi.Schedule/Jobs/Ombi/RefreshMetadata.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using Hangfire; using Microsoft.Extensions.Logging; using Ombi.Api.TheMovieDb; using Ombi.Api.TheMovieDb.Models; @@ -9,6 +10,8 @@ using Ombi.Api.TvMaze; using Ombi.Core.Settings; using Ombi.Core.Settings.Models.External; using Ombi.Helpers; +using Ombi.Schedule.Jobs.Emby; +using Ombi.Schedule.Jobs.Plex; using Ombi.Store.Entities; using Ombi.Store.Repository; @@ -18,7 +21,7 @@ namespace Ombi.Schedule.Jobs.Ombi { public RefreshMetadata(IPlexContentRepository plexRepo, IEmbyContentRepository embyRepo, ILogger log, ITvMazeApi tvApi, ISettingsService plexSettings, - IMovieDbApi movieApi, ISettingsService embySettings) + IMovieDbApi movieApi, ISettingsService embySettings, IPlexAvailabilityChecker plexAvailability, IEmbyAvaliabilityChecker embyAvaliability) { _plexRepo = plexRepo; _embyRepo = embyRepo; @@ -27,10 +30,14 @@ namespace Ombi.Schedule.Jobs.Ombi _tvApi = tvApi; _plexSettings = plexSettings; _embySettings = embySettings; + _plexAvailabilityChecker = plexAvailability; + _embyAvaliabilityChecker = embyAvaliability; } private readonly IPlexContentRepository _plexRepo; private readonly IEmbyContentRepository _embyRepo; + private readonly IPlexAvailabilityChecker _plexAvailabilityChecker; + private readonly IEmbyAvaliabilityChecker _embyAvaliabilityChecker; private readonly ILogger _log; private readonly IMovieDbApi _movieApi; private readonly ITvMazeApi _tvApi; @@ -64,10 +71,11 @@ namespace Ombi.Schedule.Jobs.Ombi public async Task ProcessPlexServerContent(IEnumerable contentIds) { _log.LogInformation("Starting the Metadata refresh from RecentlyAddedSync"); + var plexSettings = await _plexSettings.GetSettingsAsync(); + var embySettings = await _embySettings.GetSettingsAsync(); try { - var settings = await _plexSettings.GetSettingsAsync(); - if (settings.Enable) + if (plexSettings.Enable) { await StartPlexWithKnownContent(contentIds); } @@ -77,6 +85,19 @@ namespace Ombi.Schedule.Jobs.Ombi _log.LogError(e, "Exception when refreshing the Plex Metadata"); throw; } + finally + { + if (plexSettings.Enable) + { + BackgroundJob.Enqueue(() => _plexAvailabilityChecker.Start()); + } + + if (embySettings.Enable) + { + BackgroundJob.Enqueue(() => _embyAvaliabilityChecker.Start()); + + } + } } private async Task StartPlexWithKnownContent(IEnumerable contentids) From 802ef183e4081049b94043fdeaedb22411c0fe3d Mon Sep 17 00:00:00 2001 From: Jamie Date: Thu, 7 Jun 2018 21:39:40 +0100 Subject: [PATCH 12/55] !wip changelog --- CHANGELOG.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c75a0206..c9c0c46fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,12 +2,25 @@ ## (unreleased) +### **Fixes** + +- Run the availability checker on finish of the recentlty added sync. [Jamie] + +- Fixed the issue with the Recently Added Sync sometimes not working as expected. [Jamie] + +- The UI looks at the local time to see if the JWT token has expired. Use local time to generate the token. [Jamie Rees] + + +## v3.0.3368 (2018-06-03) + ### **New Features** - Added the subscribe on the sarch page. [Jamie Rees] - Added the subscribe button to the search page if we have an existing request. [Jamie Rees] +- Update CHANGELOG.md. [Jamie] + ### **Fixes** - Use selected episodes in submitRequest. [Calvin] From b036e1c001ace770e7aae3139791c86dea711cf5 Mon Sep 17 00:00:00 2001 From: Jamie Date: Thu, 7 Jun 2018 21:57:29 +0100 Subject: [PATCH 13/55] Minor improvements --- src/Ombi.Core/Engine/TvRequestEngine.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Ombi.Core/Engine/TvRequestEngine.cs b/src/Ombi.Core/Engine/TvRequestEngine.cs index 8bb6d86e6..1667873ea 100644 --- a/src/Ombi.Core/Engine/TvRequestEngine.cs +++ b/src/Ombi.Core/Engine/TvRequestEngine.cs @@ -178,6 +178,7 @@ namespace Ombi.Core.Engine .Include(x => x.ChildRequests) .ThenInclude(x => x.SeasonRequests) .ThenInclude(x => x.Episodes) + .Where(x => x.ChildRequests.Any()) .OrderByDescending(x => x.ChildRequests.Max(y => y.RequestedDate)) .Skip(position).Take(count).ToListAsync(); @@ -189,6 +190,7 @@ namespace Ombi.Core.Engine .Include(x => x.ChildRequests) .ThenInclude(x => x.SeasonRequests) .ThenInclude(x => x.Episodes) + .Where(x => x.ChildRequests.Any()) .OrderByDescending(x => x.ChildRequests.Max(y => y.RequestedDate)) .Skip(position).Take(count).ToListAsync(); } From 4158589ec30b876700d14e65940b574f23d9eafd Mon Sep 17 00:00:00 2001 From: Jamie Date: Thu, 7 Jun 2018 22:22:35 +0100 Subject: [PATCH 14/55] !wip changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e531bc248..2404e5203 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## v3.0.3368 (2018-06-03) +## v3.0.3383 (2018-06-07) ### **Fixes** From b6efe9760234f214d51afe00e2c8ccf78a35039d Mon Sep 17 00:00:00 2001 From: Jamie Date: Tue, 12 Jun 2018 21:33:47 +0100 Subject: [PATCH 15/55] Update build.cake --- build.cake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.cake b/build.cake index a497c5f77..d200eac98 100644 --- a/build.cake +++ b/build.cake @@ -26,7 +26,7 @@ var csProj = "./src/Ombi/Ombi.csproj"; // Path to the project.csproj var solutionFile = "Ombi.sln"; // Solution file if needed GitVersion versionInfo = null; -var frameworkVer = "netcoreapp2.0"; +var frameworkVer = "netcoreapp2.1"; var buildSettings = new DotNetCoreBuildSettings { From dc30f7b6657d12169618e6774ca2cccb84260790 Mon Sep 17 00:00:00 2001 From: Jamie Date: Wed, 13 Jun 2018 08:38:18 +0100 Subject: [PATCH 16/55] Update appveyor.yml --- appveyor.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 39d67b91d..862993a21 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -15,19 +15,19 @@ test: off after_build: - cmd: >- - appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.0\windows.zip" + appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.1\windows.zip" - appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.0\osx.tar.gz" + appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.1\osx.tar.gz" - appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.0\linux.tar.gz" + appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.1\linux.tar.gz" - appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.0\linux-arm.tar.gz" + appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.1\linux-arm.tar.gz" - appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.0\windows-32bit.zip" + appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.1\windows-32bit.zip" # appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.0\linux-arm64.tar.gz" From 919d245f630bd66ec79dad3dbe5211f490ff991a Mon Sep 17 00:00:00 2001 From: Jamie Date: Wed, 13 Jun 2018 21:37:51 +0100 Subject: [PATCH 17/55] Fixed #2317 --- src/Ombi.Helpers/OmbiRoles.cs | 2 +- src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs | 2 +- src/Ombi.Store/Context/OmbiContext.cs | 6 +- .../20180613203443_RoleRename.Designer.cs | 981 ++++++++++++++++++ .../Migrations/20180613203443_RoleRename.cs | 24 + .../Migrations/OmbiContextModelSnapshot.cs | 2 +- src/Ombi/Controllers/IdentityController.cs | 2 +- 7 files changed, 1012 insertions(+), 7 deletions(-) create mode 100644 src/Ombi.Store/Migrations/20180613203443_RoleRename.Designer.cs create mode 100644 src/Ombi.Store/Migrations/20180613203443_RoleRename.cs diff --git a/src/Ombi.Helpers/OmbiRoles.cs b/src/Ombi.Helpers/OmbiRoles.cs index ba8c8d087..5b62a2bae 100644 --- a/src/Ombi.Helpers/OmbiRoles.cs +++ b/src/Ombi.Helpers/OmbiRoles.cs @@ -11,6 +11,6 @@ public const string RequestTv = nameof(RequestTv); public const string RequestMovie = nameof(RequestMovie); public const string Disabled = nameof(Disabled); - public const string RecievesNewsletter = nameof(RecievesNewsletter); + public const string ReceivesNewsletter = nameof(ReceivesNewsletter); } } \ No newline at end of file diff --git a/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs b/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs index 3c6a4458f..8f88a4a18 100644 --- a/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs +++ b/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs @@ -131,7 +131,7 @@ namespace Ombi.Schedule.Jobs.Ombi if (!test) { // Get the users to send it to - var users = await _userManager.GetUsersInRoleAsync(OmbiRoles.RecievesNewsletter); + var users = await _userManager.GetUsersInRoleAsync(OmbiRoles.ReceivesNewsletter); if (!users.Any()) { return; diff --git a/src/Ombi.Store/Context/OmbiContext.cs b/src/Ombi.Store/Context/OmbiContext.cs index 8fb05f6ae..e9937509e 100644 --- a/src/Ombi.Store/Context/OmbiContext.cs +++ b/src/Ombi.Store/Context/OmbiContext.cs @@ -117,12 +117,12 @@ namespace Ombi.Store.Context Database.ExecuteSqlCommand("VACUUM;"); // Make sure we have the roles - var roles = Roles.Where(x => x.Name == OmbiRoles.RecievesNewsletter); + var roles = Roles.Where(x => x.Name == OmbiRoles.ReceivesNewsletter); if (!roles.Any()) { - Roles.Add(new IdentityRole(OmbiRoles.RecievesNewsletter) + Roles.Add(new IdentityRole(OmbiRoles.ReceivesNewsletter) { - NormalizedName = OmbiRoles.RecievesNewsletter.ToUpper() + NormalizedName = OmbiRoles.ReceivesNewsletter.ToUpper() }); SaveChanges(); } diff --git a/src/Ombi.Store/Migrations/20180613203443_RoleRename.Designer.cs b/src/Ombi.Store/Migrations/20180613203443_RoleRename.Designer.cs new file mode 100644 index 000000000..c370e06ce --- /dev/null +++ b/src/Ombi.Store/Migrations/20180613203443_RoleRename.Designer.cs @@ -0,0 +1,981 @@ +// +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage; +using Microsoft.EntityFrameworkCore.Storage.Internal; +using Ombi.Helpers; +using Ombi.Store.Context; +using Ombi.Store.Entities; +using Ombi.Store.Entities.Requests; +using System; + +namespace Ombi.Store.Migrations +{ + [DbContext(typeof(OmbiContext))] + [Migration("20180613203443_RoleRename")] + partial class RoleRename + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.0.3-rtm-10026"); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Name") + .HasMaxLength(256); + + b.Property("NormalizedName") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasName("RoleNameIndex"); + + b.ToTable("AspNetRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("RoleId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider"); + + b.Property("ProviderKey"); + + b.Property("ProviderDisplayName"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId"); + + b.Property("RoleId"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId"); + + b.Property("LoginProvider"); + + b.Property("Name"); + + b.Property("Value"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.ApplicationConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Type"); + + b.Property("Value"); + + b.HasKey("Id"); + + b.ToTable("ApplicationConfiguration"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Audit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AuditArea"); + + b.Property("AuditType"); + + b.Property("DateTime"); + + b.Property("Description"); + + b.Property("User"); + + b.HasKey("Id"); + + b.ToTable("Audit"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.CouchPotatoCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TheMovieDbId"); + + b.HasKey("Id"); + + b.ToTable("CouchPotatoCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("EmbyId") + .IsRequired(); + + b.Property("ImdbId"); + + b.Property("ProviderId"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.Property("Type"); + + b.Property("Url"); + + b.HasKey("Id"); + + b.ToTable("EmbyContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("EmbyId"); + + b.Property("EpisodeNumber"); + + b.Property("ImdbId"); + + b.Property("ParentId"); + + b.Property("ProviderId"); + + b.Property("SeasonNumber"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.ToTable("EmbyEpisode"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.GlobalSettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Content"); + + b.Property("SettingsName"); + + b.HasKey("Id"); + + b.ToTable("GlobalSettings"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationTemplates", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Agent"); + + b.Property("Enabled"); + + b.Property("Message"); + + b.Property("NotificationType"); + + b.Property("Subject"); + + b.HasKey("Id"); + + b.ToTable("NotificationTemplates"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationUserId", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("PlayerId"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("NotificationUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.OmbiUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AccessFailedCount"); + + b.Property("Alias"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Email") + .HasMaxLength(256); + + b.Property("EmailConfirmed"); + + b.Property("EmbyConnectUserId"); + + b.Property("EpisodeRequestLimit"); + + b.Property("LastLoggedIn"); + + b.Property("LockoutEnabled"); + + b.Property("LockoutEnd"); + + b.Property("MovieRequestLimit"); + + b.Property("NormalizedEmail") + .HasMaxLength(256); + + b.Property("NormalizedUserName") + .HasMaxLength(256); + + b.Property("PasswordHash"); + + b.Property("PhoneNumber"); + + b.Property("PhoneNumberConfirmed"); + + b.Property("ProviderUserId"); + + b.Property("SecurityStamp"); + + b.Property("TwoFactorEnabled"); + + b.Property("UserAccessToken"); + + b.Property("UserName") + .HasMaxLength(256); + + b.Property("UserType"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasName("UserNameIndex"); + + b.ToTable("AspNetUsers"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeNumber"); + + b.Property("GrandparentKey"); + + b.Property("Key"); + + b.Property("ParentKey"); + + b.Property("SeasonNumber"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("GrandparentKey"); + + b.ToTable("PlexEpisode"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ParentKey"); + + b.Property("PlexContentId"); + + b.Property("PlexServerContentId"); + + b.Property("SeasonKey"); + + b.Property("SeasonNumber"); + + b.HasKey("Id"); + + b.HasIndex("PlexServerContentId"); + + b.ToTable("PlexSeasonsContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexServerContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("ImdbId"); + + b.Property("Key"); + + b.Property("Quality"); + + b.Property("ReleaseYear"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.Property("Type"); + + b.Property("Url"); + + b.HasKey("Id"); + + b.ToTable("PlexServerContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RadarrCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("HasFile"); + + b.Property("TheMovieDbId"); + + b.HasKey("Id"); + + b.ToTable("RadarrCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RecentlyAddedLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("ContentId"); + + b.Property("ContentType"); + + b.Property("EpisodeNumber"); + + b.Property("SeasonNumber"); + + b.Property("Type"); + + b.HasKey("Id"); + + b.ToTable("RecentlyAddedLog"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Approved"); + + b.Property("Available"); + + b.Property("Denied"); + + b.Property("DeniedReason"); + + b.Property("IssueId"); + + b.Property("ParentRequestId"); + + b.Property("RequestType"); + + b.Property("RequestedDate"); + + b.Property("RequestedUserId"); + + b.Property("SeriesType"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("ParentRequestId"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("ChildRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueCategory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Value"); + + b.HasKey("Id"); + + b.ToTable("IssueCategory"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueComments", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Comment"); + + b.Property("Date"); + + b.Property("IssuesId"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("IssuesId"); + + b.HasIndex("UserId"); + + b.ToTable("IssueComments"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Description"); + + b.Property("IssueCategoryId"); + + b.Property("IssueId"); + + b.Property("ProviderId"); + + b.Property("RequestId"); + + b.Property("RequestType"); + + b.Property("ResovledDate"); + + b.Property("Status"); + + b.Property("Subject"); + + b.Property("Title"); + + b.Property("UserReportedId"); + + b.HasKey("Id"); + + b.HasIndex("IssueCategoryId"); + + b.HasIndex("IssueId"); + + b.HasIndex("UserReportedId"); + + b.ToTable("Issues"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Approved"); + + b.Property("Available"); + + b.Property("Background"); + + b.Property("Denied"); + + b.Property("DeniedReason"); + + b.Property("DigitalReleaseDate"); + + b.Property("ImdbId"); + + b.Property("IssueId"); + + b.Property("Overview"); + + b.Property("PosterPath"); + + b.Property("QualityOverride"); + + b.Property("ReleaseDate"); + + b.Property("RequestType"); + + b.Property("RequestedDate"); + + b.Property("RequestedUserId"); + + b.Property("RootPathOverride"); + + b.Property("Status"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("MovieRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.RequestLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeCount"); + + b.Property("RequestDate"); + + b.Property("RequestId"); + + b.Property("RequestType"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("RequestLog"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.TvRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Background"); + + b.Property("ImdbId"); + + b.Property("Overview"); + + b.Property("PosterPath"); + + b.Property("QualityOverride"); + + b.Property("ReleaseDate"); + + b.Property("RootFolder"); + + b.Property("Status"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("TvRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RequestSubscription", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("RequestId"); + + b.Property("RequestType"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("RequestSubscription"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SickRageCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SickRageCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SickRageEpisodeCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeNumber"); + + b.Property("SeasonNumber"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SickRageEpisodeCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SonarrCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SonarrCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SonarrEpisodeCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeNumber"); + + b.Property("HasFile"); + + b.Property("SeasonNumber"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SonarrEpisodeCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Tokens", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Token"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Tokens"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AirDate"); + + b.Property("Approved"); + + b.Property("Available"); + + b.Property("EpisodeNumber"); + + b.Property("Requested"); + + b.Property("SeasonId"); + + b.Property("Title"); + + b.Property("Url"); + + b.HasKey("Id"); + + b.HasIndex("SeasonId"); + + b.ToTable("EpisodeRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChildRequestId"); + + b.Property("SeasonNumber"); + + b.HasKey("Id"); + + b.HasIndex("ChildRequestId"); + + b.ToTable("SeasonRequests"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b => + { + b.HasOne("Ombi.Store.Entities.EmbyContent", "Series") + .WithMany("Episodes") + .HasForeignKey("ParentId") + .HasPrincipalKey("EmbyId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationUserId", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany("NotificationUserIds") + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b => + { + b.HasOne("Ombi.Store.Entities.PlexServerContent", "Series") + .WithMany("Episodes") + .HasForeignKey("GrandparentKey") + .HasPrincipalKey("Key") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b => + { + b.HasOne("Ombi.Store.Entities.PlexServerContent") + .WithMany("Seasons") + .HasForeignKey("PlexServerContentId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b => + { + b.HasOne("Ombi.Store.Entities.Requests.TvRequests", "ParentRequest") + .WithMany("ChildRequests") + .HasForeignKey("ParentRequestId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueComments", b => + { + b.HasOne("Ombi.Store.Entities.Requests.Issues", "Issues") + .WithMany("Comments") + .HasForeignKey("IssuesId"); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b => + { + b.HasOne("Ombi.Store.Entities.Requests.IssueCategory", "IssueCategory") + .WithMany() + .HasForeignKey("IssueCategoryId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ombi.Store.Entities.Requests.ChildRequests") + .WithMany("Issues") + .HasForeignKey("IssueId"); + + b.HasOne("Ombi.Store.Entities.Requests.MovieRequests") + .WithMany("Issues") + .HasForeignKey("IssueId"); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "UserReported") + .WithMany() + .HasForeignKey("UserReportedId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.RequestLog", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RequestSubscription", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Tokens", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b => + { + b.HasOne("Ombi.Store.Repository.Requests.SeasonRequests", "Season") + .WithMany("Episodes") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b => + { + b.HasOne("Ombi.Store.Entities.Requests.ChildRequests", "ChildRequest") + .WithMany("SeasonRequests") + .HasForeignKey("ChildRequestId") + .OnDelete(DeleteBehavior.Cascade); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Ombi.Store/Migrations/20180613203443_RoleRename.cs b/src/Ombi.Store/Migrations/20180613203443_RoleRename.cs new file mode 100644 index 000000000..76c0023f1 --- /dev/null +++ b/src/Ombi.Store/Migrations/20180613203443_RoleRename.cs @@ -0,0 +1,24 @@ +using Microsoft.EntityFrameworkCore.Migrations; +using System; +using System.Collections.Generic; + +namespace Ombi.Store.Migrations +{ + public partial class RoleRename : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.Sql(@" + UPDATE AspNetRoles +SET Name = 'ReceivesNewsletter', +NORMALIZEDNAME = 'RECEIVESNEWSLETTER' +where Name = 'RecievesNewsletter' + "); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + + } + } +} diff --git a/src/Ombi.Store/Migrations/OmbiContextModelSnapshot.cs b/src/Ombi.Store/Migrations/OmbiContextModelSnapshot.cs index 2da0729a8..ea624fadb 100644 --- a/src/Ombi.Store/Migrations/OmbiContextModelSnapshot.cs +++ b/src/Ombi.Store/Migrations/OmbiContextModelSnapshot.cs @@ -947,7 +947,7 @@ namespace Ombi.Store.Migrations modelBuilder.Entity("Ombi.Store.Entities.RequestSubscription", b => { - b.HasOne("Ombi.Store.Entities.OmbiUser", "user") + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") .WithMany() .HasForeignKey("UserId"); }); diff --git a/src/Ombi/Controllers/IdentityController.cs b/src/Ombi/Controllers/IdentityController.cs index 4aaf86d4b..e9fb17af6 100644 --- a/src/Ombi/Controllers/IdentityController.cs +++ b/src/Ombi/Controllers/IdentityController.cs @@ -203,7 +203,7 @@ namespace Ombi.Controllers await CreateRole(OmbiRoles.RequestMovie); await CreateRole(OmbiRoles.RequestTv); await CreateRole(OmbiRoles.Disabled); - await CreateRole(OmbiRoles.RecievesNewsletter); + await CreateRole(OmbiRoles.ReceivesNewsletter); } private async Task CreateRole(string role) From 4cdd0e5875928d30e7ee7cae079c78de535b7e14 Mon Sep 17 00:00:00 2001 From: Jamie Date: Wed, 13 Jun 2018 21:44:55 +0100 Subject: [PATCH 18/55] Fixed #2312 --- .../NotificationMessageCurlys.cs | 24 +++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/src/Ombi.Notifications/NotificationMessageCurlys.cs b/src/Ombi.Notifications/NotificationMessageCurlys.cs index fb5dd976b..dce0ce482 100644 --- a/src/Ombi.Notifications/NotificationMessageCurlys.cs +++ b/src/Ombi.Notifications/NotificationMessageCurlys.cs @@ -44,8 +44,17 @@ namespace Ombi.Notifications } Overview = req?.Overview; Year = req?.ReleaseDate.Year.ToString(); - PosterImage = req?.RequestType == RequestType.Movie ? - string.Format("https://image.tmdb.org/t/p/w300{0}", req?.PosterPath) : req?.PosterPath; + + 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 + { + PosterImage = req?.PosterPath; + } + AdditionalInformation = opts?.AdditionalInformation ?? string.Empty; } @@ -88,8 +97,15 @@ namespace Ombi.Notifications Overview = req?.ParentRequest.Overview; Year = req?.ParentRequest.ReleaseDate.Year.ToString(); - PosterImage = req?.RequestType == RequestType.Movie ? - $"https://image.tmdb.org/t/p/w300{req?.ParentRequest.PosterPath}" : req?.ParentRequest.PosterPath; + 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 + { + PosterImage = req?.ParentRequest.PosterPath; + } AdditionalInformation = opts.AdditionalInformation; // DO Episode and Season Lists From 1bd62d13d03ec2d14c08a6e4b6e0f082337a0286 Mon Sep 17 00:00:00 2001 From: Jamie Date: Wed, 13 Jun 2018 21:50:26 +0100 Subject: [PATCH 19/55] Maybe this will fix #2298 --- .../ClientApp/app/settings/massemail/massemail.component.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Ombi/ClientApp/app/settings/massemail/massemail.component.html b/src/Ombi/ClientApp/app/settings/massemail/massemail.component.html index c0191b322..e66d83a18 100644 --- a/src/Ombi/ClientApp/app/settings/massemail/massemail.component.html +++ b/src/Ombi/ClientApp/app/settings/massemail/massemail.component.html @@ -39,8 +39,8 @@
- - + +
From e2dbdc190a87f2fcf9045cdb7da62719e11bc76e Mon Sep 17 00:00:00 2001 From: Jamie Date: Wed, 13 Jun 2018 21:57:00 +0100 Subject: [PATCH 20/55] Fixed #2321 --- src/Ombi.Notifications/NotificationMessageCurlys.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Ombi.Notifications/NotificationMessageCurlys.cs b/src/Ombi.Notifications/NotificationMessageCurlys.cs index dce0ce482..748f2adb5 100644 --- a/src/Ombi.Notifications/NotificationMessageCurlys.cs +++ b/src/Ombi.Notifications/NotificationMessageCurlys.cs @@ -149,6 +149,8 @@ namespace Ombi.Notifications 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; } private void LoadIssues(NotificationOptions opts) From 8cbdab8e220ad6415c68c9b5d78ed4c5d6a76f7c Mon Sep 17 00:00:00 2001 From: Jamie Date: Thu, 14 Jun 2018 22:08:28 +0100 Subject: [PATCH 21/55] Fixed the API not working due to a bug in .Net Core 2.1 --- src/Ombi/Startup.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ombi/Startup.cs b/src/Ombi/Startup.cs index 4b373d457..8037a3021 100644 --- a/src/Ombi/Startup.cs +++ b/src/Ombi/Startup.cs @@ -218,7 +218,7 @@ namespace Ombi app.UseMiddleware(); - app.ApiKeyMiddlewear(serviceProvider); + app.ApiKeyMiddlewear(app.ApplicationServices); app.UseSwagger(); app.UseSwaggerUI(c => { From 05a0369acaf445aaf2d0833ebb85cd131da8b2ab Mon Sep 17 00:00:00 2001 From: Jamie Date: Fri, 15 Jun 2018 22:04:06 +0100 Subject: [PATCH 22/55] Fixed the issue where when we find an episode for the recently added sync, we don't check if we should run the availbility checker. --- .../Interfaces/IPlexAvailabilityChecker.cs | 3 +- .../Jobs/Plex/Interfaces/IPlexEpisodeSync.cs | 3 +- .../Jobs/Plex/Models/ProcessedContent.cs | 14 +++ .../Jobs/Plex/PlexAvailabilityChecker.cs | 28 +++++- .../Jobs/Plex/PlexContentSync.cs | 89 ++++++++++++------- .../Jobs/Plex/PlexEpisodeSync.cs | 3 +- .../Repository/PlexContentRepository.cs | 1 + 7 files changed, 102 insertions(+), 39 deletions(-) create mode 100644 src/Ombi.Schedule/Jobs/Plex/Models/ProcessedContent.cs diff --git a/src/Ombi.Schedule/Jobs/Plex/Interfaces/IPlexAvailabilityChecker.cs b/src/Ombi.Schedule/Jobs/Plex/Interfaces/IPlexAvailabilityChecker.cs index 3de9749d6..2d115ab88 100644 --- a/src/Ombi.Schedule/Jobs/Plex/Interfaces/IPlexAvailabilityChecker.cs +++ b/src/Ombi.Schedule/Jobs/Plex/Interfaces/IPlexAvailabilityChecker.cs @@ -1,4 +1,5 @@ -using System.Threading.Tasks; +using System.Collections.Generic; +using System.Threading.Tasks; namespace Ombi.Schedule.Jobs.Plex { diff --git a/src/Ombi.Schedule/Jobs/Plex/Interfaces/IPlexEpisodeSync.cs b/src/Ombi.Schedule/Jobs/Plex/Interfaces/IPlexEpisodeSync.cs index ede393790..7de7c3c0c 100644 --- a/src/Ombi.Schedule/Jobs/Plex/Interfaces/IPlexEpisodeSync.cs +++ b/src/Ombi.Schedule/Jobs/Plex/Interfaces/IPlexEpisodeSync.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Ombi.Api.Plex.Models; @@ -9,6 +10,6 @@ namespace Ombi.Schedule.Jobs.Plex.Interfaces public interface IPlexEpisodeSync : IBaseJob { Task Start(); - Task ProcessEpsiodes(Metadata[] episodes, IQueryable currentEpisodes); + Task> ProcessEpsiodes(Metadata[] episodes, IQueryable currentEpisodes); } } \ No newline at end of file diff --git a/src/Ombi.Schedule/Jobs/Plex/Models/ProcessedContent.cs b/src/Ombi.Schedule/Jobs/Plex/Models/ProcessedContent.cs new file mode 100644 index 000000000..be70d0029 --- /dev/null +++ b/src/Ombi.Schedule/Jobs/Plex/Models/ProcessedContent.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; +using System.Linq; + +namespace Ombi.Schedule.Jobs.Plex.Models +{ + public class ProcessedContent + { + public IEnumerable Content { get; set; } + public IEnumerable Episodes { get; set; } + + public bool HasProcessedContent => Content.Any(); + public bool HasProcessedEpisodes => Episodes.Any(); + } +} \ No newline at end of file diff --git a/src/Ombi.Schedule/Jobs/Plex/PlexAvailabilityChecker.cs b/src/Ombi.Schedule/Jobs/Plex/PlexAvailabilityChecker.cs index 343c4256e..9c86a13c4 100644 --- a/src/Ombi.Schedule/Jobs/Plex/PlexAvailabilityChecker.cs +++ b/src/Ombi.Schedule/Jobs/Plex/PlexAvailabilityChecker.cs @@ -4,10 +4,12 @@ using System.Linq; using System.Threading.Tasks; using Hangfire; using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; using Ombi.Core.Notifications; using Ombi.Helpers; using Ombi.Notifications.Models; using Ombi.Store.Entities; +using Ombi.Store.Entities.Requests; using Ombi.Store.Repository; using Ombi.Store.Repository.Requests; @@ -16,13 +18,14 @@ namespace Ombi.Schedule.Jobs.Plex public class PlexAvailabilityChecker : IPlexAvailabilityChecker { public PlexAvailabilityChecker(IPlexContentRepository repo, ITvRequestRepository tvRequest, IMovieRequestRepository movies, - INotificationService notification, IBackgroundJobClient background) + INotificationService notification, IBackgroundJobClient background, ILogger log) { _tvRepo = tvRequest; _repo = repo; _movieRepo = movies; _notificationService = notification; _backgroundJobClient = background; + _log = log; } private readonly ITvRequestRepository _tvRepo; @@ -30,16 +33,29 @@ namespace Ombi.Schedule.Jobs.Plex private readonly IPlexContentRepository _repo; private readonly INotificationService _notificationService; private readonly IBackgroundJobClient _backgroundJobClient; + private readonly ILogger _log; public async Task Start() { - await ProcessMovies(); - await ProcessTv(); + try + { + await ProcessMovies(); + await ProcessTv(); + } + catch (Exception e) + { + _log.LogError(e, "Exception thrown in Plex availbility checker"); + } } - private async Task ProcessTv() + private Task ProcessTv() { var tv = _tvRepo.GetChild().Where(x => !x.Available); + return ProcessTv(tv); + } + + private async Task ProcessTv(IQueryable tv) + { var plexEpisodes = _repo.GetAllEpisodes().Include(x => x.Series); foreach (var child in tv) @@ -81,6 +97,10 @@ namespace Ombi.Schedule.Jobs.Plex { foreach (var episode in season.Episodes) { + if (episode.Available) + { + continue; + } var foundEp = await seriesEpisodes.FirstOrDefaultAsync( x => x.EpisodeNumber == episode.EpisodeNumber && x.SeasonNumber == episode.Season.SeasonNumber); diff --git a/src/Ombi.Schedule/Jobs/Plex/PlexContentSync.cs b/src/Ombi.Schedule/Jobs/Plex/PlexContentSync.cs index 7fdb5d19a..fec69bcc3 100644 --- a/src/Ombi.Schedule/Jobs/Plex/PlexContentSync.cs +++ b/src/Ombi.Schedule/Jobs/Plex/PlexContentSync.cs @@ -39,6 +39,7 @@ using Ombi.Core.Settings.Models.External; using Ombi.Helpers; using Ombi.Schedule.Jobs.Ombi; using Ombi.Schedule.Jobs.Plex.Interfaces; +using Ombi.Schedule.Jobs.Plex.Models; using Ombi.Store.Entities; using Ombi.Store.Repository; @@ -47,7 +48,7 @@ namespace Ombi.Schedule.Jobs.Plex public class PlexContentSync : IPlexContentSync { public PlexContentSync(ISettingsService plex, IPlexApi plexApi, ILogger logger, IPlexContentRepository repo, - IPlexEpisodeSync epsiodeSync, IRefreshMetadata metadataRefresh) + IPlexEpisodeSync epsiodeSync, IRefreshMetadata metadataRefresh, IPlexAvailabilityChecker checker) { Plex = plex; PlexApi = plexApi; @@ -55,6 +56,7 @@ namespace Ombi.Schedule.Jobs.Plex Repo = repo; EpisodeSync = epsiodeSync; Metadata = metadataRefresh; + Checker = checker; plex.ClearCache(); } @@ -64,6 +66,7 @@ namespace Ombi.Schedule.Jobs.Plex private IPlexContentRepository Repo { get; } private IPlexEpisodeSync EpisodeSync { get; } private IRefreshMetadata Metadata { get; } + private IPlexAvailabilityChecker Checker { get; } public async Task CacheContent(bool recentlyAddedSearch = false) { @@ -77,17 +80,13 @@ namespace Ombi.Schedule.Jobs.Plex Logger.LogError("Plex Settings are not valid"); return; } - var processedContent = new HashSet(); + var processedContent = new ProcessedContent(); Logger.LogInformation("Starting Plex Content Cacher"); try { if (recentlyAddedSearch) { - var result = await StartTheCache(plexSettings, true); - foreach (var r in result) - { - processedContent.Add(r); - } + processedContent = await StartTheCache(plexSettings, true); } else { @@ -105,31 +104,32 @@ namespace Ombi.Schedule.Jobs.Plex BackgroundJob.Enqueue(() => EpisodeSync.Start()); } - if (processedContent.Any() && recentlyAddedSearch) + if (processedContent.HasProcessedContent && recentlyAddedSearch) { // Just check what we send it - BackgroundJob.Enqueue(() => Metadata.ProcessPlexServerContent(processedContent)); + BackgroundJob.Enqueue(() => Metadata.ProcessPlexServerContent(processedContent.Content)); + } + + if (processedContent.HasProcessedEpisodes && recentlyAddedSearch) + { + BackgroundJob.Enqueue(() => Checker.Start()); } } - private async Task> StartTheCache(PlexSettings plexSettings, bool recentlyAddedSearch) + private async Task StartTheCache(PlexSettings plexSettings, bool recentlyAddedSearch) { - var processedContent = new HashSet(); + var processedContent = new ProcessedContent(); foreach (var servers in plexSettings.Servers ?? new List()) { try { - Logger.LogInformation("Starting to cache the content on server {0}", servers.Name); - + Logger.LogInformation("Starting to cache the content on server {0}", servers.Name); + if (recentlyAddedSearch) { // If it's recently added search then we want the results to pass to the metadata job // This way the metadata job is smaller in size to process, it only need to look at newly added shit - var result = await ProcessServer(servers, true); - foreach (var plexServerContent in result) - { - processedContent.Add(plexServerContent); - } + return await ProcessServer(servers, true); } else { @@ -145,9 +145,11 @@ namespace Ombi.Schedule.Jobs.Plex return processedContent; } - private async Task> ProcessServer(PlexServers servers, bool recentlyAddedSearch) + private async Task ProcessServer(PlexServers servers, bool recentlyAddedSearch) { - var processedContent = new Dictionary(); + var retVal = new ProcessedContent(); + var contentProcessed = new Dictionary(); + var episodesProcessed = new List(); Logger.LogInformation("Getting all content from server {0}", servers.Name); var allContent = await GetAllContent(servers, recentlyAddedSearch); Logger.LogInformation("We found {0} items", allContent.Count); @@ -170,12 +172,12 @@ namespace Ombi.Schedule.Jobs.Plex // Lookup the rating key var showMetadata = await PlexApi.GetMetadata(servers.PlexAuthToken, servers.FullUri, grandParentKey); var show = showMetadata.MediaContainer.Metadata.FirstOrDefault(); - if(show == null) + if (show == null) { continue; } - await ProcessTvShow(servers, show, contentToAdd, processedContent); + await ProcessTvShow(servers, show, contentToAdd, contentProcessed); if (contentToAdd.Any()) { await Repo.AddRange(contentToAdd, false); @@ -183,7 +185,7 @@ namespace Ombi.Schedule.Jobs.Plex { foreach (var plexServerContent in contentToAdd) { - processedContent.Add(plexServerContent.Id, plexServerContent.Key); + contentProcessed.Add(plexServerContent.Id, plexServerContent.Key); } } contentToAdd.Clear(); @@ -198,7 +200,8 @@ namespace Ombi.Schedule.Jobs.Plex // Save just to make sure we don't leave anything hanging await Repo.SaveChangesAsync(); - await EpisodeSync.ProcessEpsiodes(content.Metadata, allEps); + var episodesAdded = await EpisodeSync.ProcessEpsiodes(content.Metadata, allEps); + episodesProcessed.AddRange(episodesAdded.Select(x => x.Id)); } if (content.viewGroup.Equals(PlexMediaType.Show.ToString(), StringComparison.CurrentCultureIgnoreCase)) { @@ -208,7 +211,7 @@ namespace Ombi.Schedule.Jobs.Plex foreach (var show in content.Metadata ?? new Metadata[] { }) { count++; - await ProcessTvShow(servers, show, contentToAdd, processedContent); + await ProcessTvShow(servers, show, contentToAdd, contentProcessed); if (contentToAdd.Any()) { @@ -217,7 +220,7 @@ namespace Ombi.Schedule.Jobs.Plex { foreach (var plexServerContent in contentToAdd) { - processedContent.Add(plexServerContent.Id, plexServerContent.Key); + contentProcessed.Add(plexServerContent.Id, plexServerContent.Key); } } contentToAdd.Clear(); @@ -299,7 +302,7 @@ namespace Ombi.Schedule.Jobs.Plex await Repo.AddRange(contentToAdd); foreach (var c in contentToAdd) { - processedContent.Add(c.Id, c.Key); + contentProcessed.Add(c.Id, c.Key); } contentToAdd.Clear(); } @@ -310,7 +313,7 @@ namespace Ombi.Schedule.Jobs.Plex await Repo.AddRange(contentToAdd); foreach (var c in contentToAdd) { - processedContent.Add(c.Id, c.Key); + contentProcessed.Add(c.Id, c.Key); } contentToAdd.Clear(); } @@ -321,14 +324,16 @@ namespace Ombi.Schedule.Jobs.Plex await Repo.AddRange(contentToAdd); foreach (var c in contentToAdd) { - processedContent.Add(c.Id, c.Key); + contentProcessed.Add(c.Id, c.Key); } } - return processedContent.Values; + retVal.Content = contentProcessed.Values; + retVal.Episodes = episodesProcessed; + return retVal; } - private async Task ProcessTvShow(PlexServers servers, Metadata show, HashSet contentToAdd, Dictionary contentProcessed) + private async Task ProcessTvShow(PlexServers servers, Metadata show, HashSet contentToAdd, Dictionary contentProcessed) { var seasonList = await PlexApi.GetSeasons(servers.PlexAuthToken, servers.FullUri, show.ratingKey); @@ -349,7 +354,7 @@ namespace Ombi.Schedule.Jobs.Plex var existingContent = await Repo.GetFirstContentByCustom(x => x.Title == show.title && x.ReleaseYear == show.year.ToString() && x.Type == PlexMediaTypeEntity.Show); - + // Just double check the rating key, since this is our unique constraint var existingKey = await Repo.GetByKey(show.ratingKey); @@ -422,6 +427,26 @@ namespace Ombi.Schedule.Jobs.Plex if (seasonExists != null) { // We already have this season + // check if we have the episode + //if (episode != null) + //{ + // var existing = existingContent.Episodes.Any(x => + // x.SeasonNumber == episode.parentIndex && x.EpisodeNumber == episode.index); + // if (!existing) + // { + // // We don't have this episode, lets add it + // existingContent.Episodes.Add(new PlexEpisode + // { + // EpisodeNumber = episode.index, + // SeasonNumber = episode.parentIndex, + // GrandparentKey = episode.grandparentRatingKey, + // ParentKey = episode.parentRatingKey, + // Key = episode.ratingKey, + // Title = episode.title + // }); + // itemAdded = true; + // } + //} continue; } diff --git a/src/Ombi.Schedule/Jobs/Plex/PlexEpisodeSync.cs b/src/Ombi.Schedule/Jobs/Plex/PlexEpisodeSync.cs index d98eace4a..5652d126b 100644 --- a/src/Ombi.Schedule/Jobs/Plex/PlexEpisodeSync.cs +++ b/src/Ombi.Schedule/Jobs/Plex/PlexEpisodeSync.cs @@ -129,7 +129,7 @@ namespace Ombi.Schedule.Jobs.Plex await _repo.SaveChangesAsync(); } - public async Task ProcessEpsiodes(Metadata[] episodes, IQueryable currentEpisodes) + public async Task> ProcessEpsiodes(Metadata[] episodes, IQueryable currentEpisodes) { var ep = new HashSet(); try @@ -179,6 +179,7 @@ namespace Ombi.Schedule.Jobs.Plex } await _repo.AddRange(ep); + return ep; } catch (Exception e) { diff --git a/src/Ombi.Store/Repository/PlexContentRepository.cs b/src/Ombi.Store/Repository/PlexContentRepository.cs index 098466310..e452eeb7d 100644 --- a/src/Ombi.Store/Repository/PlexContentRepository.cs +++ b/src/Ombi.Store/Repository/PlexContentRepository.cs @@ -89,6 +89,7 @@ namespace Ombi.Store.Repository { return await Db.PlexServerContent .Include(x => x.Seasons) + .Include(x => x.Episodes) .FirstOrDefaultAsync(predicate); } From 7e7376bbe54719c6262cf83cde4344cf0a91048a Mon Sep 17 00:00:00 2001 From: Jamie Date: Mon, 18 Jun 2018 19:42:20 +0100 Subject: [PATCH 23/55] !wip changelog --- CHANGELOG.md | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c9c0c46fe..45f1e34a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,8 +2,47 @@ ## (unreleased) +### **New Features** + +- Update appveyor.yml. [Jamie] + +- Update build.cake. [Jamie] + ### **Fixes** +- Fixed the issue where when we find an episode for the recently added sync, we don't check if we should run the availbility checker. [Jamie] + +- Fixed the API not working due to a bug in .Net Core 2.1. [Jamie] + +- Fixed #2321. [Jamie] + +- Maybe this will fix #2298. [Jamie] + +- Fixed #2312. [Jamie] + +- Fixed the SickRage/Medusa Issue where it was always being set as Skipped/Ignore #2084. [Jamie] + +- Fixed the sorting and filtering on the Movie Requests page, it all functions correctly now. [Jamie] + +- Fixed #2288. [Jamie] + +- Upgrade packages. [Jamie] + +- Inital Migration. [Jamie] + +- Fixed #2317. [Jamie] + + +## v3.0.3383 (2018-06-07) + +### **New Features** + +- Update CHANGELOG.md. [Jamie] + +### **Fixes** + +- Minor improvements. [Jamie] + - Run the availability checker on finish of the recentlty added sync. [Jamie] - Fixed the issue with the Recently Added Sync sometimes not working as expected. [Jamie] From 75a4683ac8c5169323519f6530c508e7cbd722ef Mon Sep 17 00:00:00 2001 From: Jamie Date: Mon, 18 Jun 2018 21:27:14 +0100 Subject: [PATCH 24/55] !wip changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 767994b0e..ec5635093 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## v3.0.3383 (2018-06-07) +## v3.0.3407 (2018-06-07) ### **New Features** From 48baef1dfdfb985ca7095b83146016681b38eca1 Mon Sep 17 00:00:00 2001 From: Jamie Date: Wed, 20 Jun 2018 21:14:16 +0100 Subject: [PATCH 25/55] Update to .net 2.1.1 --- src/Ombi.Api.Radarr/Ombi.Api.Radarr.csproj | 2 +- src/Ombi.Api.Service/Ombi.Api.Service.csproj | 2 +- src/Ombi.Api/Ombi.Api.csproj | 2 +- src/Ombi.Core.Tests/Ombi.Core.Tests.csproj | 2 +- src/Ombi.Core/Ombi.Core.csproj | 4 +--- .../Ombi.DependencyInjection.csproj | 6 +++--- src/Ombi.Helpers/Ombi.Helpers.csproj | 4 ++-- .../Ombi.Notifications.Tests.csproj | 2 +- .../Ombi.Schedule.Tests.csproj | 4 ++-- .../PlexAvailabilityCheckerTests.cs | 2 +- src/Ombi.Settings/Ombi.Settings.csproj | 2 +- src/Ombi.Store/Ombi.Store.csproj | 8 ++++---- src/Ombi.Tests/Ombi.Tests.csproj | 4 ++-- src/Ombi.Updater/Ombi.Updater.csproj | 16 ++++++++-------- src/Ombi/Controllers/SettingsController.cs | 10 ++-------- src/Ombi/Ombi.csproj | 9 ++++----- src/Ombi/Startup.cs | 10 ---------- src/Ombi/StartupExtensions.cs | 5 ++--- 18 files changed, 37 insertions(+), 57 deletions(-) diff --git a/src/Ombi.Api.Radarr/Ombi.Api.Radarr.csproj b/src/Ombi.Api.Radarr/Ombi.Api.Radarr.csproj index ff506dc35..0c615f301 100644 --- a/src/Ombi.Api.Radarr/Ombi.Api.Radarr.csproj +++ b/src/Ombi.Api.Radarr/Ombi.Api.Radarr.csproj @@ -9,7 +9,7 @@ - + diff --git a/src/Ombi.Api.Service/Ombi.Api.Service.csproj b/src/Ombi.Api.Service/Ombi.Api.Service.csproj index 4a7f4865e..8cbddd874 100644 --- a/src/Ombi.Api.Service/Ombi.Api.Service.csproj +++ b/src/Ombi.Api.Service/Ombi.Api.Service.csproj @@ -11,7 +11,7 @@ - + diff --git a/src/Ombi.Api/Ombi.Api.csproj b/src/Ombi.Api/Ombi.Api.csproj index 533bcfcda..804512945 100644 --- a/src/Ombi.Api/Ombi.Api.csproj +++ b/src/Ombi.Api/Ombi.Api.csproj @@ -9,7 +9,7 @@ - + diff --git a/src/Ombi.Core.Tests/Ombi.Core.Tests.csproj b/src/Ombi.Core.Tests/Ombi.Core.Tests.csproj index b9960aa7f..a4f6aeed8 100644 --- a/src/Ombi.Core.Tests/Ombi.Core.Tests.csproj +++ b/src/Ombi.Core.Tests/Ombi.Core.Tests.csproj @@ -9,7 +9,7 @@ - + diff --git a/src/Ombi.Core/Ombi.Core.csproj b/src/Ombi.Core/Ombi.Core.csproj index 1c09c88e3..6bd3e0f9d 100644 --- a/src/Ombi.Core/Ombi.Core.csproj +++ b/src/Ombi.Core/Ombi.Core.csproj @@ -12,9 +12,7 @@ - - - + diff --git a/src/Ombi.DependencyInjection/Ombi.DependencyInjection.csproj b/src/Ombi.DependencyInjection/Ombi.DependencyInjection.csproj index d2f94127f..2e7f984a7 100644 --- a/src/Ombi.DependencyInjection/Ombi.DependencyInjection.csproj +++ b/src/Ombi.DependencyInjection/Ombi.DependencyInjection.csproj @@ -9,9 +9,9 @@ - - - + + + diff --git a/src/Ombi.Helpers/Ombi.Helpers.csproj b/src/Ombi.Helpers/Ombi.Helpers.csproj index 4b104eadb..e94afc816 100644 --- a/src/Ombi.Helpers/Ombi.Helpers.csproj +++ b/src/Ombi.Helpers/Ombi.Helpers.csproj @@ -10,8 +10,8 @@ - - + + diff --git a/src/Ombi.Notifications.Tests/Ombi.Notifications.Tests.csproj b/src/Ombi.Notifications.Tests/Ombi.Notifications.Tests.csproj index 01721db3f..451aba98b 100644 --- a/src/Ombi.Notifications.Tests/Ombi.Notifications.Tests.csproj +++ b/src/Ombi.Notifications.Tests/Ombi.Notifications.Tests.csproj @@ -8,7 +8,7 @@ - + diff --git a/src/Ombi.Schedule.Tests/Ombi.Schedule.Tests.csproj b/src/Ombi.Schedule.Tests/Ombi.Schedule.Tests.csproj index d147af88f..24a7308cb 100644 --- a/src/Ombi.Schedule.Tests/Ombi.Schedule.Tests.csproj +++ b/src/Ombi.Schedule.Tests/Ombi.Schedule.Tests.csproj @@ -5,12 +5,12 @@ - + - + diff --git a/src/Ombi.Schedule.Tests/PlexAvailabilityCheckerTests.cs b/src/Ombi.Schedule.Tests/PlexAvailabilityCheckerTests.cs index 0ebb4732c..55c9dc288 100644 --- a/src/Ombi.Schedule.Tests/PlexAvailabilityCheckerTests.cs +++ b/src/Ombi.Schedule.Tests/PlexAvailabilityCheckerTests.cs @@ -26,7 +26,7 @@ namespace Ombi.Schedule.Tests _tv = new Mock(); _movie = new Mock(); _notify = new Mock(); - Checker = new PlexAvailabilityChecker(_repo.Object, _tv.Object, _movie.Object, _notify.Object, new Mock().Object); + Checker = new PlexAvailabilityChecker(_repo.Object, _tv.Object, _movie.Object, _notify.Object, new Mock().Object, null); } diff --git a/src/Ombi.Settings/Ombi.Settings.csproj b/src/Ombi.Settings/Ombi.Settings.csproj index 8709fd906..19a415a47 100644 --- a/src/Ombi.Settings/Ombi.Settings.csproj +++ b/src/Ombi.Settings/Ombi.Settings.csproj @@ -9,7 +9,7 @@ - + diff --git a/src/Ombi.Store/Ombi.Store.csproj b/src/Ombi.Store/Ombi.Store.csproj index b9ff73ef1..db034ca12 100644 --- a/src/Ombi.Store/Ombi.Store.csproj +++ b/src/Ombi.Store/Ombi.Store.csproj @@ -10,10 +10,10 @@ - - - - + + + + diff --git a/src/Ombi.Tests/Ombi.Tests.csproj b/src/Ombi.Tests/Ombi.Tests.csproj index 639276a75..1b7dd3fe3 100644 --- a/src/Ombi.Tests/Ombi.Tests.csproj +++ b/src/Ombi.Tests/Ombi.Tests.csproj @@ -7,12 +7,12 @@ - + - + diff --git a/src/Ombi.Updater/Ombi.Updater.csproj b/src/Ombi.Updater/Ombi.Updater.csproj index e46a45551..07fb92d81 100644 --- a/src/Ombi.Updater/Ombi.Updater.csproj +++ b/src/Ombi.Updater/Ombi.Updater.csproj @@ -12,14 +12,14 @@ - - - - - - - - + + + + + + + + diff --git a/src/Ombi/Controllers/SettingsController.cs b/src/Ombi/Controllers/SettingsController.cs index 2e489adf2..6f013b9c1 100644 --- a/src/Ombi/Controllers/SettingsController.cs +++ b/src/Ombi/Controllers/SettingsController.cs @@ -1,21 +1,15 @@ using System; using System.Collections.Generic; using System.Globalization; +using System.IO; using System.Linq; -using System.Net; -using System.Net.Http; -using System.Net.Http.Headers; using System.Reflection; using System.Runtime.InteropServices; -using System.Text; using System.Threading.Tasks; using AutoMapper; using Hangfire; -using Hangfire.RecurringJobExtensions; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Caching.Memory; -using Microsoft.Extensions.PlatformAbstractions; using NCrontab; using Ombi.Api.Emby; using Ombi.Attributes; @@ -115,7 +109,7 @@ namespace Ombi.Controllers OsArchitecture = RuntimeInformation.OSArchitecture.ToString(), OsDescription = RuntimeInformation.OSDescription, ProcessArchitecture = RuntimeInformation.ProcessArchitecture.ToString(), - ApplicationBasePath =PlatformServices.Default.Application.ApplicationBasePath + ApplicationBasePath =Directory.GetCurrentDirectory() }; var version = AssemblyHelper.GetRuntimeVersion(); diff --git a/src/Ombi/Ombi.csproj b/src/Ombi/Ombi.csproj index add872a33..628acd2bc 100644 --- a/src/Ombi/Ombi.csproj +++ b/src/Ombi/Ombi.csproj @@ -66,11 +66,10 @@ - - - - - + + + + diff --git a/src/Ombi/Startup.cs b/src/Ombi/Startup.cs index 8037a3021..9db142625 100644 --- a/src/Ombi/Startup.cs +++ b/src/Ombi/Startup.cs @@ -1,20 +1,12 @@ using System; -using System.Collections.Generic; using System.IO; -using System.Linq; -using System.Net; -using System.Security.Principal; -using System.Threading.Tasks; using AutoMapper; using AutoMapper.EquivalencyExpression; using Hangfire; -using Hangfire.Console; using Hangfire.Dashboard; using Hangfire.SQLite; -using Microsoft.ApplicationInsights.Extensibility; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.HttpOverrides; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.SpaServices.Webpack; @@ -34,7 +26,6 @@ using Ombi.Store.Context; using Ombi.Store.Entities; using Ombi.Store.Repository; using Serilog; -using Serilog.Events; namespace Ombi { @@ -87,7 +78,6 @@ namespace Ombi // This method gets called by the runtime. Use this method to add services to the container. public IServiceProvider ConfigureServices(IServiceCollection services) { - TelemetryConfiguration.Active.DisableTelemetry = true; // Add framework services. services.AddDbContext(); diff --git a/src/Ombi/StartupExtensions.cs b/src/Ombi/StartupExtensions.cs index e4dae18e4..52bc26d0d 100644 --- a/src/Ombi/StartupExtensions.cs +++ b/src/Ombi/StartupExtensions.cs @@ -2,6 +2,7 @@ using System.IO; using System.Linq; using System.Net; +using System.Reflection; using System.Security.Principal; using System.Text; using System.Threading.Tasks; @@ -11,8 +12,6 @@ using Microsoft.AspNetCore.Http; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.PlatformAbstractions; -using Microsoft.Extensions.Primitives; using Microsoft.IdentityModel.Tokens; using Ombi.Config; using Ombi.Core.Authentication; @@ -45,7 +44,7 @@ namespace Ombi } }); c.CustomSchemaIds(x => x.FullName); - var basePath = PlatformServices.Default.Application.ApplicationBasePath; + var basePath = Directory.GetCurrentDirectory(); var xmlPath = Path.Combine(basePath, "Swagger.xml"); try { From a7ccb74ad9ad14295d3c72e8a35a2bede36001b2 Mon Sep 17 00:00:00 2001 From: Jamie Rees Date: Thu, 21 Jun 2018 14:13:15 +0100 Subject: [PATCH 26/55] Added a smaller and simplier way of getting TV Request info --- .../Engine/Interfaces/ITvRequestEngine.cs | 2 + src/Ombi.Core/Engine/TvRequestEngine.cs | 64 +++++++++++++++++-- .../PlexAvailabilityCheckerTests.cs | 2 +- .../Requests/ITvRequestRepository.cs | 2 + .../Requests/TvRequestRepository.cs | 18 ++++++ src/Ombi/Controllers/RequestController.cs | 21 ++++++ src/Ombi/appsettings.json | 1 - 7 files changed, 101 insertions(+), 9 deletions(-) diff --git a/src/Ombi.Core/Engine/Interfaces/ITvRequestEngine.cs b/src/Ombi.Core/Engine/Interfaces/ITvRequestEngine.cs index 28eb066d4..064ddf6c9 100644 --- a/src/Ombi.Core/Engine/Interfaces/ITvRequestEngine.cs +++ b/src/Ombi.Core/Engine/Interfaces/ITvRequestEngine.cs @@ -10,6 +10,7 @@ namespace Ombi.Core.Engine.Interfaces { Task RemoveTvRequest(int requestId); + Task GetTvRequest(int requestId); Task RequestTvShow(TvRequestViewModel tv); Task DenyChildRequest(int requestId); Task> SearchTvRequest(string search); @@ -20,5 +21,6 @@ namespace Ombi.Core.Engine.Interfaces Task UpdateChildRequest(ChildRequests request); Task RemoveTvChild(int requestId); Task ApproveChildRequest(int id); + Task> GetRequestsLite(); } } \ No newline at end of file diff --git a/src/Ombi.Core/Engine/TvRequestEngine.cs b/src/Ombi.Core/Engine/TvRequestEngine.cs index 1667873ea..6dfd5b7f3 100644 --- a/src/Ombi.Core/Engine/TvRequestEngine.cs +++ b/src/Ombi.Core/Engine/TvRequestEngine.cs @@ -218,6 +218,45 @@ namespace Ombi.Core.Engine return allRequests; } + + public async Task> GetRequestsLite() + { + var shouldHide = await HideFromOtherUsers(); + List allRequests; + if (shouldHide.Hide) + { + allRequests = await TvRepository.GetLite(shouldHide.UserId).ToListAsync(); + + FilterChildren(allRequests, shouldHide); + } + else + { + allRequests = await TvRepository.GetLite().ToListAsync(); + } + + allRequests.ForEach(async r => { await CheckForSubscription(shouldHide, r); }); + return allRequests; + } + + public async Task GetTvRequest(int requestId) + { + var shouldHide = await HideFromOtherUsers(); + TvRequests request; + if (shouldHide.Hide) + { + request = await TvRepository.Get(shouldHide.UserId).Where(x => x.Id == requestId).FirstOrDefaultAsync(); + + FilterChildren(request, shouldHide); + } + else + { + request = await TvRepository.Get().Where(x => x.Id == requestId).FirstOrDefaultAsync(); + } + + await CheckForSubscription(shouldHide, request); + return request; + } + private static void FilterChildren(IEnumerable allRequests, HideResult shouldHide) { // Filter out children @@ -225,16 +264,27 @@ namespace Ombi.Core.Engine { for (var j = 0; j < t.ChildRequests.Count; j++) { - var child = t.ChildRequests[j]; - if (child.RequestedUserId != shouldHide.UserId) - { - t.ChildRequests.RemoveAt(j); - j--; - } + FilterChildren(t, shouldHide); } } } + private static void FilterChildren(TvRequests t, HideResult shouldHide) + { + // Filter out children + + for (var j = 0; j < t.ChildRequests.Count; j++) + { + var child = t.ChildRequests[j]; + if (child.RequestedUserId != shouldHide.UserId) + { + t.ChildRequests.RemoveAt(j); + j--; + } + } + + } + public async Task> GetAllChldren(int tvId) { var shouldHide = await HideFromOtherUsers(); @@ -470,7 +520,7 @@ namespace Ombi.Core.Engine { foreach (var tv in x.ChildRequests) { - await CheckForSubscription(shouldHide, tv); + await CheckForSubscription(shouldHide, tv); } } diff --git a/src/Ombi.Schedule.Tests/PlexAvailabilityCheckerTests.cs b/src/Ombi.Schedule.Tests/PlexAvailabilityCheckerTests.cs index 0ebb4732c..55c9dc288 100644 --- a/src/Ombi.Schedule.Tests/PlexAvailabilityCheckerTests.cs +++ b/src/Ombi.Schedule.Tests/PlexAvailabilityCheckerTests.cs @@ -26,7 +26,7 @@ namespace Ombi.Schedule.Tests _tv = new Mock(); _movie = new Mock(); _notify = new Mock(); - Checker = new PlexAvailabilityChecker(_repo.Object, _tv.Object, _movie.Object, _notify.Object, new Mock().Object); + Checker = new PlexAvailabilityChecker(_repo.Object, _tv.Object, _movie.Object, _notify.Object, new Mock().Object, null); } diff --git a/src/Ombi.Store/Repository/Requests/ITvRequestRepository.cs b/src/Ombi.Store/Repository/Requests/ITvRequestRepository.cs index 749b67c73..f08f7812f 100644 --- a/src/Ombi.Store/Repository/Requests/ITvRequestRepository.cs +++ b/src/Ombi.Store/Repository/Requests/ITvRequestRepository.cs @@ -14,7 +14,9 @@ namespace Ombi.Store.Repository.Requests Task Delete(TvRequests request); Task DeleteChild(ChildRequests request); IQueryable Get(); + IQueryable GetLite(); IQueryable Get(string userId); + IQueryable GetLite(string userId); Task GetRequestAsync(int tvDbId); TvRequests GetRequest(int tvDbId); Task Update(TvRequests request); diff --git a/src/Ombi.Store/Repository/Requests/TvRequestRepository.cs b/src/Ombi.Store/Repository/Requests/TvRequestRepository.cs index 28a141908..daac7d4df 100644 --- a/src/Ombi.Store/Repository/Requests/TvRequestRepository.cs +++ b/src/Ombi.Store/Repository/Requests/TvRequestRepository.cs @@ -60,6 +60,24 @@ namespace Ombi.Store.Repository.Requests .Where(x => x.ChildRequests.Any(a => a.RequestedUserId == userId)) .AsQueryable(); } + + public IQueryable GetLite(string userId) + { + return Db.TvRequests + .Include(x => x.ChildRequests) + .ThenInclude(x => x.RequestedUser) + .Where(x => x.ChildRequests.Any(a => a.RequestedUserId == userId)) + .AsQueryable(); + } + + public IQueryable GetLite() + { + return Db.TvRequests + .Include(x => x.ChildRequests) + .ThenInclude(x => x.RequestedUser) + .AsQueryable(); + } + public IQueryable GetChild() { return Db.ChildRequests diff --git a/src/Ombi/Controllers/RequestController.cs b/src/Ombi/Controllers/RequestController.cs index 47329a0ec..95ea4c09e 100644 --- a/src/Ombi/Controllers/RequestController.cs +++ b/src/Ombi/Controllers/RequestController.cs @@ -204,6 +204,27 @@ namespace Ombi.Controllers return await TvRequestEngine.GetRequests(); } + /// + /// Gets the tv requests without the whole object graph (Does not include seasons/episodes). + /// + /// + [HttpGet("tvlite")] + public async Task> GetTvRequestsLite() + { + return await TvRequestEngine.GetRequestsLite(); + } + + /// + /// Returns the full request object for the specified requestId + /// + /// + /// + [HttpGet("tv/{requestId:int}")] + public async Task GetTvRequest(int requestId) + { + return await TvRequestEngine.GetTvRequest(requestId); + } + /// /// Requests a tv show/episode/season. /// diff --git a/src/Ombi/appsettings.json b/src/Ombi/appsettings.json index ed9a1b88a..9505f62a2 100644 --- a/src/Ombi/appsettings.json +++ b/src/Ombi/appsettings.json @@ -10,7 +10,6 @@ }, "ApplicationSettings": { "Verison": "{{VERSIONNUMBER}}", - "OmbiService": "https://ombiservice.azurewebsites.net/", "Branch": "{{BRANCH}}", "FriendlyVersion": "v3.0.0" }, From 6adb0351ea9b3d0837c81952964b3315ffa92429 Mon Sep 17 00:00:00 2001 From: Jamie Date: Thu, 21 Jun 2018 22:33:05 +0100 Subject: [PATCH 27/55] Fixed #2348 --- .../Jobs/Plex/PlexContentSync.cs | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/Ombi.Schedule/Jobs/Plex/PlexContentSync.cs b/src/Ombi.Schedule/Jobs/Plex/PlexContentSync.cs index fec69bcc3..9184755f0 100644 --- a/src/Ombi.Schedule/Jobs/Plex/PlexContentSync.cs +++ b/src/Ombi.Schedule/Jobs/Plex/PlexContentSync.cs @@ -104,13 +104,13 @@ namespace Ombi.Schedule.Jobs.Plex BackgroundJob.Enqueue(() => EpisodeSync.Start()); } - if (processedContent.HasProcessedContent && recentlyAddedSearch) + if ((processedContent?.HasProcessedContent ?? false) && recentlyAddedSearch) { // Just check what we send it BackgroundJob.Enqueue(() => Metadata.ProcessPlexServerContent(processedContent.Content)); } - if (processedContent.HasProcessedEpisodes && recentlyAddedSearch) + if ((processedContent?.HasProcessedEpisodes ?? false) && recentlyAddedSearch) { BackgroundJob.Enqueue(() => Checker.Start()); } @@ -165,7 +165,7 @@ namespace Ombi.Schedule.Jobs.Plex { Logger.LogInformation("Found some episodes, this must be a recently added sync"); var count = 0; - foreach (var epInfo in content.Metadata) + foreach (var epInfo in content.Metadata ?? new Metadata[]{}) { count++; var grandParentKey = epInfo.grandparentRatingKey; @@ -199,9 +199,11 @@ namespace Ombi.Schedule.Jobs.Plex // Save just to make sure we don't leave anything hanging await Repo.SaveChangesAsync(); - - var episodesAdded = await EpisodeSync.ProcessEpsiodes(content.Metadata, allEps); - episodesProcessed.AddRange(episodesAdded.Select(x => x.Id)); + if (content.Metadata != null) + { + var episodesAdded = await EpisodeSync.ProcessEpsiodes(content.Metadata, allEps); + episodesProcessed.AddRange(episodesAdded.Select(x => x.Id)); + } } if (content.viewGroup.Equals(PlexMediaType.Show.ToString(), StringComparison.CurrentCultureIgnoreCase)) { @@ -350,7 +352,7 @@ namespace Ombi.Schedule.Jobs.Plex } // Do we already have this item? - // Let's try and match + // Let's try and match var existingContent = await Repo.GetFirstContentByCustom(x => x.Title == show.title && x.ReleaseYear == show.year.ToString() && x.Type == PlexMediaTypeEntity.Show); @@ -371,6 +373,10 @@ namespace Ombi.Schedule.Jobs.Plex await Repo.Delete(existingKey); existingKey = null; } + else if(existingContent == null) + { + existingContent = await Repo.GetFirstContentByCustom(x => x.Key == show.ratingKey); + } } if (existingContent != null) From f097dedea663143f0995026da81490d6ef890229 Mon Sep 17 00:00:00 2001 From: Jamie Date: Fri, 22 Jun 2018 23:13:47 +0100 Subject: [PATCH 28/55] Added TVRequestsLite --- .../Engine/Interfaces/ITvRequestEngine.cs | 3 +- src/Ombi.Core/Engine/TvRequestEngine.cs | 29 +++++++++++++++++++ src/Ombi/Controllers/RequestController.cs | 24 +++++++++++++-- 3 files changed, 53 insertions(+), 3 deletions(-) diff --git a/src/Ombi.Core/Engine/Interfaces/ITvRequestEngine.cs b/src/Ombi.Core/Engine/Interfaces/ITvRequestEngine.cs index 064ddf6c9..348dc91e7 100644 --- a/src/Ombi.Core/Engine/Interfaces/ITvRequestEngine.cs +++ b/src/Ombi.Core/Engine/Interfaces/ITvRequestEngine.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using System.Threading.Tasks; using Ombi.Core.Models.Requests; -using Ombi.Core.Models.Search; +using Ombi.Core.Models.UI; using Ombi.Store.Entities.Requests; namespace Ombi.Core.Engine.Interfaces @@ -13,6 +13,7 @@ namespace Ombi.Core.Engine.Interfaces Task GetTvRequest(int requestId); Task RequestTvShow(TvRequestViewModel tv); Task DenyChildRequest(int requestId); + Task> GetRequestsLite(int count, int position, OrderFilterModel type); Task> SearchTvRequest(string search); Task>>> SearchTvRequestTree(string search); Task UpdateTvRequest(TvRequests request); diff --git a/src/Ombi.Core/Engine/TvRequestEngine.cs b/src/Ombi.Core/Engine/TvRequestEngine.cs index 6dfd5b7f3..e8fc65a26 100644 --- a/src/Ombi.Core/Engine/TvRequestEngine.cs +++ b/src/Ombi.Core/Engine/TvRequestEngine.cs @@ -168,6 +168,35 @@ namespace Ombi.Core.Engine }; } + public async Task> GetRequestsLite(int count, int position, OrderFilterModel type) + { + var shouldHide = await HideFromOtherUsers(); + List allRequests; + if (shouldHide.Hide) + { + allRequests = await TvRepository.GetLite(shouldHide.UserId) + .OrderByDescending(x => x.ChildRequests.Max(y => y.RequestedDate)) + .Skip(position).Take(count).ToListAsync(); + + // Filter out children + + FilterChildren(allRequests, shouldHide); + } + else + { + allRequests = await TvRepository.GetLite() + .OrderByDescending(x => x.ChildRequests.Max(y => y.RequestedDate)) + .Skip(position).Take(count).ToListAsync(); + } + + allRequests.ForEach(async r => { await CheckForSubscription(shouldHide, r); }); + + return new RequestsViewModel + { + Collection = allRequests + }; + } + public async Task>>> GetRequestsTreeNode(int count, int position) { var shouldHide = await HideFromOtherUsers(); diff --git a/src/Ombi/Controllers/RequestController.cs b/src/Ombi/Controllers/RequestController.cs index 95ea4c09e..483b92280 100644 --- a/src/Ombi/Controllers/RequestController.cs +++ b/src/Ombi/Controllers/RequestController.cs @@ -189,8 +189,28 @@ namespace Ombi.Controllers return await TvRequestEngine.GetRequests(count, position, new OrderFilterModel { OrderType = (OrderType)orderType, - AvailabilityFilter = (FilterType) availabilityType, - StatusFilter = (FilterType) statusType, + AvailabilityFilter = (FilterType)availabilityType, + StatusFilter = (FilterType)statusType, + }); + } + + /// + /// Gets the tv requests lite. + /// + /// The count of items you want to return. + /// The position. + /// + /// + /// + /// + [HttpGet("tv/{count:int}/{position:int}/{orderType:int}/{statusFilterType:int}/{availabilityFilterType:int}")] + public async Task> GetTvRequestsLite(int count, int position, int orderType, int statusType, int availabilityType) + { + return await TvRequestEngine.GetRequestsLite(count, position, new OrderFilterModel + { + OrderType = (OrderType)orderType, + AvailabilityFilter = (FilterType)availabilityType, + StatusFilter = (FilterType)statusType, }); } From 54afe5edee61911cf44113dac39ea21d9043cbdb Mon Sep 17 00:00:00 2001 From: Jamie Date: Fri, 22 Jun 2018 23:38:08 +0100 Subject: [PATCH 29/55] !wip missing thing.... --- src/Ombi/Controllers/RequestController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ombi/Controllers/RequestController.cs b/src/Ombi/Controllers/RequestController.cs index 483b92280..baeaefcd5 100644 --- a/src/Ombi/Controllers/RequestController.cs +++ b/src/Ombi/Controllers/RequestController.cs @@ -203,7 +203,7 @@ namespace Ombi.Controllers /// /// /// - [HttpGet("tv/{count:int}/{position:int}/{orderType:int}/{statusFilterType:int}/{availabilityFilterType:int}")] + [HttpGet("tvlite/{count:int}/{position:int}/{orderType:int}/{statusFilterType:int}/{availabilityFilterType:int}")] public async Task> GetTvRequestsLite(int count, int position, int orderType, int statusType, int availabilityType) { return await TvRequestEngine.GetRequestsLite(count, position, new OrderFilterModel From 7eb6b01e752d6dabec8b18f560677ea8089332b9 Mon Sep 17 00:00:00 2001 From: Jamie Date: Sat, 23 Jun 2018 00:12:44 +0100 Subject: [PATCH 30/55] Show the popular movies and tv shows by default --- src/Ombi/ClientApp/app/search/moviesearch.component.ts | 1 + src/Ombi/ClientApp/app/search/tvsearch.component.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/src/Ombi/ClientApp/app/search/moviesearch.component.ts b/src/Ombi/ClientApp/app/search/moviesearch.component.ts index ff570441a..236ae8ed8 100644 --- a/src/Ombi/ClientApp/app/search/moviesearch.component.ts +++ b/src/Ombi/ClientApp/app/search/moviesearch.component.ts @@ -70,6 +70,7 @@ export class MovieSearchComponent implements OnInit { result: false, errorMessage: "", }; + this.popularMovies(); } public search(text: any) { diff --git a/src/Ombi/ClientApp/app/search/tvsearch.component.ts b/src/Ombi/ClientApp/app/search/tvsearch.component.ts index 8db75125c..2a456cb6e 100644 --- a/src/Ombi/ClientApp/app/search/tvsearch.component.ts +++ b/src/Ombi/ClientApp/app/search/tvsearch.component.ts @@ -93,6 +93,7 @@ export class TvSearchComponent implements OnInit { result: false, errorMessage:"", }; + this.popularShows(); } public search(text: any) { From b21fc8ea303c5ed5203097c849ca73b0112ed9fa Mon Sep 17 00:00:00 2001 From: Jamie Date: Sat, 23 Jun 2018 21:10:15 +0100 Subject: [PATCH 31/55] !wip --- src/Ombi/Controllers/SettingsController.cs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/Ombi/Controllers/SettingsController.cs b/src/Ombi/Controllers/SettingsController.cs index 2e489adf2..2bdb6730a 100644 --- a/src/Ombi/Controllers/SettingsController.cs +++ b/src/Ombi/Controllers/SettingsController.cs @@ -2,19 +2,13 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; -using System.Net; -using System.Net.Http; -using System.Net.Http.Headers; using System.Reflection; using System.Runtime.InteropServices; -using System.Text; using System.Threading.Tasks; using AutoMapper; using Hangfire; -using Hangfire.RecurringJobExtensions; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.PlatformAbstractions; using NCrontab; using Ombi.Api.Emby; From f222224459b4d849c4d1b42946c1bfa92622732c Mon Sep 17 00:00:00 2001 From: Jamie Date: Sat, 23 Jun 2018 21:10:55 +0100 Subject: [PATCH 32/55] !wip changelog --- CHANGELOG.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 45f1e34a8..61d22c16f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,21 @@ ### **New Features** +- Added TVRequestsLite. [Jamie] + +- Added a smaller and simplier way of getting TV Request info. [Jamie Rees] + +### **Fixes** + +- Show the popular movies and tv shows by default. [Jamie] + +- Fixed #2348. [Jamie] + + +## v3.0.3407 (2018-06-18) + +### **New Features** + - Update appveyor.yml. [Jamie] - Update build.cake. [Jamie] From 917a2c44128e6524822682f3db7e035a90aa42f4 Mon Sep 17 00:00:00 2001 From: Jamie Date: Sat, 23 Jun 2018 23:19:08 +0100 Subject: [PATCH 33/55] !wip --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8603c6f66..5eb51423c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## v3.0.3407 (2018-06-07) +## v3.0.3421 (2018-06-23) ### **New Features** From a1c512426955693cfb31713280dfb8d3c4da7504 Mon Sep 17 00:00:00 2001 From: Jamie Rees Date: Mon, 25 Jun 2018 12:48:11 +0100 Subject: [PATCH 34/55] Swap out the old way of validating the API key with a real middlewear this time. --- src/Ombi/ApiKeyMiddlewear.cs | 105 ++++++++++++++++++++++++++++++++++ src/Ombi/Startup.cs | 3 +- src/Ombi/StartupExtensions.cs | 40 ------------- 3 files changed, 107 insertions(+), 41 deletions(-) create mode 100644 src/Ombi/ApiKeyMiddlewear.cs diff --git a/src/Ombi/ApiKeyMiddlewear.cs b/src/Ombi/ApiKeyMiddlewear.cs new file mode 100644 index 000000000..d30ba0d21 --- /dev/null +++ b/src/Ombi/ApiKeyMiddlewear.cs @@ -0,0 +1,105 @@ +using System; +using System.Linq; +using System.Net; +using System.Security.Principal; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.EntityFrameworkCore; +using Ombi.Core.Authentication; +using Ombi.Core.Settings; +using Ombi.Settings.Settings.Models; + +namespace Ombi +{ + public class ApiKeyMiddlewear + { + public ApiKeyMiddlewear(RequestDelegate next, ISettingsService repo, OmbiUserManager um) + { + _next = next; + _repo = repo; + _userManager = um; + } + private readonly RequestDelegate _next; + private readonly ISettingsService _repo; + private readonly OmbiUserManager _userManager; + + public async Task Invoke(HttpContext context) + { + if (context.Request.Path.StartsWithSegments(new PathString("/api"))) + { + //Let's check if this is an API Call + if (context.Request.Headers.Keys.Contains("ApiKey")) + { + // validate the supplied API key + // Validate it + var headerKey = context.Request.Headers["ApiKey"].FirstOrDefault(); + await ValidateApiKey(context, _next, headerKey); + } + else if (context.Request.Query.ContainsKey("apikey")) + { + if (context.Request.Query.TryGetValue("apikey", out var queryKey)) + { + await ValidateApiKey(context, _next, queryKey); + } + } + // User access token used by the mobile app + else if (context.Request.Headers["UserAccessToken"].Any()) + { + var headerKey = context.Request.Headers["UserAccessToken"].FirstOrDefault(); + await ValidateUserAccessToken(context, _next, headerKey); + } + else + { + await _next.Invoke(context); + } + } + else + { + await _next.Invoke(context); + } + } + + private async Task ValidateUserAccessToken(HttpContext context, RequestDelegate next, string key) + { + if (string.IsNullOrEmpty(key)) + { + await context.Response.WriteAsync("Invalid User Access Token"); + return; + } + + var user = await _userManager.Users.FirstOrDefaultAsync(x => x.UserAccessToken == key); + if (user == null) + { + context.Response.StatusCode = (int)HttpStatusCode.Unauthorized; + await context.Response.WriteAsync("Invalid User Access Token"); + } + else + { + + var identity = new GenericIdentity(user.UserName); + var roles = await _userManager.GetRolesAsync(user); + var principal = new GenericPrincipal(identity, roles.ToArray()); + context.User = principal; + await next.Invoke(context); + } + } + + private async Task ValidateApiKey(HttpContext context, RequestDelegate next, string key) + { + var ombiSettings = await _repo.GetSettingsAsync(); + var valid = ombiSettings.ApiKey.Equals(key, StringComparison.CurrentCultureIgnoreCase); + if (!valid) + { + context.Response.StatusCode = (int)HttpStatusCode.Unauthorized; + await context.Response.WriteAsync("Invalid API Key"); + } + else + { + var identity = new GenericIdentity("API"); + var principal = new GenericPrincipal(identity, new[] { "Admin", "ApiUser" }); + context.User = principal; + await next.Invoke(context); + } + } + } +} \ No newline at end of file diff --git a/src/Ombi/Startup.cs b/src/Ombi/Startup.cs index 8037a3021..1daaad05b 100644 --- a/src/Ombi/Startup.cs +++ b/src/Ombi/Startup.cs @@ -217,8 +217,9 @@ namespace Ombi app.UseAuthentication(); app.UseMiddleware(); + app.UseMiddleware(); - app.ApiKeyMiddlewear(app.ApplicationServices); + //app.ApiKeyMiddlewear(app.ApplicationServices); app.UseSwagger(); app.UseSwaggerUI(c => { diff --git a/src/Ombi/StartupExtensions.cs b/src/Ombi/StartupExtensions.cs index e4dae18e4..5c6355bb0 100644 --- a/src/Ombi/StartupExtensions.cs +++ b/src/Ombi/StartupExtensions.cs @@ -115,46 +115,6 @@ namespace Ombi }); } - - public static void ApiKeyMiddlewear(this IApplicationBuilder app, IServiceProvider serviceProvider) - { - app.Use(async (context, next) => - { - if (context.Request.Path.StartsWithSegments(new PathString("/api"))) - { - // Let's check if this is an API Call - if (context.Request.Headers["ApiKey"].Any()) - { - // validate the supplied API key - // Validate it - var headerKey = context.Request.Headers["ApiKey"].FirstOrDefault(); - await ValidateApiKey(serviceProvider, context, next, headerKey); - } - else if (context.Request.Query.ContainsKey("apikey")) - { - if (context.Request.Query.TryGetValue("apikey", out var queryKey)) - { - await ValidateApiKey(serviceProvider, context, next, queryKey); - } - } - // User access token used by the mobile app - else if (context.Request.Headers["UserAccessToken"].Any()) - { - var headerKey = context.Request.Headers["UserAccessToken"].FirstOrDefault(); - await ValidateUserAccessToken(serviceProvider, context, next, headerKey); - } - else - { - await next(); - } - } - else - { - await next(); - } - }); - } - private static async Task ValidateUserAccessToken(IServiceProvider serviceProvider, HttpContext context, Func next, string key) { if (key.IsNullOrEmpty()) From b56bc8c807c03d9810efc29f7c127bf08e2643b9 Mon Sep 17 00:00:00 2001 From: Jamie Rees Date: Tue, 26 Jun 2018 08:20:58 +0100 Subject: [PATCH 35/55] Removed old code --- src/Ombi/StartupExtensions.cs | 45 ----------------------------------- 1 file changed, 45 deletions(-) diff --git a/src/Ombi/StartupExtensions.cs b/src/Ombi/StartupExtensions.cs index 5c6355bb0..e89691adc 100644 --- a/src/Ombi/StartupExtensions.cs +++ b/src/Ombi/StartupExtensions.cs @@ -114,50 +114,5 @@ namespace Ombi x.TokenValidationParameters = tokenValidationParameters; }); } - - private static async Task ValidateUserAccessToken(IServiceProvider serviceProvider, HttpContext context, Func next, string key) - { - if (key.IsNullOrEmpty()) - { - await context.Response.WriteAsync("Invalid User Access Token"); - return; - } - - var um = serviceProvider.GetService(); - var user = await um.Users.FirstOrDefaultAsync(x => x.UserAccessToken == key); - if (user == null) - { - context.Response.StatusCode = (int)HttpStatusCode.Unauthorized; - await context.Response.WriteAsync("Invalid User Access Token"); - } - else - { - - var identity = new GenericIdentity(user.UserName); - var roles = await um.GetRolesAsync(user); - var principal = new GenericPrincipal(identity, roles.ToArray()); - context.User = principal; - await next(); - } - } - - private static async Task ValidateApiKey(IServiceProvider serviceProvider, HttpContext context, Func next, string key) - { - var settingsProvider = serviceProvider.GetService>(); - var ombiSettings = settingsProvider.GetSettings(); - var valid = ombiSettings.ApiKey.Equals(key, StringComparison.CurrentCultureIgnoreCase); - if (!valid) - { - context.Response.StatusCode = (int)HttpStatusCode.Unauthorized; - await context.Response.WriteAsync("Invalid API Key"); - } - else - { - var identity = new GenericIdentity("API"); - var principal = new GenericPrincipal(identity, new[] { "Admin", "ApiUser" }); - context.User = principal; - await next(); - } - } } } \ No newline at end of file From 53d7965968f2d275f03d694944f4c42c0834adf9 Mon Sep 17 00:00:00 2001 From: Jamie Rees Date: Tue, 26 Jun 2018 08:28:27 +0100 Subject: [PATCH 36/55] Downgrade Microsoft.AspNetCore.All package back to 2.0.8 --- src/Ombi/Ombi.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ombi/Ombi.csproj b/src/Ombi/Ombi.csproj index add872a33..183323e5b 100644 --- a/src/Ombi/Ombi.csproj +++ b/src/Ombi/Ombi.csproj @@ -67,7 +67,7 @@ - + From 1e4c53b8242eb88634633eb4ce0954fa93575c4b Mon Sep 17 00:00:00 2001 From: Randall Bruder Date: Tue, 26 Jun 2018 20:41:02 -0400 Subject: [PATCH 37/55] Minor style tweaks --- src/Ombi/ClientApp/styles/base.scss | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Ombi/ClientApp/styles/base.scss b/src/Ombi/ClientApp/styles/base.scss index eac9da0a0..fb41c8617 100644 --- a/src/Ombi/ClientApp/styles/base.scss +++ b/src/Ombi/ClientApp/styles/base.scss @@ -95,6 +95,7 @@ hr { .btn { border-radius: .25rem $i; + margin-bottom: 10px; } .btn-group-separated .btn, @@ -141,6 +142,10 @@ p { font-size: 1.1rem $i; } +.tags { + display: block; +} + label { display: inline-block $i; margin-bottom: .5rem $i; @@ -443,6 +448,7 @@ $border-radius: 10px; margin-right: 10px; position: absolute; left: 0; + top: 3px; bottom: 1px; border: 2px solid #eee; border-radius: 3px; @@ -940,7 +946,7 @@ a > h4:hover { .backdrop{ box-shadow: 3px 3px 10px #000000; - background-position: center; + background-position: 50% 33%; background-size: cover; } From 07acfb53a11b9108db10f6631d9142e6b33523be Mon Sep 17 00:00:00 2001 From: Jamie Rees Date: Wed, 27 Jun 2018 08:29:27 +0100 Subject: [PATCH 38/55] Downgrade to .net core 2.0 --- appveyor.yml | 10 +++++----- build.cake | 2 +- src/Ombi.Schedule.Tests/Ombi.Schedule.Tests.csproj | 2 +- src/Ombi.Updater/Ombi.Updater.csproj | 6 +++--- src/Ombi/Ombi.csproj | 8 ++++---- src/Ombi/Properties/launchSettings.json | 1 - 6 files changed, 14 insertions(+), 15 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 862993a21..39d67b91d 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -15,19 +15,19 @@ test: off after_build: - cmd: >- - appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.1\windows.zip" + appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.0\windows.zip" - appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.1\osx.tar.gz" + appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.0\osx.tar.gz" - appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.1\linux.tar.gz" + appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.0\linux.tar.gz" - appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.1\linux-arm.tar.gz" + appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.0\linux-arm.tar.gz" - appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.1\windows-32bit.zip" + appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.0\windows-32bit.zip" # appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.0\linux-arm64.tar.gz" diff --git a/build.cake b/build.cake index d200eac98..a497c5f77 100644 --- a/build.cake +++ b/build.cake @@ -26,7 +26,7 @@ var csProj = "./src/Ombi/Ombi.csproj"; // Path to the project.csproj var solutionFile = "Ombi.sln"; // Solution file if needed GitVersion versionInfo = null; -var frameworkVer = "netcoreapp2.1"; +var frameworkVer = "netcoreapp2.0"; var buildSettings = new DotNetCoreBuildSettings { diff --git a/src/Ombi.Schedule.Tests/Ombi.Schedule.Tests.csproj b/src/Ombi.Schedule.Tests/Ombi.Schedule.Tests.csproj index d147af88f..5c34e3e5a 100644 --- a/src/Ombi.Schedule.Tests/Ombi.Schedule.Tests.csproj +++ b/src/Ombi.Schedule.Tests/Ombi.Schedule.Tests.csproj @@ -5,7 +5,7 @@ - + diff --git a/src/Ombi.Updater/Ombi.Updater.csproj b/src/Ombi.Updater/Ombi.Updater.csproj index e46a45551..87ef885cc 100644 --- a/src/Ombi.Updater/Ombi.Updater.csproj +++ b/src/Ombi.Updater/Ombi.Updater.csproj @@ -3,7 +3,7 @@ Exe win10-x64;win10-x86;osx-x64;ubuntu-x64;debian.8-x64;centos.7-x64;linux-x64;linux-arm;linux-arm64; - netcoreapp2.1 + netcoreapp2.0 3.0.0.0 3.0.0.0 @@ -14,12 +14,12 @@ - + - + diff --git a/src/Ombi/Ombi.csproj b/src/Ombi/Ombi.csproj index 183323e5b..e05c83382 100644 --- a/src/Ombi/Ombi.csproj +++ b/src/Ombi/Ombi.csproj @@ -1,7 +1,7 @@  - netcoreapp2.1 + netcoreapp2.0 win10-x64;win10-x86;osx-x64;ubuntu-x64;debian.8-x64;centos.7-x64;linux-x64;linux-arm;linux-arm64; false Latest @@ -68,9 +68,9 @@ - - - + + + diff --git a/src/Ombi/Properties/launchSettings.json b/src/Ombi/Properties/launchSettings.json index 33794436c..ec5deb319 100644 --- a/src/Ombi/Properties/launchSettings.json +++ b/src/Ombi/Properties/launchSettings.json @@ -10,7 +10,6 @@ "profiles": { "IIS Express": { "commandName": "IISExpress", - "commandLineArgs": "-baseurl /testing", "launchBrowser": true, "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" From 209b7cf50bc68e96ebe465a3d3889d3a9aca09c0 Mon Sep 17 00:00:00 2001 From: Jamie Rees Date: Wed, 27 Jun 2018 08:41:01 +0100 Subject: [PATCH 39/55] !wip downgrade --- src/Ombi/StartupExtensions.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Ombi/StartupExtensions.cs b/src/Ombi/StartupExtensions.cs index 9858dc6db..a2c44e7f3 100644 --- a/src/Ombi/StartupExtensions.cs +++ b/src/Ombi/StartupExtensions.cs @@ -12,6 +12,7 @@ using Microsoft.AspNetCore.Http; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.PlatformAbstractions; using Microsoft.IdentityModel.Tokens; using Ombi.Config; using Ombi.Core.Authentication; @@ -44,7 +45,7 @@ namespace Ombi } }); c.CustomSchemaIds(x => x.FullName); - var basePath = Directory.GetCurrentDirectory(); + var basePath = PlatformServices.Default.Application.ApplicationBasePath; var xmlPath = Path.Combine(basePath, "Swagger.xml"); try { From 880aea1221ace3fa637a79c92534ed55e541265e Mon Sep 17 00:00:00 2001 From: Jamie Rees Date: Wed, 27 Jun 2018 08:50:58 +0100 Subject: [PATCH 40/55] Fixed a small error in the Mobile Notification Provider --- src/Ombi.Notifications/Agents/MobileNotification.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Ombi.Notifications/Agents/MobileNotification.cs b/src/Ombi.Notifications/Agents/MobileNotification.cs index 35619ad1f..bfb7dcd88 100644 --- a/src/Ombi.Notifications/Agents/MobileNotification.cs +++ b/src/Ombi.Notifications/Agents/MobileNotification.cs @@ -264,13 +264,13 @@ namespace Ombi.Notifications.Agents ? MovieRequest?.RequestedUser?.NotificationUserIds : TvRequest?.RequestedUser?.NotificationUserIds; } - if (model.UserId.HasValue() && !notificationIds.Any()) + if (model.UserId.HasValue() && (!notificationIds?.Any() ?? true)) { var user= _userManager.Users.Include(x => x.NotificationUserIds).FirstOrDefault(x => x.Id == model.UserId); notificationIds = user.NotificationUserIds; } - if (!notificationIds.Any()) + if (!notificationIds?.Any() ?? true) { _logger.LogInformation( $"there are no admins to send a notification for {type}, for agent {NotificationAgent.Mobile}"); From 0221254d4bc86f682e2d7c0752766c8cbdb7416d Mon Sep 17 00:00:00 2001 From: Jamie Rees Date: Wed, 27 Jun 2018 09:05:17 +0100 Subject: [PATCH 41/55] Added API at /api/v1/status/info to get branch and version information #2331 --- src/Ombi/Controllers/StatusController.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/Ombi/Controllers/StatusController.cs b/src/Ombi/Controllers/StatusController.cs index 14ead2ba1..bc0fd3908 100644 --- a/src/Ombi/Controllers/StatusController.cs +++ b/src/Ombi/Controllers/StatusController.cs @@ -30,6 +30,7 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Ombi.Core.Settings; +using Ombi.Helpers; using Ombi.Settings.Settings.Models; namespace Ombi.Controllers @@ -57,6 +58,18 @@ namespace Ombi.Controllers } + /// + /// Returns information about this ombi instance + /// + /// + [AllowAnonymous] + [HttpGet("info")] + public string GetInfo() + { + return AssemblyHelper.GetRuntimeVersion(); + } + + /// /// Checks to see if we have run through the wizard /// From 935cea35416c291972761e8d56331d0c54e7c757 Mon Sep 17 00:00:00 2001 From: Jamie Rees Date: Wed, 27 Jun 2018 10:03:10 +0100 Subject: [PATCH 42/55] Added chunk hashing to resolve #2330 --- src/Ombi/webpack.config.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Ombi/webpack.config.ts b/src/Ombi/webpack.config.ts index dceb29992..ee6f00516 100644 --- a/src/Ombi/webpack.config.ts +++ b/src/Ombi/webpack.config.ts @@ -6,7 +6,8 @@ import { BundleAnalyzerPlugin } from "webpack-bundle-analyzer"; import * as webpack from "webpack"; module.exports = (env: any) => { - const prod = env && env.prod as boolean; + // const prod = env && env.prod as boolean; + const prod = true; console.log(prod ? "Production" : "Dev" + " main build"); const analyse = env && env.analyse as boolean; if (analyse) { console.log("Analysing build"); } @@ -20,6 +21,7 @@ module.exports = (env: any) => { devtool: prod ? "source-map" : "eval-source-map", output: { filename: "[name].js", + chunkFilename: "[id].[chunkhash].js", publicPath: "/dist/", path: path.join(__dirname, outputDir), }, From d8fdc75cfb9b8be1c54b2b0af3296d2f4fef28da Mon Sep 17 00:00:00 2001 From: Jamie Rees Date: Wed, 27 Jun 2018 21:10:29 +0100 Subject: [PATCH 43/55] revert --- src/Ombi.Api.Radarr/Ombi.Api.Radarr.csproj | 2 +- src/Ombi.Api.Service/Ombi.Api.Service.csproj | 2 +- src/Ombi.Api/Ombi.Api.csproj | 2 +- src/Ombi.Core.Tests/Ombi.Core.Tests.csproj | 2 +- src/Ombi.Core/Ombi.Core.csproj | 4 +++- .../Ombi.DependencyInjection.csproj | 6 +++--- src/Ombi.Helpers/Ombi.Helpers.csproj | 4 ++-- .../Ombi.Notifications.Tests.csproj | 2 +- src/Ombi.Notifications/Ombi.Notifications.csproj | 2 +- src/Ombi.Schedule.Tests/Ombi.Schedule.Tests.csproj | 6 +++--- src/Ombi.Settings/Ombi.Settings.csproj | 2 +- src/Ombi.Store/Ombi.Store.csproj | 8 ++++---- src/Ombi.Tests/Ombi.Tests.csproj | 4 ++-- src/Ombi.Updater/Ombi.Updater.csproj | 12 ++++++------ src/Ombi/Ombi.csproj | 2 +- 15 files changed, 31 insertions(+), 29 deletions(-) diff --git a/src/Ombi.Api.Radarr/Ombi.Api.Radarr.csproj b/src/Ombi.Api.Radarr/Ombi.Api.Radarr.csproj index 0c615f301..d2b605337 100644 --- a/src/Ombi.Api.Radarr/Ombi.Api.Radarr.csproj +++ b/src/Ombi.Api.Radarr/Ombi.Api.Radarr.csproj @@ -9,7 +9,7 @@ - + diff --git a/src/Ombi.Api.Service/Ombi.Api.Service.csproj b/src/Ombi.Api.Service/Ombi.Api.Service.csproj index 8cbddd874..67f37c80d 100644 --- a/src/Ombi.Api.Service/Ombi.Api.Service.csproj +++ b/src/Ombi.Api.Service/Ombi.Api.Service.csproj @@ -11,7 +11,7 @@ - + diff --git a/src/Ombi.Api/Ombi.Api.csproj b/src/Ombi.Api/Ombi.Api.csproj index 804512945..32fc60bb6 100644 --- a/src/Ombi.Api/Ombi.Api.csproj +++ b/src/Ombi.Api/Ombi.Api.csproj @@ -9,7 +9,7 @@ - + diff --git a/src/Ombi.Core.Tests/Ombi.Core.Tests.csproj b/src/Ombi.Core.Tests/Ombi.Core.Tests.csproj index a4f6aeed8..db98876df 100644 --- a/src/Ombi.Core.Tests/Ombi.Core.Tests.csproj +++ b/src/Ombi.Core.Tests/Ombi.Core.Tests.csproj @@ -1,7 +1,7 @@  - netcoreapp2.1 + netcoreapp2.0 diff --git a/src/Ombi.Core/Ombi.Core.csproj b/src/Ombi.Core/Ombi.Core.csproj index 6bd3e0f9d..2037113bd 100644 --- a/src/Ombi.Core/Ombi.Core.csproj +++ b/src/Ombi.Core/Ombi.Core.csproj @@ -12,7 +12,9 @@ - + + + diff --git a/src/Ombi.DependencyInjection/Ombi.DependencyInjection.csproj b/src/Ombi.DependencyInjection/Ombi.DependencyInjection.csproj index 2e7f984a7..675f6461b 100644 --- a/src/Ombi.DependencyInjection/Ombi.DependencyInjection.csproj +++ b/src/Ombi.DependencyInjection/Ombi.DependencyInjection.csproj @@ -9,9 +9,9 @@ - - - + + + diff --git a/src/Ombi.Helpers/Ombi.Helpers.csproj b/src/Ombi.Helpers/Ombi.Helpers.csproj index e94afc816..12c6fecc4 100644 --- a/src/Ombi.Helpers/Ombi.Helpers.csproj +++ b/src/Ombi.Helpers/Ombi.Helpers.csproj @@ -10,8 +10,8 @@ - - + + diff --git a/src/Ombi.Notifications.Tests/Ombi.Notifications.Tests.csproj b/src/Ombi.Notifications.Tests/Ombi.Notifications.Tests.csproj index 451aba98b..339986ef5 100644 --- a/src/Ombi.Notifications.Tests/Ombi.Notifications.Tests.csproj +++ b/src/Ombi.Notifications.Tests/Ombi.Notifications.Tests.csproj @@ -1,7 +1,7 @@  - netcoreapp2.1 + netcoreapp2.0 diff --git a/src/Ombi.Notifications/Ombi.Notifications.csproj b/src/Ombi.Notifications/Ombi.Notifications.csproj index 529f01357..46a64072e 100644 --- a/src/Ombi.Notifications/Ombi.Notifications.csproj +++ b/src/Ombi.Notifications/Ombi.Notifications.csproj @@ -10,7 +10,7 @@ - + diff --git a/src/Ombi.Schedule.Tests/Ombi.Schedule.Tests.csproj b/src/Ombi.Schedule.Tests/Ombi.Schedule.Tests.csproj index 24a7308cb..bdfbd60e6 100644 --- a/src/Ombi.Schedule.Tests/Ombi.Schedule.Tests.csproj +++ b/src/Ombi.Schedule.Tests/Ombi.Schedule.Tests.csproj @@ -1,16 +1,16 @@ - netcoreapp2.1 + netcoreapp2.0 - + - + diff --git a/src/Ombi.Settings/Ombi.Settings.csproj b/src/Ombi.Settings/Ombi.Settings.csproj index 19a415a47..3cb56cb07 100644 --- a/src/Ombi.Settings/Ombi.Settings.csproj +++ b/src/Ombi.Settings/Ombi.Settings.csproj @@ -9,7 +9,7 @@ - + diff --git a/src/Ombi.Store/Ombi.Store.csproj b/src/Ombi.Store/Ombi.Store.csproj index db034ca12..703bbf672 100644 --- a/src/Ombi.Store/Ombi.Store.csproj +++ b/src/Ombi.Store/Ombi.Store.csproj @@ -10,10 +10,10 @@ - - - - + + + + diff --git a/src/Ombi.Tests/Ombi.Tests.csproj b/src/Ombi.Tests/Ombi.Tests.csproj index 1b7dd3fe3..6296d9e8c 100644 --- a/src/Ombi.Tests/Ombi.Tests.csproj +++ b/src/Ombi.Tests/Ombi.Tests.csproj @@ -7,12 +7,12 @@ - + - + diff --git a/src/Ombi.Updater/Ombi.Updater.csproj b/src/Ombi.Updater/Ombi.Updater.csproj index 87ef885cc..3a55a7040 100644 --- a/src/Ombi.Updater/Ombi.Updater.csproj +++ b/src/Ombi.Updater/Ombi.Updater.csproj @@ -12,13 +12,13 @@ - - + + - - - - + + + + diff --git a/src/Ombi/Ombi.csproj b/src/Ombi/Ombi.csproj index e05c83382..8b9e00c45 100644 --- a/src/Ombi/Ombi.csproj +++ b/src/Ombi/Ombi.csproj @@ -70,7 +70,7 @@ - + From 62b0c4d450e7dd1c477f0e2ab3db084c892628ca Mon Sep 17 00:00:00 2001 From: Jamie Rees Date: Wed, 27 Jun 2018 21:59:39 +0100 Subject: [PATCH 44/55] Improved the Emby API #2230 Thanks Luke! --- src/Ombi.Api.Emby/EmbyApi.cs | 11 ++++-- .../Models/Media/Movie/EmbyMovie.cs | 2 + .../Models/Media/Tv/EmbyEpisodes.cs | 1 + .../Models/Media/Tv/EmbySeries.cs | 2 + .../Jobs/Emby/EmbyContentSync.cs | 37 +++++-------------- .../Jobs/Emby/EmbyEpisodeSync.cs | 20 ++-------- 6 files changed, 25 insertions(+), 48 deletions(-) diff --git a/src/Ombi.Api.Emby/EmbyApi.cs b/src/Ombi.Api.Emby/EmbyApi.cs index 3af6d0dd5..3ac70c844 100644 --- a/src/Ombi.Api.Emby/EmbyApi.cs +++ b/src/Ombi.Api.Emby/EmbyApi.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Net.Http; using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore.Internal; using Newtonsoft.Json; using Ombi.Api.Emby.Models; using Ombi.Api.Emby.Models.Media.Tv; @@ -100,7 +101,7 @@ namespace Ombi.Api.Emby public async Task> GetAllMovies(string apiKey, string userId, string baseUri) { - return await GetAll("Movie", apiKey, userId, baseUri); + return await GetAll("Movie", apiKey, userId, baseUri, true); } public async Task> GetAllEpisodes(string apiKey, string userId, string baseUri) @@ -129,20 +130,22 @@ namespace Ombi.Api.Emby private async Task GetInformation(string mediaId, string apiKey, string userId, string baseUrl) { var request = new Request($"emby/users/{userId}/items/{mediaId}", baseUrl, HttpMethod.Get); + AddHeaders(request, apiKey); var response = await Api.RequestContent(request); return JsonConvert.DeserializeObject(response); } - - - private async Task> GetAll(string type, string apiKey, string userId, string baseUri) + private async Task> GetAll(string type, string apiKey, string userId, string baseUri, bool includeOverview = false) { var request = new Request($"emby/users/{userId}/items", baseUri, HttpMethod.Get); request.AddQueryString("Recursive", true.ToString()); request.AddQueryString("IncludeItemTypes", type); + request.AddQueryString("Fields", includeOverview ? "ProviderIds,Overview" : "ProviderIds"); + + request.AddQueryString("VirtualItem","False"); AddHeaders(request, apiKey); diff --git a/src/Ombi.Api.Emby/Models/Media/Movie/EmbyMovie.cs b/src/Ombi.Api.Emby/Models/Media/Movie/EmbyMovie.cs index 34038edd8..a10ddaae6 100644 --- a/src/Ombi.Api.Emby/Models/Media/Movie/EmbyMovie.cs +++ b/src/Ombi.Api.Emby/Models/Media/Movie/EmbyMovie.cs @@ -28,5 +28,7 @@ namespace Ombi.Api.Emby.Models.Movie public string MediaType { get; set; } public bool HasSubtitles { get; set; } public int CriticRating { get; set; } + public string Overview { get; set; } + public EmbyProviderids ProviderIds { get; set; } } } \ No newline at end of file diff --git a/src/Ombi.Api.Emby/Models/Media/Tv/EmbyEpisodes.cs b/src/Ombi.Api.Emby/Models/Media/Tv/EmbyEpisodes.cs index d02c99e41..d76915923 100644 --- a/src/Ombi.Api.Emby/Models/Media/Tv/EmbyEpisodes.cs +++ b/src/Ombi.Api.Emby/Models/Media/Tv/EmbyEpisodes.cs @@ -39,5 +39,6 @@ namespace Ombi.Api.Emby.Models.Media.Tv public string LocationType { get; set; } public string MediaType { get; set; } public bool HasSubtitles { get; set; } + public EmbyProviderids ProviderIds { get; set; } } } \ No newline at end of file diff --git a/src/Ombi.Api.Emby/Models/Media/Tv/EmbySeries.cs b/src/Ombi.Api.Emby/Models/Media/Tv/EmbySeries.cs index 853c64d10..2aaf8d492 100644 --- a/src/Ombi.Api.Emby/Models/Media/Tv/EmbySeries.cs +++ b/src/Ombi.Api.Emby/Models/Media/Tv/EmbySeries.cs @@ -26,5 +26,7 @@ namespace Ombi.Api.Emby.Models.Media.Tv public string[] BackdropImageTags { get; set; } public string LocationType { get; set; } public DateTime EndDate { get; set; } + + public EmbyProviderids ProviderIds { get; set; } } } \ No newline at end of file diff --git a/src/Ombi.Schedule/Jobs/Emby/EmbyContentSync.cs b/src/Ombi.Schedule/Jobs/Emby/EmbyContentSync.cs index acea1535e..af8125ab2 100644 --- a/src/Ombi.Schedule/Jobs/Emby/EmbyContentSync.cs +++ b/src/Ombi.Schedule/Jobs/Emby/EmbyContentSync.cs @@ -75,34 +75,15 @@ namespace Ombi.Schedule.Jobs.Emby var mediaToAdd = new HashSet(); foreach (var movie in movies.Items) { - if (movie.Type.Equals("boxset", StringComparison.CurrentCultureIgnoreCase)) - { - var movieInfo = - await _api.GetCollection(movie.Id, server.ApiKey, server.AdministratorId, server.FullUri); - foreach (var item in movieInfo.Items) - { - var info = await _api.GetMovieInformation(item.Id, server.ApiKey, - server.AdministratorId, server.FullUri); - await ProcessMovies(info, mediaToAdd); - } - } - else - { - // Regular movie - var movieInfo = await _api.GetMovieInformation(movie.Id, server.ApiKey, - server.AdministratorId, server.FullUri); - - await ProcessMovies(movieInfo, mediaToAdd); - } + // Regular movie + await ProcessMovies(movie, mediaToAdd); } // TV Time var tv = await _api.GetAllShows(server.ApiKey, server.AdministratorId, server.FullUri); foreach (var tvShow in tv.Items) - { - var tvInfo = await _api.GetSeriesInformation(tvShow.Id, server.ApiKey, server.AdministratorId, - server.FullUri); - if (string.IsNullOrEmpty(tvInfo.ProviderIds?.Tvdb)) + { + if (string.IsNullOrEmpty(tvShow.ProviderIds?.Tvdb)) { Log.Error("Provider Id on tv {0} is null", tvShow.Name); continue; @@ -112,10 +93,10 @@ namespace Ombi.Schedule.Jobs.Emby if (existingTv == null) mediaToAdd.Add(new EmbyContent { - TvDbId = tvInfo.ProviderIds?.Tvdb, - ImdbId = tvInfo.ProviderIds?.Imdb, - TheMovieDbId = tvInfo.ProviderIds?.Tmdb, - Title = tvInfo.Name, + TvDbId = tvShow.ProviderIds?.Tvdb, + ImdbId = tvShow.ProviderIds?.Imdb, + TheMovieDbId = tvShow.ProviderIds?.Tmdb, + Title = tvShow.Name, Type = EmbyMediaType.Series, EmbyId = tvShow.Id, Url = EmbyHelper.GetEmbyMediaUrl(tvShow.Id), @@ -127,7 +108,7 @@ namespace Ombi.Schedule.Jobs.Emby await _repo.AddRange(mediaToAdd); } - private async Task ProcessMovies(MovieInformation movieInfo, ICollection content) + private async Task ProcessMovies(EmbyMovie movieInfo, ICollection content) { // Check if it exists var existingMovie = await _repo.GetByEmbyId(movieInfo.Id); diff --git a/src/Ombi.Schedule/Jobs/Emby/EmbyEpisodeSync.cs b/src/Ombi.Schedule/Jobs/Emby/EmbyEpisodeSync.cs index df00a37e6..b5ed6d443 100644 --- a/src/Ombi.Schedule/Jobs/Emby/EmbyEpisodeSync.cs +++ b/src/Ombi.Schedule/Jobs/Emby/EmbyEpisodeSync.cs @@ -78,21 +78,9 @@ namespace Ombi.Schedule.Jobs.Emby foreach (var ep in allEpisodes.Items) { - if (ep.LocationType.Equals("Virtual", StringComparison.CurrentCultureIgnoreCase)) - { - // This means that we don't actully have the file, it's just Emby being nice and showing future stuff - continue; - } - - var epInfo = await _api.GetEpisodeInformation(ep.Id, server.ApiKey, server.AdministratorId, server.FullUri); - //if (epInfo?.ProviderIds?.Tvdb == null) - //{ - // continue; - //} - // Let's make sure we have the parent request, stop those pesky forign key errors, // Damn me having data integrity - var parent = await _repo.GetByEmbyId(epInfo.SeriesId); + var parent = await _repo.GetByEmbyId(ep.SeriesId); if (parent == null) { _logger.LogInformation("The episode {0} does not relate to a series, so we cannot save this", ep.Name); @@ -109,9 +97,9 @@ namespace Ombi.Schedule.Jobs.Emby EpisodeNumber = ep.IndexNumber, SeasonNumber = ep.ParentIndexNumber, ParentId = ep.SeriesId, - TvDbId = epInfo.ProviderIds.Tvdb, - TheMovieDbId = epInfo.ProviderIds.Tmdb, - ImdbId = epInfo.ProviderIds.Imdb, + TvDbId = ep.ProviderIds.Tvdb, + TheMovieDbId = ep.ProviderIds.Tmdb, + ImdbId = ep.ProviderIds.Imdb, Title = ep.Name, AddedAt = DateTime.UtcNow }); From 51475b05c0de1117f9a18f3b00618619da4682ab Mon Sep 17 00:00:00 2001 From: Jamie Rees Date: Wed, 27 Jun 2018 22:05:32 +0100 Subject: [PATCH 45/55] Fixed the api key being case sensative #2350 --- src/Ombi/ApiKeyMiddlewear.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ombi/ApiKeyMiddlewear.cs b/src/Ombi/ApiKeyMiddlewear.cs index d30ba0d21..6110fedcc 100644 --- a/src/Ombi/ApiKeyMiddlewear.cs +++ b/src/Ombi/ApiKeyMiddlewear.cs @@ -28,7 +28,7 @@ namespace Ombi if (context.Request.Path.StartsWithSegments(new PathString("/api"))) { //Let's check if this is an API Call - if (context.Request.Headers.Keys.Contains("ApiKey")) + if (context.Request.Headers.Keys.Contains("ApiKey", StringComparer.InvariantCultureIgnoreCase)) { // validate the supplied API key // Validate it From 2fad7658ec7b576aa53bdaa77c9da2e759a0027d Mon Sep 17 00:00:00 2001 From: Jamie Rees Date: Thu, 28 Jun 2018 08:04:34 +0100 Subject: [PATCH 46/55] Removed some logging statements --- src/Ombi.Core/Engine/MovieSearchEngine.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Ombi.Core/Engine/MovieSearchEngine.cs b/src/Ombi.Core/Engine/MovieSearchEngine.cs index fbda62023..09b4cea6b 100644 --- a/src/Ombi.Core/Engine/MovieSearchEngine.cs +++ b/src/Ombi.Core/Engine/MovieSearchEngine.cs @@ -60,7 +60,6 @@ namespace Ombi.Core.Engine if (result != null) { - Logger.LogDebug("Search Result: {result}", result); return await TransformMovieResultsToResponse(result.Take(10)); // Take 10 to stop us overloading the API } return null; @@ -91,7 +90,6 @@ namespace Ombi.Core.Engine var result = await Cache.GetOrAdd(CacheKeys.PopularMovies, async () => await MovieApi.PopularMovies(), DateTime.Now.AddHours(12)); if (result != null) { - Logger.LogDebug("Search Result: {result}", result); return await TransformMovieResultsToResponse(result.Take(10)); // Take 10 to stop us overloading the API } return null; @@ -106,7 +104,6 @@ namespace Ombi.Core.Engine var result = await Cache.GetOrAdd(CacheKeys.TopRatedMovies, async () => await MovieApi.TopRated(), DateTime.Now.AddHours(12)); if (result != null) { - Logger.LogDebug("Search Result: {result}", result); return await TransformMovieResultsToResponse(result.Take(10)); // Take 10 to stop us overloading the API } return null; @@ -136,7 +133,6 @@ namespace Ombi.Core.Engine var result = await Cache.GetOrAdd(CacheKeys.NowPlayingMovies, async () => await MovieApi.NowPlaying(), DateTime.Now.AddHours(12)); if (result != null) { - Logger.LogDebug("Search Result: {result}", result); return await TransformMovieResultsToResponse(result.Take(10)); // Take 10 to stop us overloading the API } return null; From 95f6426f5378449be0d2744c17361cf5155245f8 Mon Sep 17 00:00:00 2001 From: Jamie Rees Date: Thu, 28 Jun 2018 13:52:56 +0100 Subject: [PATCH 47/55] Fixed #2338 --- src/Ombi.Notifications/{Interfaces => }/BaseNotification.cs | 0 src/Ombi.Schedule/Jobs/Plex/PlexAvailabilityChecker.cs | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename src/Ombi.Notifications/{Interfaces => }/BaseNotification.cs (100%) diff --git a/src/Ombi.Notifications/Interfaces/BaseNotification.cs b/src/Ombi.Notifications/BaseNotification.cs similarity index 100% rename from src/Ombi.Notifications/Interfaces/BaseNotification.cs rename to src/Ombi.Notifications/BaseNotification.cs diff --git a/src/Ombi.Schedule/Jobs/Plex/PlexAvailabilityChecker.cs b/src/Ombi.Schedule/Jobs/Plex/PlexAvailabilityChecker.cs index 9c86a13c4..586bd75e9 100644 --- a/src/Ombi.Schedule/Jobs/Plex/PlexAvailabilityChecker.cs +++ b/src/Ombi.Schedule/Jobs/Plex/PlexAvailabilityChecker.cs @@ -122,7 +122,7 @@ namespace Ombi.Schedule.Jobs.Plex { DateTime = DateTime.Now, NotificationType = NotificationType.RequestAvailable, - RequestId = child.ParentRequestId, + RequestId = child.Id, RequestType = RequestType.TvShow, Recipient = child.RequestedUser.Email })); From bf17bd4754efc82f74a3518a8306b0152b83b9c8 Mon Sep 17 00:00:00 2001 From: Jamie Rees Date: Thu, 28 Jun 2018 14:01:09 +0100 Subject: [PATCH 48/55] Stop spamming errors when FanArt doesn't have the image --- src/Ombi.Api.FanartTv/FanartTvApi.cs | 2 ++ src/Ombi.Api/Api.cs | 16 +++++++++++++--- src/Ombi.Api/Request.cs | 2 +- .../NotificationMessageCurlys.cs | 1 - 4 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/Ombi.Api.FanartTv/FanartTvApi.cs b/src/Ombi.Api.FanartTv/FanartTvApi.cs index bc819311c..55caef72c 100644 --- a/src/Ombi.Api.FanartTv/FanartTvApi.cs +++ b/src/Ombi.Api.FanartTv/FanartTvApi.cs @@ -21,6 +21,7 @@ namespace Ombi.Api.FanartTv { var request = new Request($"tv/{tvdbId}", Endpoint, HttpMethod.Get); request.AddHeader("api-key", token); + request.IgnoreErrors = true; try { return await Api.Request(request); @@ -36,6 +37,7 @@ namespace Ombi.Api.FanartTv { var request = new Request($"movies/{movieOrImdbId}", Endpoint, HttpMethod.Get); request.AddHeader("api-key", token); + request.IgnoreErrors = true; return await Api.Request(request); } diff --git a/src/Ombi.Api/Api.cs b/src/Ombi.Api/Api.cs index 98fff5e0c..b0e7066a8 100644 --- a/src/Ombi.Api/Api.cs +++ b/src/Ombi.Api/Api.cs @@ -39,7 +39,11 @@ namespace Ombi.Api if (!httpResponseMessage.IsSuccessStatusCode) { - LogError(request, httpResponseMessage); + if (!request.IgnoreErrors) + { + LogError(request, httpResponseMessage); + } + if (request.Retry) { @@ -94,7 +98,10 @@ namespace Ombi.Api var httpResponseMessage = await _client.SendAsync(httpRequestMessage); if (!httpResponseMessage.IsSuccessStatusCode) { - LogError(request, httpResponseMessage); + if (!request.IgnoreErrors) + { + LogError(request, httpResponseMessage); + } } // do something with the response var data = httpResponseMessage.Content; @@ -112,7 +119,10 @@ namespace Ombi.Api var httpResponseMessage = await _client.SendAsync(httpRequestMessage); if (!httpResponseMessage.IsSuccessStatusCode) { - LogError(request, httpResponseMessage); + if (!request.IgnoreErrors) + { + LogError(request, httpResponseMessage); + } } } } diff --git a/src/Ombi.Api/Request.cs b/src/Ombi.Api/Request.cs index 89c3a7f2d..cfd284f54 100644 --- a/src/Ombi.Api/Request.cs +++ b/src/Ombi.Api/Request.cs @@ -25,7 +25,7 @@ namespace Ombi.Api public string Endpoint { get; } public string BaseUrl { get; } public HttpMethod HttpMethod { get; } - + public bool IgnoreErrors { get; set; } public bool Retry { get; set; } public List StatusCodeToRetry { get; set; } = new List(); diff --git a/src/Ombi.Notifications/NotificationMessageCurlys.cs b/src/Ombi.Notifications/NotificationMessageCurlys.cs index 748f2adb5..f42126156 100644 --- a/src/Ombi.Notifications/NotificationMessageCurlys.cs +++ b/src/Ombi.Notifications/NotificationMessageCurlys.cs @@ -13,7 +13,6 @@ namespace Ombi.Notifications { public class NotificationMessageCurlys { - public void Setup(NotificationOptions opts, FullBaseRequest req, CustomizationSettings s) { LoadIssues(opts); From 93b8f14c4df72df20eef22c1bc4ea9d75baafb59 Mon Sep 17 00:00:00 2001 From: Jamie Rees Date: Thu, 28 Jun 2018 14:14:54 +0100 Subject: [PATCH 49/55] Fixed #2341 --- .../app/usermanagement/usermanagement.component.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Ombi/ClientApp/app/usermanagement/usermanagement.component.ts b/src/Ombi/ClientApp/app/usermanagement/usermanagement.component.ts index 486f814ab..74ad37e12 100644 --- a/src/Ombi/ClientApp/app/usermanagement/usermanagement.component.ts +++ b/src/Ombi/ClientApp/app/usermanagement/usermanagement.component.ts @@ -45,6 +45,12 @@ export class UserManagementComponent implements OnInit { this.notificationService.error("Email Notifications are not setup, cannot send welcome email"); return; } + if(!this.emailSettings.notificationTemplates.some(x => { + return x.enabled && x.notificationType === 8; + })) { + this.notificationService.error("The Welcome Email template is not enabled in the Email Setings"); + return; + } this.identityService.sendWelcomeEmail(user).subscribe(); this.notificationService.success(`Sent a welcome email to ${user.emailAddress}`); } From e059b521ddd9f6941c9a0da61a63f627ca5b7e27 Mon Sep 17 00:00:00 2001 From: Jamie Rees Date: Thu, 28 Jun 2018 14:34:15 +0100 Subject: [PATCH 50/55] Improve the validation around the Application URL --- .../app/services/settings.service.ts | 3 +++ .../customization/customization.component.ts | 21 ++++++++++++++----- src/Ombi/Controllers/SettingsController.cs | 17 +++++++-------- src/Ombi/Models/UrlVerifyModel.cs | 7 +++++++ 4 files changed, 33 insertions(+), 15 deletions(-) create mode 100644 src/Ombi/Models/UrlVerifyModel.cs diff --git a/src/Ombi/ClientApp/app/services/settings.service.ts b/src/Ombi/ClientApp/app/services/settings.service.ts index f6f770a19..726c86d63 100644 --- a/src/Ombi/ClientApp/app/services/settings.service.ts +++ b/src/Ombi/ClientApp/app/services/settings.service.ts @@ -279,4 +279,7 @@ export class SettingsService extends ServiceHelpers { return this.http .post(`${this.url}/notifications/newsletter`, JSON.stringify(settings), {headers: this.headers}); } + public verifyUrl(url: string): Observable { + return this.http.post(`${this.url}/customization/urlverify`, JSON.stringify({url}), {headers: this.headers}); + } } diff --git a/src/Ombi/ClientApp/app/settings/customization/customization.component.ts b/src/Ombi/ClientApp/app/settings/customization/customization.component.ts index c4cb32675..77e9a16bd 100644 --- a/src/Ombi/ClientApp/app/settings/customization/customization.component.ts +++ b/src/Ombi/ClientApp/app/settings/customization/customization.component.ts @@ -43,13 +43,24 @@ export class CustomizationComponent implements OnInit { } public save() { - this.settingsService.saveCustomization(this.settings).subscribe(x => { - if (x) { - this.notificationService.success("Successfully saved Ombi settings"); - } else { - this.notificationService.success("There was an error when saving the Ombi settings"); + + this.settingsService.verifyUrl(this.settings.applicationUrl).subscribe(x => { + if(this.settings.applicationUrl) { + if(!x) { + this.notificationService.error(`The URL "${this.settings.applicationUrl}" is not valid. Please format it correctly e.g. http://www.google.com/`); + return; + } } + + this.settingsService.saveCustomization(this.settings).subscribe(x => { + if (x) { + this.notificationService.success("Successfully saved Ombi settings"); + } else { + this.notificationService.success("There was an error when saving the Ombi settings"); + } + }); }); + } public dropDownChange(event: any): void { diff --git a/src/Ombi/Controllers/SettingsController.cs b/src/Ombi/Controllers/SettingsController.cs index 6f013b9c1..895621ad3 100644 --- a/src/Ombi/Controllers/SettingsController.cs +++ b/src/Ombi/Controllers/SettingsController.cs @@ -39,16 +39,6 @@ namespace Ombi.Controllers [Produces("application/json")] public class SettingsController : Controller { - /// - /// Initializes a new instance of the class. - /// - /// The resolver. - /// The mapper. - /// The templateRepo. - /// The embyApi. - /// The radarrCacher. - /// The memory cache. - /// The memory cache. public SettingsController(ISettingsResolver resolver, IMapper mapper, INotificationTemplatesRepository templateRepo, @@ -228,6 +218,13 @@ namespace Ombi.Controllers return await Save(settings); } + [ApiExplorerSettings(IgnoreApi = true)] + [HttpPost("customization/urlverify")] + public bool VerifyUrl([FromBody]UrlVerifyModel url) + { + return Uri.TryCreate(url.Url, UriKind.Absolute, out var __); + } + /// /// Get's the preset themes available /// diff --git a/src/Ombi/Models/UrlVerifyModel.cs b/src/Ombi/Models/UrlVerifyModel.cs new file mode 100644 index 000000000..01fdb0559 --- /dev/null +++ b/src/Ombi/Models/UrlVerifyModel.cs @@ -0,0 +1,7 @@ +namespace Ombi.Models +{ + public class UrlVerifyModel + { + public string Url { get; set; } + } +} \ No newline at end of file From 8e4b33fd322490d7aa4fbb976616301c26f5d534 Mon Sep 17 00:00:00 2001 From: Jamie Date: Thu, 28 Jun 2018 21:11:43 +0100 Subject: [PATCH 51/55] This should now fix #2350 --- src/Ombi/ApiKeyMiddlewear.cs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/Ombi/ApiKeyMiddlewear.cs b/src/Ombi/ApiKeyMiddlewear.cs index 6110fedcc..f38317b3e 100644 --- a/src/Ombi/ApiKeyMiddlewear.cs +++ b/src/Ombi/ApiKeyMiddlewear.cs @@ -5,6 +5,7 @@ using System.Security.Principal; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; using Ombi.Core.Authentication; using Ombi.Core.Settings; using Ombi.Settings.Settings.Models; @@ -13,15 +14,11 @@ namespace Ombi { public class ApiKeyMiddlewear { - public ApiKeyMiddlewear(RequestDelegate next, ISettingsService repo, OmbiUserManager um) + public ApiKeyMiddlewear(RequestDelegate next) { _next = next; - _repo = repo; - _userManager = um; } private readonly RequestDelegate _next; - private readonly ISettingsService _repo; - private readonly OmbiUserManager _userManager; public async Task Invoke(HttpContext context) { @@ -66,8 +63,9 @@ namespace Ombi await context.Response.WriteAsync("Invalid User Access Token"); return; } - - var user = await _userManager.Users.FirstOrDefaultAsync(x => x.UserAccessToken == key); + + var um = context.RequestServices.GetService(); + var user = await um.Users.FirstOrDefaultAsync(x => x.UserAccessToken == key); if (user == null) { context.Response.StatusCode = (int)HttpStatusCode.Unauthorized; @@ -77,7 +75,7 @@ namespace Ombi { var identity = new GenericIdentity(user.UserName); - var roles = await _userManager.GetRolesAsync(user); + var roles = await um.GetRolesAsync(user); var principal = new GenericPrincipal(identity, roles.ToArray()); context.User = principal; await next.Invoke(context); @@ -86,7 +84,8 @@ namespace Ombi private async Task ValidateApiKey(HttpContext context, RequestDelegate next, string key) { - var ombiSettings = await _repo.GetSettingsAsync(); + var repo = context.RequestServices.GetService>(); + var ombiSettings = await repo.GetSettingsAsync(); var valid = ombiSettings.ApiKey.Equals(key, StringComparison.CurrentCultureIgnoreCase); if (!valid) { From fb7c7fdae3b18b6aa7566afdfce29c75eebc34fe Mon Sep 17 00:00:00 2001 From: Anojh Date: Fri, 29 Jun 2018 11:36:37 -0700 Subject: [PATCH 52/55] fix unclosed table tags causing overflow #2322 --- .../Templates/NewsletterTemplate.html | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Ombi.Notifications.Templates/Templates/NewsletterTemplate.html b/src/Ombi.Notifications.Templates/Templates/NewsletterTemplate.html index 81190334a..450e7df2a 100644 --- a/src/Ombi.Notifications.Templates/Templates/NewsletterTemplate.html +++ b/src/Ombi.Notifications.Templates/Templates/NewsletterTemplate.html @@ -182,14 +182,16 @@ - {@RECENTLYADDED} + {@RECENTLYADDED} + +