From 827ea857e0c818af3a952b43add2278f3349fbe0 Mon Sep 17 00:00:00 2001 From: Anojh Date: Wed, 11 Apr 2018 13:55:36 -0700 Subject: [PATCH 01/37] Inject base url if set before theme file url, see issue #1795 --- src/Ombi/Controllers/SettingsController.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Ombi/Controllers/SettingsController.cs b/src/Ombi/Controllers/SettingsController.cs index 9ffa9d81f..44702a073 100644 --- a/src/Ombi/Controllers/SettingsController.cs +++ b/src/Ombi/Controllers/SettingsController.cs @@ -274,6 +274,12 @@ namespace Ombi.Controllers public async Task GetThemeContent([FromQuery]string url) { var css = await _githubApi.GetThemesRawContent(url); + var ombiSettings = await OmbiSettings(); + if (ombiSettings.BaseUrl != null) + { + int index = css.IndexOf("/api/"); + css = css.Insert(index, ombiSettings.BaseUrl); + } return Content(css, "text/css"); } From 0000ff1ce90ffcacbd9e8f9f8c7b692a664f763e Mon Sep 17 00:00:00 2001 From: Anojh Thayaparan Date: Mon, 16 Apr 2018 06:47:01 -0700 Subject: [PATCH 02/37] Inject base url if set before theme file url, see issue #1795 (#2148) --- src/Ombi/Controllers/SettingsController.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Ombi/Controllers/SettingsController.cs b/src/Ombi/Controllers/SettingsController.cs index 30469cd57..a5aef25fb 100644 --- a/src/Ombi/Controllers/SettingsController.cs +++ b/src/Ombi/Controllers/SettingsController.cs @@ -274,6 +274,12 @@ namespace Ombi.Controllers public async Task GetThemeContent([FromQuery]string url) { var css = await _githubApi.GetThemesRawContent(url); + var ombiSettings = await OmbiSettings(); + if (ombiSettings.BaseUrl != null) + { + int index = css.IndexOf("/api/"); + css = css.Insert(index, ombiSettings.BaseUrl); + } return Content(css, "text/css"); } From bc4db4184ce9e6dfc70aa6d0840c9584c9bcd49e Mon Sep 17 00:00:00 2001 From: Anojh Thayaparan Date: Mon, 16 Apr 2018 06:47:50 -0700 Subject: [PATCH 03/37] Knocking out LC requirements in issue #2124 (#2125) --- src/Ombi.Core/Engine/TvRequestEngine.cs | 7 +- src/Ombi.Core/Helpers/TvShowRequestBuilder.cs | 16 +++- src/Ombi.Mapping/Profiles/MovieProfile.cs | 13 ++++ src/Ombi.TheMovieDbApi/IMovieDbApi.cs | 1 + src/Ombi.TheMovieDbApi/Models/SearchResult.cs | 3 + .../Models/TvSearchResult.cs | 18 +++++ src/Ombi.TheMovieDbApi/TheMovieDbApi.cs | 13 +++- .../app/issues/issueDetails.component.html | 2 +- .../app/issues/issueDetails.component.ts | 12 ++- .../app/issues/issuestable.component.html | 8 +- .../app/issues/issuestable.component.ts | 20 ++++- .../app/requests/movierequests.component.html | 61 +++++++-------- .../app/requests/movierequests.component.ts | 47 +++++++++++- .../tvrequest-children.component.html | 15 +--- .../requests/tvrequest-children.component.ts | 16 +--- .../app/requests/tvrequests.component.html | 72 ++++++++++-------- .../app/requests/tvrequests.component.ts | 18 +++++ .../app/search/moviesearch.component.html | 2 +- .../app/search/moviesearch.component.ts | 18 +++-- .../app/search/tvsearch.component.html | 2 +- .../app/search/tvsearch.component.ts | 15 +++- src/Ombi/ClientApp/styles/Themes/plex.scss | 2 +- src/Ombi/ClientApp/styles/base.scss | 4 + src/Ombi/tsconfig.json | 2 +- .../wwwroot/images/default_movie_poster.png | Bin 0 -> 1685 bytes src/Ombi/wwwroot/images/default_tv_poster.png | Bin 0 -> 1776 bytes 26 files changed, 268 insertions(+), 119 deletions(-) create mode 100644 src/Ombi.TheMovieDbApi/Models/TvSearchResult.cs create mode 100644 src/Ombi/wwwroot/images/default_movie_poster.png create mode 100644 src/Ombi/wwwroot/images/default_tv_poster.png diff --git a/src/Ombi.Core/Engine/TvRequestEngine.cs b/src/Ombi.Core/Engine/TvRequestEngine.cs index aaa2d353d..7671c13fc 100644 --- a/src/Ombi.Core/Engine/TvRequestEngine.cs +++ b/src/Ombi.Core/Engine/TvRequestEngine.cs @@ -1,6 +1,7 @@ using System; using AutoMapper; using Ombi.Api.TvMaze; +using Ombi.Api.TheMovieDb; using Ombi.Core.Models.Requests; using Ombi.Core.Models.Search; using Ombi.Helpers; @@ -26,11 +27,12 @@ namespace Ombi.Core.Engine { public class TvRequestEngine : BaseMediaEngine, ITvRequestEngine { - public TvRequestEngine(ITvMazeApi tvApi, IRequestServiceMain requestService, IPrincipal user, + public TvRequestEngine(ITvMazeApi tvApi, IMovieDbApi movApi, IRequestServiceMain requestService, IPrincipal user, INotificationHelper helper, IRuleEvaluator rule, OmbiUserManager manager, ITvSender sender, IAuditRepository audit, IRepository rl, ISettingsService settings, ICacheService cache) : base(user, requestService, rule, manager, cache, settings) { TvApi = tvApi; + MovieDbApi = movApi; NotificationHelper = helper; TvSender = sender; Audit = audit; @@ -39,6 +41,7 @@ namespace Ombi.Core.Engine private INotificationHelper NotificationHelper { get; } private ITvMazeApi TvApi { get; } + private IMovieDbApi MovieDbApi { get; } private ITvSender TvSender { get; } private IAuditRepository Audit { get; } private readonly IRepository _requestLog; @@ -47,7 +50,7 @@ namespace Ombi.Core.Engine { var user = await GetUser(); - var tvBuilder = new TvShowRequestBuilder(TvApi); + var tvBuilder = new TvShowRequestBuilder(TvApi, MovieDbApi); (await tvBuilder .GetShowInfo(tv.TvDbId)) .CreateTvList(tv) diff --git a/src/Ombi.Core/Helpers/TvShowRequestBuilder.cs b/src/Ombi.Core/Helpers/TvShowRequestBuilder.cs index 1f92536b8..6e4d20be8 100644 --- a/src/Ombi.Core/Helpers/TvShowRequestBuilder.cs +++ b/src/Ombi.Core/Helpers/TvShowRequestBuilder.cs @@ -3,7 +3,9 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Ombi.Api.TvMaze; +using Ombi.Api.TheMovieDb; using Ombi.Api.TvMaze.Models; +using Ombi.Api.TheMovieDb.Models; using Ombi.Core.Models.Requests; using Ombi.Core.Models.Search; using Ombi.Helpers; @@ -16,12 +18,14 @@ namespace Ombi.Core.Helpers public class TvShowRequestBuilder { - public TvShowRequestBuilder(ITvMazeApi tvApi) + public TvShowRequestBuilder(ITvMazeApi tvApi, IMovieDbApi movApi) { TvApi = tvApi; + MovieDbApi = movApi; } private ITvMazeApi TvApi { get; } + private IMovieDbApi MovieDbApi { get; } public ChildRequests ChildRequest { get; set; } public List TvRequests { get; protected set; } @@ -29,10 +33,20 @@ namespace Ombi.Core.Helpers public DateTime FirstAir { get; protected set; } public TvRequests NewRequest { get; protected set; } protected TvMazeShow ShowInfo { get; set; } + protected List Results { get; set; } public async Task GetShowInfo(int id) { ShowInfo = await TvApi.ShowLookupByTheTvDbId(id); + Results = await MovieDbApi.SearchTv(ShowInfo.name); + foreach (TvSearchResult result in Results) { + if (result.Name == ShowInfo.name) + { + var showIds = await MovieDbApi.GetTvExternals(result.Id); + ShowInfo.externals.imdb = showIds.imdb_id; + break; + } + } DateTime.TryParse(ShowInfo.premiered, out var dt); diff --git a/src/Ombi.Mapping/Profiles/MovieProfile.cs b/src/Ombi.Mapping/Profiles/MovieProfile.cs index d7e43fc50..dd7e03379 100644 --- a/src/Ombi.Mapping/Profiles/MovieProfile.cs +++ b/src/Ombi.Mapping/Profiles/MovieProfile.cs @@ -24,6 +24,19 @@ namespace Ombi.Mapping.Profiles .ForMember(dest => dest.VoteAverage, opts => opts.MapFrom(src => src.vote_average)) .ForMember(dest => dest.VoteCount, opts => opts.MapFrom(src => src.vote_count)); + CreateMap() + .ForMember(dest => dest.BackdropPath, opts => opts.MapFrom(src => src.backdrop_path)) + .ForMember(dest => dest.Id, opts => opts.MapFrom(src => src.id)) + .ForMember(dest => dest.OriginalLanguage, opts => opts.MapFrom(src => src.original_language)) + .ForMember(dest => dest.OriginalName, opts => opts.MapFrom(src => src.original_name)) + .ForMember(dest => dest.Overview, opts => opts.MapFrom(src => src.overview)) + .ForMember(dest => dest.Popularity, opts => opts.MapFrom(src => src.popularity)) + .ForMember(dest => dest.PosterPath, opts => opts.MapFrom(src => src.poster_path)) + .ForMember(dest => dest.ReleaseDate, opts => opts.MapFrom(src => src.first_air_date)) + .ForMember(dest => dest.Name, opts => opts.MapFrom(src => src.name)) + .ForMember(dest => dest.VoteAverage, opts => opts.MapFrom(src => src.vote_average)) + .ForMember(dest => dest.VoteCount, opts => opts.MapFrom(src => src.vote_count)); + CreateMap() .ForMember(dest => dest.Adult, opts => opts.MapFrom(src => src.adult)) .ForMember(dest => dest.BackdropPath, opts => opts.MapFrom(src => src.backdrop_path)) diff --git a/src/Ombi.TheMovieDbApi/IMovieDbApi.cs b/src/Ombi.TheMovieDbApi/IMovieDbApi.cs index 787902a4b..5d0a89992 100644 --- a/src/Ombi.TheMovieDbApi/IMovieDbApi.cs +++ b/src/Ombi.TheMovieDbApi/IMovieDbApi.cs @@ -12,6 +12,7 @@ namespace Ombi.Api.TheMovieDb Task> NowPlaying(); Task> PopularMovies(); Task> SearchMovie(string searchTerm); + Task> SearchTv(string searchTerm); Task> TopRated(); Task> Upcoming(); Task> SimilarMovies(int movieId); diff --git a/src/Ombi.TheMovieDbApi/Models/SearchResult.cs b/src/Ombi.TheMovieDbApi/Models/SearchResult.cs index 81d8115f6..7b09b5e4b 100644 --- a/src/Ombi.TheMovieDbApi/Models/SearchResult.cs +++ b/src/Ombi.TheMovieDbApi/Models/SearchResult.cs @@ -32,9 +32,12 @@ namespace Ombi.TheMovieDbApi.Models public bool adult { get; set; } public string overview { get; set; } public string release_date { get; set; } + public string first_air_date { get; set; } public int?[] genre_ids { get; set; } public int id { get; set; } public string original_title { get; set; } + public string original_name { get; set; } + public string name { get; set; } public string original_language { get; set; } public string title { get; set; } public string backdrop_path { get; set; } diff --git a/src/Ombi.TheMovieDbApi/Models/TvSearchResult.cs b/src/Ombi.TheMovieDbApi/Models/TvSearchResult.cs new file mode 100644 index 000000000..eaf93d7cc --- /dev/null +++ b/src/Ombi.TheMovieDbApi/Models/TvSearchResult.cs @@ -0,0 +1,18 @@ +namespace Ombi.Api.TheMovieDb.Models +{ + public class TvSearchResult + { + public string PosterPath { get; set; } + public string Overview { get; set; } + public string ReleaseDate { get; set; } + public int?[] GenreIds { get; set; } + public int Id { get; set; } + public string OriginalName { get; set; } + public string OriginalLanguage { get; set; } + public string Name { get; set; } + public string BackdropPath { get; set; } + public float Popularity { get; set; } + public int VoteCount { get; set; } + public float VoteAverage { get; set; } + } +} \ No newline at end of file diff --git a/src/Ombi.TheMovieDbApi/TheMovieDbApi.cs b/src/Ombi.TheMovieDbApi/TheMovieDbApi.cs index 08925e490..ccd0e52e6 100644 --- a/src/Ombi.TheMovieDbApi/TheMovieDbApi.cs +++ b/src/Ombi.TheMovieDbApi/TheMovieDbApi.cs @@ -42,7 +42,18 @@ namespace Ombi.Api.TheMovieDb return await Api.Request(request); } - + + public async Task> SearchTv(string searchTerm) + { + var request = new Request($"search/tv", BaseUri, HttpMethod.Get); + request.FullUri = request.FullUri.AddQueryParameter("api_key", ApiToken); + request.FullUri = request.FullUri.AddQueryParameter("query", searchTerm); + AddRetry(request); + + var result = await Api.Request>(request); + return Mapper.Map>(result.results); + } + public async Task GetTvExternals(int theMovieDbId) { var request = new Request($"/tv/{theMovieDbId}/external_ids", BaseUri, HttpMethod.Get); diff --git a/src/Ombi/ClientApp/app/issues/issueDetails.component.html b/src/Ombi/ClientApp/app/issues/issueDetails.component.html index e88ad621c..990b9d636 100644 --- a/src/Ombi/ClientApp/app/issues/issueDetails.component.html +++ b/src/Ombi/ClientApp/app/issues/issueDetails.component.html @@ -1,5 +1,5 @@
-
+

{{issue.title}}

diff --git a/src/Ombi/ClientApp/app/issues/issueDetails.component.ts b/src/Ombi/ClientApp/app/issues/issueDetails.component.ts index 34fcbe302..1072bedbd 100644 --- a/src/Ombi/ClientApp/app/issues/issueDetails.component.ts +++ b/src/Ombi/ClientApp/app/issues/issueDetails.component.ts @@ -98,7 +98,11 @@ export class IssueDetailsComponent implements OnInit { ("url(" + x + ")"); }); this.imageService.getMoviePoster(issue.providerId).subscribe(x => { - this.posterPath = x.toString(); + if (x.length === 0) { + this.posterPath = "../../../images/default_movie_poster.png"; + } else { + this.posterPath = x.toString(); + } }); } else { @@ -107,7 +111,11 @@ export class IssueDetailsComponent implements OnInit { ("url(" + x + ")"); }); this.imageService.getTvPoster(Number(issue.providerId)).subscribe(x => { - this.posterPath = x.toString(); + if (x.length === 0) { + this.posterPath = "../../../images/default_tv_poster.png"; + } else { + this.posterPath = x.toString(); + } }); } diff --git a/src/Ombi/ClientApp/app/issues/issuestable.component.html b/src/Ombi/ClientApp/app/issues/issuestable.component.html index f98d6eb0e..83dc8a1db 100644 --- a/src/Ombi/ClientApp/app/issues/issuestable.component.html +++ b/src/Ombi/ClientApp/app/issues/issuestable.component.html @@ -1,25 +1,25 @@ - - - - "); sb.Append( "
+ + + + diff --git a/src/Ombi/ClientApp/app/issues/issuestable.component.ts b/src/Ombi/ClientApp/app/issues/issuestable.component.ts index ee93e689d..f03dd9a6d 100644 --- a/src/Ombi/ClientApp/app/issues/issuestable.component.ts +++ b/src/Ombi/ClientApp/app/issues/issuestable.component.ts @@ -20,11 +20,25 @@ export class IssuesTableComponent { public rowCount = 10; - public setOrder(value: string) { + public setOrder(value: string, el: any) { + el = el.toElement || el.relatedTarget || el.target || el.srcElement; + + if (el.nodeName === "A") { + el = el.parentElement; + } + + const parent = el.parentElement; + const previousFilter = parent.querySelector(".active"); + if (this.order === value) { - this.reverse = !this.reverse; + this.reverse = !this.reverse; + } else { + if (previousFilter) { + previousFilter.className = ""; + } + el.className = "active"; } - + this.order = value; } diff --git a/src/Ombi/ClientApp/app/requests/movierequests.component.html b/src/Ombi/ClientApp/app/requests/movierequests.component.html index 6b2300f38..8eded8dba 100644 --- a/src/Ombi/ClientApp/app/requests/movierequests.component.html +++ b/src/Ombi/ClientApp/app/requests/movierequests.component.html @@ -67,7 +67,7 @@
- poster + poster
@@ -222,42 +222,43 @@

{{ 'Requests.Filter' | translate }}


- -

{{ 'Filter.FilterHeaderAvailability' | translate }}

-
-
- - +
+

{{ 'Filter.FilterHeaderAvailability' | translate }}

+
+
+ + +
-
-
-
- - +
+
+ + +
- -

{{ 'Filter.FilterHeaderRequestStatus' | translate }}

-
-
- - +
+

{{ 'Filter.FilterHeaderRequestStatus' | translate }}

+
+
+ + +
-
-
-
- - +
+
+ + +
-
-
-
- - +
+
+ + +
- - \ No newline at end of file diff --git a/src/Ombi/ClientApp/app/requests/movierequests.component.ts b/src/Ombi/ClientApp/app/requests/movierequests.component.ts index 0fc5f6651..e28b86eaa 100644 --- a/src/Ombi/ClientApp/app/requests/movierequests.component.ts +++ b/src/Ombi/ClientApp/app/requests/movierequests.component.ts @@ -149,7 +149,16 @@ export class MovieRequestsComponent implements OnInit { event.preventDefault(); } - public clearFilter() { + public clearFilter(el: any) { + el = el.toElement || el.relatedTarget || el.target || el.srcElement; + + el = el.parentElement; + el = el.querySelectorAll("INPUT"); + for (el of el) { + el.checked = false; + el.parentElement.classList.remove("active"); + } + this.filterDisplay = false; this.filter.availabilityFilter = FilterType.None; this.filter.statusFilter = FilterType.None; @@ -157,7 +166,8 @@ export class MovieRequestsComponent implements OnInit { this.resetSearch(); } - public filterAvailability(filter: FilterType) { + public filterAvailability(filter: FilterType, el: any) { + this.filterActiveStyle(el); this.filter.availabilityFilter = filter; this.requestService.filterMovies(this.filter) .subscribe(x => { @@ -166,7 +176,8 @@ export class MovieRequestsComponent implements OnInit { }); } - public filterStatus(filter: FilterType) { + public filterStatus(filter: FilterType, el: any) { + this.filterActiveStyle(el); this.filter.statusFilter = filter; this.requestService.filterMovies(this.filter) .subscribe(x => { @@ -190,6 +201,24 @@ export class MovieRequestsComponent implements OnInit { this.order = value; } + private filterActiveStyle(el: any) { + el = el.toElement || el.relatedTarget || el.target || el.srcElement; + + el = el.parentElement; //gets radio div + el = el.parentElement; //gets form group div + el = el.parentElement; //gets status filter div + el = el.querySelectorAll("INPUT"); + for (el of el) { + if (el.checked) { + if (!el.parentElement.classList.contains("active")) { + el.parentElement.className += " active"; + } + } else { + el.parentElement.classList.remove("active"); + } + } + } + private loadRequests(amountToLoad: number, currentlyLoaded: number) { this.requestService.getMovieRequests(amountToLoad, currentlyLoaded + 1) .subscribe(x => { @@ -243,7 +272,8 @@ export class MovieRequestsComponent implements OnInit { this.movieRequests = x; this.movieRequests.forEach((req) => { - this.movieRequests.forEach((req) => this.setBackground(req)); + this.setBackground(req); + this.setPoster(req); }); this.radarrService.getQualityProfilesFromSettings().subscribe(c => { this.radarrProfiles = c; @@ -296,11 +326,20 @@ export class MovieRequestsComponent implements OnInit { } private setOverride(req: IMovieRequests): void { + this.setPoster(req); this.setBackground(req); this.setQualityOverrides(req); this.setRootFolderOverrides(req); } + private setPoster(req: IMovieRequests): void { + if (req.posterPath === null) { + req.posterPath = "../../../images/default_movie_poster.png"; + } else { + req.posterPath = "https://image.tmdb.org/t/p/w300/" + req.posterPath; + } + } + private setBackground(req: IMovieRequests): void { req.backgroundPath = this.sanitizer.bypassSecurityTrustStyle ("url(" + "https://image.tmdb.org/t/p/w1280" + req.background + ")"); diff --git a/src/Ombi/ClientApp/app/requests/tvrequest-children.component.html b/src/Ombi/ClientApp/app/requests/tvrequest-children.component.html index 30e1398eb..736878d4b 100644 --- a/src/Ombi/ClientApp/app/requests/tvrequest-children.component.html +++ b/src/Ombi/ClientApp/app/requests/tvrequest-children.component.html @@ -22,15 +22,7 @@
- +
@@ -101,8 +93,3 @@
- - - - \ No newline at end of file diff --git a/src/Ombi/ClientApp/app/requests/tvrequest-children.component.ts b/src/Ombi/ClientApp/app/requests/tvrequest-children.component.ts index 300599063..e9da2342f 100644 --- a/src/Ombi/ClientApp/app/requests/tvrequest-children.component.ts +++ b/src/Ombi/ClientApp/app/requests/tvrequest-children.component.ts @@ -1,5 +1,5 @@ import { Component, EventEmitter, Input, Output } from "@angular/core"; -import { IChildRequests, IIssueCategory } from "../interfaces"; +import { IChildRequests } from "../interfaces"; import { NotificationService, RequestService } from "../services"; @@ -13,13 +13,6 @@ export class TvRequestChildrenComponent { @Output() public requestDeleted = new EventEmitter(); - @Input() public issueCategories: IIssueCategory[]; - @Input() public issuesEnabled: boolean; - @Input() public issueProviderId: string; - public issuesBarVisible = false; - public issueRequest: IChildRequests; - public issueCategorySelected: IIssueCategory; - constructor(private requestService: RequestService, private notificationService: NotificationService) { } @@ -101,13 +94,6 @@ export class TvRequestChildrenComponent { }); } - public reportIssue(catId: IIssueCategory, req: IChildRequests) { - this.issueRequest = req; - this.issueCategorySelected = catId; - this.issuesBarVisible = true; - this.issueProviderId = req.id.toString(); - } - private removeRequestFromUi(key: IChildRequests) { const index = this.childRequests.indexOf(key, 0); if (index > -1) { diff --git a/src/Ombi/ClientApp/app/requests/tvrequests.component.html b/src/Ombi/ClientApp/app/requests/tvrequests.component.html index a602d27e1..9a3c4d186 100644 --- a/src/Ombi/ClientApp/app/requests/tvrequests.component.html +++ b/src/Ombi/ClientApp/app/requests/tvrequests.component.html @@ -64,51 +64,63 @@
- +
- -
- - - + +
+ + + +
+ + +
+ + + +
+
- - -
- - -
- -
+ (requestDeleted)="childRequestDeleted($event)">
+ + \ No newline at end of file diff --git a/src/Ombi/ClientApp/app/requests/tvrequests.component.ts b/src/Ombi/ClientApp/app/requests/tvrequests.component.ts index ad8b4ca50..344bf5712 100644 --- a/src/Ombi/ClientApp/app/requests/tvrequests.component.ts +++ b/src/Ombi/ClientApp/app/requests/tvrequests.component.ts @@ -33,6 +33,9 @@ export class TvRequestsComponent implements OnInit { @Input() public issueCategories: IIssueCategory[]; @Input() public issuesEnabled: boolean; public issueProviderId: string; + public issuesBarVisible = false; + public issueRequest: ITvRequests; + public issueCategorySelected: IIssueCategory; public sonarrProfiles: ISonarrProfile[] = []; public sonarrRootFolders: ISonarrRootFolder[] = []; @@ -151,6 +154,13 @@ export class TvRequestsComponent implements OnInit { this.updateRequest(searchResult); } + public reportIssue(catId: IIssueCategory, req: ITvRequests) { + this.issueRequest = req; + this.issueCategorySelected = catId; + this.issuesBarVisible = true; + this.issueProviderId = req.id.toString(); + } + private setOverride(req: ITvRequests): void { this.setQualityOverrides(req); this.setRootFolderOverrides(req); @@ -191,6 +201,7 @@ export class TvRequestsComponent implements OnInit { .subscribe(x => { this.tvRequests = x; this.tvRequests.forEach((val, index) => { + this.setDefaults(val); this.loadBackdrop(val); this.setOverride(val.data); }); @@ -209,6 +220,13 @@ export class TvRequestsComponent implements OnInit { this.currentlyLoaded = 5; this.loadInit(); } + + private setDefaults(val: any) { + if (val.data.posterPath === null) { + val.data.posterPath = "../../../images/default_tv_poster.png"; + } + } + private loadBackdrop(val: TreeNode): void { this.imageService.getTvBanner(val.data.tvDbId).subscribe(x => { val.data.background = this.sanitizer.bypassSecurityTrustStyle diff --git a/src/Ombi/ClientApp/app/search/moviesearch.component.html b/src/Ombi/ClientApp/app/search/moviesearch.component.html index 58d8415fb..cac78531b 100644 --- a/src/Ombi/ClientApp/app/search/moviesearch.component.html +++ b/src/Ombi/ClientApp/app/search/moviesearch.component.html @@ -33,7 +33,7 @@
- poster + poster
diff --git a/src/Ombi/ClientApp/app/search/moviesearch.component.ts b/src/Ombi/ClientApp/app/search/moviesearch.component.ts index 819463c4f..0abc10474 100644 --- a/src/Ombi/ClientApp/app/search/moviesearch.component.ts +++ b/src/Ombi/ClientApp/app/search/moviesearch.component.ts @@ -157,12 +157,15 @@ export class MovieSearchComponent implements OnInit { private getExtraInfo() { - this.movieResults.forEach((val, index) => { - - val.background = this.sanitizer. - bypassSecurityTrustStyle - ("url(" + "https://image.tmdb.org/t/p/w1280" + val.backdropPath + ")"); - this.searchService.getMovieInformation(val.id) + this.movieResults.forEach((val, index) => { + if (val.posterPath === null) { + val.posterPath = "../../../images/default_movie_poster.png"; + } else { + val.posterPath = "https://image.tmdb.org/t/p/w300/" + val.posterPath; + } + val.background = this.sanitizer.bypassSecurityTrustStyle + ("url(" + "https://image.tmdb.org/t/p/w1280" + val.backdropPath + ")"); + this.searchService.getMovieInformation(val.id) .subscribe(m => { this.updateItem(val, m); }); @@ -174,7 +177,8 @@ export class MovieSearchComponent implements OnInit { if (index > -1) { const copy = { ...this.movieResults[index] }; this.movieResults[index] = updated; - this.movieResults[index].background = copy.background; + this.movieResults[index].background = copy.background; + this.movieResults[index].posterPath = copy.posterPath; } } private clearResults() { diff --git a/src/Ombi/ClientApp/app/search/tvsearch.component.html b/src/Ombi/ClientApp/app/search/tvsearch.component.html index fb1729e50..42232a564 100644 --- a/src/Ombi/ClientApp/app/search/tvsearch.component.html +++ b/src/Ombi/ClientApp/app/search/tvsearch.component.html @@ -62,7 +62,7 @@
- +

{{node.data.title}} ({{node.data.firstAired | date: 'yyyy'}})

diff --git a/src/Ombi/ClientApp/app/search/tvsearch.component.ts b/src/Ombi/ClientApp/app/search/tvsearch.component.ts index 961e85f63..bb30810e4 100644 --- a/src/Ombi/ClientApp/app/search/tvsearch.component.ts +++ b/src/Ombi/ClientApp/app/search/tvsearch.component.ts @@ -130,7 +130,6 @@ export class TvSearchComponent implements OnInit { public getExtraInfo() { this.tvResults.forEach((val, index) => { this.imageService.getTvBanner(val.data.id).subscribe(x => { - val.data.background = this.sanitizer. bypassSecurityTrustStyle ("url(" + x + ")"); @@ -138,6 +137,7 @@ export class TvSearchComponent implements OnInit { this.searchService.getShowInformationTreeNode(val.data.id) .subscribe(x => { if (x.data) { + this.setDefaults(x); this.updateItem(val, x); } else { const index = this.tvResults.indexOf(val, 0); @@ -216,6 +216,7 @@ export class TvSearchComponent implements OnInit { const index = this.tvResults.indexOf(key, 0); if (index > -1) { // Update certain properties, otherwise we will loose some data + this.tvResults[index].data.title = updated.data.title; this.tvResults[index].data.banner = updated.data.banner; this.tvResults[index].data.imdbId = updated.data.imdbId; this.tvResults[index].data.seasonRequests = updated.data.seasonRequests; @@ -225,6 +226,18 @@ export class TvSearchComponent implements OnInit { } } + private setDefaults(x: any) { + if (x.data.banner === null) { + x.data.banner = "../../../images/default_tv_poster.png"; + } + + if (x.data.imdbId === null) { + x.data.imdbId = "https://www.tvmaze.com/shows/" + x.data.seriesId; + } else { + x.data.imdbId = "http://www.imdb.com/title/" + x.data.imdbId + "/"; + } + } + private clearResults() { this.tvResults = []; this.searchApplied = false; diff --git a/src/Ombi/ClientApp/styles/Themes/plex.scss b/src/Ombi/ClientApp/styles/Themes/plex.scss index 1e3c27eaf..1475aef91 100644 --- a/src/Ombi/ClientApp/styles/Themes/plex.scss +++ b/src/Ombi/ClientApp/styles/Themes/plex.scss @@ -351,5 +351,5 @@ button.list-group-item:focus { position: absolute; } table.table > thead > tr > th.active { - background-color: transparent; + background-color: $primary-colour; } \ No newline at end of file diff --git a/src/Ombi/ClientApp/styles/base.scss b/src/Ombi/ClientApp/styles/base.scss index 619dd8042..3a5deb2a3 100644 --- a/src/Ombi/ClientApp/styles/base.scss +++ b/src/Ombi/ClientApp/styles/base.scss @@ -541,6 +541,10 @@ $border-radius: 10px; cursor: pointer; } +.table-usermanagement { + margin-top: 20px; +} + .input-group-sm { padding-top: 2px; padding-bottom: 2px; diff --git a/src/Ombi/tsconfig.json b/src/Ombi/tsconfig.json index daff6b451..8f860760c 100644 --- a/src/Ombi/tsconfig.json +++ b/src/Ombi/tsconfig.json @@ -11,7 +11,7 @@ "noUnusedLocals": true, "noImplicitThis": true, "noImplicitReturns": true, - "noImplicitAny": true, + "noImplicitAny": false, "suppressImplicitAnyIndexErrors": true, "alwaysStrict": true, "emitDecoratorMetadata": true, diff --git a/src/Ombi/wwwroot/images/default_movie_poster.png b/src/Ombi/wwwroot/images/default_movie_poster.png new file mode 100644 index 0000000000000000000000000000000000000000..f5cec85a5b313321a2840ebbb07c5bd52843678b GIT binary patch literal 1685 zcmeAS@N?(olHy`uVBq!ia0vp^Cm0wQbvW37thQf&_AxN9#dx|nhE&XXd)MDzDq80F zL*t5{{xK7>axU-q_ILjhy;;k0V#ong(>C9n;y>TcRBr!{9U9FC7hEZgUAKPy>9oz2N3ZUcz5BDX>_Uy5{^px& zg8BbUzw-L)sT?!y&p-d%@P4`G|8xI;tv+g#2^mm8;lT*mq2z`OVFPiJklx^Z2ehb`4aO7_j`|1VjJ98Nzq ziu+lm`TqWX{lgDGJh=bdUuA;roqPA}ZZ?O6hN?~Wl#tSm3gzYH zOV_>LbT_a5_xFn#AwX}hUh{UXu!r~Sue)mg|Eqnz@%HW8MU|B|SN8rY+pQbFzphru zUDN)WwG5y4{`>ZaPTdomt{+uZQITP@?QUMVok0J~ou*5FAGU`l*UXnso-Fwo5gI!8 z)`IDu{>-}c_o4i{q^(!pe?MJgcmL<2`zb~*QxwwE(}C_#KhDnl7p?U$vM_s-&JKO7W#)iK^>&8k&}e6QcVTUU7J?g~$9 zrcBM=Svq3fa~G@qt=nJo=f}qd#*u&P>Ys~hE>_cbR?yulX?n9%_4Sh{Pc){7-0qph z@ucz_G)*P&U%Iqk*0i#s!s3+L-{-SteN%Mvy(Q@34K!&__4mB><))GAuRs0#{5<>q zGru1mZdaFnw`$d?pFb-fr^ZQtDP!5xelus=_1EmDpMHAqe`40|yVIT)?flGWFV|nb z&(66TV$br3>K!|GX3CVme{>}$_ImH*clo(SKOrvcm4Wx>weSTSu&S_fqj2p zZT21J-s8#AU&ZDmZG95!`_Bhn2y6j!K>=x!_3883)w?B5fE+SAe0S5;K8J4`-mf^B zZObuxUw`1_|9%SWCNg}dj~+dG(B9#Adb3QQ10OJ@{bX5T?xO&AO**5VSog8Q^2KUz zIaF@snC-V?EZZ7o+BfS;>D`^Zzs?6vj(xT3-`mM~=CjXc>%H|YoM~QR^WyuR<`vzG z)lNSCy*YcqM0uNL7W?^zyDV1z7BZXdyYls}k25C*ty;Z$b>81s&3uJjtfPe(JPIi& c&j0Yl zO)W`OsL0L9E4HezRRWu91!RMS^_3LBN=mYAl^~9a@C{IK&M!(;Fx4~DO*S+!QZTpF zGc+@>G&0dqFfuSS*EcZNH#F8YFtjo-vof|+fC43;ZAB?*RzWUqP`iLUTcwPWk^(Dz z{qpj1y>er{{GxPyLrY6beFGzXBO_g)3fZE`@j@w*YQzUNJP7fB~jokyxN_sAmB35=^15FMg%Dxp39RB|)hO_hL1; zIHa;5RX-@TIKQ+g85nVC${?!>telHd6HD@oLh|!-V4)b0kzbNuoRMFk;OwlR5uTZs zl3!k|30CgwYvq|&T#}fVoa*Ufs{}MaFEca6%FM*V$kfEu*x1s*($LV=!qLdW)zQSn z+1SzCz{Sbc5~kNBKe;qFHLnDwHwB^B1gBn5O2{n$+U$~Alv$RV;#QQOs{r<~RVHq? z7~(V!sy799yBiO3djj(>Wj6+5Kc& z-Lg|`mm~G2%U{@X#JgdGfY>3XL`9tz)q1Rudu9rqma}8`ceyFN<)w9o)+i&OooDey! z*KM&u`n=~Kw~9Utl2{k2yZ+T`gDi;~a<5riL@yjTRCVChyk%@+*POTMvu&%aZ@ZdR zx-;h1r$E0+N0T1j=QZ4MrzSzug{Y8fVdeiIW*^}@>4?Fhtb5jfmfQ{M*bsO5Wr@*- z4YR_hEp2SQx@VEbs#R4}GY_vweDLF5Yy9MsD%ZNhW-ZJ8#}#({YuWC*IcD!>{V_Te z{q^^|9&*r<0+{(ORQ@Dylq8^mODt z#fC-{YMuVH{NCLS_4nVF9qJ0c;qtt)&PHy2&B9fywB~%TvHKk+u@)G_aqHKfUWb`> jI8G1w)b;;eKLay^g>(Pw(m4fvpgPFY)z4*}Q$iB}QrLNN literal 0 HcmV?d00001 From 7bbcb9a6267df751c47d2b91e2ee5da0d8c0fe65 Mon Sep 17 00:00:00 2001 From: Jamie Rees Date: Wed, 18 Apr 2018 15:11:44 +0100 Subject: [PATCH 04/37] Made an attempt at PlexOAuth !wip --- src/Ombi/Controllers/PlexOAuthController.cs | 40 +++++++++ src/Ombi/Controllers/TokenController.cs | 95 ++++++++++++--------- src/Ombi/Models/UserAuthModel.cs | 1 + 3 files changed, 97 insertions(+), 39 deletions(-) create mode 100644 src/Ombi/Controllers/PlexOAuthController.cs diff --git a/src/Ombi/Controllers/PlexOAuthController.cs b/src/Ombi/Controllers/PlexOAuthController.cs new file mode 100644 index 000000000..8f33e37be --- /dev/null +++ b/src/Ombi/Controllers/PlexOAuthController.cs @@ -0,0 +1,40 @@ +using System.Net; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Http.Internal; +using System.IO; +using System.Text; + +namespace Ombi.Controllers +{ + [ApiExplorerSettings(IgnoreApi = true)] + [ApiV1] + [AllowAnonymous] + public class PlexOAuthController : Controller + { + + [HttpGet] + public IActionResult OAuthCallBack() + { + var bodyStr = ""; + var req = Request; + + // Allows using several time the stream in ASP.Net Core + req.EnableRewind(); + + // Arguments: Stream, Encoding, detect encoding, buffer size + // AND, the most important: keep stream opened + using (StreamReader reader + = new StreamReader(req.Body, Encoding.UTF8, true, 1024, true)) + { + bodyStr = reader.ReadToEnd(); + } + + // Rewind, so the core is not lost when it looks the body for the request + req.Body.Position = 0; + + // Do your work with bodyStr + return Ok(); + } + } +} diff --git a/src/Ombi/Controllers/TokenController.cs b/src/Ombi/Controllers/TokenController.cs index 18da61e3a..5d524b7ba 100644 --- a/src/Ombi/Controllers/TokenController.cs +++ b/src/Ombi/Controllers/TokenController.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.IdentityModel.Tokens.Jwt; using System.Linq; +using System.Net.Http; using System.Security.Claims; using System.Text; using System.Threading.Tasks; @@ -9,6 +10,7 @@ using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Options; using Microsoft.IdentityModel.Tokens; +using Ombi.Api; using Ombi.Core.Authentication; using Ombi.Helpers; using Ombi.Models; @@ -62,50 +64,65 @@ namespace Ombi.Controllers user.EmailLogin = true; } - // Verify Password - if (await _userManager.CheckPasswordAsync(user, model.Password)) + if (!model.UsePlexOAuth) { - var roles = await _userManager.GetRolesAsync(user); - - if (roles.Contains(OmbiRoles.Disabled)) + // Verify Password + if (await _userManager.CheckPasswordAsync(user, model.Password)) { - return new UnauthorizedResult(); + var roles = await _userManager.GetRolesAsync(user); + + if (roles.Contains(OmbiRoles.Disabled)) + { + return new UnauthorizedResult(); + } + + user.LastLoggedIn = DateTime.UtcNow; + await _userManager.UpdateAsync(user); + + var claims = new List + { + new Claim(JwtRegisteredClaimNames.Sub, user.UserName), + new Claim(ClaimTypes.NameIdentifier, user.Id), + new Claim(ClaimTypes.Name, user.UserName), + new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()) + }; + claims.AddRange(roles.Select(role => new Claim("role", role))); + + var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_tokenAuthenticationOptions.SecretKey)); + var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); + + + var token = new JwtSecurityToken( + claims: claims, + expires: model.RememberMe ? DateTime.UtcNow.AddDays(7) : DateTime.UtcNow.AddHours(5), + signingCredentials: creds, + audience: "Ombi", issuer: "Ombi" + ); + var accessToken = new JwtSecurityTokenHandler().WriteToken(token); + if (model.RememberMe) + { + // Save the token so we can refresh it later + //await _token.CreateToken(new Tokens() {Token = accessToken, User = user}); + } + + return new JsonResult(new + { + access_token = accessToken, + expiration = token.ValidTo + }); } + } + else + { + // Plex OAuth + // Redirect them to Plex - user.LastLoggedIn = DateTime.UtcNow; - await _userManager.UpdateAsync(user); - - var claims = new List - { - new Claim(JwtRegisteredClaimNames.Sub, user.UserName), - new Claim(ClaimTypes.NameIdentifier, user.Id), - new Claim(ClaimTypes.Name, user.UserName), - new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()) - }; - claims.AddRange(roles.Select(role => new Claim("role", role))); - - var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_tokenAuthenticationOptions.SecretKey)); - var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); - - - var token = new JwtSecurityToken( - claims: claims, - expires: model.RememberMe ? DateTime.UtcNow.AddDays(7) : DateTime.UtcNow.AddHours(5), - signingCredentials: creds, - audience: "Ombi", issuer:"Ombi" - ); - var accessToken = new JwtSecurityTokenHandler().WriteToken(token); - if (model.RememberMe) - { - // Save the token so we can refresh it later - //await _token.CreateToken(new Tokens() {Token = accessToken, User = user}); - } + var request = new Request("auth", "https://app.plex.tv", HttpMethod.Get); + request.AddQueryString("clientID", "OMBIv3"); + request.AddQueryString("forwardUrl", "http://localhost:5000"); + request.AddQueryString("context-device-product", "http://localhost:5000"); + return new RedirectResult("https://app.plex.tv/auth#?forwardUrl=http://localhost:5000/api/v1/plexoauth&clientID=OMBIv3&context%5Bdevice%5D%5Bproduct%5D=Ombi%20SSO"); - return new JsonResult(new - { - access_token = accessToken, - expiration = token.ValidTo - }); } return new UnauthorizedResult(); diff --git a/src/Ombi/Models/UserAuthModel.cs b/src/Ombi/Models/UserAuthModel.cs index 5b3d69a29..046119821 100644 --- a/src/Ombi/Models/UserAuthModel.cs +++ b/src/Ombi/Models/UserAuthModel.cs @@ -6,5 +6,6 @@ public string Password { get; set; } public bool RememberMe { get; set; } public bool UsePlexAdminAccount { get; set; } + public bool UsePlexOAuth { get; set; } } } \ No newline at end of file From bd741e053f5bc764ce95180bba23370fdad1041b Mon Sep 17 00:00:00 2001 From: Jamie Rees Date: Wed, 18 Apr 2018 15:15:44 +0100 Subject: [PATCH 05/37] Fixed #2169 --- src/Ombi.DependencyInjection/IocExtensions.cs | 1 - src/Ombi.Schedule/JobSetup.cs | 4 +- .../Plex/Interfaces/IPlexRecentlyAddedSync.cs | 9 ----- .../Jobs/Plex/PlexRecentlyAddedSync.cs | 40 ------------------- .../app/settings/jobs/jobs.component.html | 2 +- 5 files changed, 2 insertions(+), 54 deletions(-) delete mode 100644 src/Ombi.Schedule/Jobs/Plex/Interfaces/IPlexRecentlyAddedSync.cs delete mode 100644 src/Ombi.Schedule/Jobs/Plex/PlexRecentlyAddedSync.cs diff --git a/src/Ombi.DependencyInjection/IocExtensions.cs b/src/Ombi.DependencyInjection/IocExtensions.cs index e2f667465..92ecf8282 100644 --- a/src/Ombi.DependencyInjection/IocExtensions.cs +++ b/src/Ombi.DependencyInjection/IocExtensions.cs @@ -175,7 +175,6 @@ namespace Ombi.DependencyInjection services.AddTransient(); services.AddTransient(); services.AddTransient(); - services.AddTransient(); } } } diff --git a/src/Ombi.Schedule/JobSetup.cs b/src/Ombi.Schedule/JobSetup.cs index 1e78d226c..6626ef933 100644 --- a/src/Ombi.Schedule/JobSetup.cs +++ b/src/Ombi.Schedule/JobSetup.cs @@ -19,7 +19,7 @@ namespace Ombi.Schedule IOmbiAutomaticUpdater updater, IEmbyContentSync embySync, IPlexUserImporter userImporter, IEmbyUserImporter embyUserImporter, ISonarrSync cache, ICouchPotatoSync cpCache, ISettingsService jobsettings, ISickRageSync srSync, IRefreshMetadata refresh, - INewsletterJob newsletter, IPlexRecentlyAddedSync recentlyAddedSync) + INewsletterJob newsletter) { _plexContentSync = plexContentSync; _radarrSync = radarrSync; @@ -33,7 +33,6 @@ namespace Ombi.Schedule _srSync = srSync; _refreshMetadata = refresh; _newsletter = newsletter; - _plexRecentlyAddedSync = recentlyAddedSync; } private readonly IPlexContentSync _plexContentSync; @@ -48,7 +47,6 @@ namespace Ombi.Schedule private readonly ISettingsService _jobSettings; private readonly IRefreshMetadata _refreshMetadata; private readonly INewsletterJob _newsletter; - private readonly IPlexRecentlyAddedSync _plexRecentlyAddedSync; public void Setup() { diff --git a/src/Ombi.Schedule/Jobs/Plex/Interfaces/IPlexRecentlyAddedSync.cs b/src/Ombi.Schedule/Jobs/Plex/Interfaces/IPlexRecentlyAddedSync.cs deleted file mode 100644 index 6616f29bc..000000000 --- a/src/Ombi.Schedule/Jobs/Plex/Interfaces/IPlexRecentlyAddedSync.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System.Threading.Tasks; - -namespace Ombi.Schedule.Jobs.Plex -{ - public interface IPlexRecentlyAddedSync : IBaseJob - { - Task Start(); - } -} \ No newline at end of file diff --git a/src/Ombi.Schedule/Jobs/Plex/PlexRecentlyAddedSync.cs b/src/Ombi.Schedule/Jobs/Plex/PlexRecentlyAddedSync.cs deleted file mode 100644 index dfcb9cac1..000000000 --- a/src/Ombi.Schedule/Jobs/Plex/PlexRecentlyAddedSync.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System; -using System.Threading.Tasks; -using Ombi.Api.Plex; - -namespace Ombi.Schedule.Jobs.Plex -{ - public class PlexRecentlyAddedSync : IPlexRecentlyAddedSync - { - public PlexRecentlyAddedSync(IPlexContentSync contentSync) - { - _sync = contentSync; - } - - private readonly IPlexContentSync _sync; - - public async Task Start() - { - await _sync.CacheContent(true); - } - - private bool _disposed; - protected virtual void Dispose(bool disposing) - { - if (_disposed) - return; - - if (disposing) - { - _sync?.Dispose(); - } - _disposed = true; - } - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - } -} \ No newline at end of file diff --git a/src/Ombi/ClientApp/app/settings/jobs/jobs.component.html b/src/Ombi/ClientApp/app/settings/jobs/jobs.component.html index 72b78a64d..8b9eb89f5 100644 --- a/src/Ombi/ClientApp/app/settings/jobs/jobs.component.html +++ b/src/Ombi/ClientApp/app/settings/jobs/jobs.component.html @@ -59,7 +59,7 @@
- + The Plex Sync is required
From 025797c1baebe7bf830d50f8caf82c84d659f501 Mon Sep 17 00:00:00 2001 From: Anojh Thayaparan Date: Thu, 19 Apr 2018 00:05:49 -0700 Subject: [PATCH 06/37] added background property to tvrequests API (#2172) * Added background property to TvRequests * set background property for new requests and display in UI --- src/Ombi.Core/Engine/TvRequestEngine.cs | 1 + src/Ombi.Core/Helpers/TvShowRequestBuilder.cs | 5 +- src/Ombi.Helpers/PosterPathHelper.cs | 14 + .../Entities/Requests/TvRequests.cs | 1 + ...413021646_tvrequestsbackground.Designer.cs | 950 ++++++++++++++++++ .../20180413021646_tvrequestsbackground.cs | 24 + .../Migrations/OmbiContextModelSnapshot.cs | 2 + .../app/requests/tvrequests.component.ts | 9 +- 8 files changed, 1003 insertions(+), 3 deletions(-) create mode 100644 src/Ombi.Store/Migrations/20180413021646_tvrequestsbackground.Designer.cs create mode 100644 src/Ombi.Store/Migrations/20180413021646_tvrequestsbackground.cs diff --git a/src/Ombi.Core/Engine/TvRequestEngine.cs b/src/Ombi.Core/Engine/TvRequestEngine.cs index 7671c13fc..44eec3fd1 100644 --- a/src/Ombi.Core/Engine/TvRequestEngine.cs +++ b/src/Ombi.Core/Engine/TvRequestEngine.cs @@ -277,6 +277,7 @@ namespace Ombi.Core.Engine results.ImdbId = request.ImdbId; results.Overview = request.Overview; results.PosterPath = PosterPathHelper.FixPosterPath(request.PosterPath); + results.Background = PosterPathHelper.FixBackgroundPath(request.Background); results.QualityOverride = request.QualityOverride; results.RootFolder = request.RootFolder; diff --git a/src/Ombi.Core/Helpers/TvShowRequestBuilder.cs b/src/Ombi.Core/Helpers/TvShowRequestBuilder.cs index 6e4d20be8..0b7d1ee6e 100644 --- a/src/Ombi.Core/Helpers/TvShowRequestBuilder.cs +++ b/src/Ombi.Core/Helpers/TvShowRequestBuilder.cs @@ -30,6 +30,7 @@ namespace Ombi.Core.Helpers public ChildRequests ChildRequest { get; set; } public List TvRequests { get; protected set; } public string PosterPath { get; protected set; } + public string BackdropPath { get; protected set; } public DateTime FirstAir { get; protected set; } public TvRequests NewRequest { get; protected set; } protected TvMazeShow ShowInfo { get; set; } @@ -44,6 +45,7 @@ namespace Ombi.Core.Helpers { var showIds = await MovieDbApi.GetTvExternals(result.Id); ShowInfo.externals.imdb = showIds.imdb_id; + BackdropPath = result.BackdropPath; break; } } @@ -240,7 +242,8 @@ namespace Ombi.Core.Helpers ImdbId = ShowInfo.externals?.imdb ?? string.Empty, TvDbId = tv.TvDbId, ChildRequests = new List(), - TotalSeasons = tv.Seasons.Count() + TotalSeasons = tv.Seasons.Count(), + Background = BackdropPath }; NewRequest.ChildRequests.Add(ChildRequest); diff --git a/src/Ombi.Helpers/PosterPathHelper.cs b/src/Ombi.Helpers/PosterPathHelper.cs index 7de767e44..ed1dee5c0 100644 --- a/src/Ombi.Helpers/PosterPathHelper.cs +++ b/src/Ombi.Helpers/PosterPathHelper.cs @@ -18,5 +18,19 @@ namespace Ombi.Helpers return poster; } + + public static string FixBackgroundPath(string background) + { + // https://image.tmdb.org/t/p/w1280/fJAvGOitU8y53ByeHnM4avtKFaG.jpg + + if (background.Contains("image.tmdb.org", CompareOptions.IgnoreCase)) + { + // Somehow we have a full path here for the poster, we only want the last segment + var backgroundSegments = background.Split('/'); + return backgroundSegments.Last(); + } + + return background; + } } } \ No newline at end of file diff --git a/src/Ombi.Store/Entities/Requests/TvRequests.cs b/src/Ombi.Store/Entities/Requests/TvRequests.cs index 88e1c36d7..7a6abc792 100644 --- a/src/Ombi.Store/Entities/Requests/TvRequests.cs +++ b/src/Ombi.Store/Entities/Requests/TvRequests.cs @@ -13,6 +13,7 @@ namespace Ombi.Store.Entities.Requests public string Overview { get; set; } public string Title { get; set; } public string PosterPath { get; set; } + public string Background { get; set; } public DateTime ReleaseDate { get; set; } public string Status { get; set; } /// diff --git a/src/Ombi.Store/Migrations/20180413021646_tvrequestsbackground.Designer.cs b/src/Ombi.Store/Migrations/20180413021646_tvrequestsbackground.Designer.cs new file mode 100644 index 000000000..b79528e34 --- /dev/null +++ b/src/Ombi.Store/Migrations/20180413021646_tvrequestsbackground.Designer.cs @@ -0,0 +1,950 @@ +// +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("20180413021646_tvrequestsbackground")] + partial class tvrequestsbackground + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.0.2-rtm-10011"); + + 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.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("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.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.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/20180413021646_tvrequestsbackground.cs b/src/Ombi.Store/Migrations/20180413021646_tvrequestsbackground.cs new file mode 100644 index 000000000..d385d6000 --- /dev/null +++ b/src/Ombi.Store/Migrations/20180413021646_tvrequestsbackground.cs @@ -0,0 +1,24 @@ +using Microsoft.EntityFrameworkCore.Migrations; +using System; +using System.Collections.Generic; + +namespace Ombi.Store.Migrations +{ + public partial class tvrequestsbackground : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "Background", + table: "TvRequests", + nullable: true); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "Background", + table: "TvRequests"); + } + } +} diff --git a/src/Ombi.Store/Migrations/OmbiContextModelSnapshot.cs b/src/Ombi.Store/Migrations/OmbiContextModelSnapshot.cs index 19e14a4ca..b722b0d30 100644 --- a/src/Ombi.Store/Migrations/OmbiContextModelSnapshot.cs +++ b/src/Ombi.Store/Migrations/OmbiContextModelSnapshot.cs @@ -645,6 +645,8 @@ namespace Ombi.Store.Migrations b.Property("Id") .ValueGeneratedOnAdd(); + b.Property("Background"); + b.Property("ImdbId"); b.Property("Overview"); diff --git a/src/Ombi/ClientApp/app/requests/tvrequests.component.ts b/src/Ombi/ClientApp/app/requests/tvrequests.component.ts index 344bf5712..1ce5f0b8b 100644 --- a/src/Ombi/ClientApp/app/requests/tvrequests.component.ts +++ b/src/Ombi/ClientApp/app/requests/tvrequests.component.ts @@ -228,9 +228,14 @@ export class TvRequestsComponent implements OnInit { } private loadBackdrop(val: TreeNode): void { - this.imageService.getTvBanner(val.data.tvDbId).subscribe(x => { + if (val.data.background != null) { val.data.background = this.sanitizer.bypassSecurityTrustStyle - ("url(" + x + ")"); + ("url(https://image.tmdb.org/t/p/w1280" + val.data.background + ")"); + } else { + this.imageService.getTvBanner(val.data.tvDbId).subscribe(x => { + val.data.background = this.sanitizer.bypassSecurityTrustStyle + ("url(" + x + ")"); }); + } } } From 2fc4ef2613a1e26c30e24e4e8ed6205ba30d04f0 Mon Sep 17 00:00:00 2001 From: Jamie Rees Date: Thu, 19 Apr 2018 14:26:22 +0100 Subject: [PATCH 07/37] Got plex oauth working! !wip --- src/Ombi.Api.Plex/IPlexApi.cs | 7 +- src/Ombi.Api.Plex/Models/OAuth/OAuthPin.cs | 27 ++++ src/Ombi.Api.Plex/PlexApi.cs | 79 ++++++++++- src/Ombi.Api/Request.cs | 4 +- .../Authentication/PlexOAuthManager.cs | 76 ++++++++++ src/Ombi.Core/IPlexOAuthManager.cs | 16 +++ src/Ombi.DependencyInjection/IocExtensions.cs | 2 + .../Settings/Models/External/PlexSettings.cs | 2 +- src/Ombi/ClientApp/app/auth/auth.service.ts | 4 + src/Ombi/ClientApp/app/interfaces/IPlex.ts | 4 + .../app/services/applications/index.ts | 1 + .../app/services/applications/plex.service.ts | 4 + .../applications/plexoauth.service.ts | 20 +++ .../app/wizard/plex/plex.component.html | 7 + .../app/wizard/plex/plex.component.ts | 8 ++ .../app/wizard/plex/plexoauth.component.html | 14 ++ .../app/wizard/plex/plexoauth.component.ts | 76 ++++++++++ .../ClientApp/app/wizard/wizard.module.ts | 5 + .../Controllers/External/PlexController.cs | 38 ++++- src/Ombi/Controllers/PlexOAuthController.cs | 72 +++++++--- src/Ombi/Controllers/SettingsController.cs | 8 +- src/Ombi/Controllers/TokenController.cs | 133 +++++++++++------- 22 files changed, 534 insertions(+), 73 deletions(-) create mode 100644 src/Ombi.Api.Plex/Models/OAuth/OAuthPin.cs create mode 100644 src/Ombi.Core/Authentication/PlexOAuthManager.cs create mode 100644 src/Ombi.Core/IPlexOAuthManager.cs create mode 100644 src/Ombi/ClientApp/app/services/applications/plexoauth.service.ts create mode 100644 src/Ombi/ClientApp/app/wizard/plex/plexoauth.component.html create mode 100644 src/Ombi/ClientApp/app/wizard/plex/plexoauth.component.ts diff --git a/src/Ombi.Api.Plex/IPlexApi.cs b/src/Ombi.Api.Plex/IPlexApi.cs index 0734262cf..cc61dfa5d 100644 --- a/src/Ombi.Api.Plex/IPlexApi.cs +++ b/src/Ombi.Api.Plex/IPlexApi.cs @@ -1,6 +1,8 @@ -using System.Threading.Tasks; +using System; +using System.Threading.Tasks; using Ombi.Api.Plex.Models; using Ombi.Api.Plex.Models.Friends; +using Ombi.Api.Plex.Models.OAuth; using Ombi.Api.Plex.Models.Server; using Ombi.Api.Plex.Models.Status; @@ -20,5 +22,8 @@ namespace Ombi.Api.Plex Task GetUsers(string authToken); Task GetAccount(string authToken); Task GetRecentlyAdded(string authToken, string uri, string sectionId); + Task CreatePin(); + Task GetPin(int pinId); + Uri GetOAuthUrl(int pinId, string code, string applicationUrl, bool wizard); } } \ No newline at end of file diff --git a/src/Ombi.Api.Plex/Models/OAuth/OAuthPin.cs b/src/Ombi.Api.Plex/Models/OAuth/OAuthPin.cs new file mode 100644 index 000000000..e65cd91d4 --- /dev/null +++ b/src/Ombi.Api.Plex/Models/OAuth/OAuthPin.cs @@ -0,0 +1,27 @@ +using System; + +namespace Ombi.Api.Plex.Models.OAuth +{ + public class OAuthPin + { + public int id { get; set; } + public string code { get; set; } + public bool trusted { get; set; } + public string clientIdentifier { get; set; } + public Location location { get; set; } + public int expiresIn { get; set; } + public DateTime createdAt { get; set; } + public DateTime expiresAt { get; set; } + public string authToken { get; set; } + } + + public class Location + { + public string code { get; set; } + public string country { get; set; } + public string city { get; set; } + public string subdivisions { get; set; } + public string coordinates { get; set; } + } + +} \ No newline at end of file diff --git a/src/Ombi.Api.Plex/PlexApi.cs b/src/Ombi.Api.Plex/PlexApi.cs index e6c52d1df..5fb87aca7 100644 --- a/src/Ombi.Api.Plex/PlexApi.cs +++ b/src/Ombi.Api.Plex/PlexApi.cs @@ -1,20 +1,49 @@ -using System.Net.Http; +using System; +using System.Net.Http; +using System.Reflection; using System.Threading.Tasks; using Ombi.Api.Plex.Models; using Ombi.Api.Plex.Models.Friends; +using Ombi.Api.Plex.Models.OAuth; using Ombi.Api.Plex.Models.Server; using Ombi.Api.Plex.Models.Status; +using Ombi.Core.Settings; +using Ombi.Core.Settings.Models.External; +using Ombi.Helpers; namespace Ombi.Api.Plex { public class PlexApi : IPlexApi { - public PlexApi(IApi api) + public PlexApi(IApi api, ISettingsService settings) { Api = api; + _plex = settings; } private IApi Api { get; } + private readonly ISettingsService _plex; + + private string _clientId; + private string ClientIdSecret + { + get + { + if (string.IsNullOrEmpty(_clientId)) + { + var settings = _plex.GetSettings(); + if (settings.UniqueInstallCode.IsNullOrEmpty()) + { + settings.UniqueInstallCode = Guid.NewGuid().ToString("N"); + _plex.SaveSettings(settings); + } + + _clientId = settings.UniqueInstallCode; + } + + return _clientId; + } + } private const string SignInUri = "https://plex.tv/users/sign_in.json"; private const string FriendsUri = "https://plex.tv/pms/friends/all"; @@ -156,6 +185,50 @@ namespace Ombi.Api.Plex return await Api.Request(request); } + public async Task CreatePin() + { + var request = new Request($"api/v2/pins", "https://plex.tv/", HttpMethod.Post); + request.AddQueryString("strong", "true"); + AddHeaders(request); + + return await Api.Request(request); + } + + public async Task GetPin(int pinId) + { + var request = new Request($"api/v2/pins/{pinId}", "https://plex.tv/", HttpMethod.Get); + AddHeaders(request); + + return await Api.Request(request); + } + + public Uri GetOAuthUrl(int pinId, string code, string applicationUrl, bool wizard) + { + var request = new Request("auth#", "https://app.plex.tv", HttpMethod.Get); + AddHeaders(request); + var forwardUrl = wizard + ? new Request($"Wizard/OAuth/{pinId}", applicationUrl, HttpMethod.Get) + : new Request($"api/v1/PlexOAuth/{pinId}", applicationUrl, HttpMethod.Get); + + request.AddQueryString("forwardUrl", forwardUrl.FullUri.ToString()); + request.AddQueryString("pinID", pinId.ToString()); + request.AddQueryString("code", code); + request.AddQueryString("context[device][product]", "Ombi"); + request.AddQueryString("context[device][environment]", "bundled"); + request.AddQueryString("clientID", $"OmbiV3{ClientIdSecret}"); + + if (request.FullUri.Fragment.Equals("#")) + { + var uri = request.FullUri.ToString(); + var withoutEnd = uri.Remove(uri.Length - 1, 1); + var startOfQueryLocation = withoutEnd.IndexOf('?'); + var better = withoutEnd.Insert(startOfQueryLocation, "#"); + request.FullUri = new Uri(better); + } + + return request.FullUri; + } + /// /// Adds the required headers and also the authorization header /// @@ -173,7 +246,7 @@ namespace Ombi.Api.Plex /// private void AddHeaders(Request request) { - request.AddHeader("X-Plex-Client-Identifier", $"OmbiV3"); + request.AddHeader("X-Plex-Client-Identifier", $"OmbiV3{ClientIdSecret}"); request.AddHeader("X-Plex-Product", "Ombi"); request.AddHeader("X-Plex-Version", "3"); request.AddContentHeader("Content-Type", request.ContentType == ContentType.Json ? "application/json" : "application/xml"); diff --git a/src/Ombi.Api/Request.cs b/src/Ombi.Api/Request.cs index e4120ed9c..89c3a7f2d 100644 --- a/src/Ombi.Api/Request.cs +++ b/src/Ombi.Api/Request.cs @@ -10,7 +10,7 @@ namespace Ombi.Api { public Request() { - + } public Request(string endpoint, string baseUrl, HttpMethod http, ContentType contentType = ContentType.Json) @@ -105,10 +105,10 @@ namespace Ombi.Api hasQuery = true; startingTag = builder.Query.Contains("?") ? "&" : "?"; } - builder.Query = hasQuery ? $"{builder.Query}{startingTag}{key}={value}" : $"{startingTag}{key}={value}"; + _modified = builder.Uri; } diff --git a/src/Ombi.Core/Authentication/PlexOAuthManager.cs b/src/Ombi.Core/Authentication/PlexOAuthManager.cs new file mode 100644 index 000000000..d3bab0a05 --- /dev/null +++ b/src/Ombi.Core/Authentication/PlexOAuthManager.cs @@ -0,0 +1,76 @@ +using System; +using System.Threading.Tasks; +using Ombi.Api.Plex; +using Ombi.Api.Plex.Models; +using Ombi.Api.Plex.Models.OAuth; +using Ombi.Core.Settings; +using Ombi.Helpers; +using Ombi.Settings.Settings.Models; + +namespace Ombi.Core.Authentication +{ + public class PlexOAuthManager : IPlexOAuthManager + { + public PlexOAuthManager(IPlexApi api, ISettingsService settings) + { + _api = api; + _customizationSettingsService = settings; + } + + private readonly IPlexApi _api; + private readonly ISettingsService _customizationSettingsService; + + public async Task RequestPin() + { + var pin = await _api.CreatePin(); + return pin; + } + + public async Task GetAccessTokenFromPin(int pinId) + { + var pin = await _api.GetPin(pinId); + if (pin.expiresAt < DateTime.UtcNow) + { + return string.Empty; + } + + if (pin.authToken.IsNullOrEmpty()) + { + // Looks like we do not have a pin yet, we should retry a few times. + var retryCount = 0; + var retryMax = 5; + var retryWaitMs = 1000; + while (pin.authToken.IsNullOrEmpty() && retryCount < retryMax) + { + retryCount++; + await Task.Delay(retryWaitMs); + pin = await _api.GetPin(pinId); + } + } + return pin.authToken; + } + + public async Task GetAccount(string accessToken) + { + return await _api.GetAccount(accessToken); + } + + public async Task GetOAuthUrl(int pinId, string code) + { + var settings = await _customizationSettingsService.GetSettingsAsync(); + if (settings.ApplicationUrl.IsNullOrEmpty()) + { + return null; + } + + var url = _api.GetOAuthUrl(pinId, code, settings.ApplicationUrl, false); + return url; + } + + public Uri GetWizardOAuthUrl(int pinId, string code, string websiteAddress) + { + var url = _api.GetOAuthUrl(pinId, code, websiteAddress, true); + return url; + } + } +} \ No newline at end of file diff --git a/src/Ombi.Core/IPlexOAuthManager.cs b/src/Ombi.Core/IPlexOAuthManager.cs new file mode 100644 index 000000000..142d4162a --- /dev/null +++ b/src/Ombi.Core/IPlexOAuthManager.cs @@ -0,0 +1,16 @@ +using System; +using System.Threading.Tasks; +using Ombi.Api.Plex.Models; +using Ombi.Api.Plex.Models.OAuth; + +namespace Ombi.Core.Authentication +{ + public interface IPlexOAuthManager + { + Task GetAccessTokenFromPin(int pinId); + Task RequestPin(); + Task GetOAuthUrl(int pinId, string code); + Uri GetWizardOAuthUrl(int pinId, string code, string websiteAddress); + Task GetAccount(string accessToken); + } +} \ No newline at end of file diff --git a/src/Ombi.DependencyInjection/IocExtensions.cs b/src/Ombi.DependencyInjection/IocExtensions.cs index e2f667465..68f4b7218 100644 --- a/src/Ombi.DependencyInjection/IocExtensions.cs +++ b/src/Ombi.DependencyInjection/IocExtensions.cs @@ -51,6 +51,7 @@ using Ombi.Store.Repository.Requests; using Ombi.Updater; using PlexContentCacher = Ombi.Schedule.Jobs.Plex; using Ombi.Api.Telegram; +using Ombi.Core.Authentication; using Ombi.Core.Processor; using Ombi.Schedule.Jobs.Plex.Interfaces; using Ombi.Schedule.Jobs.SickRage; @@ -82,6 +83,7 @@ namespace Ombi.DependencyInjection services.AddTransient(); services.AddTransient(); services.AddTransient(); + services.AddTransient(); } public static void RegisterHttp(this IServiceCollection services) { diff --git a/src/Ombi.Settings/Settings/Models/External/PlexSettings.cs b/src/Ombi.Settings/Settings/Models/External/PlexSettings.cs index 3fcde951a..dd92eba18 100644 --- a/src/Ombi.Settings/Settings/Models/External/PlexSettings.cs +++ b/src/Ombi.Settings/Settings/Models/External/PlexSettings.cs @@ -6,8 +6,8 @@ namespace Ombi.Core.Settings.Models.External public sealed class PlexSettings : Ombi.Settings.Settings.Models.Settings { public bool Enable { get; set; } + public string UniqueInstallCode { get; set; } public List Servers { get; set; } - } public class PlexServers : ExternalSettings diff --git a/src/Ombi/ClientApp/app/auth/auth.service.ts b/src/Ombi/ClientApp/app/auth/auth.service.ts index b9899c9a4..92b41ccd9 100644 --- a/src/Ombi/ClientApp/app/auth/auth.service.ts +++ b/src/Ombi/ClientApp/app/auth/auth.service.ts @@ -18,6 +18,10 @@ export class AuthService extends ServiceHelpers { return this.http.post(`${this.url}/`, JSON.stringify(login), {headers: this.headers}); } + public oAuth(pin: number): Observable { + return this.http.get(`${this.url}/${pin}`, {headers: this.headers}); + } + public requiresPassword(login: IUserLogin): Observable { return this.http.post(`${this.url}/requirePassword`, JSON.stringify(login), {headers: this.headers}); } diff --git a/src/Ombi/ClientApp/app/interfaces/IPlex.ts b/src/Ombi/ClientApp/app/interfaces/IPlex.ts index 7125ae4bc..823b80d32 100644 --- a/src/Ombi/ClientApp/app/interfaces/IPlex.ts +++ b/src/Ombi/ClientApp/app/interfaces/IPlex.ts @@ -2,6 +2,10 @@ user: IPlexUser; } +export interface IPlexOAuthAccessToken { + accessToken: string; +} + export interface IPlexUser { email: string; uuid: string; diff --git a/src/Ombi/ClientApp/app/services/applications/index.ts b/src/Ombi/ClientApp/app/services/applications/index.ts index 9433dfce0..98c61cf04 100644 --- a/src/Ombi/ClientApp/app/services/applications/index.ts +++ b/src/Ombi/ClientApp/app/services/applications/index.ts @@ -4,3 +4,4 @@ export * from "./plex.service"; export * from "./radarr.service"; export * from "./sonarr.service"; export * from "./tester.service"; +export * from "./plexoauth.service"; diff --git a/src/Ombi/ClientApp/app/services/applications/plex.service.ts b/src/Ombi/ClientApp/app/services/applications/plex.service.ts index c04a990e1..53fd31f9d 100644 --- a/src/Ombi/ClientApp/app/services/applications/plex.service.ts +++ b/src/Ombi/ClientApp/app/services/applications/plex.service.ts @@ -29,4 +29,8 @@ export class PlexService extends ServiceHelpers { public getFriends(): Observable { return this.http.get(`${this.url}Friends`, {headers: this.headers}); } + + public oAuth(wizard: boolean): Observable { + return this.http.get(`${this.url}oauth/${wizard}`, {headers: this.headers}); + } } diff --git a/src/Ombi/ClientApp/app/services/applications/plexoauth.service.ts b/src/Ombi/ClientApp/app/services/applications/plexoauth.service.ts new file mode 100644 index 000000000..59a884714 --- /dev/null +++ b/src/Ombi/ClientApp/app/services/applications/plexoauth.service.ts @@ -0,0 +1,20 @@ +import { PlatformLocation } from "@angular/common"; +import { HttpClient } from "@angular/common/http"; +import { Injectable } from "@angular/core"; + +import { Observable } from "rxjs/Rx"; + +import { ServiceHelpers } from "../service.helpers"; + +import { IPlexOAuthAccessToken } from "../../interfaces"; + +@Injectable() +export class PlexOAuthService extends ServiceHelpers { + constructor(http: HttpClient, public platformLocation: PlatformLocation) { + super(http, "/api/v1/PlexOAuth/", platformLocation); + } + + public oAuth(pin: number): Observable { + return this.http.get(`${this.url}${pin}`, {headers: this.headers}); + } +} diff --git a/src/Ombi/ClientApp/app/wizard/plex/plex.component.html b/src/Ombi/ClientApp/app/wizard/plex/plex.component.html index 2ba63c43e..6c4b846a7 100644 --- a/src/Ombi/ClientApp/app/wizard/plex/plex.component.html +++ b/src/Ombi/ClientApp/app/wizard/plex/plex.component.html @@ -20,6 +20,13 @@
+ +
+
+ +
+
+ diff --git a/src/Ombi/ClientApp/app/wizard/plex/plex.component.ts b/src/Ombi/ClientApp/app/wizard/plex/plex.component.ts index 146d794fe..6bc21d628 100644 --- a/src/Ombi/ClientApp/app/wizard/plex/plex.component.ts +++ b/src/Ombi/ClientApp/app/wizard/plex/plex.component.ts @@ -70,4 +70,12 @@ export class PlexComponent { }); }); } + + public oauth() { + this.plexService.oAuth(true).subscribe(x => { + if(x.url) { + window.location.href = x.url; + } + }); + } } diff --git a/src/Ombi/ClientApp/app/wizard/plex/plexoauth.component.html b/src/Ombi/ClientApp/app/wizard/plex/plexoauth.component.html new file mode 100644 index 000000000..d60f1d1f0 --- /dev/null +++ b/src/Ombi/ClientApp/app/wizard/plex/plexoauth.component.html @@ -0,0 +1,14 @@ + + +
+
+
+

Plex Authentication

+
+ +
+
+
+
+ + \ No newline at end of file diff --git a/src/Ombi/ClientApp/app/wizard/plex/plexoauth.component.ts b/src/Ombi/ClientApp/app/wizard/plex/plexoauth.component.ts new file mode 100644 index 000000000..1397fe7f5 --- /dev/null +++ b/src/Ombi/ClientApp/app/wizard/plex/plexoauth.component.ts @@ -0,0 +1,76 @@ +import { Component, OnInit } from "@angular/core"; +import { ActivatedRoute, Router } from "@angular/router"; + +import { ConfirmationService } from "primeng/primeng"; + +import { PlexOAuthService, IdentityService, SettingsService } from "../../services"; +import { AuthService } from "./../../auth/auth.service"; + +@Component({ + templateUrl: "./plexoauth.component.html", +}) +export class PlexOAuthComponent implements OnInit { + public pinId: number; + + constructor(private route: ActivatedRoute, + private plexOauth: PlexOAuthService, + private confirmationService: ConfirmationService, + private identityService: IdentityService, + private settings: SettingsService, + private router: Router, + private auth: AuthService) { + + this.route.params + .subscribe((params: any) => { + this.pinId = params.pin; + }); + } + + ngOnInit(): void { + this.plexOauth.oAuth(this.pinId).subscribe(x => { + x.accessToken; + + this.confirmationService.confirm({ + message: "Do you want your Plex user to be the main admin account on Ombi?", + header: "Use Plex Account", + icon: "fa fa-check", + accept: () => { + this.identityService.createWizardUser({ + username: "", + password: "", + usePlexAdminAccount: true, + }).subscribe(x => { + if (x) { + this.auth.oAuth(this.pinId).subscribe(c => { + localStorage.setItem("id_token", c.access_token); + + // Mark that we have done the settings now + this.settings.getOmbi().subscribe(ombi => { + ombi.wizard = true; + + this.settings.saveOmbi(ombi).subscribe(x => { + this.settings.getUserManagementSettings().subscribe(usr => { + + usr.importPlexAdmin = true; + this.settings.saveUserManagementSettings(usr).subscribe(saved => { + this.router.navigate(["login"]); + }); + }); + + }); + }); + }); + } else { + //this.notificationService.error("Could not get the Plex Admin Information"); + return; + } + }); + }, + reject: () => { + this.router.navigate(["Wizard/CreateAdmin"]); + }, + }); + }); + } + +} diff --git a/src/Ombi/ClientApp/app/wizard/wizard.module.ts b/src/Ombi/ClientApp/app/wizard/wizard.module.ts index 96cbdddc1..7eae2e4f4 100644 --- a/src/Ombi/ClientApp/app/wizard/wizard.module.ts +++ b/src/Ombi/ClientApp/app/wizard/wizard.module.ts @@ -14,6 +14,8 @@ import { WelcomeComponent } from "./welcome/welcome.component"; import { EmbyService } from "../services"; import { PlexService } from "../services"; import { IdentityService } from "../services"; +import { PlexOAuthService } from "../services"; +import { PlexOAuthComponent } from "./plex/plexoauth.component"; const routes: Routes = [ { path: "", component: WelcomeComponent}, @@ -21,6 +23,7 @@ const routes: Routes = [ { path: "Plex", component: PlexComponent}, { path: "Emby", component: EmbyComponent}, { path: "CreateAdmin", component: CreateAdminComponent}, + { path: "OAuth/:pin", component: PlexOAuthComponent}, ]; @NgModule({ imports: [ @@ -33,6 +36,7 @@ const routes: Routes = [ WelcomeComponent, MediaServerComponent, PlexComponent, + PlexOAuthComponent, CreateAdminComponent, EmbyComponent, ], @@ -44,6 +48,7 @@ const routes: Routes = [ IdentityService, EmbyService, ConfirmationService, + PlexOAuthService, ], }) diff --git a/src/Ombi/Controllers/External/PlexController.cs b/src/Ombi/Controllers/External/PlexController.cs index 2d45d7565..cde78bc64 100644 --- a/src/Ombi/Controllers/External/PlexController.cs +++ b/src/Ombi/Controllers/External/PlexController.cs @@ -8,6 +8,7 @@ using Microsoft.Extensions.Logging; using Ombi.Api.Plex; using Ombi.Api.Plex.Models; using Ombi.Attributes; +using Ombi.Core.Authentication; using Ombi.Core.Settings; using Ombi.Core.Settings.Models.External; using Ombi.Helpers; @@ -21,16 +22,18 @@ namespace Ombi.Controllers.External public class PlexController : Controller { public PlexController(IPlexApi plexApi, ISettingsService plexSettings, - ILogger logger) + ILogger logger, IPlexOAuthManager manager) { PlexApi = plexApi; PlexSettings = plexSettings; _log = logger; + _plexOAuthManager = manager; } private IPlexApi PlexApi { get; } private ISettingsService PlexSettings { get; } private readonly ILogger _log; + private readonly IPlexOAuthManager _plexOAuthManager; /// /// Signs into the Plex API. @@ -66,6 +69,7 @@ namespace Ombi.Controllers.External _log.LogDebug("Adding first server"); settings.Enable = true; + settings.UniqueInstallCode = Guid.NewGuid().ToString("N"); settings.Servers = new List { new PlexServers { @@ -173,5 +177,37 @@ namespace Ombi.Controllers.External // Filter out any dupes return vm.DistinctBy(x => x.Id); } + + [HttpGet("oauth/{wizard:bool}")] + [AllowAnonymous] + public async Task OAuth(bool wizard) + { + //https://app.plex.tv/auth#?forwardUrl=http://google.com/&clientID=Ombi-Test&context%5Bdevice%5D%5Bproduct%5D=Ombi%20SSO&pinID=798798&code=4lgfd + // Plex OAuth + // Redirect them to Plex + // We need a PIN first + var pin = await _plexOAuthManager.RequestPin(); + + Uri url; + if (!wizard) + { + url = await _plexOAuthManager.GetOAuthUrl(pin.id, pin.code); + } + else + { + var websiteAddress =$"{this.Request.Scheme}://{this.Request.Host}{this.Request.PathBase}"; + url = _plexOAuthManager.GetWizardOAuthUrl(pin.id, pin.code, websiteAddress); + } + + if (url == null) + { + return new JsonResult(new + { + error = "Application URL has not been set" + }); + } + + return new JsonResult(new {url = url.ToString()}); + } } } diff --git a/src/Ombi/Controllers/PlexOAuthController.cs b/src/Ombi/Controllers/PlexOAuthController.cs index 8f33e37be..2aad2a2a9 100644 --- a/src/Ombi/Controllers/PlexOAuthController.cs +++ b/src/Ombi/Controllers/PlexOAuthController.cs @@ -1,9 +1,19 @@ -using System.Net; +using System; +using System.Collections.Generic; +using System.Net; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Http.Internal; using System.IO; +using System.Linq; using System.Text; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Ombi.Api.Plex; +using Ombi.Core.Authentication; +using Ombi.Core.Settings; +using Ombi.Core.Settings.Models.External; +using Ombi.Helpers; namespace Ombi.Controllers { @@ -12,29 +22,57 @@ namespace Ombi.Controllers [AllowAnonymous] public class PlexOAuthController : Controller { - - [HttpGet] - public IActionResult OAuthCallBack() + public PlexOAuthController(IPlexOAuthManager manager, IPlexApi plexApi, ISettingsService plexSettings, + ILogger log) { - var bodyStr = ""; - var req = Request; + _manager = manager; + _plexApi = plexApi; + _plexSettings = plexSettings; + _log = log; + } - // Allows using several time the stream in ASP.Net Core - req.EnableRewind(); + private readonly IPlexOAuthManager _manager; + private readonly IPlexApi _plexApi; + private readonly ISettingsService _plexSettings; + private readonly ILogger _log; - // Arguments: Stream, Encoding, detect encoding, buffer size - // AND, the most important: keep stream opened - using (StreamReader reader - = new StreamReader(req.Body, Encoding.UTF8, true, 1024, true)) + [HttpGet("{pinId:int}")] + public async Task OAuthWizardCallBack([FromRoute] int pinId) + { + var accessToken = await _manager.GetAccessTokenFromPin(pinId); + if (accessToken.IsNullOrEmpty()) + { + return Json(new + { + success = false, + error = "Authentication did not work. Please try again" + }); + } + var settings = await _plexSettings.GetSettingsAsync(); + var server = await _plexApi.GetServer(accessToken); + var servers = server.Server.FirstOrDefault(); + if (servers == null) { - bodyStr = reader.ReadToEnd(); + _log.LogWarning("Looks like we can't find any Plex Servers"); } + _log.LogDebug("Adding first server"); - // Rewind, so the core is not lost when it looks the body for the request - req.Body.Position = 0; + settings.Enable = true; + settings.Servers = new List { + new PlexServers + { + PlexAuthToken = accessToken, + Id = new Random().Next(), + Ip = servers?.LocalAddresses?.Split(new []{','}, StringSplitOptions.RemoveEmptyEntries)?.FirstOrDefault() ?? string.Empty, + MachineIdentifier = servers?.MachineIdentifier ?? string.Empty, + Port = int.Parse(servers?.Port ?? "0"), + Ssl = (servers?.Scheme ?? "http") != "http", + Name = "Server 1", + } + }; - // Do your work with bodyStr - return Ok(); + await _plexSettings.SaveSettingsAsync(settings); + return Json(new { accessToken }); } } } diff --git a/src/Ombi/Controllers/SettingsController.cs b/src/Ombi/Controllers/SettingsController.cs index 30469cd57..867a7517c 100644 --- a/src/Ombi/Controllers/SettingsController.cs +++ b/src/Ombi/Controllers/SettingsController.cs @@ -142,7 +142,13 @@ namespace Ombi.Controllers [HttpGet("plex")] public async Task PlexSettings() { - return await Get(); + var s = await Get(); + if (s.UniqueInstallCode.IsNullOrEmpty()) + { + s.UniqueInstallCode = Guid.NewGuid().ToString("N"); + } + + return s; } /// diff --git a/src/Ombi/Controllers/TokenController.cs b/src/Ombi/Controllers/TokenController.cs index 5d524b7ba..62e43635d 100644 --- a/src/Ombi/Controllers/TokenController.cs +++ b/src/Ombi/Controllers/TokenController.cs @@ -25,18 +25,21 @@ namespace Ombi.Controllers [Produces("application/json")] public class TokenController { - public TokenController(OmbiUserManager um, IOptions ta, IAuditRepository audit, ITokenRepository token) + public TokenController(OmbiUserManager um, IOptions ta, IAuditRepository audit, ITokenRepository token, + IPlexOAuthManager oAuthManager) { _userManager = um; _tokenAuthenticationOptions = ta.Value; _audit = audit; _token = token; + _plexOAuthManager = oAuthManager; } private readonly TokenAuthentication _tokenAuthenticationOptions; private readonly IAuditRepository _audit; private readonly ITokenRepository _token; private readonly OmbiUserManager _userManager; + private readonly IPlexOAuthManager _plexOAuthManager; /// /// Gets the token. @@ -69,63 +72,99 @@ namespace Ombi.Controllers // Verify Password if (await _userManager.CheckPasswordAsync(user, model.Password)) { - var roles = await _userManager.GetRolesAsync(user); - - if (roles.Contains(OmbiRoles.Disabled)) - { - return new UnauthorizedResult(); - } - - user.LastLoggedIn = DateTime.UtcNow; - await _userManager.UpdateAsync(user); - - var claims = new List - { - new Claim(JwtRegisteredClaimNames.Sub, user.UserName), - new Claim(ClaimTypes.NameIdentifier, user.Id), - new Claim(ClaimTypes.Name, user.UserName), - new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()) - }; - claims.AddRange(roles.Select(role => new Claim("role", role))); - - var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_tokenAuthenticationOptions.SecretKey)); - var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); - - - var token = new JwtSecurityToken( - claims: claims, - expires: model.RememberMe ? DateTime.UtcNow.AddDays(7) : DateTime.UtcNow.AddHours(5), - signingCredentials: creds, - audience: "Ombi", issuer: "Ombi" - ); - var accessToken = new JwtSecurityTokenHandler().WriteToken(token); - if (model.RememberMe) - { - // Save the token so we can refresh it later - //await _token.CreateToken(new Tokens() {Token = accessToken, User = user}); - } + return await CreateToken(model.RememberMe, user); + } + } + else + { + // Plex OAuth + // Redirect them to Plex + // We need a PIN first + var pin = await _plexOAuthManager.RequestPin(); + //https://app.plex.tv/auth#?forwardUrl=http://google.com/&clientID=Ombi-Test&context%5Bdevice%5D%5Bproduct%5D=Ombi%20SSO&pinID=798798&code=4lgfd + var url = await _plexOAuthManager.GetOAuthUrl(pin.id, pin.code); + if (url == null) + { return new JsonResult(new { - access_token = accessToken, - expiration = token.ValidTo + error = "Application URL has not been set" }); } + return new RedirectResult(url.ToString()); } - else + + return new UnauthorizedResult(); + } + + private async Task CreateToken(bool rememberMe, OmbiUser user) + { + var roles = await _userManager.GetRolesAsync(user); + + if (roles.Contains(OmbiRoles.Disabled)) { - // Plex OAuth - // Redirect them to Plex + return new UnauthorizedResult(); + } - var request = new Request("auth", "https://app.plex.tv", HttpMethod.Get); - request.AddQueryString("clientID", "OMBIv3"); - request.AddQueryString("forwardUrl", "http://localhost:5000"); - request.AddQueryString("context-device-product", "http://localhost:5000"); - return new RedirectResult("https://app.plex.tv/auth#?forwardUrl=http://localhost:5000/api/v1/plexoauth&clientID=OMBIv3&context%5Bdevice%5D%5Bproduct%5D=Ombi%20SSO"); + user.LastLoggedIn = DateTime.UtcNow; + await _userManager.UpdateAsync(user); + var claims = new List + { + new Claim(JwtRegisteredClaimNames.Sub, user.UserName), + new Claim(ClaimTypes.NameIdentifier, user.Id), + new Claim(ClaimTypes.Name, user.UserName), + new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()) + }; + claims.AddRange(roles.Select(role => new Claim("role", role))); + + var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_tokenAuthenticationOptions.SecretKey)); + var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); + + + var token = new JwtSecurityToken( + claims: claims, + expires: rememberMe ? DateTime.UtcNow.AddDays(7) : DateTime.UtcNow.AddHours(5), + signingCredentials: creds, + audience: "Ombi", issuer: "Ombi" + ); + var accessToken = new JwtSecurityTokenHandler().WriteToken(token); + if (rememberMe) + { + // Save the token so we can refresh it later + //await _token.CreateToken(new Tokens() {Token = accessToken, User = user}); } - return new UnauthorizedResult(); + return new JsonResult(new + { + access_token = accessToken, + expiration = token.ValidTo + }); + } + + [HttpGet("{pinId:int}")] + public async Task OAuth(int pinId) + { + var accessToken = await _plexOAuthManager.GetAccessTokenFromPin(pinId); + + // Let's look for the users account + var account = await _plexOAuthManager.GetAccount(accessToken); + + // Get the ombi user + var user = await _userManager.FindByNameAsync(account.user.username); + + if (user == null) + { + // Could this be an email login? + user = await _userManager.FindByEmailAsync(account.user.email); + + if (user == null) + { + return new UnauthorizedResult(); + } + } + + return await CreateToken(true, user); } /// From 1c54eedc6ff66ac93ecdc64da7cb7612c42485d6 Mon Sep 17 00:00:00 2001 From: Anojh Thayaparan Date: Thu, 19 Apr 2018 11:08:49 -0700 Subject: [PATCH 08/37] Added View on Emby Button (#2173) * Added Emby content url property * Fetch and set the Emby url property * Added View on Emby button to frontend and did UI work * removed debug logging --- .../Models/Search/SearchViewModel.cs | 1 + .../Rule/Rules/Search/EmbyAvailabilityRule.cs | 1 + src/Ombi.Helpers/EmbyHelper.cs | 17 + .../Jobs/Emby/EmbyContentSync.cs | 2 + src/Ombi.Store/Entities/EmbyContent.cs | 1 + .../20180419054711_EmbyButton.Designer.cs | 950 ++++++++++++++++++ .../Migrations/20180419054711_EmbyButton.cs | 24 + .../Migrations/OmbiContextModelSnapshot.cs | 2 + .../app/interfaces/ISearchMovieResult.ts | 1 + .../app/interfaces/ISearchTvResult.ts | 1 + .../app/search/moviesearch.component.html | 1 + .../app/search/tvsearch.component.html | 24 +- src/Ombi/wwwroot/translations/da.json | 1 + src/Ombi/wwwroot/translations/de.json | 1 + src/Ombi/wwwroot/translations/en.json | 1 + src/Ombi/wwwroot/translations/es.json | 1 + src/Ombi/wwwroot/translations/fr.json | 1 + src/Ombi/wwwroot/translations/it.json | 1 + src/Ombi/wwwroot/translations/nl.json | 1 + src/Ombi/wwwroot/translations/no.json | 1 + src/Ombi/wwwroot/translations/sv.json | 1 + 21 files changed, 1026 insertions(+), 8 deletions(-) create mode 100644 src/Ombi.Helpers/EmbyHelper.cs create mode 100644 src/Ombi.Store/Migrations/20180419054711_EmbyButton.Designer.cs create mode 100644 src/Ombi.Store/Migrations/20180419054711_EmbyButton.cs diff --git a/src/Ombi.Core/Models/Search/SearchViewModel.cs b/src/Ombi.Core/Models/Search/SearchViewModel.cs index 8c8d49aff..0de295a69 100644 --- a/src/Ombi.Core/Models/Search/SearchViewModel.cs +++ b/src/Ombi.Core/Models/Search/SearchViewModel.cs @@ -10,6 +10,7 @@ namespace Ombi.Core.Models.Search public bool Requested { get; set; } public bool Available { get; set; } public string PlexUrl { get; set; } + public string EmbyUrl { get; set; } public string Quality { get; set; } public abstract RequestType Type { get; } diff --git a/src/Ombi.Core/Rule/Rules/Search/EmbyAvailabilityRule.cs b/src/Ombi.Core/Rule/Rules/Search/EmbyAvailabilityRule.cs index 8ac96701d..4f2f56b28 100644 --- a/src/Ombi.Core/Rule/Rules/Search/EmbyAvailabilityRule.cs +++ b/src/Ombi.Core/Rule/Rules/Search/EmbyAvailabilityRule.cs @@ -44,6 +44,7 @@ namespace Ombi.Core.Rule.Rules.Search if (item != null) { obj.Available = true; + obj.EmbyUrl = item.Url; if (obj.Type == RequestType.TvShow) { diff --git a/src/Ombi.Helpers/EmbyHelper.cs b/src/Ombi.Helpers/EmbyHelper.cs new file mode 100644 index 000000000..cf6904a0c --- /dev/null +++ b/src/Ombi.Helpers/EmbyHelper.cs @@ -0,0 +1,17 @@ +using System; +using System.Globalization; +using System.Collections.Generic; +using System.Text; + +namespace Ombi.Helpers +{ + public class EmbyHelper + { + public static string GetEmbyMediaUrl(string mediaId) + { + var url = + $"http://app.emby.media/itemdetails.html?id={mediaId}"; + return url; + } + } +} diff --git a/src/Ombi.Schedule/Jobs/Emby/EmbyContentSync.cs b/src/Ombi.Schedule/Jobs/Emby/EmbyContentSync.cs index 99d4f5a85..4d0026f48 100644 --- a/src/Ombi.Schedule/Jobs/Emby/EmbyContentSync.cs +++ b/src/Ombi.Schedule/Jobs/Emby/EmbyContentSync.cs @@ -114,6 +114,7 @@ namespace Ombi.Schedule.Jobs.Emby Title = tvInfo.Name, Type = EmbyMediaType.Series, EmbyId = tvShow.Id, + Url = EmbyHelper.GetEmbyMediaUrl(tvShow.Id), AddedAt = DateTime.UtcNow }); } @@ -135,6 +136,7 @@ namespace Ombi.Schedule.Jobs.Emby Title = movieInfo.Name, Type = EmbyMediaType.Movie, EmbyId = movieInfo.Id, + Url = EmbyHelper.GetEmbyMediaUrl(movieInfo.Id), AddedAt = DateTime.UtcNow, }); } diff --git a/src/Ombi.Store/Entities/EmbyContent.cs b/src/Ombi.Store/Entities/EmbyContent.cs index 1d3f57f13..4506ef071 100644 --- a/src/Ombi.Store/Entities/EmbyContent.cs +++ b/src/Ombi.Store/Entities/EmbyContent.cs @@ -48,6 +48,7 @@ namespace Ombi.Store.Entities public string TheMovieDbId { get; set; } public string TvDbId { get; set; } + public string Url { get; set; } public ICollection Episodes { get; set; } } diff --git a/src/Ombi.Store/Migrations/20180419054711_EmbyButton.Designer.cs b/src/Ombi.Store/Migrations/20180419054711_EmbyButton.Designer.cs new file mode 100644 index 000000000..c5ddc8fa2 --- /dev/null +++ b/src/Ombi.Store/Migrations/20180419054711_EmbyButton.Designer.cs @@ -0,0 +1,950 @@ +// +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("20180419054711_EmbyButton")] + partial class EmbyButton + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.0.2-rtm-10011"); + + 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("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("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.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.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/20180419054711_EmbyButton.cs b/src/Ombi.Store/Migrations/20180419054711_EmbyButton.cs new file mode 100644 index 000000000..07e5cb730 --- /dev/null +++ b/src/Ombi.Store/Migrations/20180419054711_EmbyButton.cs @@ -0,0 +1,24 @@ +using Microsoft.EntityFrameworkCore.Migrations; +using System; +using System.Collections.Generic; + +namespace Ombi.Store.Migrations +{ + public partial class EmbyButton : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "Url", + table: "EmbyContent", + nullable: true); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "Url", + table: "EmbyContent"); + } + } +} diff --git a/src/Ombi.Store/Migrations/OmbiContextModelSnapshot.cs b/src/Ombi.Store/Migrations/OmbiContextModelSnapshot.cs index b722b0d30..248551d97 100644 --- a/src/Ombi.Store/Migrations/OmbiContextModelSnapshot.cs +++ b/src/Ombi.Store/Migrations/OmbiContextModelSnapshot.cs @@ -197,6 +197,8 @@ namespace Ombi.Store.Migrations b.Property("Type"); + b.Property("Url"); + b.HasKey("Id"); b.ToTable("EmbyContent"); diff --git a/src/Ombi/ClientApp/app/interfaces/ISearchMovieResult.ts b/src/Ombi/ClientApp/app/interfaces/ISearchMovieResult.ts index c5301d2b0..ee66598fd 100644 --- a/src/Ombi/ClientApp/app/interfaces/ISearchMovieResult.ts +++ b/src/Ombi/ClientApp/app/interfaces/ISearchMovieResult.ts @@ -21,6 +21,7 @@ requested: boolean; available: boolean; plexUrl: string; + embyUrl: string; quality: string; digitalReleaseDate: Date; diff --git a/src/Ombi/ClientApp/app/interfaces/ISearchTvResult.ts b/src/Ombi/ClientApp/app/interfaces/ISearchTvResult.ts index f0afa76b2..81d716bfc 100644 --- a/src/Ombi/ClientApp/app/interfaces/ISearchTvResult.ts +++ b/src/Ombi/ClientApp/app/interfaces/ISearchTvResult.ts @@ -27,6 +27,7 @@ export interface ISearchTvResult { requested: boolean; available: boolean; plexUrl: string; + embyUrl: string; firstSeason: boolean; latestSeason: boolean; } diff --git a/src/Ombi/ClientApp/app/search/moviesearch.component.html b/src/Ombi/ClientApp/app/search/moviesearch.component.html index cac78531b..3130cb79d 100644 --- a/src/Ombi/ClientApp/app/search/moviesearch.component.html +++ b/src/Ombi/ClientApp/app/search/moviesearch.component.html @@ -85,6 +85,7 @@

+ + {{ 'Search.ViewOnPlex' | translate }} + + +
-
-
+
+
diff --git a/src/Ombi/wwwroot/translations/da.json b/src/Ombi/wwwroot/translations/da.json index ec4b0bce5..dd3872ac0 100644 --- a/src/Ombi/wwwroot/translations/da.json +++ b/src/Ombi/wwwroot/translations/da.json @@ -76,6 +76,7 @@ "DigitalDate": "Digital Release: {{date}}", "TheatricalRelease": "Theatrical Release: {{date}}", "ViewOnPlex": "Se på Plex", + "ViewOnEmby": "Se på Emby", "RequestAdded": "{{title}} er anmodet med succes", "Similar": "Similar", "Movies": { diff --git a/src/Ombi/wwwroot/translations/de.json b/src/Ombi/wwwroot/translations/de.json index df8b6a724..2a46e2db3 100644 --- a/src/Ombi/wwwroot/translations/de.json +++ b/src/Ombi/wwwroot/translations/de.json @@ -76,6 +76,7 @@ "DigitalDate": "Digital Release: {{date}}", "TheatricalRelease": "Theatrical Release: {{date}}", "ViewOnPlex": "In Plex anschauen", + "ViewOnEmby": "In Emby anschauen", "RequestAdded": "Anfrage für {{title}} wurde erfolgreich hinzugefügt", "Similar": "Similar", "Movies": { diff --git a/src/Ombi/wwwroot/translations/en.json b/src/Ombi/wwwroot/translations/en.json index 124c26fc5..cb7c0fe8b 100644 --- a/src/Ombi/wwwroot/translations/en.json +++ b/src/Ombi/wwwroot/translations/en.json @@ -81,6 +81,7 @@ "DigitalDate": "Digital Release: {{date}}", "TheatricalRelease":"Theatrical Release: {{date}}", "ViewOnPlex": "View On Plex", + "ViewOnEmby": "View On Emby", "RequestAdded": "Request for {{title}} has been added successfully", "Similar":"Similar", "Movies": { diff --git a/src/Ombi/wwwroot/translations/es.json b/src/Ombi/wwwroot/translations/es.json index 6b2f87874..17036ade3 100644 --- a/src/Ombi/wwwroot/translations/es.json +++ b/src/Ombi/wwwroot/translations/es.json @@ -76,6 +76,7 @@ "DigitalDate": "Digital Release: {{date}}", "TheatricalRelease": "Theatrical Release: {{date}}", "ViewOnPlex": "Ver en Plex", + "ViewOnEmby": "Ver en Emby", "RequestAdded": "La solicitud de {{title}} se ha agregado con éxito", "Similar": "Similar", "Movies": { diff --git a/src/Ombi/wwwroot/translations/fr.json b/src/Ombi/wwwroot/translations/fr.json index 983763a08..bc3f395dd 100644 --- a/src/Ombi/wwwroot/translations/fr.json +++ b/src/Ombi/wwwroot/translations/fr.json @@ -76,6 +76,7 @@ "DigitalDate": "Digital Release: {{date}}", "TheatricalRelease": "Theatrical Release: {{date}}", "ViewOnPlex": "Regarder sur Plex", + "ViewOnEmby": "Regarder sur Emby", "RequestAdded": "La demande pour {{title}} a été ajoutée avec succès", "Similar": "Similar", "Movies": { diff --git a/src/Ombi/wwwroot/translations/it.json b/src/Ombi/wwwroot/translations/it.json index 2081ce2a1..1e91047ff 100644 --- a/src/Ombi/wwwroot/translations/it.json +++ b/src/Ombi/wwwroot/translations/it.json @@ -76,6 +76,7 @@ "DigitalDate": "Digital Release: {{date}}", "TheatricalRelease": "Theatrical Release: {{date}}", "ViewOnPlex": "Guarda su Plex", + "ViewOnEmby": "Guarda su Emby", "RequestAdded": "La richiesta per {{title}} è stata aggiunta correttamente", "Similar": "Similar", "Movies": { diff --git a/src/Ombi/wwwroot/translations/nl.json b/src/Ombi/wwwroot/translations/nl.json index a9ba6b683..d5fd62bba 100644 --- a/src/Ombi/wwwroot/translations/nl.json +++ b/src/Ombi/wwwroot/translations/nl.json @@ -76,6 +76,7 @@ "DigitalDate": "Digital Release: {{date}}", "TheatricalRelease": "Theatrical Release: {{date}}", "ViewOnPlex": "Bekijk op Plex", + "ViewOnEmby": "Bekijk op Emby", "RequestAdded": "Aanvraag voor {{title}} is succesvol toegevoegd", "Similar": "Similar", "Movies": { diff --git a/src/Ombi/wwwroot/translations/no.json b/src/Ombi/wwwroot/translations/no.json index cbaf1379a..d94e977df 100644 --- a/src/Ombi/wwwroot/translations/no.json +++ b/src/Ombi/wwwroot/translations/no.json @@ -76,6 +76,7 @@ "DigitalDate": "Digital utgivelse: {{date}}", "TheatricalRelease": "Kinopremiere: {{date}}", "ViewOnPlex": "Spill av på Plex", + "ViewOnEmby": "Spill av på Emby", "RequestAdded": "Forespørsel om {{title}} er lagt til", "Similar": "Lignende", "Movies": { diff --git a/src/Ombi/wwwroot/translations/sv.json b/src/Ombi/wwwroot/translations/sv.json index b750b5e0c..cbc87c2d4 100644 --- a/src/Ombi/wwwroot/translations/sv.json +++ b/src/Ombi/wwwroot/translations/sv.json @@ -76,6 +76,7 @@ "DigitalDate": "Digital Release: {{date}}", "TheatricalRelease": "Theatrical Release: {{date}}", "ViewOnPlex": "Visa på Plex", + "ViewOnEmby": "Visa på Emby", "RequestAdded": "Efterfrågan om {{title}} har lagts till", "Similar": "Similar", "Movies": { From 2cd9c4d9b123f4849c11304d5fd2e159553f32c7 Mon Sep 17 00:00:00 2001 From: Jamie Rees Date: Thu, 19 Apr 2018 22:15:20 +0100 Subject: [PATCH 09/37] frontend is all done !wip --- src/Ombi.Api.Plex/PlexApi.cs | 2 +- src/Ombi/ClientApp/app/app.module.ts | 3 ++ src/Ombi/ClientApp/app/auth/IUserLogin.ts | 1 + .../ClientApp/app/login/login.component.html | 12 ++++++ .../ClientApp/app/login/login.component.ts | 39 ++++++++++++++++++- .../app/login/loginoauth.component.html | 9 +++++ .../app/login/loginoauth.component.ts | 35 +++++++++++++++++ .../app/services/settings.service.ts | 4 ++ .../createadmin/createadmin.component.ts | 2 +- .../app/wizard/plex/plex.component.html | 2 +- .../app/wizard/plex/plex.component.ts | 24 ++++-------- .../app/wizard/plex/plexoauth.component.ts | 26 +++++-------- src/Ombi/Controllers/SettingsController.cs | 10 +++++ src/Ombi/Controllers/TokenController.cs | 29 +++++++------- 14 files changed, 146 insertions(+), 52 deletions(-) create mode 100644 src/Ombi/ClientApp/app/login/loginoauth.component.html create mode 100644 src/Ombi/ClientApp/app/login/loginoauth.component.ts diff --git a/src/Ombi.Api.Plex/PlexApi.cs b/src/Ombi.Api.Plex/PlexApi.cs index 5fb87aca7..6d814adf3 100644 --- a/src/Ombi.Api.Plex/PlexApi.cs +++ b/src/Ombi.Api.Plex/PlexApi.cs @@ -208,7 +208,7 @@ namespace Ombi.Api.Plex AddHeaders(request); var forwardUrl = wizard ? new Request($"Wizard/OAuth/{pinId}", applicationUrl, HttpMethod.Get) - : new Request($"api/v1/PlexOAuth/{pinId}", applicationUrl, HttpMethod.Get); + : new Request($"Login/OAuth/{pinId}", applicationUrl, HttpMethod.Get); request.AddQueryString("forwardUrl", forwardUrl.FullUri.ToString()); request.AddQueryString("pinID", pinId.ToString()); diff --git a/src/Ombi/ClientApp/app/app.module.ts b/src/Ombi/ClientApp/app/app.module.ts index 0936b13cd..1421b1117 100644 --- a/src/Ombi/ClientApp/app/app.module.ts +++ b/src/Ombi/ClientApp/app/app.module.ts @@ -24,6 +24,7 @@ import { CookieComponent } from "./auth/cookie.component"; import { PageNotFoundComponent } from "./errors/not-found.component"; import { LandingPageComponent } from "./landingpage/landingpage.component"; import { LoginComponent } from "./login/login.component"; +import { LoginOAuthComponent } from "./login/loginoauth.component"; import { ResetPasswordComponent } from "./login/resetpassword.component"; import { TokenResetPasswordComponent } from "./login/tokenresetpassword.component"; @@ -41,6 +42,7 @@ const routes: Routes = [ { path: "*", component: PageNotFoundComponent }, { path: "", redirectTo: "/search", pathMatch: "full" }, { path: "login", component: LoginComponent }, + { path: "Login/OAuth/:pin", component: LoginOAuthComponent }, { path: "login/:landing", component: LoginComponent }, { path: "reset", component: ResetPasswordComponent }, { path: "token", component: TokenResetPasswordComponent }, @@ -116,6 +118,7 @@ export function HttpLoaderFactory(http: HttpClient, platformLocation: PlatformLo ResetPasswordComponent, TokenResetPasswordComponent, CookieComponent, + LoginOAuthComponent, ], providers: [ NotificationService, diff --git a/src/Ombi/ClientApp/app/auth/IUserLogin.ts b/src/Ombi/ClientApp/app/auth/IUserLogin.ts index af152a6b1..609055c8e 100644 --- a/src/Ombi/ClientApp/app/auth/IUserLogin.ts +++ b/src/Ombi/ClientApp/app/auth/IUserLogin.ts @@ -2,6 +2,7 @@ username: string; password: string; rememberMe: boolean; + usePlexOAuth: boolean; } export interface ILocalUser { diff --git a/src/Ombi/ClientApp/app/login/login.component.html b/src/Ombi/ClientApp/app/login/login.component.html index 5fc0150ff..528fe9917 100644 --- a/src/Ombi/ClientApp/app/login/login.component.html +++ b/src/Ombi/ClientApp/app/login/login.component.html @@ -7,11 +7,13 @@ include the remember me checkbox
+

+
+
+ + + diff --git a/src/Ombi/ClientApp/app/login/login.component.ts b/src/Ombi/ClientApp/app/login/login.component.ts index 661850e86..cde2ef085 100644 --- a/src/Ombi/ClientApp/app/login/login.component.ts +++ b/src/Ombi/ClientApp/app/login/login.component.ts @@ -25,9 +25,39 @@ export class LoginComponent implements OnDestroy, OnInit { public form: FormGroup; public customizationSettings: ICustomizationSettings; public authenticationSettings: IAuthenticationSettings; + public plexEnabled: boolean; public background: any; public landingFlag: boolean; public baseUrl: string; + + public get showLoginForm(): boolean { + if(this.customizationSettings.applicationUrl && this.plexEnabled) { + this.loginWithOmbi = false; + return false; + } + if(!this.customizationSettings.applicationUrl || !this.plexEnabled) { + + this.loginWithOmbi = true; + return true; + } + if(this.loginWithOmbi) { + return true; + } + + + this.loginWithOmbi = true; + return true; + } + public loginWithOmbi: boolean = false; + + public get appName(): string { + if(this.customizationSettings.applicationName) { + return this.customizationSettings.applicationName; + } else { + return "Ombi"; + } + } + private timer: any; private errorBody: string; @@ -68,6 +98,7 @@ export class LoginComponent implements OnDestroy, OnInit { public ngOnInit() { this.settingsService.getAuthentication().subscribe(x => this.authenticationSettings = x); this.settingsService.getCustomization().subscribe(x => this.customizationSettings = x); + this.settingsService.getStatusPlex().subscribe(x => this.plexEnabled = x); this.images.getRandomBackground().subscribe(x => { this.background = this.sanitizer.bypassSecurityTrustStyle("linear-gradient(-10deg, transparent 20%, rgba(0,0,0,0.7) 20.0%, rgba(0,0,0,0.7) 80.0%, transparent 80%),url(" + x.url + ")"); }); @@ -90,7 +121,7 @@ export class LoginComponent implements OnDestroy, OnInit { return; } const value = form.value; - const user = { password: value.password, username: value.username, rememberMe:value.rememberMe }; + const user = { password: value.password, username: value.username, rememberMe: value.rememberMe, usePlexOAuth: false }; this.authService.requiresPassword(user).subscribe(x => { if(x && this.authenticationSettings.allowNoPassword) { // Looks like this user requires a password @@ -111,6 +142,12 @@ export class LoginComponent implements OnDestroy, OnInit { }); } + public oauth() { + this.authService.login({usePlexOAuth: true, password:"",rememberMe:true,username:""}).subscribe(x => { + window.location.href = x.url; + }) + } + public ngOnDestroy() { clearInterval(this.timer); } diff --git a/src/Ombi/ClientApp/app/login/loginoauth.component.html b/src/Ombi/ClientApp/app/login/loginoauth.component.html new file mode 100644 index 000000000..e80f1239f --- /dev/null +++ b/src/Ombi/ClientApp/app/login/loginoauth.component.html @@ -0,0 +1,9 @@ + +
+ +
+
+ +
+
+
diff --git a/src/Ombi/ClientApp/app/login/loginoauth.component.ts b/src/Ombi/ClientApp/app/login/loginoauth.component.ts new file mode 100644 index 000000000..a0a9a9414 --- /dev/null +++ b/src/Ombi/ClientApp/app/login/loginoauth.component.ts @@ -0,0 +1,35 @@ +import { Component, OnInit } from "@angular/core"; +import { ActivatedRoute, Router } from "@angular/router"; + +import { AuthService } from "../auth/auth.service"; + +@Component({ + templateUrl: "./loginoauth.component.html" +}) +export class LoginOAuthComponent implements OnInit { + public pin: number; + + constructor(private authService: AuthService, private router: Router, + private route: ActivatedRoute) { + this.route.params + .subscribe((params: any) => { + this.pin = params.pin; + + }); + } + + ngOnInit(): void { + this.auth(); + } + + public auth() { + this.authService.oAuth(this.pin).subscribe(x => { + localStorage.setItem("id_token", x.access_token); + + if (this.authService.loggedIn()) { + this.router.navigate(["search"]); + } + + }); + } +} diff --git a/src/Ombi/ClientApp/app/services/settings.service.ts b/src/Ombi/ClientApp/app/services/settings.service.ts index f6f770a19..61a85874e 100644 --- a/src/Ombi/ClientApp/app/services/settings.service.ts +++ b/src/Ombi/ClientApp/app/services/settings.service.ts @@ -71,6 +71,10 @@ export class SettingsService extends ServiceHelpers { return this.http.get(`${this.url}/Plex/`, {headers: this.headers}); } + public getStatusPlex(): Observable { + return this.http.get(`${this.url}/Plexstatus/`, {headers: this.headers}); + } + public savePlex(settings: IPlexSettings): Observable { return this.http.post(`${this.url}/Plex/`, JSON.stringify(settings), {headers: this.headers}); } diff --git a/src/Ombi/ClientApp/app/wizard/createadmin/createadmin.component.ts b/src/Ombi/ClientApp/app/wizard/createadmin/createadmin.component.ts index 481c96a99..07f47f265 100644 --- a/src/Ombi/ClientApp/app/wizard/createadmin/createadmin.component.ts +++ b/src/Ombi/ClientApp/app/wizard/createadmin/createadmin.component.ts @@ -21,7 +21,7 @@ export class CreateAdminComponent { this.identityService.createWizardUser({username: this.username, password: this.password, usePlexAdminAccount: false}).subscribe(x => { if (x) { // Log me in. - this.auth.login({ username: this.username, password: this.password, rememberMe:false }).subscribe(c => { + this.auth.login({ username: this.username, password: this.password, rememberMe: false, usePlexOAuth:false }).subscribe(c => { localStorage.setItem("id_token", c.access_token); diff --git a/src/Ombi/ClientApp/app/wizard/plex/plex.component.html b/src/Ombi/ClientApp/app/wizard/plex/plex.component.html index 6c4b846a7..5d69c435b 100644 --- a/src/Ombi/ClientApp/app/wizard/plex/plex.component.html +++ b/src/Ombi/ClientApp/app/wizard/plex/plex.component.html @@ -23,7 +23,7 @@
- +
diff --git a/src/Ombi/ClientApp/app/wizard/plex/plex.component.ts b/src/Ombi/ClientApp/app/wizard/plex/plex.component.ts index 6bc21d628..2f5b2dd79 100644 --- a/src/Ombi/ClientApp/app/wizard/plex/plex.component.ts +++ b/src/Ombi/ClientApp/app/wizard/plex/plex.component.ts @@ -1,7 +1,6 @@ import { Component } from "@angular/core"; import { Router } from "@angular/router"; -import { ConfirmationService } from "primeng/primeng"; import { PlexService } from "../../services"; import { IdentityService, NotificationService, SettingsService } from "../../services"; @@ -17,7 +16,6 @@ export class PlexComponent { constructor(private plexService: PlexService, private router: Router, private notificationService: NotificationService, - private confirmationService: ConfirmationService, private identityService: IdentityService, private settings: SettingsService, private auth: AuthService) { } @@ -28,25 +26,21 @@ export class PlexComponent { this.notificationService.error("Username or password was incorrect. Could not authenticate with Plex."); return; } - this.confirmationService.confirm({ - message: "Do you want your Plex user to be the main admin account on Ombi?", - header: "Use Plex Account", - icon: "fa fa-check", - accept: () => { + this.identityService.createWizardUser({ username: "", password: "", usePlexAdminAccount: true, - }).subscribe(x => { - if (x) { - this.auth.login({ username: this.login, password: this.password, rememberMe:false }).subscribe(c => { + }).subscribe(y => { + if (y) { + this.auth.login({ username: this.login, password: this.password, rememberMe: false, usePlexOAuth: false }).subscribe(c => { localStorage.setItem("id_token", c.access_token); // Mark that we have done the settings now this.settings.getOmbi().subscribe(ombi => { ombi.wizard = true; - this.settings.saveOmbi(ombi).subscribe(x => { + this.settings.saveOmbi(ombi).subscribe(s => { this.settings.getUserManagementSettings().subscribe(usr => { usr.importPlexAdmin = true; @@ -63,12 +57,8 @@ export class PlexComponent { return; } }); - }, - reject: () => { - this.router.navigate(["Wizard/CreateAdmin"]); - }, - }); - }); + } + ); } public oauth() { diff --git a/src/Ombi/ClientApp/app/wizard/plex/plexoauth.component.ts b/src/Ombi/ClientApp/app/wizard/plex/plexoauth.component.ts index 1397fe7f5..7d0d0bf9d 100644 --- a/src/Ombi/ClientApp/app/wizard/plex/plexoauth.component.ts +++ b/src/Ombi/ClientApp/app/wizard/plex/plexoauth.component.ts @@ -1,8 +1,6 @@ import { Component, OnInit } from "@angular/core"; import { ActivatedRoute, Router } from "@angular/router"; -import { ConfirmationService } from "primeng/primeng"; - import { PlexOAuthService, IdentityService, SettingsService } from "../../services"; import { AuthService } from "./../../auth/auth.service"; @@ -14,7 +12,6 @@ export class PlexOAuthComponent implements OnInit { constructor(private route: ActivatedRoute, private plexOauth: PlexOAuthService, - private confirmationService: ConfirmationService, private identityService: IdentityService, private settings: SettingsService, private router: Router, @@ -28,19 +25,18 @@ export class PlexOAuthComponent implements OnInit { ngOnInit(): void { this.plexOauth.oAuth(this.pinId).subscribe(x => { - x.accessToken; + if(!x.accessToken) { + return; + // RETURN + } - this.confirmationService.confirm({ - message: "Do you want your Plex user to be the main admin account on Ombi?", - header: "Use Plex Account", - icon: "fa fa-check", - accept: () => { + this.identityService.createWizardUser({ username: "", password: "", usePlexAdminAccount: true, - }).subscribe(x => { - if (x) { + }).subscribe(u => { + if (u) { this.auth.oAuth(this.pinId).subscribe(c => { localStorage.setItem("id_token", c.access_token); @@ -48,7 +44,7 @@ export class PlexOAuthComponent implements OnInit { this.settings.getOmbi().subscribe(ombi => { ombi.wizard = true; - this.settings.saveOmbi(ombi).subscribe(x => { + this.settings.saveOmbi(ombi).subscribe(s => { this.settings.getUserManagementSettings().subscribe(usr => { usr.importPlexAdmin = true; @@ -65,11 +61,7 @@ export class PlexOAuthComponent implements OnInit { return; } }); - }, - reject: () => { - this.router.navigate(["Wizard/CreateAdmin"]); - }, - }); + }); } diff --git a/src/Ombi/Controllers/SettingsController.cs b/src/Ombi/Controllers/SettingsController.cs index 867a7517c..1f0d62147 100644 --- a/src/Ombi/Controllers/SettingsController.cs +++ b/src/Ombi/Controllers/SettingsController.cs @@ -151,6 +151,16 @@ namespace Ombi.Controllers return s; } + [HttpGet("plexstatus")] + [AllowAnonymous] + public async Task PlexStatusSettings() + { + var s = await Get(); + + + return s.Enable; + } + /// /// Save the Plex settings. /// diff --git a/src/Ombi/Controllers/TokenController.cs b/src/Ombi/Controllers/TokenController.cs index 62e43635d..624267989 100644 --- a/src/Ombi/Controllers/TokenController.cs +++ b/src/Ombi/Controllers/TokenController.cs @@ -49,26 +49,27 @@ namespace Ombi.Controllers [HttpPost] public async Task GetToken([FromBody] UserAuthModel model) { - await _audit.Record(AuditType.None, AuditArea.Authentication, + if (!model.UsePlexOAuth) + { + await _audit.Record(AuditType.None, AuditArea.Authentication, $"UserName {model.Username} attempting to authenticate"); - var user = await _userManager.FindByNameAsync(model.Username); - - if (user == null) - { - // Could this be an email login? - user = await _userManager.FindByEmailAsync(model.Username); + var user = await _userManager.FindByNameAsync(model.Username); if (user == null) { - return new UnauthorizedResult(); + // Could this be an email login? + user = await _userManager.FindByEmailAsync(model.Username); + + if (user == null) + { + return new UnauthorizedResult(); + } + + user.EmailLogin = true; } - user.EmailLogin = true; - } - if (!model.UsePlexOAuth) - { // Verify Password if (await _userManager.CheckPasswordAsync(user, model.Password)) { @@ -91,7 +92,7 @@ namespace Ombi.Controllers error = "Application URL has not been set" }); } - return new RedirectResult(url.ToString()); + return new JsonResult(new { url = url.ToString() }); } return new UnauthorizedResult(); @@ -183,7 +184,7 @@ namespace Ombi.Controllers { return new UnauthorizedResult(); } - + throw new NotImplementedException(); } From 5531af462cc67189f6a5d37f1c388dd230ea1fac Mon Sep 17 00:00:00 2001 From: Jamie Rees Date: Thu, 19 Apr 2018 22:18:25 +0100 Subject: [PATCH 10/37] Made the app name customizable for the OAUth !wip --- src/Ombi.Api.Plex/PlexApi.cs | 34 +++++++++++-------- .../Settings/Models/External/PlexSettings.cs | 1 - 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/src/Ombi.Api.Plex/PlexApi.cs b/src/Ombi.Api.Plex/PlexApi.cs index 6d814adf3..a16dee9ec 100644 --- a/src/Ombi.Api.Plex/PlexApi.cs +++ b/src/Ombi.Api.Plex/PlexApi.cs @@ -10,38 +10,42 @@ using Ombi.Api.Plex.Models.Status; using Ombi.Core.Settings; using Ombi.Core.Settings.Models.External; using Ombi.Helpers; +using Ombi.Settings.Settings.Models; namespace Ombi.Api.Plex { public class PlexApi : IPlexApi { - public PlexApi(IApi api, ISettingsService settings) + public PlexApi(IApi api, ISettingsService settings) { Api = api; - _plex = settings; + _custom = settings; } private IApi Api { get; } - private readonly ISettingsService _plex; + private readonly ISettingsService _custom; - private string _clientId; - private string ClientIdSecret + private string _app; + private string ApplicationName { get { - if (string.IsNullOrEmpty(_clientId)) + if (string.IsNullOrEmpty(_app)) { - var settings = _plex.GetSettings(); - if (settings.UniqueInstallCode.IsNullOrEmpty()) + var settings = _custom.GetSettings(); + if (settings.ApplicationName.IsNullOrEmpty()) { - settings.UniqueInstallCode = Guid.NewGuid().ToString("N"); - _plex.SaveSettings(settings); + _app = "Ombi"; + } + else + { + _app = settings.ApplicationName; } - _clientId = settings.UniqueInstallCode; + return _app; } - return _clientId; + return _app; } } @@ -215,7 +219,7 @@ namespace Ombi.Api.Plex request.AddQueryString("code", code); request.AddQueryString("context[device][product]", "Ombi"); request.AddQueryString("context[device][environment]", "bundled"); - request.AddQueryString("clientID", $"OmbiV3{ClientIdSecret}"); + request.AddQueryString("clientID", $"OmbiV3"); if (request.FullUri.Fragment.Equals("#")) { @@ -246,8 +250,8 @@ namespace Ombi.Api.Plex /// private void AddHeaders(Request request) { - request.AddHeader("X-Plex-Client-Identifier", $"OmbiV3{ClientIdSecret}"); - request.AddHeader("X-Plex-Product", "Ombi"); + request.AddHeader("X-Plex-Client-Identifier", $"OmbiV3"); + request.AddHeader("X-Plex-Product", ApplicationName); request.AddHeader("X-Plex-Version", "3"); request.AddContentHeader("Content-Type", request.ContentType == ContentType.Json ? "application/json" : "application/xml"); request.AddHeader("Accept", "application/json"); diff --git a/src/Ombi.Settings/Settings/Models/External/PlexSettings.cs b/src/Ombi.Settings/Settings/Models/External/PlexSettings.cs index dd92eba18..3faba3e42 100644 --- a/src/Ombi.Settings/Settings/Models/External/PlexSettings.cs +++ b/src/Ombi.Settings/Settings/Models/External/PlexSettings.cs @@ -6,7 +6,6 @@ namespace Ombi.Core.Settings.Models.External public sealed class PlexSettings : Ombi.Settings.Settings.Models.Settings { public bool Enable { get; set; } - public string UniqueInstallCode { get; set; } public List Servers { get; set; } } From f05a9a5090ecc68ba56522c7ecf453e3dc97b549 Mon Sep 17 00:00:00 2001 From: Jamie Rees Date: Fri, 20 Apr 2018 10:41:40 +0100 Subject: [PATCH 11/37] Removed no longer needed stuff and fixed linting !wip --- src/Ombi/ClientApp/app/login/login.component.ts | 4 +--- src/Ombi/ClientApp/app/login/loginoauth.component.ts | 4 ++-- src/Ombi/ClientApp/app/wizard/plex/plex.component.ts | 5 ++--- src/Ombi/ClientApp/app/wizard/plex/plexoauth.component.ts | 7 +++---- src/Ombi/Controllers/External/PlexController.cs | 1 - src/Ombi/Controllers/SettingsController.cs | 4 ---- 6 files changed, 8 insertions(+), 17 deletions(-) diff --git a/src/Ombi/ClientApp/app/login/login.component.ts b/src/Ombi/ClientApp/app/login/login.component.ts index cde2ef085..3c2636b2f 100644 --- a/src/Ombi/ClientApp/app/login/login.component.ts +++ b/src/Ombi/ClientApp/app/login/login.component.ts @@ -44,7 +44,6 @@ export class LoginComponent implements OnDestroy, OnInit { return true; } - this.loginWithOmbi = true; return true; } @@ -145,7 +144,7 @@ export class LoginComponent implements OnDestroy, OnInit { public oauth() { this.authService.login({usePlexOAuth: true, password:"",rememberMe:true,username:""}).subscribe(x => { window.location.href = x.url; - }) + }); } public ngOnDestroy() { @@ -161,5 +160,4 @@ export class LoginComponent implements OnDestroy, OnInit { .bypassSecurityTrustStyle("linear-gradient(-10deg, transparent 20%, rgba(0,0,0,0.7) 20.0%, rgba(0,0,0,0.7) 80.0%, transparent 80%), url(" + x.url + ")"); }); } - } diff --git a/src/Ombi/ClientApp/app/login/loginoauth.component.ts b/src/Ombi/ClientApp/app/login/loginoauth.component.ts index a0a9a9414..03922d8b3 100644 --- a/src/Ombi/ClientApp/app/login/loginoauth.component.ts +++ b/src/Ombi/ClientApp/app/login/loginoauth.component.ts @@ -4,7 +4,7 @@ import { ActivatedRoute, Router } from "@angular/router"; import { AuthService } from "../auth/auth.service"; @Component({ - templateUrl: "./loginoauth.component.html" + templateUrl: "./loginoauth.component.html", }) export class LoginOAuthComponent implements OnInit { public pin: number; @@ -18,7 +18,7 @@ export class LoginOAuthComponent implements OnInit { }); } - ngOnInit(): void { + public ngOnInit(): void { this.auth(); } diff --git a/src/Ombi/ClientApp/app/wizard/plex/plex.component.ts b/src/Ombi/ClientApp/app/wizard/plex/plex.component.ts index 2f5b2dd79..bafe67756 100644 --- a/src/Ombi/ClientApp/app/wizard/plex/plex.component.ts +++ b/src/Ombi/ClientApp/app/wizard/plex/plex.component.ts @@ -1,7 +1,6 @@ import { Component } from "@angular/core"; import { Router } from "@angular/router"; - import { PlexService } from "../../services"; import { IdentityService, NotificationService, SettingsService } from "../../services"; import { AuthService } from "./../../auth/auth.service"; @@ -27,7 +26,7 @@ export class PlexComponent { return; } - this.identityService.createWizardUser({ + this.identityService.createWizardUser({ username: "", password: "", usePlexAdminAccount: true, @@ -57,7 +56,7 @@ export class PlexComponent { return; } }); - } + }, ); } diff --git a/src/Ombi/ClientApp/app/wizard/plex/plexoauth.component.ts b/src/Ombi/ClientApp/app/wizard/plex/plexoauth.component.ts index 7d0d0bf9d..25517541c 100644 --- a/src/Ombi/ClientApp/app/wizard/plex/plexoauth.component.ts +++ b/src/Ombi/ClientApp/app/wizard/plex/plexoauth.component.ts @@ -1,7 +1,7 @@ import { Component, OnInit } from "@angular/core"; import { ActivatedRoute, Router } from "@angular/router"; -import { PlexOAuthService, IdentityService, SettingsService } from "../../services"; +import { IdentityService, PlexOAuthService, SettingsService } from "../../services"; import { AuthService } from "./../../auth/auth.service"; @Component({ @@ -23,15 +23,14 @@ export class PlexOAuthComponent implements OnInit { }); } - ngOnInit(): void { + public ngOnInit(): void { this.plexOauth.oAuth(this.pinId).subscribe(x => { if(!x.accessToken) { return; // RETURN } - - this.identityService.createWizardUser({ + this.identityService.createWizardUser({ username: "", password: "", usePlexAdminAccount: true, diff --git a/src/Ombi/Controllers/External/PlexController.cs b/src/Ombi/Controllers/External/PlexController.cs index cde78bc64..4819ed8a0 100644 --- a/src/Ombi/Controllers/External/PlexController.cs +++ b/src/Ombi/Controllers/External/PlexController.cs @@ -69,7 +69,6 @@ namespace Ombi.Controllers.External _log.LogDebug("Adding first server"); settings.Enable = true; - settings.UniqueInstallCode = Guid.NewGuid().ToString("N"); settings.Servers = new List { new PlexServers { diff --git a/src/Ombi/Controllers/SettingsController.cs b/src/Ombi/Controllers/SettingsController.cs index 1f0d62147..0a515d9cb 100644 --- a/src/Ombi/Controllers/SettingsController.cs +++ b/src/Ombi/Controllers/SettingsController.cs @@ -143,10 +143,6 @@ namespace Ombi.Controllers public async Task PlexSettings() { var s = await Get(); - if (s.UniqueInstallCode.IsNullOrEmpty()) - { - s.UniqueInstallCode = Guid.NewGuid().ToString("N"); - } return s; } From e980ac6fc540595a5798c128ec2440a25bbe19e5 Mon Sep 17 00:00:00 2001 From: Jamie Rees Date: Fri, 20 Apr 2018 10:48:25 +0100 Subject: [PATCH 12/37] Fixed styling !wip --- src/Ombi/ClientApp/app/wizard/plex/plex.component.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Ombi/ClientApp/app/wizard/plex/plex.component.html b/src/Ombi/ClientApp/app/wizard/plex/plex.component.html index 5d69c435b..a48c0f73e 100644 --- a/src/Ombi/ClientApp/app/wizard/plex/plex.component.html +++ b/src/Ombi/ClientApp/app/wizard/plex/plex.component.html @@ -17,13 +17,13 @@ Please note we do not store this information, we only store your Plex Authorization Token that will allow Ombi to view your media and friends
- +
- +

OR

- +
From 738bdb46d72f3c1cd13c521548ec2fad89f03042 Mon Sep 17 00:00:00 2001 From: Anojh Date: Fri, 20 Apr 2018 14:02:19 -0700 Subject: [PATCH 13/37] Fix baseurl breaking themes --- src/Ombi/Controllers/SettingsController.cs | 6 ------ src/Ombi/Views/Shared/_Layout.cshtml | 10 ++++++++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Ombi/Controllers/SettingsController.cs b/src/Ombi/Controllers/SettingsController.cs index 44702a073..9ffa9d81f 100644 --- a/src/Ombi/Controllers/SettingsController.cs +++ b/src/Ombi/Controllers/SettingsController.cs @@ -274,12 +274,6 @@ namespace Ombi.Controllers public async Task GetThemeContent([FromQuery]string url) { var css = await _githubApi.GetThemesRawContent(url); - var ombiSettings = await OmbiSettings(); - if (ombiSettings.BaseUrl != null) - { - int index = css.IndexOf("/api/"); - css = css.Insert(index, ombiSettings.BaseUrl); - } return Content(css, "text/css"); } diff --git a/src/Ombi/Views/Shared/_Layout.cshtml b/src/Ombi/Views/Shared/_Layout.cshtml index cafd75357..1f2610e1c 100644 --- a/src/Ombi/Views/Shared/_Layout.cshtml +++ b/src/Ombi/Views/Shared/_Layout.cshtml @@ -85,8 +85,14 @@ O:::::::OOO:::::::Om::::m m::::m m::::mb:::::bbbbbb::::::bi::::::i @{ - if (customization.HasPresetTheme) - { + if (customization.HasPresetTheme) + { + if (!string.IsNullOrEmpty(baseUrl)) + { + var index = customization.PresetThemeContent.IndexOf("/api/"); + customization.PresetThemeContent = customization.PresetThemeContent.Insert(index, "/" + baseUrl); + } + From 2c08908c8b29bc9c9f26ef1a5f08a99321b09492 Mon Sep 17 00:00:00 2001 From: Jamie Date: Fri, 20 Apr 2018 23:29:00 +0100 Subject: [PATCH 14/37] Fixed the ombi login button !wip --- .../ClientApp/app/login/login.component.html | 65 +++++++++++-------- .../ClientApp/app/login/login.component.ts | 22 +------ .../app/login/loginoauth.component.html | 7 ++ .../app/login/loginoauth.component.ts | 7 ++ src/Ombi/Controllers/TokenController.cs | 9 +++ 5 files changed, 63 insertions(+), 47 deletions(-) diff --git a/src/Ombi/ClientApp/app/login/login.component.html b/src/Ombi/ClientApp/app/login/login.component.html index 528fe9917..50d887feb 100644 --- a/src/Ombi/ClientApp/app/login/login.component.html +++ b/src/Ombi/ClientApp/app/login/login.component.html @@ -9,39 +9,50 @@ include the remember me checkbox
-
-
+
+ +
+
+ +

-
- -
+ +
+ +
+ + \ No newline at end of file diff --git a/src/Ombi/ClientApp/app/login/login.component.ts b/src/Ombi/ClientApp/app/login/login.component.ts index 3c2636b2f..5bc5f022a 100644 --- a/src/Ombi/ClientApp/app/login/login.component.ts +++ b/src/Ombi/ClientApp/app/login/login.component.ts @@ -29,26 +29,8 @@ export class LoginComponent implements OnDestroy, OnInit { public background: any; public landingFlag: boolean; public baseUrl: string; - - public get showLoginForm(): boolean { - if(this.customizationSettings.applicationUrl && this.plexEnabled) { - this.loginWithOmbi = false; - return false; - } - if(!this.customizationSettings.applicationUrl || !this.plexEnabled) { - - this.loginWithOmbi = true; - return true; - } - if(this.loginWithOmbi) { - return true; - } - - this.loginWithOmbi = true; - return true; - } - public loginWithOmbi: boolean = false; - + public loginWithOmbi: boolean; + public get appName(): string { if(this.customizationSettings.applicationName) { return this.customizationSettings.applicationName; diff --git a/src/Ombi/ClientApp/app/login/loginoauth.component.html b/src/Ombi/ClientApp/app/login/loginoauth.component.html index e80f1239f..e2118c7aa 100644 --- a/src/Ombi/ClientApp/app/login/loginoauth.component.html +++ b/src/Ombi/ClientApp/app/login/loginoauth.component.html @@ -4,6 +4,13 @@
+ +
+ + + + Back +
diff --git a/src/Ombi/ClientApp/app/login/loginoauth.component.ts b/src/Ombi/ClientApp/app/login/loginoauth.component.ts index 03922d8b3..1adcff19c 100644 --- a/src/Ombi/ClientApp/app/login/loginoauth.component.ts +++ b/src/Ombi/ClientApp/app/login/loginoauth.component.ts @@ -8,6 +8,7 @@ import { AuthService } from "../auth/auth.service"; }) export class LoginOAuthComponent implements OnInit { public pin: number; + public error: string; constructor(private authService: AuthService, private router: Router, private route: ActivatedRoute) { @@ -24,11 +25,17 @@ export class LoginOAuthComponent implements OnInit { public auth() { this.authService.oAuth(this.pin).subscribe(x => { + if(x.access_token) { localStorage.setItem("id_token", x.access_token); if (this.authService.loggedIn()) { this.router.navigate(["search"]); + return; } + } + if(x.errorMessage) { + this.error = x.errorMessage; + } }); } diff --git a/src/Ombi/Controllers/TokenController.cs b/src/Ombi/Controllers/TokenController.cs index 624267989..b337b3f51 100644 --- a/src/Ombi/Controllers/TokenController.cs +++ b/src/Ombi/Controllers/TokenController.cs @@ -148,6 +148,15 @@ namespace Ombi.Controllers { var accessToken = await _plexOAuthManager.GetAccessTokenFromPin(pinId); + if (accessToken.IsNullOrEmpty()) + { + // Looks like we are not authenticated. + return new JsonResult(new + { + errorMessage = "Could not authenticate with Plex" + }); + } + // Let's look for the users account var account = await _plexOAuthManager.GetAccount(accessToken); From 77280cf4aea29b4cf2642b7269e29cd00bb14203 Mon Sep 17 00:00:00 2001 From: Avi Date: Fri, 20 Apr 2018 18:25:58 -0500 Subject: [PATCH 15/37] Sign In rather than Login/Continue --- src/Ombi/ClientApp/app/login/login.component.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Ombi/ClientApp/app/login/login.component.html b/src/Ombi/ClientApp/app/login/login.component.html index 50d887feb..a50040fab 100644 --- a/src/Ombi/ClientApp/app/login/login.component.html +++ b/src/Ombi/ClientApp/app/login/login.component.html @@ -43,11 +43,11 @@ include the remember me checkbox @@ -55,4 +55,4 @@ include the remember me checkbox - \ No newline at end of file + From 04af799efb02671d6b9d9214d4a2f2a527457654 Mon Sep 17 00:00:00 2001 From: Jamie Rees Date: Sat, 21 Apr 2018 21:22:25 +0100 Subject: [PATCH 16/37] Fixed the newsletter not sending #2134 --- src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs | 248 ++++++++++-------- .../Notifications/NewsletterSettings.cs | 2 +- 2 files changed, 133 insertions(+), 117 deletions(-) diff --git a/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs b/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs index 1a8b21fbd..6cfe67bb6 100644 --- a/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs +++ b/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs @@ -6,6 +6,7 @@ using System.Threading.Tasks; using MailKit; using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; using Ombi.Api.TheMovieDb; using Ombi.Api.TheMovieDb.Models; using Ombi.Api.TvMaze; @@ -26,7 +27,7 @@ namespace Ombi.Schedule.Jobs.Ombi public NewsletterJob(IPlexContentRepository plex, IEmbyContentRepository emby, IRepository addedLog, IMovieDbApi movieApi, ITvMazeApi tvApi, IEmailProvider email, ISettingsService custom, ISettingsService emailSettings, INotificationTemplatesRepository templateRepo, - UserManager um, ISettingsService newsletter) + UserManager um, ISettingsService newsletter, ILogger log) { _plex = plex; _emby = emby; @@ -42,6 +43,7 @@ namespace Ombi.Schedule.Jobs.Ombi _emailSettings.ClearCache(); _customizationSettings.ClearCache(); _newsletterSettings.ClearCache(); + _log = log; } private readonly IPlexContentRepository _plex; @@ -55,6 +57,7 @@ namespace Ombi.Schedule.Jobs.Ombi private readonly ISettingsService _emailSettings; private readonly ISettingsService _newsletterSettings; private readonly UserManager _userManager; + private readonly ILogger _log; public async Task Start(NewsletterSettings settings, bool test) { @@ -74,152 +77,167 @@ namespace Ombi.Schedule.Jobs.Ombi return; } - var customization = await _customizationSettings.GetSettingsAsync(); + try + { - // Get the Content - var plexContent = _plex.GetAll().Include(x => x.Episodes).AsNoTracking(); - var embyContent = _emby.GetAll().Include(x => x.Episodes).AsNoTracking(); - var addedLog = _recentlyAddedLog.GetAll(); - var addedPlexMovieLogIds = addedLog.Where(x => x.Type == RecentlyAddedType.Plex && x.ContentType == ContentType.Parent).Select(x => x.ContentId); - var addedEmbyMoviesLogIds = addedLog.Where(x => x.Type == RecentlyAddedType.Emby && x.ContentType == ContentType.Parent).Select(x => x.ContentId); + var customization = await _customizationSettings.GetSettingsAsync(); - var addedPlexEpisodesLogIds = addedLog.Where(x => x.Type == RecentlyAddedType.Plex && x.ContentType == ContentType.Episode).Select(x => x.ContentId); - var addedEmbyEpisodesLogIds = addedLog.Where(x => x.Type == RecentlyAddedType.Emby && x.ContentType == ContentType.Episode).Select(x => x.ContentId); + // Get the Content + var plexContent = _plex.GetAll().Include(x => x.Episodes).AsNoTracking(); + var embyContent = _emby.GetAll().Include(x => x.Episodes).AsNoTracking(); - // Filter out the ones that we haven't sent yet - var plexContentMoviesToSend = plexContent.Where(x => x.Type == PlexMediaTypeEntity.Movie && !addedPlexMovieLogIds.Contains(x.Id)); - var embyContentMoviesToSend = embyContent.Where(x => x.Type == EmbyMediaType.Movie && !addedEmbyMoviesLogIds.Contains(x.Id)); + var addedLog = _recentlyAddedLog.GetAll(); + var addedPlexMovieLogIds = addedLog.Where(x => x.Type == RecentlyAddedType.Plex && x.ContentType == ContentType.Parent).Select(x => x.ContentId); + var addedEmbyMoviesLogIds = addedLog.Where(x => x.Type == RecentlyAddedType.Emby && x.ContentType == ContentType.Parent).Select(x => x.ContentId); - var plexEpisodesToSend = _plex.GetAllEpisodes().Include(x => x.Series).Where(x => !addedPlexEpisodesLogIds.Contains(x.Id)).AsNoTracking(); - var embyEpisodesToSend = _emby.GetAllEpisodes().Include(x => x.Series).Where(x => !addedEmbyEpisodesLogIds.Contains(x.Id)).AsNoTracking(); + var addedPlexEpisodesLogIds = addedLog.Where(x => x.Type == RecentlyAddedType.Plex && x.ContentType == ContentType.Episode).Select(x => x.ContentId); + var addedEmbyEpisodesLogIds = addedLog.Where(x => x.Type == RecentlyAddedType.Emby && x.ContentType == ContentType.Episode).Select(x => x.ContentId); - var body = string.Empty; - if (test) - { - var plexm = plexContent.Where(x => x.Type == PlexMediaTypeEntity.Movie).OrderByDescending(x => x.AddedAt).Take(10); - var embym = embyContent.Where(x => x.Type == EmbyMediaType.Movie).OrderByDescending(x => x.AddedAt).Take(10); - var plext = _plex.GetAllEpisodes().Include(x => x.Series).OrderByDescending(x => x.Series.AddedAt).Take(10); - var embyt = _emby.GetAllEpisodes().Include(x => x.Series).OrderByDescending(x => x.AddedAt).Take(10); - body = await BuildHtml(plexm, embym, plext, embyt, settings); - } - else - { - body = await BuildHtml(plexContentMoviesToSend, embyContentMoviesToSend, plexEpisodesToSend, embyEpisodesToSend, settings); - if (body.IsNullOrEmpty()) - { - return; - } + // Filter out the ones that we haven't sent yet + var plexContentMoviesToSend = plexContent.Where(x => x.Type == PlexMediaTypeEntity.Movie && !addedPlexMovieLogIds.Contains(x.Id)); + var embyContentMoviesToSend = embyContent.Where(x => x.Type == EmbyMediaType.Movie && !addedEmbyMoviesLogIds.Contains(x.Id)); + _log.LogInformation("Plex Movies to send: {0}", plexContentMoviesToSend.Count()); + _log.LogInformation("Emby Movies to send: {0}", embyContentMoviesToSend.Count()); - } - - if (!test) - { - // Get the users to send it to - var users = await _userManager.GetUsersInRoleAsync(OmbiRoles.RecievesNewsletter); - if (!users.Any()) + var plexEpisodesToSend = _plex.GetAllEpisodes().Include(x => x.Series).Where(x => !addedPlexEpisodesLogIds.Contains(x.Id)).AsNoTracking(); + var embyEpisodesToSend = _emby.GetAllEpisodes().Include(x => x.Series).Where(x => !addedEmbyEpisodesLogIds.Contains(x.Id)).AsNoTracking(); + + _log.LogInformation("Plex Episodes to send: {0}", plexEpisodesToSend.Count()); + _log.LogInformation("Emby Episodes to send: {0}", embyEpisodesToSend.Count()); + + var body = string.Empty; + if (test) { - return; + var plexm = plexContent.Where(x => x.Type == PlexMediaTypeEntity.Movie).OrderByDescending(x => x.AddedAt).Take(10); + var embym = embyContent.Where(x => x.Type == EmbyMediaType.Movie).OrderByDescending(x => x.AddedAt).Take(10); + var plext = _plex.GetAllEpisodes().Include(x => x.Series).OrderByDescending(x => x.Series.AddedAt).Take(10); + var embyt = _emby.GetAllEpisodes().Include(x => x.Series).OrderByDescending(x => x.AddedAt).Take(10); + body = await BuildHtml(plexm, embym, plext, embyt, settings); } - - foreach (var emails in settings.ExternalEmails) + else { - users.Add(new OmbiUser + body = await BuildHtml(plexContentMoviesToSend, embyContentMoviesToSend, plexEpisodesToSend, embyEpisodesToSend, settings); + if (body.IsNullOrEmpty()) { - UserName = emails, - Email = emails - }); + return; + } } - var emailTasks = new List(); - foreach (var user in users) + + if (!test) { - if (user.Email.IsNullOrEmpty()) + // Get the users to send it to + var users = await _userManager.GetUsersInRoleAsync(OmbiRoles.RecievesNewsletter); + if (!users.Any()) { - continue; + return; } - var messageContent = ParseTemplate(template, customization, user); - var email = new NewsletterTemplate(); + foreach (var emails in settings.ExternalEmails) + { + users.Add(new OmbiUser + { + UserName = emails, + Email = emails + }); + } + var emailTasks = new List(); + foreach (var user in users) + { + if (user.Email.IsNullOrEmpty()) + { + continue; + } - var html = email.LoadTemplate(messageContent.Subject, messageContent.Message, body, customization.Logo); + var messageContent = ParseTemplate(template, customization, user); + var email = new NewsletterTemplate(); - emailTasks.Add(_email.Send( - new NotificationMessage { Message = html, Subject = messageContent.Subject, To = user.Email }, - emailSettings)); - } + var html = email.LoadTemplate(messageContent.Subject, messageContent.Message, body, customization.Logo); - // Now add all of this to the Recently Added log - var recentlyAddedLog = new HashSet(); - foreach (var p in plexContentMoviesToSend) - { - recentlyAddedLog.Add(new RecentlyAddedLog + emailTasks.Add(_email.Send( + new NotificationMessage { Message = html, Subject = messageContent.Subject, To = user.Email }, + emailSettings)); + } + + // Now add all of this to the Recently Added log + var recentlyAddedLog = new HashSet(); + foreach (var p in plexContentMoviesToSend) { - AddedAt = DateTime.Now, - Type = RecentlyAddedType.Plex, - ContentType = ContentType.Parent, - ContentId = p.Id - }); + recentlyAddedLog.Add(new RecentlyAddedLog + { + AddedAt = DateTime.Now, + Type = RecentlyAddedType.Plex, + ContentType = ContentType.Parent, + ContentId = p.Id + }); - } + } - foreach (var p in plexEpisodesToSend) - { - recentlyAddedLog.Add(new RecentlyAddedLog + foreach (var p in plexEpisodesToSend) { - AddedAt = DateTime.Now, - Type = RecentlyAddedType.Plex, - ContentType = ContentType.Episode, - ContentId = p.Id - }); - } + recentlyAddedLog.Add(new RecentlyAddedLog + { + AddedAt = DateTime.Now, + Type = RecentlyAddedType.Plex, + ContentType = ContentType.Episode, + ContentId = p.Id + }); + } - foreach (var e in embyContentMoviesToSend) - { - if (e.Type == EmbyMediaType.Movie) + foreach (var e in embyContentMoviesToSend) + { + if (e.Type == EmbyMediaType.Movie) + { + recentlyAddedLog.Add(new RecentlyAddedLog + { + AddedAt = DateTime.Now, + Type = RecentlyAddedType.Emby, + ContentType = ContentType.Parent, + ContentId = e.Id + }); + } + } + + foreach (var p in embyEpisodesToSend) { recentlyAddedLog.Add(new RecentlyAddedLog { AddedAt = DateTime.Now, Type = RecentlyAddedType.Emby, - ContentType = ContentType.Parent, - ContentId = e.Id + ContentType = ContentType.Episode, + ContentId = p.Id }); } - } + await _recentlyAddedLog.AddRange(recentlyAddedLog); - foreach (var p in embyEpisodesToSend) - { - recentlyAddedLog.Add(new RecentlyAddedLog - { - AddedAt = DateTime.Now, - Type = RecentlyAddedType.Emby, - ContentType = ContentType.Episode, - ContentId = p.Id - }); + await Task.WhenAll(emailTasks.ToArray()); } - await _recentlyAddedLog.AddRange(recentlyAddedLog); - - await Task.WhenAll(emailTasks.ToArray()); - } - else - { - var admins = await _userManager.GetUsersInRoleAsync(OmbiRoles.Admin); - foreach (var a in admins) + else { - if (a.Email.IsNullOrEmpty()) + var admins = await _userManager.GetUsersInRoleAsync(OmbiRoles.Admin); + foreach (var a in admins) { - continue; - } - var messageContent = ParseTemplate(template, customization, a); + if (a.Email.IsNullOrEmpty()) + { + continue; + } + var messageContent = ParseTemplate(template, customization, a); - var email = new NewsletterTemplate(); + var email = new NewsletterTemplate(); - var html = email.LoadTemplate(messageContent.Subject, messageContent.Message, body, customization.Logo); + var html = email.LoadTemplate(messageContent.Subject, messageContent.Message, body, customization.Logo); - await _email.Send( - new NotificationMessage { Message = html, Subject = messageContent.Subject, To = a.Email }, - emailSettings); + await _email.Send( + new NotificationMessage { Message = html, Subject = messageContent.Subject, To = a.Email }, + emailSettings); + } } + + } + catch (Exception e) + { + _log.LogError(e, "Error when attempting to create newsletter"); + throw; } } @@ -285,8 +303,7 @@ namespace Ombi.Schedule.Jobs.Ombi } catch (Exception e) { - Console.WriteLine(e); - throw; + _log.LogError(e, "Error when Processing Plex Movies {0}", info.Title); } finally { @@ -315,7 +332,7 @@ namespace Ombi.Schedule.Jobs.Ombi theMovieDbId = result.id.ToString(); } - + var info = await _movieApi.GetMovieInformationWithExtraInfo(int.Parse(theMovieDbId)); if (info == null) { @@ -327,8 +344,7 @@ namespace Ombi.Schedule.Jobs.Ombi } catch (Exception e) { - Console.WriteLine(e); - throw; + _log.LogError(e, "Error when processing Emby Movies {0}", info.Title); } finally { @@ -437,7 +453,7 @@ namespace Ombi.Schedule.Jobs.Ombi sb.Append("
"); - + var title = $"{t.Title} ({t.ReleaseYear})"; Href(sb, $"https://www.imdb.com/title/{info.externals.imdb}/"); @@ -483,7 +499,7 @@ namespace Ombi.Schedule.Jobs.Ombi } catch (Exception e) { - //Log.Error(e); + _log.LogError(e, "Error when processing Plex TV {0}", t.Title); } finally { @@ -584,7 +600,7 @@ namespace Ombi.Schedule.Jobs.Ombi } catch (Exception e) { - //Log.Error(e); + _log.LogError(e, "Error when processing Emby TV {0}", t.Title); } finally { diff --git a/src/Ombi.Settings/Settings/Models/Notifications/NewsletterSettings.cs b/src/Ombi.Settings/Settings/Models/Notifications/NewsletterSettings.cs index f043cd74c..e79f3182c 100644 --- a/src/Ombi.Settings/Settings/Models/Notifications/NewsletterSettings.cs +++ b/src/Ombi.Settings/Settings/Models/Notifications/NewsletterSettings.cs @@ -7,6 +7,6 @@ namespace Ombi.Settings.Settings.Models.Notifications public bool DisableTv { get; set; } public bool DisableMovies { get; set; } public bool Enabled { get; set; } - public List ExternalEmails { get; set; } + public List ExternalEmails { get; set; } = new List(); } } \ No newline at end of file From 703203ed5c301de21d3e7b0fbe28886f8459aacd Mon Sep 17 00:00:00 2001 From: Jamie Date: Sat, 21 Apr 2018 21:27:42 +0100 Subject: [PATCH 17/37] !wip --- src/Ombi.Core/Engine/RecentlyAddedEngine.cs | 32 +- src/Ombi.Helpers/LinqHelpers.cs | 7 + src/Ombi.Schedule/JobSetup.cs | 2 + src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs | 76 +- src/Ombi.Store/Entities/RecentlyAddedLog.cs | 4 +- ...180420225638_NewsletterChanges.Designer.cs | 956 ++++++++++++++++++ .../20180420225638_NewsletterChanges.cs | 33 + .../Migrations/OmbiContextModelSnapshot.cs | 4 + 8 files changed, 1091 insertions(+), 23 deletions(-) create mode 100644 src/Ombi.Store/Migrations/20180420225638_NewsletterChanges.Designer.cs create mode 100644 src/Ombi.Store/Migrations/20180420225638_NewsletterChanges.cs diff --git a/src/Ombi.Core/Engine/RecentlyAddedEngine.cs b/src/Ombi.Core/Engine/RecentlyAddedEngine.cs index 8782ea028..114d32a24 100644 --- a/src/Ombi.Core/Engine/RecentlyAddedEngine.cs +++ b/src/Ombi.Core/Engine/RecentlyAddedEngine.cs @@ -63,13 +63,17 @@ namespace Ombi.Core.Engine var recentlyAddedLog = new HashSet(); foreach (var p in plexContent) { + if (!p.HasTheMovieDb) + { + continue; + } if (p.Type == PlexMediaTypeEntity.Movie) { recentlyAddedLog.Add(new RecentlyAddedLog { AddedAt = DateTime.Now, Type = RecentlyAddedType.Plex, - ContentId = p.Id, + ContentId = int.Parse(p.TheMovieDbId), ContentType = ContentType.Parent }); } @@ -78,12 +82,18 @@ namespace Ombi.Core.Engine // Add the episodes foreach (var ep in p.Episodes) { + if (!ep.Series.HasTvDb) + { + continue; + } recentlyAddedLog.Add(new RecentlyAddedLog { AddedAt = DateTime.Now, Type = RecentlyAddedType.Plex, - ContentId = ep.Id, - ContentType = ContentType.Episode + ContentId = int.Parse(ep.Series.TvDbId), + ContentType = ContentType.Episode, + EpisodeNumber = ep.EpisodeNumber, + SeasonNumber = ep.SeasonNumber }); } } @@ -91,13 +101,17 @@ namespace Ombi.Core.Engine foreach (var e in embyContent) { + if (e.TheMovieDbId.IsNullOrEmpty()) + { + continue; + } if (e.Type == EmbyMediaType.Movie) { recentlyAddedLog.Add(new RecentlyAddedLog { AddedAt = DateTime.Now, Type = RecentlyAddedType.Emby, - ContentId = e.Id, + ContentId = int.Parse(e.TheMovieDbId), ContentType = ContentType.Parent }); } @@ -106,12 +120,18 @@ namespace Ombi.Core.Engine // Add the episodes foreach (var ep in e.Episodes) { + if (ep.Series.TvDbId.IsNullOrEmpty()) + { + continue; + } recentlyAddedLog.Add(new RecentlyAddedLog { AddedAt = DateTime.Now, Type = RecentlyAddedType.Emby, - ContentId = ep.Id, - ContentType = ContentType.Episode + ContentId = int.Parse(ep.Series.TvDbId), + ContentType = ContentType.Episode, + EpisodeNumber = ep.EpisodeNumber, + SeasonNumber = ep.SeasonNumber }); } } diff --git a/src/Ombi.Helpers/LinqHelpers.cs b/src/Ombi.Helpers/LinqHelpers.cs index 279d161b7..67fdb5c53 100644 --- a/src/Ombi.Helpers/LinqHelpers.cs +++ b/src/Ombi.Helpers/LinqHelpers.cs @@ -14,5 +14,12 @@ namespace Ombi.Helpers yield return source1; } } + + public static HashSet ToHashSet( + this IEnumerable source, + IEqualityComparer comparer = null) + { + return new HashSet(source, comparer); + } } } \ No newline at end of file diff --git a/src/Ombi.Schedule/JobSetup.cs b/src/Ombi.Schedule/JobSetup.cs index 6626ef933..73d7c3536 100644 --- a/src/Ombi.Schedule/JobSetup.cs +++ b/src/Ombi.Schedule/JobSetup.cs @@ -66,6 +66,8 @@ namespace Ombi.Schedule RecurringJob.AddOrUpdate(() => _embyUserImporter.Start(), JobSettingsHelper.UserImporter(s)); RecurringJob.AddOrUpdate(() => _plexUserImporter.Start(), JobSettingsHelper.UserImporter(s)); RecurringJob.AddOrUpdate(() => _newsletter.Start(), JobSettingsHelper.Newsletter(s)); + + BackgroundJob.Enqueue(() => _refreshMetadata.Start()); } diff --git a/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs b/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs index 1a8b21fbd..75dc9723d 100644 --- a/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs +++ b/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; +using System.Runtime.CompilerServices; using System.Text; using System.Threading.Tasks; using MailKit; @@ -84,23 +86,28 @@ namespace Ombi.Schedule.Jobs.Ombi var addedPlexMovieLogIds = addedLog.Where(x => x.Type == RecentlyAddedType.Plex && x.ContentType == ContentType.Parent).Select(x => x.ContentId); var addedEmbyMoviesLogIds = addedLog.Where(x => x.Type == RecentlyAddedType.Emby && x.ContentType == ContentType.Parent).Select(x => x.ContentId); - var addedPlexEpisodesLogIds = addedLog.Where(x => x.Type == RecentlyAddedType.Plex && x.ContentType == ContentType.Episode).Select(x => x.ContentId); - var addedEmbyEpisodesLogIds = addedLog.Where(x => x.Type == RecentlyAddedType.Emby && x.ContentType == ContentType.Episode).Select(x => x.ContentId); + var addedPlexEpisodesLogIds = + addedLog.Where(x => x.Type == RecentlyAddedType.Plex && x.ContentType == ContentType.Episode); + var addedEmbyEpisodesLogIds = + addedLog.Where(x => x.Type == RecentlyAddedType.Emby && x.ContentType == ContentType.Episode); // Filter out the ones that we haven't sent yet - var plexContentMoviesToSend = plexContent.Where(x => x.Type == PlexMediaTypeEntity.Movie && !addedPlexMovieLogIds.Contains(x.Id)); - var embyContentMoviesToSend = embyContent.Where(x => x.Type == EmbyMediaType.Movie && !addedEmbyMoviesLogIds.Contains(x.Id)); + var plexContentMoviesToSend = plexContent.Where(x => x.Type == PlexMediaTypeEntity.Movie && !addedPlexMovieLogIds.Contains(int.Parse(x.TheMovieDbId))); + var embyContentMoviesToSend = embyContent.Where(x => x.Type == EmbyMediaType.Movie && !addedEmbyMoviesLogIds.Contains(int.Parse(x.TheMovieDbId))); - var plexEpisodesToSend = _plex.GetAllEpisodes().Include(x => x.Series).Where(x => !addedPlexEpisodesLogIds.Contains(x.Id)).AsNoTracking(); - var embyEpisodesToSend = _emby.GetAllEpisodes().Include(x => x.Series).Where(x => !addedEmbyEpisodesLogIds.Contains(x.Id)).AsNoTracking(); + + var plexEpisodesToSend = + FilterPlexEpisodes(_plex.GetAllEpisodes().Include(x => x.Series).AsNoTracking(), addedPlexEpisodesLogIds); + var embyEpisodesToSend = FilterEmbyEpisodes(_emby.GetAllEpisodes().Include(x => x.Series).AsNoTracking(), + addedEmbyEpisodesLogIds); var body = string.Empty; if (test) { var plexm = plexContent.Where(x => x.Type == PlexMediaTypeEntity.Movie).OrderByDescending(x => x.AddedAt).Take(10); var embym = embyContent.Where(x => x.Type == EmbyMediaType.Movie).OrderByDescending(x => x.AddedAt).Take(10); - var plext = _plex.GetAllEpisodes().Include(x => x.Series).OrderByDescending(x => x.Series.AddedAt).Take(10); - var embyt = _emby.GetAllEpisodes().Include(x => x.Series).OrderByDescending(x => x.AddedAt).Take(10); + var plext = _plex.GetAllEpisodes().Include(x => x.Series).OrderByDescending(x => x.Series.AddedAt).Take(10).ToHashSet(); + var embyt = _emby.GetAllEpisodes().Include(x => x.Series).OrderByDescending(x => x.AddedAt).Take(10).ToHashSet(); body = await BuildHtml(plexm, embym, plext, embyt, settings); } else @@ -110,7 +117,6 @@ namespace Ombi.Schedule.Jobs.Ombi { return; } - } if (!test) @@ -157,7 +163,7 @@ namespace Ombi.Schedule.Jobs.Ombi AddedAt = DateTime.Now, Type = RecentlyAddedType.Plex, ContentType = ContentType.Parent, - ContentId = p.Id + ContentId = int.Parse(p.TheMovieDbId), }); } @@ -169,7 +175,9 @@ namespace Ombi.Schedule.Jobs.Ombi AddedAt = DateTime.Now, Type = RecentlyAddedType.Plex, ContentType = ContentType.Episode, - ContentId = p.Id + ContentId = int.Parse(p.Series.TvDbId), + EpisodeNumber = p.EpisodeNumber, + SeasonNumber = p.SeasonNumber }); } @@ -182,7 +190,7 @@ namespace Ombi.Schedule.Jobs.Ombi AddedAt = DateTime.Now, Type = RecentlyAddedType.Emby, ContentType = ContentType.Parent, - ContentId = e.Id + ContentId = int.Parse(e.TheMovieDbId), }); } } @@ -194,7 +202,9 @@ namespace Ombi.Schedule.Jobs.Ombi AddedAt = DateTime.Now, Type = RecentlyAddedType.Emby, ContentType = ContentType.Episode, - ContentId = p.Id + ContentId = int.Parse(p.Series.TvDbId), + EpisodeNumber = p.EpisodeNumber, + SeasonNumber = p.SeasonNumber }); } await _recentlyAddedLog.AddRange(recentlyAddedLog); @@ -229,6 +239,40 @@ namespace Ombi.Schedule.Jobs.Ombi await Start(newsletterSettings, false); } + private HashSet FilterPlexEpisodes(IEnumerable source, IQueryable recentlyAdded) + { + var itemsToReturn = new HashSet(); + foreach (var ep in source) + { + var tvDbId = int.Parse(ep.Series.TvDbId); + if (recentlyAdded.Any(x => x.ContentId == tvDbId && x.EpisodeNumber == ep.EpisodeNumber && x.SeasonNumber == ep.SeasonNumber)) + { + continue; + } + + itemsToReturn.Add(ep); + } + + return itemsToReturn; + } + + private HashSet FilterEmbyEpisodes(IEnumerable source, IQueryable recentlyAdded) + { + var itemsToReturn = new HashSet(); + foreach (var ep in source) + { + var tvDbId = int.Parse(ep.Series.TvDbId); + if (recentlyAdded.Any(x => x.ContentId == tvDbId && x.EpisodeNumber == ep.EpisodeNumber && x.SeasonNumber == ep.SeasonNumber)) + { + continue; + } + + itemsToReturn.Add(ep); + } + + return itemsToReturn; + } + private NotificationMessageContent ParseTemplate(NotificationTemplates template, CustomizationSettings settings, OmbiUser username) { var resolver = new NotificationMessageResolver(); @@ -239,7 +283,7 @@ namespace Ombi.Schedule.Jobs.Ombi return resolver.ParseMessage(template, curlys); } - private async Task BuildHtml(IQueryable plexContentToSend, IQueryable embyContentToSend, IQueryable plexEpisodes, IQueryable embyEp, NewsletterSettings settings) + private async Task BuildHtml(IQueryable plexContentToSend, IQueryable embyContentToSend, HashSet plexEpisodes, HashSet embyEp, NewsletterSettings settings) { var sb = new StringBuilder(); @@ -366,7 +410,7 @@ namespace Ombi.Schedule.Jobs.Ombi AddParagraph(sb, info.Overview); } - private async Task ProcessPlexTv(IQueryable plexContent, StringBuilder sb) + private async Task ProcessPlexTv(HashSet plexContent, StringBuilder sb) { var series = new List(); foreach (var plexEpisode in plexContent) @@ -494,7 +538,7 @@ namespace Ombi.Schedule.Jobs.Ombi } - private async Task ProcessEmbyTv(IQueryable embyContent, StringBuilder sb) + private async Task ProcessEmbyTv(HashSet embyContent, StringBuilder sb) { var series = new List(); foreach (var episode in embyContent) diff --git a/src/Ombi.Store/Entities/RecentlyAddedLog.cs b/src/Ombi.Store/Entities/RecentlyAddedLog.cs index ba26eb566..1ef091149 100644 --- a/src/Ombi.Store/Entities/RecentlyAddedLog.cs +++ b/src/Ombi.Store/Entities/RecentlyAddedLog.cs @@ -8,7 +8,9 @@ namespace Ombi.Store.Entities { public RecentlyAddedType Type { get; set; } public ContentType ContentType { get; set; } - public int ContentId { get; set; } // This is dependant on the type + public int ContentId { get; set; } // This is dependant on the type, it's either TMDBID or TVDBID + public int? EpisodeNumber { get; set; } + public int? SeasonNumber { get; set; } public DateTime AddedAt { get; set; } } diff --git a/src/Ombi.Store/Migrations/20180420225638_NewsletterChanges.Designer.cs b/src/Ombi.Store/Migrations/20180420225638_NewsletterChanges.Designer.cs new file mode 100644 index 000000000..64c581108 --- /dev/null +++ b/src/Ombi.Store/Migrations/20180420225638_NewsletterChanges.Designer.cs @@ -0,0 +1,956 @@ +// +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("20180420225638_NewsletterChanges")] + partial class NewsletterChanges + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.0.2-rtm-10011"); + + 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.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.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/20180420225638_NewsletterChanges.cs b/src/Ombi.Store/Migrations/20180420225638_NewsletterChanges.cs new file mode 100644 index 000000000..bea4d7080 --- /dev/null +++ b/src/Ombi.Store/Migrations/20180420225638_NewsletterChanges.cs @@ -0,0 +1,33 @@ +using Microsoft.EntityFrameworkCore.Migrations; +using System; +using System.Collections.Generic; + +namespace Ombi.Store.Migrations +{ + public partial class NewsletterChanges : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "EpisodeNumber", + table: "RecentlyAddedLog", + nullable: true); + + migrationBuilder.AddColumn( + name: "SeasonNumber", + table: "RecentlyAddedLog", + nullable: true); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "EpisodeNumber", + table: "RecentlyAddedLog"); + + migrationBuilder.DropColumn( + name: "SeasonNumber", + table: "RecentlyAddedLog"); + } + } +} diff --git a/src/Ombi.Store/Migrations/OmbiContextModelSnapshot.cs b/src/Ombi.Store/Migrations/OmbiContextModelSnapshot.cs index 248551d97..794bde8a6 100644 --- a/src/Ombi.Store/Migrations/OmbiContextModelSnapshot.cs +++ b/src/Ombi.Store/Migrations/OmbiContextModelSnapshot.cs @@ -455,6 +455,10 @@ namespace Ombi.Store.Migrations b.Property("ContentType"); + b.Property("EpisodeNumber"); + + b.Property("SeasonNumber"); + b.Property("Type"); b.HasKey("Id"); From 443c79b0cae49ec43d8d6c52e42f0c6f2e0c9d69 Mon Sep 17 00:00:00 2001 From: Jamie Rees Date: Sat, 21 Apr 2018 21:43:33 +0100 Subject: [PATCH 18/37] Fixed #2179. Note: This requires you to press the update database again before using the newsletter! --- src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs | 16 ++++++++-------- .../20180420225638_NewsletterChanges.cs | 2 ++ 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs b/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs index 75dc9723d..ea3276982 100644 --- a/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs +++ b/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs @@ -128,14 +128,14 @@ namespace Ombi.Schedule.Jobs.Ombi return; } - foreach (var emails in settings.ExternalEmails) - { - users.Add(new OmbiUser - { - UserName = emails, - Email = emails - }); - } + //foreach (var emails in settings.ExternalEmails) + //{ + // users.Add(new OmbiUser + // { + // UserName = emails, + // Email = emails + // }); + //} var emailTasks = new List(); foreach (var user in users) { diff --git a/src/Ombi.Store/Migrations/20180420225638_NewsletterChanges.cs b/src/Ombi.Store/Migrations/20180420225638_NewsletterChanges.cs index bea4d7080..ad4786772 100644 --- a/src/Ombi.Store/Migrations/20180420225638_NewsletterChanges.cs +++ b/src/Ombi.Store/Migrations/20180420225638_NewsletterChanges.cs @@ -17,6 +17,8 @@ namespace Ombi.Store.Migrations name: "SeasonNumber", table: "RecentlyAddedLog", nullable: true); + + migrationBuilder.Sql("DELETE FROM RecentlyAddedLog"); } protected override void Down(MigrationBuilder migrationBuilder) From b80ef04ab7b5f2d55349eae0420e274cc5f07e88 Mon Sep 17 00:00:00 2001 From: Jamie Rees Date: Sat, 21 Apr 2018 22:04:44 +0100 Subject: [PATCH 19/37] !wip fixed merge --- src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs | 104 +++++++++---------- 1 file changed, 47 insertions(+), 57 deletions(-) diff --git a/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs b/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs index ba8725b69..79731637b 100644 --- a/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs +++ b/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs @@ -84,62 +84,51 @@ namespace Ombi.Schedule.Jobs.Ombi var customization = await _customizationSettings.GetSettingsAsync(); + // Get the Content + var plexContent = _plex.GetAll().Include(x => x.Episodes).AsNoTracking(); + var embyContent = _emby.GetAll().Include(x => x.Episodes).AsNoTracking(); - var addedPlexEpisodesLogIds = + var addedLog = _recentlyAddedLog.GetAll(); + var addedPlexMovieLogIds = addedLog.Where(x => x.Type == RecentlyAddedType.Plex && x.ContentType == ContentType.Parent).Select(x => x.ContentId); + var addedEmbyMoviesLogIds = addedLog.Where(x => x.Type == RecentlyAddedType.Emby && x.ContentType == ContentType.Parent).Select(x => x.ContentId); + + var addedPlexEpisodesLogIds = addedLog.Where(x => x.Type == RecentlyAddedType.Plex && x.ContentType == ContentType.Episode); - var addedEmbyEpisodesLogIds = - addedLog.Where(x => x.Type == RecentlyAddedType.Emby && x.ContentType == ContentType.Episode); + var addedEmbyEpisodesLogIds = + addedLog.Where(x => x.Type == RecentlyAddedType.Emby && x.ContentType == ContentType.Episode); - // Filter out the ones that we haven't sent yet - var plexContentMoviesToSend = plexContent.Where(x => x.Type == PlexMediaTypeEntity.Movie && !addedPlexMovieLogIds.Contains(int.Parse(x.TheMovieDbId))); - var embyContentMoviesToSend = embyContent.Where(x => x.Type == EmbyMediaType.Movie && !addedEmbyMoviesLogIds.Contains(int.Parse(x.TheMovieDbId))); + // Filter out the ones that we haven't sent yet + var plexContentMoviesToSend = plexContent.Where(x => x.Type == PlexMediaTypeEntity.Movie && !addedPlexMovieLogIds.Contains(int.Parse(x.TheMovieDbId))); + var embyContentMoviesToSend = embyContent.Where(x => x.Type == EmbyMediaType.Movie && !addedEmbyMoviesLogIds.Contains(int.Parse(x.TheMovieDbId))); _log.LogInformation("Plex Movies to send: {0}", plexContentMoviesToSend.Count()); _log.LogInformation("Emby Movies to send: {0}", embyContentMoviesToSend.Count()); - var plexEpisodesToSend = - FilterPlexEpisodes(_plex.GetAllEpisodes().Include(x => x.Series).AsNoTracking(), addedPlexEpisodesLogIds); - var embyEpisodesToSend = FilterEmbyEpisodes(_emby.GetAllEpisodes().Include(x => x.Series).AsNoTracking(), - addedEmbyEpisodesLogIds); + var plexEpisodesToSend = + FilterPlexEpisodes(_plex.GetAllEpisodes().Include(x => x.Series).AsNoTracking(), addedPlexEpisodesLogIds); + var embyEpisodesToSend = FilterEmbyEpisodes(_emby.GetAllEpisodes().Include(x => x.Series).AsNoTracking(), + addedEmbyEpisodesLogIds); _log.LogInformation("Plex Episodes to send: {0}", plexEpisodesToSend.Count()); _log.LogInformation("Emby Episodes to send: {0}", embyEpisodesToSend.Count()); - var body = string.Empty; - if (test) - { - var plexm = plexContent.Where(x => x.Type == PlexMediaTypeEntity.Movie).OrderByDescending(x => x.AddedAt).Take(10); - var embym = embyContent.Where(x => x.Type == EmbyMediaType.Movie).OrderByDescending(x => x.AddedAt).Take(10); - var plext = _plex.GetAllEpisodes().Include(x => x.Series).OrderByDescending(x => x.Series.AddedAt).Take(10).ToHashSet(); - var embyt = _emby.GetAllEpisodes().Include(x => x.Series).OrderByDescending(x => x.AddedAt).Take(10).ToHashSet(); - body = await BuildHtml(plexm, embym, plext, embyt, settings); - } - else - { - body = await BuildHtml(plexContentMoviesToSend, embyContentMoviesToSend, plexEpisodesToSend, embyEpisodesToSend, settings); - if (body.IsNullOrEmpty()) + var body = string.Empty; + if (test) { - return; + var plexm = plexContent.Where(x => x.Type == PlexMediaTypeEntity.Movie).OrderByDescending(x => x.AddedAt).Take(10); + var embym = embyContent.Where(x => x.Type == EmbyMediaType.Movie).OrderByDescending(x => x.AddedAt).Take(10); + var plext = _plex.GetAllEpisodes().Include(x => x.Series).OrderByDescending(x => x.Series.AddedAt).Take(10).ToHashSet(); + var embyt = _emby.GetAllEpisodes().Include(x => x.Series).OrderByDescending(x => x.AddedAt).Take(10).ToHashSet(); + body = await BuildHtml(plexm, embym, plext, embyt, settings); } - } - - if (!test) - { - // Get the users to send it to - var users = await _userManager.GetUsersInRoleAsync(OmbiRoles.RecievesNewsletter); - if (!users.Any()) - { - return; - } - - foreach (var emails in settings.ExternalEmails) + else { - users.Add(new OmbiUser + body = await BuildHtml(plexContentMoviesToSend, embyContentMoviesToSend, plexEpisodesToSend, embyEpisodesToSend, settings); + if (body.IsNullOrEmpty()) { - UserName = emails, - Email = emails - }); + return; + } } - var emailTasks = new List(); - foreach (var user in users) + + if (!test) { // Get the users to send it to var users = await _userManager.GetUsersInRoleAsync(OmbiRoles.RecievesNewsletter); @@ -159,6 +148,7 @@ namespace Ombi.Schedule.Jobs.Ombi var emailTasks = new List(); foreach (var user in users) { + // Get the users to send it to if (user.Email.IsNullOrEmpty()) { continue; @@ -180,26 +170,26 @@ namespace Ombi.Schedule.Jobs.Ombi { recentlyAddedLog.Add(new RecentlyAddedLog { - AddedAt = DateTime.Now, - Type = RecentlyAddedType.Plex, - ContentType = ContentType.Parent, - ContentId = int.Parse(p.TheMovieDbId), - }); + AddedAt = DateTime.Now, + Type = RecentlyAddedType.Plex, + ContentType = ContentType.Parent, + ContentId = int.Parse(p.TheMovieDbId), + }); } foreach (var p in plexEpisodesToSend) - { - recentlyAddedLog.Add(new RecentlyAddedLog + { + recentlyAddedLog.Add(new RecentlyAddedLog { - AddedAt = DateTime.Now, - Type = RecentlyAddedType.Plex, - ContentType = ContentType.Episode, - ContentId = int.Parse(p.Series.TvDbId), - EpisodeNumber = p.EpisodeNumber, - SeasonNumber = p.SeasonNumber - }); - } + AddedAt = DateTime.Now, + Type = RecentlyAddedType.Plex, + ContentType = ContentType.Episode, + ContentId = int.Parse(p.Series.TvDbId), + EpisodeNumber = p.EpisodeNumber, + SeasonNumber = p.SeasonNumber + }); + } foreach (var e in embyContentMoviesToSend) { if (e.Type == EmbyMediaType.Movie) From 0ce9fb2df94e292bfa061a8d8ca33dedd6722b2c Mon Sep 17 00:00:00 2001 From: Jamie Rees Date: Sat, 21 Apr 2018 22:25:53 +0100 Subject: [PATCH 20/37] Fixed #2170 --- .../Repository/SettingsJsonRepository.cs | 1 + src/Ombi/Program.cs | 2 +- src/Ombi/Startup.cs | 28 ++++++++----------- 3 files changed, 14 insertions(+), 17 deletions(-) diff --git a/src/Ombi.Store/Repository/SettingsJsonRepository.cs b/src/Ombi.Store/Repository/SettingsJsonRepository.cs index 09bf61695..248413ccc 100644 --- a/src/Ombi.Store/Repository/SettingsJsonRepository.cs +++ b/src/Ombi.Store/Repository/SettingsJsonRepository.cs @@ -81,6 +81,7 @@ namespace Ombi.Store.Repository public void Update(GlobalSettings entity) { + Db.Update(entity); //_cache.Remove(GetName(entity.SettingsName)); Db.SaveChanges(); } diff --git a/src/Ombi/Program.cs b/src/Ombi/Program.cs index 9294852f9..7e9fa6f78 100644 --- a/src/Ombi/Program.cs +++ b/src/Ombi/Program.cs @@ -82,7 +82,7 @@ namespace Ombi ctx.SaveChanges(); } } - else if(!baseUrl.Equals(dbBaseUrl.Value)) + else if(baseUrl.HasValue() && !baseUrl.Equals(dbBaseUrl.Value)) { dbBaseUrl.Value = baseUrl; ctx.SaveChanges(); diff --git a/src/Ombi/Startup.cs b/src/Ombi/Startup.cs index 7fc0522ed..55a49fa92 100644 --- a/src/Ombi/Startup.cs +++ b/src/Ombi/Startup.cs @@ -173,26 +173,22 @@ namespace Ombi settings.ApiKey = Guid.NewGuid().ToString("N"); ombiService.SaveSettings(settings); } - if (settings.BaseUrl.HasValue()) - { - app.UsePathBase(settings.BaseUrl); - } - else + + // Check if it's in the startup args + var appConfig = serviceProvider.GetService(); + var baseUrl = appConfig.Get(ConfigurationTypes.BaseUrl).Result; + if (baseUrl != null) { - // Check if it's in the startup args - var appConfig = serviceProvider.GetService(); - var baseUrl = appConfig.Get(ConfigurationTypes.BaseUrl).Result; - if (baseUrl != null) + if (baseUrl.Value.HasValue()) { - if (baseUrl.Value.HasValue()) - { - settings.BaseUrl = baseUrl.Value; - ombiService.SaveSettings(settings); - - app.UsePathBase(settings.BaseUrl); - } + settings.BaseUrl = baseUrl.Value; + ombiService.SaveSettings(settings); } } + if (settings.BaseUrl.HasValue()) + { + app.UsePathBase(settings.BaseUrl); + } app.UseHangfireServer(new BackgroundJobServerOptions { WorkerCount = 1, ServerTimeout = TimeSpan.FromDays(1), ShutdownTimeout = TimeSpan.FromDays(1)}); app.UseHangfireDashboard(settings.BaseUrl.HasValue() ? $"{settings.BaseUrl}/hangfire" : "/hangfire", From a0dbd1c442b5e32ccab40335e378988bd57fce89 Mon Sep 17 00:00:00 2001 From: Jamie Rees Date: Sat, 21 Apr 2018 22:47:01 +0100 Subject: [PATCH 21/37] Fixed #2151 --- src/Ombi.Core/Engine/Interfaces/BaseEngine.cs | 15 +-------------- src/Ombi.Store/Context/OmbiContext.cs | 16 ++++++++++++++++ src/Ombi.Store/Entities/User.cs | 1 + src/Ombi/Controllers/IdentityController.cs | 2 +- src/Ombi/Properties/launchSettings.json | 1 + 5 files changed, 20 insertions(+), 15 deletions(-) diff --git a/src/Ombi.Core/Engine/Interfaces/BaseEngine.cs b/src/Ombi.Core/Engine/Interfaces/BaseEngine.cs index e608ffebb..26bc5969c 100644 --- a/src/Ombi.Core/Engine/Interfaces/BaseEngine.cs +++ b/src/Ombi.Core/Engine/Interfaces/BaseEngine.cs @@ -32,14 +32,7 @@ namespace Ombi.Core.Engine.Interfaces private OmbiUser _user; protected async Task GetUser() { - if (IsApiUser) - { - return new OmbiUser - { - UserName = Username, - }; - } - return _user ?? (_user = await UserManager.Users.FirstOrDefaultAsync(x => x.UserName == Username)); + return _user ?? (_user = await UserManager.Users.FirstOrDefaultAsync(x => x.UserName.Equals(Username, StringComparison.CurrentCultureIgnoreCase))); } protected async Task UserAlias() @@ -49,10 +42,6 @@ namespace Ombi.Core.Engine.Interfaces protected async Task IsInRole(string roleName) { - if (IsApiUser && roleName != OmbiRoles.Disabled) - { - return true; - } return await UserManager.IsInRoleAsync(await GetUser(), roleName); } @@ -72,7 +61,5 @@ namespace Ombi.Core.Engine.Interfaces var ruleResults = await Rules.StartSpecificRules(model, rule); return ruleResults; } - - private bool IsApiUser => Username.Equals("Api", StringComparison.CurrentCultureIgnoreCase); } } \ No newline at end of file diff --git a/src/Ombi.Store/Context/OmbiContext.cs b/src/Ombi.Store/Context/OmbiContext.cs index e4c9be516..d1963e765 100644 --- a/src/Ombi.Store/Context/OmbiContext.cs +++ b/src/Ombi.Store/Context/OmbiContext.cs @@ -123,7 +123,23 @@ namespace Ombi.Store.Context { NormalizedName = OmbiRoles.RecievesNewsletter.ToUpper() }); + SaveChanges(); } + + // Make sure we have the API User + var apiUserExists = Users.Any(x => x.UserName.Equals("Api", StringComparison.CurrentCultureIgnoreCase)); + if (!apiUserExists) + { + Users.Add(new OmbiUser + { + UserName = "Api", + UserType = UserType.SystemUser, + NormalizedUserName = "API", + + }); + SaveChanges(); + } + //Check if templates exist var templates = NotificationTemplates.ToList(); diff --git a/src/Ombi.Store/Entities/User.cs b/src/Ombi.Store/Entities/User.cs index d9a3207b2..68d1dbe00 100644 --- a/src/Ombi.Store/Entities/User.cs +++ b/src/Ombi.Store/Entities/User.cs @@ -29,6 +29,7 @@ namespace Ombi.Store.Entities { public enum UserType { + SystemUser = 0, LocalUser = 1, PlexUser = 2, EmbyUser = 3, diff --git a/src/Ombi/Controllers/IdentityController.cs b/src/Ombi/Controllers/IdentityController.cs index 5db5f2168..fe63226e6 100644 --- a/src/Ombi/Controllers/IdentityController.cs +++ b/src/Ombi/Controllers/IdentityController.cs @@ -213,7 +213,7 @@ namespace Ombi.Controllers [PowerUser] public async Task> GetAllUsers() { - var users = await UserManager.Users + var users = await UserManager.Users.Where(x => x.UserType != UserType.LocalUser) .ToListAsync(); var model = new List(); diff --git a/src/Ombi/Properties/launchSettings.json b/src/Ombi/Properties/launchSettings.json index ec5deb319..33794436c 100644 --- a/src/Ombi/Properties/launchSettings.json +++ b/src/Ombi/Properties/launchSettings.json @@ -10,6 +10,7 @@ "profiles": { "IIS Express": { "commandName": "IISExpress", + "commandLineArgs": "-baseurl /testing", "launchBrowser": true, "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" From 8927c5fa9cc312bfbd84882925a117fd348deec2 Mon Sep 17 00:00:00 2001 From: Jamie Rees Date: Sat, 21 Apr 2018 22:51:56 +0100 Subject: [PATCH 22/37] Fixed #2164 --- src/Ombi.Api.Pushover/PushoverApi.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Ombi.Api.Pushover/PushoverApi.cs b/src/Ombi.Api.Pushover/PushoverApi.cs index 96f4d2e95..4ac08c0f5 100644 --- a/src/Ombi.Api.Pushover/PushoverApi.cs +++ b/src/Ombi.Api.Pushover/PushoverApi.cs @@ -1,4 +1,5 @@ using System; +using System.Net; using System.Net.Http; using System.Threading.Tasks; using Ombi.Api.Pushover.Models; @@ -17,7 +18,7 @@ namespace Ombi.Api.Pushover public async Task PushAsync(string accessToken, string message, string userToken) { - var request = new Request($"messages.json?token={accessToken}&user={userToken}&message={message}", PushoverEndpoint, HttpMethod.Post); + var request = new Request($"messages.json?token={accessToken}&user={userToken}&message={WebUtility.HtmlEncode(message)}", PushoverEndpoint, HttpMethod.Post); var result = await _api.Request(request); return result; From 0c6083dd9af91275b0ab88050cd5420b89dffc1c Mon Sep 17 00:00:00 2001 From: Anojh Date: Sat, 21 Apr 2018 22:26:25 -0700 Subject: [PATCH 23/37] detect if baseurl is already set, and reset the link --- src/Ombi/Views/Shared/_Layout.cshtml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/Ombi/Views/Shared/_Layout.cshtml b/src/Ombi/Views/Shared/_Layout.cshtml index 1f2610e1c..8433f7b4e 100644 --- a/src/Ombi/Views/Shared/_Layout.cshtml +++ b/src/Ombi/Views/Shared/_Layout.cshtml @@ -88,10 +88,20 @@ O:::::::OOO:::::::Om::::m m::::m m::::mb:::::bbbbbb::::::bi::::::i if (customization.HasPresetTheme) { if (!string.IsNullOrEmpty(baseUrl)) + { + if (!customization.PresetThemeContent.Contains("/" + baseUrl)) { var index = customization.PresetThemeContent.IndexOf("/api/"); customization.PresetThemeContent = customization.PresetThemeContent.Insert(index, "/" + baseUrl); + } else + { + var startIndex = customization.PresetThemeContent.IndexOf("href="); + var index = customization.PresetThemeContent.IndexOf("/api/"); + customization.PresetThemeContent = customization.PresetThemeContent.Remove(startIndex+6, (index-(startIndex+6))); + index = customization.PresetThemeContent.IndexOf("/api/"); + customization.PresetThemeContent = customization.PresetThemeContent.Insert(index, "/" + baseUrl); } + } + } if (!string.IsNullOrEmpty(customization.CustomCssLink)) { - + } } From b8c64dff2d1e014e6feb872fb9b4f4b7876f0f3b Mon Sep 17 00:00:00 2001 From: Jamie Rees Date: Mon, 23 Apr 2018 16:27:33 +0100 Subject: [PATCH 26/37] Update Hangfire, Newtonsoft and Swagger --- src/Ombi.Api.Mattermost/Ombi.Api.Mattermost.csproj | 2 +- src/Ombi.Api/Ombi.Api.csproj | 2 +- src/Ombi.Core/Ombi.Core.csproj | 4 ++-- src/Ombi.Helpers/Ombi.Helpers.csproj | 2 +- src/Ombi.Schedule/Ombi.Schedule.csproj | 6 +++--- src/Ombi.Settings/Ombi.Settings.csproj | 2 +- src/Ombi.Store/Ombi.Store.csproj | 2 +- src/Ombi/Ombi.csproj | 6 +++--- src/Ombi/Startup.cs | 1 - 9 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/Ombi.Api.Mattermost/Ombi.Api.Mattermost.csproj b/src/Ombi.Api.Mattermost/Ombi.Api.Mattermost.csproj index ca15bae05..83318be7b 100644 --- a/src/Ombi.Api.Mattermost/Ombi.Api.Mattermost.csproj +++ b/src/Ombi.Api.Mattermost/Ombi.Api.Mattermost.csproj @@ -9,7 +9,7 @@ - + diff --git a/src/Ombi.Api/Ombi.Api.csproj b/src/Ombi.Api/Ombi.Api.csproj index 325f316b8..8379691c5 100644 --- a/src/Ombi.Api/Ombi.Api.csproj +++ b/src/Ombi.Api/Ombi.Api.csproj @@ -10,7 +10,7 @@ - + diff --git a/src/Ombi.Core/Ombi.Core.csproj b/src/Ombi.Core/Ombi.Core.csproj index c2af094bb..de6f5c8fa 100644 --- a/src/Ombi.Core/Ombi.Core.csproj +++ b/src/Ombi.Core/Ombi.Core.csproj @@ -11,12 +11,12 @@ - + - + diff --git a/src/Ombi.Helpers/Ombi.Helpers.csproj b/src/Ombi.Helpers/Ombi.Helpers.csproj index 00cd8d5e9..9bb599c2a 100644 --- a/src/Ombi.Helpers/Ombi.Helpers.csproj +++ b/src/Ombi.Helpers/Ombi.Helpers.csproj @@ -12,7 +12,7 @@ - + diff --git a/src/Ombi.Schedule/Ombi.Schedule.csproj b/src/Ombi.Schedule/Ombi.Schedule.csproj index 5088bc9f8..47e599e80 100644 --- a/src/Ombi.Schedule/Ombi.Schedule.csproj +++ b/src/Ombi.Schedule/Ombi.Schedule.csproj @@ -10,9 +10,9 @@ - - - + + + diff --git a/src/Ombi.Settings/Ombi.Settings.csproj b/src/Ombi.Settings/Ombi.Settings.csproj index 5a99cc830..3cb56cb07 100644 --- a/src/Ombi.Settings/Ombi.Settings.csproj +++ b/src/Ombi.Settings/Ombi.Settings.csproj @@ -10,7 +10,7 @@ - + diff --git a/src/Ombi.Store/Ombi.Store.csproj b/src/Ombi.Store/Ombi.Store.csproj index 901882669..522a96957 100644 --- a/src/Ombi.Store/Ombi.Store.csproj +++ b/src/Ombi.Store/Ombi.Store.csproj @@ -14,7 +14,7 @@ - + diff --git a/src/Ombi/Ombi.csproj b/src/Ombi/Ombi.csproj index f5e523db7..7f3cd8055 100644 --- a/src/Ombi/Ombi.csproj +++ b/src/Ombi/Ombi.csproj @@ -61,8 +61,8 @@ - - + + @@ -78,7 +78,7 @@ - + diff --git a/src/Ombi/Startup.cs b/src/Ombi/Startup.cs index 55a49fa92..be78c5b11 100644 --- a/src/Ombi/Startup.cs +++ b/src/Ombi/Startup.cs @@ -219,7 +219,6 @@ namespace Ombi app.UseSwaggerUI(c => { c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1"); - c.ShowJsonEditor(); }); app.UseMvc(routes => From f24b0f20eb35ff6a1899dad09b7c564bb58c03ec Mon Sep 17 00:00:00 2001 From: Jamie Rees Date: Mon, 23 Apr 2018 16:30:02 +0100 Subject: [PATCH 27/37] Updated Mailkit dependancy --- src/Ombi.Notifications/Ombi.Notifications.csproj | 2 +- src/Ombi/Ombi.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Ombi.Notifications/Ombi.Notifications.csproj b/src/Ombi.Notifications/Ombi.Notifications.csproj index ccdb29e65..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/Ombi.csproj b/src/Ombi/Ombi.csproj index 7f3cd8055..89e432580 100644 --- a/src/Ombi/Ombi.csproj +++ b/src/Ombi/Ombi.csproj @@ -67,7 +67,7 @@ - + From 6806b97c1b19158d697196248a1701cf80dd384c Mon Sep 17 00:00:00 2001 From: Jamie Date: Mon, 23 Apr 2018 21:15:03 +0100 Subject: [PATCH 28/37] Fixed the bug where only showing API User #2187 --- src/Ombi/Controllers/IdentityController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ombi/Controllers/IdentityController.cs b/src/Ombi/Controllers/IdentityController.cs index fe63226e6..8a9c76a34 100644 --- a/src/Ombi/Controllers/IdentityController.cs +++ b/src/Ombi/Controllers/IdentityController.cs @@ -213,7 +213,7 @@ namespace Ombi.Controllers [PowerUser] public async Task> GetAllUsers() { - var users = await UserManager.Users.Where(x => x.UserType != UserType.LocalUser) + var users = await UserManager.Users.Where(x => x.UserType != UserType.SystemUser) .ToListAsync(); var model = new List(); From f62e97bb32818c3b454ca6f347053e82e1029866 Mon Sep 17 00:00:00 2001 From: Jamie Date: Tue, 24 Apr 2018 08:09:11 +0100 Subject: [PATCH 29/37] Fixed bug #2188 #2134 --- src/Ombi.Helpers/StringHelper.cs | 10 ++++++++++ src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs | 18 +++++++++--------- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/src/Ombi.Helpers/StringHelper.cs b/src/Ombi.Helpers/StringHelper.cs index daa8cabe7..aba120c65 100644 --- a/src/Ombi.Helpers/StringHelper.cs +++ b/src/Ombi.Helpers/StringHelper.cs @@ -65,5 +65,15 @@ namespace Ombi.Helpers securePassword.MakeReadOnly(); return securePassword; } + + public static int IntParseLinq(string stringIn) + { + if (int.TryParse(stringIn, out var result)) + { + return result; + } + + return -1; + } } } \ 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 79731637b..8b246e97e 100644 --- a/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs +++ b/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs @@ -98,8 +98,8 @@ namespace Ombi.Schedule.Jobs.Ombi addedLog.Where(x => x.Type == RecentlyAddedType.Emby && x.ContentType == ContentType.Episode); // Filter out the ones that we haven't sent yet - var plexContentMoviesToSend = plexContent.Where(x => x.Type == PlexMediaTypeEntity.Movie && !addedPlexMovieLogIds.Contains(int.Parse(x.TheMovieDbId))); - var embyContentMoviesToSend = embyContent.Where(x => x.Type == EmbyMediaType.Movie && !addedEmbyMoviesLogIds.Contains(int.Parse(x.TheMovieDbId))); + var plexContentMoviesToSend = plexContent.Where(x => x.Type == PlexMediaTypeEntity.Movie && !addedPlexMovieLogIds.Contains(StringHelper.IntParseLinq(x.TheMovieDbId))); + var embyContentMoviesToSend = embyContent.Where(x => x.Type == EmbyMediaType.Movie && !addedEmbyMoviesLogIds.Contains(StringHelper.IntParseLinq(x.TheMovieDbId))); _log.LogInformation("Plex Movies to send: {0}", plexContentMoviesToSend.Count()); _log.LogInformation("Emby Movies to send: {0}", embyContentMoviesToSend.Count()); @@ -173,7 +173,7 @@ namespace Ombi.Schedule.Jobs.Ombi AddedAt = DateTime.Now, Type = RecentlyAddedType.Plex, ContentType = ContentType.Parent, - ContentId = int.Parse(p.TheMovieDbId), + ContentId = StringHelper.IntParseLinq(p.TheMovieDbId), }); } @@ -185,7 +185,7 @@ namespace Ombi.Schedule.Jobs.Ombi AddedAt = DateTime.Now, Type = RecentlyAddedType.Plex, ContentType = ContentType.Episode, - ContentId = int.Parse(p.Series.TvDbId), + ContentId = StringHelper.IntParseLinq(p.Series.TvDbId), EpisodeNumber = p.EpisodeNumber, SeasonNumber = p.SeasonNumber }); @@ -199,7 +199,7 @@ namespace Ombi.Schedule.Jobs.Ombi AddedAt = DateTime.Now, Type = RecentlyAddedType.Emby, ContentType = ContentType.Parent, - ContentId = int.Parse(e.TheMovieDbId), + ContentId = StringHelper.IntParseLinq(e.TheMovieDbId), }); } } @@ -211,7 +211,7 @@ namespace Ombi.Schedule.Jobs.Ombi AddedAt = DateTime.Now, Type = RecentlyAddedType.Emby, ContentType = ContentType.Episode, - ContentId = int.Parse(p.Series.TvDbId), + ContentId = StringHelper.IntParseLinq(p.Series.TvDbId), EpisodeNumber = p.EpisodeNumber, SeasonNumber = p.SeasonNumber }); @@ -259,7 +259,7 @@ namespace Ombi.Schedule.Jobs.Ombi var itemsToReturn = new HashSet(); foreach (var ep in source) { - var tvDbId = int.Parse(ep.Series.TvDbId); + var tvDbId = StringHelper.IntParseLinq(ep.Series.TvDbId); if (recentlyAdded.Any(x => x.ContentId == tvDbId && x.EpisodeNumber == ep.EpisodeNumber && x.SeasonNumber == ep.SeasonNumber)) { continue; @@ -276,7 +276,7 @@ namespace Ombi.Schedule.Jobs.Ombi var itemsToReturn = new HashSet(); foreach (var ep in source) { - var tvDbId = int.Parse(ep.Series.TvDbId); + var tvDbId = StringHelper.IntParseLinq(ep.Series.TvDbId); if (recentlyAdded.Any(x => x.ContentId == tvDbId && x.EpisodeNumber == ep.EpisodeNumber && x.SeasonNumber == ep.SeasonNumber)) { continue; @@ -374,7 +374,7 @@ namespace Ombi.Schedule.Jobs.Ombi theMovieDbId = result.id.ToString(); } - var info = await _movieApi.GetMovieInformationWithExtraInfo(int.Parse(theMovieDbId)); + var info = await _movieApi.GetMovieInformationWithExtraInfo(StringHelper.IntParseLinq(theMovieDbId)); if (info == null) { continue; From e12146c98666d5de0471b106e918ff8ea7568468 Mon Sep 17 00:00:00 2001 From: Jamie Date: Tue, 24 Apr 2018 08:32:31 +0100 Subject: [PATCH 30/37] More improvements to the Plex OAuth, Added the ability to turn it off if needed --- .../Authentication/PlexOAuthManager.cs | 19 ++++++++++++++----- src/Ombi.Core/IPlexOAuthManager.cs | 2 +- src/Ombi.Helpers/UriHelper.cs | 1 + .../Settings/Models/External/PlexSettings.cs | 1 + .../ClientApp/app/interfaces/ISettings.ts | 1 + .../ClientApp/app/login/login.component.html | 9 +++++---- .../ClientApp/app/login/login.component.ts | 8 +++++++- .../app/settings/plex/plex.component.html | 6 ++++++ src/Ombi/Controllers/SettingsController.cs | 2 +- src/Ombi/Controllers/TokenController.cs | 5 +++-- 10 files changed, 40 insertions(+), 14 deletions(-) diff --git a/src/Ombi.Core/Authentication/PlexOAuthManager.cs b/src/Ombi.Core/Authentication/PlexOAuthManager.cs index d3bab0a05..f3a3e4d01 100644 --- a/src/Ombi.Core/Authentication/PlexOAuthManager.cs +++ b/src/Ombi.Core/Authentication/PlexOAuthManager.cs @@ -55,15 +55,24 @@ namespace Ombi.Core.Authentication return await _api.GetAccount(accessToken); } - public async Task GetOAuthUrl(int pinId, string code) + public async Task GetOAuthUrl(int pinId, string code, string websiteAddress = null) { - var settings = await _customizationSettingsService.GetSettingsAsync(); - if (settings.ApplicationUrl.IsNullOrEmpty()) + Uri url; + if (websiteAddress.IsNullOrEmpty()) { - return null; + var settings = await _customizationSettingsService.GetSettingsAsync(); + if (settings.ApplicationUrl.IsNullOrEmpty()) + { + return null; + } + + url = _api.GetOAuthUrl(pinId, code, settings.ApplicationUrl, false); + } + else + { + url = _api.GetOAuthUrl(pinId, code, websiteAddress, false); } - var url = _api.GetOAuthUrl(pinId, code, settings.ApplicationUrl, false); return url; } diff --git a/src/Ombi.Core/IPlexOAuthManager.cs b/src/Ombi.Core/IPlexOAuthManager.cs index 142d4162a..9c4f0582e 100644 --- a/src/Ombi.Core/IPlexOAuthManager.cs +++ b/src/Ombi.Core/IPlexOAuthManager.cs @@ -9,7 +9,7 @@ namespace Ombi.Core.Authentication { Task GetAccessTokenFromPin(int pinId); Task RequestPin(); - Task GetOAuthUrl(int pinId, string code); + Task GetOAuthUrl(int pinId, string code, string websiteAddress = null); Uri GetWizardOAuthUrl(int pinId, string code, string websiteAddress); Task GetAccount(string accessToken); } diff --git a/src/Ombi.Helpers/UriHelper.cs b/src/Ombi.Helpers/UriHelper.cs index 83cd27e9d..6ec8047ae 100644 --- a/src/Ombi.Helpers/UriHelper.cs +++ b/src/Ombi.Helpers/UriHelper.cs @@ -112,6 +112,7 @@ namespace Ombi.Helpers return uriBuilder.Uri; } + } public class ApplicationSettingsException : Exception diff --git a/src/Ombi.Settings/Settings/Models/External/PlexSettings.cs b/src/Ombi.Settings/Settings/Models/External/PlexSettings.cs index 3faba3e42..a77b54a87 100644 --- a/src/Ombi.Settings/Settings/Models/External/PlexSettings.cs +++ b/src/Ombi.Settings/Settings/Models/External/PlexSettings.cs @@ -6,6 +6,7 @@ namespace Ombi.Core.Settings.Models.External public sealed class PlexSettings : Ombi.Settings.Settings.Models.Settings { public bool Enable { get; set; } + public bool EnableOAuth { get; set; } public List Servers { get; set; } } diff --git a/src/Ombi/ClientApp/app/interfaces/ISettings.ts b/src/Ombi/ClientApp/app/interfaces/ISettings.ts index d67ebc698..234e0aa5b 100644 --- a/src/Ombi/ClientApp/app/interfaces/ISettings.ts +++ b/src/Ombi/ClientApp/app/interfaces/ISettings.ts @@ -43,6 +43,7 @@ export interface IEmbyServer extends IExternalSettings { export interface IPlexSettings extends ISettings { enable: boolean; + enableOAuth: boolean; servers: IPlexServer[]; } diff --git a/src/Ombi/ClientApp/app/login/login.component.html b/src/Ombi/ClientApp/app/login/login.component.html index a50040fab..40a5ef5a0 100644 --- a/src/Ombi/ClientApp/app/login/login.component.html +++ b/src/Ombi/ClientApp/app/login/login.component.html @@ -17,7 +17,7 @@ include the remember me checkbox

-
+
-
+
+
+ + +
+
diff --git a/src/Ombi/Controllers/SettingsController.cs b/src/Ombi/Controllers/SettingsController.cs index 0a515d9cb..1bbb5af49 100644 --- a/src/Ombi/Controllers/SettingsController.cs +++ b/src/Ombi/Controllers/SettingsController.cs @@ -154,7 +154,7 @@ namespace Ombi.Controllers var s = await Get(); - return s.Enable; + return s.Enable && s.EnableOAuth; } /// diff --git a/src/Ombi/Controllers/TokenController.cs b/src/Ombi/Controllers/TokenController.cs index b337b3f51..a488ba172 100644 --- a/src/Ombi/Controllers/TokenController.cs +++ b/src/Ombi/Controllers/TokenController.cs @@ -23,7 +23,7 @@ namespace Ombi.Controllers { [ApiV1] [Produces("application/json")] - public class TokenController + public class TokenController : Controller { public TokenController(OmbiUserManager um, IOptions ta, IAuditRepository audit, ITokenRepository token, IPlexOAuthManager oAuthManager) @@ -83,8 +83,9 @@ namespace Ombi.Controllers // We need a PIN first var pin = await _plexOAuthManager.RequestPin(); + var websiteAddress = $"{this.Request.Scheme}://{this.Request.Host}{this.Request.PathBase}"; //https://app.plex.tv/auth#?forwardUrl=http://google.com/&clientID=Ombi-Test&context%5Bdevice%5D%5Bproduct%5D=Ombi%20SSO&pinID=798798&code=4lgfd - var url = await _plexOAuthManager.GetOAuthUrl(pin.id, pin.code); + var url = await _plexOAuthManager.GetOAuthUrl(pin.id, pin.code, websiteAddress); if (url == null) { return new JsonResult(new From c244faca7d28a138ce2287e27dd9ddfc33efd22f Mon Sep 17 00:00:00 2001 From: Jamie Date: Tue, 24 Apr 2018 08:34:48 +0100 Subject: [PATCH 31/37] !wip changelog --- CHANGELOG.md | 3655 +------------------------------------------------- 1 file changed, 45 insertions(+), 3610 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ecb1a790d..49d85aca7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,51 @@ ### **New Features** +- Updated Mailkit dependancy. [Jamie Rees] + +- Update Hangfire, Newtonsoft and Swagger. [Jamie Rees] + +- Added View on Emby Button (#2173) [Anojh Thayaparan] + +- Added background property to tvrequests API (#2172) [Anojh Thayaparan] + +### **Fixes** + +- More improvements to the Plex OAuth, Added the ability to turn it off if needed. [Jamie] + +- Fixed bug #2188 #2134. [Jamie] + +- Fixed the bug where only showing API User #2187. [Jamie] + +- Detect if baseurl is already set, and reset the link. [Anojh] + +- Fixed #2164. [Jamie Rees] + +- Fixed #2151. [Jamie Rees] + +- Fixed #2170. [Jamie Rees] + +- Fixed the newsletter not sending #2134. [Jamie Rees] + +- Fix baseurl breaking themes. [Anojh] + +- Inject base url if set before theme file url, see issue #1795. [Anojh] + +- Sign In rather than Login/Continue. [Avi] + +- Fixed #2179. [Jamie Rees] + +- Fixed #2169. [Jamie Rees] + +- Knocking out LC requirements in issue #2124 (#2125) [Anojh Thayaparan] + +- Inject base url if set before theme file url, see issue #1795 (#2148) [Anojh Thayaparan] + + +## v3.0.3185 (2018-04-16) + +### **New Features** + - Added a new Job. Plex Recently Added, this is a slimmed down version of the Plex Sync job, this will just scan the recently added list and not the whole library. I'd reccomend running this very regulary and the full scan not as regular. [Jamie] ### **Fixes** @@ -819,3613 +864,3 @@ - Switch to use a single HTTPClient rather than a new one every request !dev. [tidusjar] -- Fix non-admin rights (#1820) [Rob Gökemeijer] - -- Fix duplicated "Requests" element ID on new Issues link (#1817) [Shoghi Cervantes] - -- Add the Issue Reporting functionality (#1811) [Jamie] - -- Removed the forum. [tidusjar] - -- #1659 Made the option to ignore notifcations for auto approve. [Jamie] - -- New Crowdin translations (#1806) [Jamie] - -- Fixed a launch issue. [Jamie] - -- Allow users to login without a password. [Jamie] - -- Fixed the emby notifications not being sent. [Jamie] - -- #1802 and other small fixes. [tidusjar] - -- So... This sickrage thing should work now. [tidusjar] - -- Fixed emby connect login issue. [tidusjar] - -- Stop making unnecessary calls to the update service. [Jamie] - -- Fixed a bug where it blocked users with 0 limits. [Jamie] - -- Done #1788. [tidusjar] - -- More logging. [Jamie] - -- Fixed #1738. [Jamie] - -- Fixed build. [Jamie] - -- Fixed the issue where notifications were not sendind unless we restarted #1732. [tidusjar] - -- Fixed an issue with a trailing space in the subdir. [tidusjar] - -- Fixed #1774. [Jamie] - -- #1773. [Jamie] - -- Roll back rxjs (#1778) [bazhip] - -- Fixed build. [Jamie] - -- Fixed #1763. [Jamie] - -- Fix "content length error" on preview gif (#1768) [OoGuru] - -- New preview gif for Ombi V3 README (#1767) [OoGuru] - -- Remove debug code. [tidusjar] - -- Fix #1762. [tidusjar] - -- Fixed the preset themes not loading. [tidusjar] - -- Fixed #1760 and improvements on the auto updater. We may now support windows services... #1460. [Jamie] - -- Fixed #1754. [Jamie] - -- Hide the subject when it's not being used. [Jamie] - -- Error handling #1749. [Jamie] - -- New Crowdin translations (#1741) [Jamie] - -- #1732 #1722 #1711. [Jamie] - -- Fixed an issue with switching the preset themes. [Jamie] - -- Fixed #1743. [Jamie] - -- Fixed #1742. [tidusjar] - -- Fix #1742. [tidusjar] - -- Fixed landing page. [Jamie] - -- Fixed. [Jamie] - -- Translated the Requests page and fixed #1740. [Jamie] - -- Fix crash. [Jamie] - -- Sickrage done. Ish... So i've written all the code by looking at the API. the key there is i've looked at the api. I have not tested anything so expect this to fail. [Jamie] - -- SickRage settings UI. [Jamie] - -- Fixed #1721. [tidusjar] - -- Fixed the preset themes issue. [tidusjar] - -- New Crowdin translations (#1654) [Jamie] - -- Fix build. [Jamie] - -- #1460. [Jamie] - -- Fixed tests. [Jamie] - -- Return css as MIME text/css. [Jamie] - -- More added for the preset themes. [Jamie] - -- Moved around the custom styles. [Jamie] - -- More renames. [Jamie] - -- Renames. [Jamie] - -- Load the first 100 requests. [Jamie] - -- Reduce the memory consumption #1720. [Jamie] - -- Moved the schedules jobs into it's own database, see if it helps with the db locking #1720. [Jamie] - -- Fixed #1712. [tidusjar] - -- Potential fix for #1702. [tidusjar] - -- Fixed #1708. [tidusjar] - -- Fixed #1677. [tidusjar] - -- Fixed build. [tidusjar] - -- Potential fix for the DB locking issue #1720. [tidusjar] - -- #1698. [Jamie] - -- Fixed #1705. [tidusjar] - -- Fixed #1703. [tidusjar] - -- Finished adding preset themes. [Jamie] - -- Fixed #17000. [Jamie] - -- Remove the themes because waiting for a merge from lerams project. [Jamie] - -- Finsihed adding preset themes. [Jamie] - -- Fixed #1677. [Jamie] - -- Temp fix for #1683. [Jamie] - -- Fixed #1685. [Jamie] - -- Lossless Compression of images saves 83 KB (#1676) [Fish2] - -- Fixed the availability checker. [tidusjar] - -- Fixed build. [tidusjar] - -- Push out missing migration. [tidusjar] - -- Potential fix for #1674. [tidusjar] - -- Fixed an issue with the caching. [tidusjar] - -- Fixed telegram #1667. [tidusjar] - -- Fixed #1663. [tidusjar] - -- Should fix #1663. [tidusjar] - -- Stop logged in users going to the login page. [Jamie] - -- Fixed it not updating. Styles should be good now. [Jamie] - -- Re did some of the styling on the movie search page, let me know your thoughts. [Jamie] - -- Fixed #1657. [Jamie] - -- Fixed #1655. [Jamie] - -- Removed authentication resul. [Jamie] - -- New Crowdin translations (#1651) [Jamie] - -- New Crowdin translations (#1648) [Jamie] - -- New Crowdin translations (#1638) [Jamie] - -- Fixed #1644. [Jamie] - -- Moar logs #1643. [tidusjar] - -- Fixed #1640. [tidusjar] - -- Fixed the null ref exception #1460. [tidusjar] - -- Fixed landing page. [TidusJar] - -- Fixed #1641. [TidusJar] - -- Fixed #1641. [TidusJar] - -- New Crowdin translations (#1635) [Jamie] - -- Fixed #1631 and improved translation support Included startup args for the auto updater #1460 Mark TV requests as available #1632. [tidusjar] - -- Remove 32bit. [Jamie] - -- More 32bit support. [Jamie] - -- We now show "Available" for tv shows that is fully available #1602. [tidusjar] - -- Fixed the issue where we have got an episode but not the related series. #1620. [tidusjar] - -- Fixed the dropdown not working on iOS in the settings #1615. [tidusjar] - -- Fixed sonarr not monitoring the latest season #1534. [tidusjar] - -- Fixed the issue with firefox #1544. [tidusjar] - -- Fixed discord #1623. [tidusjar] - -- Add browserstack thanks (#1627) [Matt Jeanes] - -- Fix the exception #1613. [Jamie] - -- Found where we potentially are setting a new poster path, looks like the entity was being modified and being set as Tracked by entity framework, so the next time we called SaveChangesAsync() it would save the new posterpath on the entity. [Jamie] - -- Small modifications. [Jamie] - -- Fixed #1622. [Jamie] - -- Various improvements to webpack/gulp/vscode support (#1617) [Matt Jeanes] - -- Episodes in requests are now in order #1597 (#1614) [masterhuck] - -- Fixed a null reference issue in the Plex Content Cacher. [Jamie.Rees] - -- Fixed #1610. [tidusjar] - -- Really fixed the build this time. [tidusjar] - -- Fixed build. [tidusjar] - -- Made the updater work again #1460. [tidusjar] - -- Adding logging into the auto updater and also added more logging around the create inital user for #1604. [tidusjar] - -- Fixed the issue where we did not check if they are already in sonarr when choosing certain options #1540. [tidusjar] - -- We can now delete tv child requests and the parent will get remove #1603. [tidusjar] - -- Finished the api changes requested #1601. [tidusjar] - -- Fixed the Hangfire server timeout issue #1605. [tidusjar] - -- Fixed notifications not sending #1594. [tidusjar] - -- Fixed #1583 you can now delete users. Fixed the issue where the requested by was not showing. Finally fixed the broken poster paths. [tidusjar] - -- Fixed the issue where movie requests were no longer being requested. [tidusjar] - -- Started adding some more unit tests #1596. [Jamie.Rees] - -- #1588 When we make changes to any requests that we can trigger a notification, always send it to all notification agents, even if the user wont recieve it. [Jamie.Rees] - -- Add a message when email notifications are not setup when requesting a password reset. #1590. [Jamie.Rees] - -- Removed text that we no longer need. [Jamie.Rees] - -- Fixed #1574. [Jamie.Rees] - -- #1460 looks like the permissions issue has been resolved. Just need to make sure the Ombi process is terminated. [Jamie.Rees] - -- Put back the old download code. [Jamie.Rees] - -- Test. [Jamie] - -- Build sln. [Jamie.Rees] - -- Order by the username #1581. [Jamie.Rees] - -- Remove sonarr episodes from the cache table. [Jamie.Rees] - -- Couchpotato finished. [tidusjar] - -- Disable run import button if no import options are selected. [tidusjar] - -- Fixed #1574. [tidusjar] - -- Fixed build. [tidusjar] - -- Fixes the issue with non windows systems unable to unzip the tarball #1460. [tidusjar] - -- Finished the couchpotato settings. [tidusjar] - -- Fixed build. [tidusjar] - -- Fixed #1570 #1571. [tidusjar] - -- Fixed #1547. [tidusjar] - -- Should fix #1538. [tidusjar] - -- Fixed #1553. [tidusjar] - -- Fixed #1546. [tidusjar] - -- Fixed #1543. [tidusjar] - -- Fixes an issue with Movie caching not working on develop branch of Radarr (#1567) [Jeffrey Peters] - -- This adds two fields to the Email Notifications settings page. It allows for the disabling of TLS/SSL as well as the ability to disable certificate validation when sending notification emails. (#1552) [Jeffrey Peters] - -- Fixed typo (#1551) [Codehhh] - -- Use Sqlite storage for Hangfire. [tidusjar] - -- Fixed the overrides #1539 also display it on screen now too. [tidusjar] - -- Fixed #1542 also added VSCode support. [tidusjar] - -- Fixed some cosmetic issues #865. [Jamie.Rees] - -- Fixed #1531. [Jamie.Rees] - -- Small fixes #865. [Jamie.Rees] - -- Some errors fixed and some ui improvements #865. [tidusjar] - -- Auto-scale large images down to container size (#1529) [Avi] - -- Fix logo on login page. (#1528) [Avi] - -- Another potential issue? :/ [tidusjar] - -- Real fix. [tidusjar] - -- #1513 Added storage path. [Jamie.Rees] - -- Fixed the discord issue relating to images #1513. [Jamie.Rees] - -- Fixed the issue sending movies to Radarr #1513 Fixed typo #1524. [Jamie.Rees] - -- Fixed logo on reset password pages fixed the run importer button on the user management settings. [Jamie.Rees] - -- Fixed crash/error #865. [tidusjar] - -- #1513 fixed the landing page and also the reverse proxy images. [tidusjar] - -- #1513 correctly set the child requests as approved. [tidusjar] - -- Fixed an issue that potentially causes as issue when siging into plex #865. [tidusjar] - -- Remove dev branch. [PotatoQuality] - -- Prepare readme for upcoming beta. [PotatoQuality] - -- #1513 partially fixed a bug. [tidusjar] - -- Fixed the exception. [tidusjar] - -- Fixed the application url not saving #1513. [tidusjar] - -- Fixed liniting. [tidusjar] - -- REVERSE PROXY BITCH! #1513. [tidusjar] - -- Fixed a bug where we were marking the wrong episodes as available #1513 #865. [Jamie.Rees] - -- Fixed an issue where we messed up the pages and routing. [Jamie.Rees] - -- Emby user importer is now therer! #1456. [tidusjar] - -- #1513 Added the update available icon. [tidusjar] - -- Fixed the issue of it showing as not requested when we find it in Radarr. Made the tv shows match a bit more to the movie requests Added the ability for plex and emby users to login Improved the welcome email, will only show for users that have not logged in Fixed discord notifications the about screen now checks if there is an update ready #1513. [tidusjar] - -- Support email addresses as usernames #1513. [Jamie.Rees] - -- Link to issue treath. [PotatoQuality] - -- Give correct feedback when testing email notifications #1513. [Jamie.Rees] - -- Report issue removed and the deny dropdown removed #1513. [Jamie.Rees] - -- #1513 removed the discord text when testing pushbullet. [Jamie.Rees] - -- Made a lot of changes around the notifcations to support the custom app name also started on the welcome email ##1456. [Jamie.Rees] - -- Fixed the bug where we were displaying shows where we do not have enough information to request #1513. [Jamie.Rees] - -- #1513 added the network to tv shows. [Jamie.Rees] - -- Fixed the whitespace issue #1513. [Jamie.Rees] - -- Fixed the swagger endpoint #865 #1513 Fixed the custom image issue on the login page Fixed the bug when clicking on the tab on the requests page it would switch to the wrong one Swagger is now back @ /swagger. [tidusjar] - -- Optimized images, Update old compressed image with a new lossless one. (#1514) [camjac251] - -- #1513 #865 Fixed the issue where we do not send the requests to Radarr/Sonarr when approving. [tidusjar] - -- #1506 #865 Fixed an issue with the test buttons not working correctly. [tidusjar] - -- #865 Added donation link. [tidusjar] - -- Fixed a bunch of issues on #1513. [tidusjar] - -- #1460 Added the Updater, it all seems to be working correctly. #865. [Jamie.Rees] - -- Removed percentage. [Jamie.Rees] - -- Fixed linter. [Jamie.Rees] - -- Fixed some bugs in the UI #865. [Jamie.Rees] - -- Improved the search buttons #865. [Jamie.Rees] - -- More logging #865. [Jamie.Rees] - -- Made build faster. [Jamie.Rees] - -- More logging. [Jamie.Rees] - -- Set debug level to Debug for now. [Jamie.Rees] - -- Add linting and indexes for interfaces/services (#1510) [Matt Jeanes] - -- Fixed the issue with the tv search not working #1463. [Jamie.Rees] - -- Latest practices... also probably broke some styles - sorry (#1508) [Matt Jeanes] - -- Build with the branch version. [tidusjar] - -- Build fix. [tidusjar] - -- Fixed build. [tidusjar] - -- Omgwtf so many changes. #865. [tidusjar] - -- Tests. [Jamie.Rees] - -- #1456 Started on the User Importer Also added the remember me button. [Jamie.Rees] - -- Made some UI changes, reworked the Emby and Plex screens to make them more user friendly and no so fugly. #865 Also made the login page placeholder text slightly lighter. [Jamie.Rees] - -- Cake skip verification build stuff #865. [Jamie.Rees] - -- Some fixes around the UI and managing requests #865. [tidusjar] - -- #1486. [Jamie.Rees] - -- #1486. [Jamie.Rees] - -- Upgraded to .net core 2.0 #1486. [Jamie.Rees] - -- #865 Finished the landing page, we now check the server's status. [Jamie.Rees] - -- Fixed build. [TidusJar] - -- Removed the telegram api. [Jamie.Rees] - -- Small changes on the updater #1460 #865. [Jamie.Rees] - -- Remove unused functions. [Dhruv Bhavsar] - -- Make Episode picker similar to Requests Child view. #1457 #1463. [Dhruv Bhavsar] - -- Fix merge conflict for TvRequests component. [Dhruv Bhavsar] - -- Upstream Changes... [Dhruv Bhavsar] - -- Clean up Requests page code by moving children request to old component, remove additional REST calls when merging and update component names to make more sense. [Dhruv Bhavsar] - -- Lots of different UI enhancements and fixes #865. [tidusjar] - -- Gitchangelog. [tidusjar] - -- Fixed the issue where we were using the wrong availability options. [tidusjar] - -- Fixed a bunch of bugs in Ombi #865. [tidusjar] - -- Build versioning. [Jamie.Rees] - -- #1460 The assembly versioning seems to work correctly now. [Jamie.Rees] - -- More build versioning changes #865. [tidusjar] - -- Fixed cake script. [Jamie.Rees] - -- WIP on the build versioning for the Updater #1460 #865. [Jamie.Rees] - -- Versioning. [Jamie.Rees] - -- Package versions. [Jamie.Rees] - -- #1460 #865 working on the auto updater. [Jamie.Rees] - -- Fixed build. [Jamie.Rees] - -- Small changes around the roles #865. [tidusjar] - -- Improvements to the UI and also finished the availability checker #865 #1464. [Jamie.Rees] - -- Availability Checker #1464 #865. [Jamie.Rees] - -- Fixed ##1492 and finished the episode searcher for #1464. [Jamie.Rees] - -- #1464. [tidusjar] - -- Reload the settings #1464 #865. [Jamie.Rees] - -- #1464 added the Plex episode cacher #865. [Jamie.Rees] - -- Fixed some issues around the tv requests area Added mattermost and telegram notifications #1459 #865 #1457. [tidusjar] - -- Fix global.json. [Dhruv Bhavsar] - -- Working UI for Requests. Approval/Deny does not work as it doesn't in your code either. [Dhruv Bhavsar] - -- Enable diagnostic on build #865. [Jamie.Rees] - -- Fixed the user token issue #865. [Jamie.Rees] - -- Some small refresh token work #865. [Jamie.Rees] - -- Initial TV Requests UI rebuild. [Dhruv Bhavsar] - -- Made a start on supporting multiple emby servers, the UI needs rework #865. [Jamie.Rees] - -- #865 #1459 Added the Sender From field for email notifcations. We can now have "Friendly Names" for email notifications. [Jamie.Rees] - -- Redirect to the landing page when enabled #1458 #865. [Jamie.Rees] - -- Removed IdentityServer, it was overkill #865. [Jamie.Rees] - -- Fixed another bug with identity. #865 I'm thinking about removing it. Causing more hassle than it's worth. [tidusjar] - -- #1460 #865. [tidusjar] - -- Delete appveyor_old.yml. [Jamie] - -- Fixed path. [Jamie.Rees] - -- Silent build level. [Jamie.Rees] - -- #1459 Forgot to get the Pushbullet agent to look up the pusbullet templates rather than the Discord ones. Updated the Gitchange log. [Jamie.Rees] - -- Made the placeholder color on the login page a bit lighter #865. [Jamie.Rees] - -- Landing and login page changes #865 #1485. [tidusjar] - -- #1458 #865 More work on landing. [Jamie.Rees] - -- Working on the landing page #1458 #865. [tidusjar] - -- A lot of clean up and added a new Image api #865. [Jamie.Rees] - -- Cleaned up the Logging API slightly #1465 #865. [Jamie.Rees] - -- Fixed the Identity Server discovery bug #1456 #865. [tidusjar] - -- Fixed the issue with the Identity Server running on a different port, we can now use -url #865. [Jamie.Rees] - -- Try again. [TidusJar] - -- Publish ubuntu 16.04. [Jamie.Rees] - -- Chnaged the updater job from Minutely to Hourly. [Jamie.Rees] - -- Some work around the Auto Updater and other small changes #1460 #865. [Jamie.Rees] - -- Missed a file. [tidusjar] - -- Fixed the swagger issue. [tidusjar] - -- RDP issues. [tidusjar] - -- Appveyor build rdp investigation. [tidusjar] - -- Working on the requests page #1457 #865. [tidusjar] - -- Made the password reset email style the same as other email notifications #1456 #865. [Jamie.Rees] - -- Fixed some bugs around the authentication #1456 #865. [Jamie.Rees] - -- Fixed build. [Jamie.Rees] - -- Fixed build #1456. [Jamie.Rees] - -- #1456 #865 Started on allowing Plex Users to sign in through the new authentication server. [Jamie.Rees] - -- Removed covalent. [Jamie.Rees] - -- #1456 Reset Password stuff #865. [Jamie.Rees] - -- Finished implimenting Identity with IdentityServer4. #865 #1456. [Jamie.Rees] - -- Moved over to using Identity Server with Asp.Net Core Identity #1456 #865. [Jamie.Rees] - -- Started on the requests rework #865. [Jamie.Rees] - -- Extended the Emby API. [Jamie.Rees] - -- Started reworking the usermanagement page #1456 #865. [tidusjar] - -- Lots of refactoring #865. [Jamie.Rees] - -- Created an individual user api endpoint so we can make the user management pages better #865. [TidusJar] - -- Lot's of refactoring. [Jamie.Rees] - -- #1462 #865 Had to refactor how we use notificaitons. So we now have more notification fields about the request. [Jamie.Rees] - -- Looks like Sonarr is finished and works. A lot simplier this time around. #865. [tidusjar] - -- More work on the Sonarr Api Integration #865. [tidusjar] - -- Started on sonarr #865. [tidusjar] - -- Small changes #865. [tidusjar] - -- Damn son. So many changes... Fixed alot of stuff around tv episodes with the new DB model #865. [tidusjar] - -- Fixed the TV Requests issue #865. [Jamie.Rees] - -- Fixed a load of bugs need to figure out what is wrong with tv requests #865. [tidusjar] - -- #865 rework the backend data. Actually use real models rather than a JSON store. [Jamie.Rees] - -- Fixed the build issue #865. [tidusjar] - -- Allow us to use Emby as a media server. [tidusjar] - -- More Update #865. [Jamie.Rees] - -- Deployment changes. [Jamie.Rees] - -- More work on the Updater. [Jamie.Rees] - -- Lots of fixes. Becoming more stable now. #865. [tidusjar] - -- Small fixes around the searching. [Jamie.Rees] - -- Some rules #865. [Jamie.Rees] - -- Oops. [TidusJar] - -- Started on the Discord API settings page. [TidusJar] - -- Email Notifications are now fully customizable and work! #865. [Jamie.Rees] - -- Small changes and fixed some stylingon the plex page #865. [Jamie.Rees] - -- More on #865 TODO, Find out whats going on with the notifications and why exceptions are being thrown. [Jamie.Rees] - -- Oops. [Jamie.Rees] - -- Ok #865 fixed the published exe. [Jamie.Rees] - -- Fixed errors. [Jamie.Rees] - -- Fixed build script. [Jamie.Rees] - -- Fixed build. [Jamie.Rees] - -- Some more #865. [Jamie.Rees] - -- Create appveyor.yml. [Jamie] - -- The Approving child requests now work! [Jamie.Rees] - -- Fixed many bugs #865. [Jamie.Rees] - -- Loads of changes, improved the movie search stylings is back. [Jamie.Rees] - -- Moved to webpack and started on new style. [Jamie.Rees] - -- Fixed the TV search via Trakt not returning Images anymore. #865. [Jamie.Rees] - -- Rules changes and rework. [Jamie.Rees] - -- Request Grid test. [Jamie.Rees] - -- Small cleanup #865. [Jamie.Rees] - -- Fixed build. [Jamie.Rees] - -- Started the Radarr Settings #865. [Jamie.Rees] - -- Massive amount of rework on the plex settings page. It's pretty decent now! #865. [tidusjar] - -- Fixed build. [Jamie.Rees] - -- Fixed build. [tidusjar] - -- Rules #865. [tidusjar] - -- Stuff. [Jamie.Rees] - -- Forgot to uncomment. [Jamie.Rees] - -- Tetsd. [Jamie.Rees] - -- Build task changes. [Jamie.Rees] - -- Adsa. [Jamie.Rees] - -- Appveyor. [Jamie.Rees] - -- Stuff around tokens and also builds. [Jamie.Rees] - -- Finished the Plex Content Cacher. Need to do the episodes part but things are now showing as available! #865. [tidusjar] - -- Small user changes #865. [Jamie.Rees] - -- Stuff #865 need to work on the claims correctly. [Jamie.Rees] - -- Reworked the TV model AGAIN #865. [Jamie.Rees] - -- The move! [Jamie.Rees] - -- Fixed build #865. [Jamie.Rees] - -- Fixed the user management #865. [Jamie.Rees] - -- #865 Added support for multiple plex servers. [Jamie.Rees] - -- Bleh. [tidusjar] - -- Small changes. [Jamie.Rees] - -- Fixed the build. [Jamie.Rees] - -- Fixes. [Jamie.Rees] - -- Bundling changes. [Jamie.Rees] - -- Some series information stuff, changes the pace theme too. [Jamie.Rees] - -- Docker support and more, redesign the episodes. [tidusjar] - -- Stuff around episode/season searching/requesting. [Jamie.Rees] - -- Removed redundant folders. [tidusjar] - -- Lots of backend work. [tidusjar] - -- Fixed build. [Jamie.Rees] - -- TV Request stuff. [Jamie.Rees] - -- Work around the user management. [tidusjar] - -- More. [Jamie.Rees] - -- Lots and Lots of work. [Jamie.Rees] - -- Diagnostic changes. [tidusjar] - -- Fixed hangfire exception. [tidusjar] - -- Remove xunit. [tidusjar] - -- Lots more work :( [Jamie.Rees] - -- More changes. [tidusjar] - -- #865. [Jamie.Rees] - -- Small changes. [Jamie.Rees] - -- Fixed build. [Jamie.Rees] - -- More mapping. [Jamie.Rees] - -- Mapping mainly. [Jamie.Rees] - -- Fix systemjs config not being included. [Matt Jeanes] - -- Fixed bundling and various improvements. [Matt Jeanes] - -- Finished the emby wizard #865. [tidusjar] - -- Finished the wizard #865 (For Plex Anyway) [tidusjar] - -- Small changes. [tidusjar] - -- More work on Wizard and Plex API #865. [tidusjar] - -- Settings. [Jamie.Rees] - -- Settings for Ombi. [Jamie.Rees] - -- Fixed some issues around the identity. [Jamie.Rees] - -- #865 more for the authentication. [tidusjar] - -- Auth. [Jamie.Rees] - -- More on the search and requests page. It's almost there for movies. Need to add some filtering logic #865. [tidusjar] - -- #865. [Jamie.Rees] - -- Fixed build. [tidusjar] - -- Messing around with the settings. [tidusjar] - -- Fixed the yml. [Jamie.Rees] - -- Remove unneeded bundle config. [Matt Jeanes] - -- Redo dotnet publish targets. [Jamie.Rees] - -- Bundling changes. [Jamie.Rees] - -- Stuff. [Jamie.Rees] - -- Move app into wwwroot. [Jamie.Rees] - -- Put uglify back in! [Jamie.Rees] - -- Wrong line. [Jamie.Rees] - -- Matt is helping. [Jamie.Rees] - -- Revert. [tidusjar] - -- Small tweaks. [tidusjar] - -- Upgrade to .Net Standard 1.6. [tidusjar] - - -## v2.2.1 (2017-04-09) - -### **New Features** - -- Update README.md. [Jamie] - -- Update README.md. [Jamie] - -- Added the forums. [tidusjar] - -- Updates. [tidusjar] - -- Update gulpfile.js. [Jamie] - -- Update gulpfile.js. [Jamie] - -- Update gulpfile.js. [Jamie] - -- Update gulpfile.js. [Jamie] - -- Update appveyor.yml. [Jamie] - -- Update gulpfile.js. [Jamie] - -- Update appveyor.yml. [Jamie] - -- Update appveyor.yml. [Jamie] - -- Update appveyor.yml. [Jamie] - -- Update appveyor.yml. [Jamie] - -- Added a retry policy around the emby newsletter. [Jamie.Rees] - -### **Fixes** - -- Revert "Merge branch 'DotNetCore' into dev" [tidusjar] - -- More borken build. [Jamie.Rees] - -- Started adding requesting. [Jamie.Rees] - -- Done the movie searching. [tidusjar] - -- #865. [tidusjar] - -- More. [tidusjar] - -- Moar. [tidusjar] - -- Small changes. [tidusjar] - -- Styling. [Jamie.Rees] - -- MOre changes. [Jamie.Rees] - -- Spacing. [Jamie.Rees] - -- Try again. [Jamie.Rees] - -- More. [Jamie.Rees] - -- Again. [Jamie.Rees] - -- Anbother. [Jamie.Rees] - -- Another. [Jamie.Rees] - -- Another. [Jamie.Rees] - -- Retry. [Jamie.Rees] - -- A. [Jamie.Rees] - -- Fixed. [Jamie.Rees] - -- Cahnge 2. [Jamie.Rees] - -- Appveyor change. [Jamie.Rees] - -- The start of a new world. [Jamie.Rees] - -- Fixed the migration number and order by the added date for the newsletter #1264. [tidusjar] - -- Forgot this change. [tidusjar] - -- Also fixed the issue for the Emby Newsletter where episodes were not getting added :( [tidusjar] - -- #1264 "They may take our lives, but they'll never take our freedom!" [tidusjar] - -- Finished reworking the Sonarr Integration. Seems to be working as expected, faster and most stable. It's Not A Toomah! [tidusjar] - -- Small bit of work. [Jamie.Rees] - -- Made a start on the new Sonarr integration. [tidusjar] - -- For test emails, if there is no new content then just grab some old data. [tidusjar] - -- Fixed an issue where the emby newsletter was always showing series. [tidusjar] - - -## v2.2.0 (2017-03-30) - -### **New Features** - -- Update appveyor.yml. [Jamie] - -- Update README.md. [Jamie] - -- Update README.md. [Jamie] - -- Added a new setting for the Netflix option, we can now disable it appearing in the search. [tidusjar] - -- Update German Translation. [Marius Schiffer] - -- Added a release notes page, you can access via Admin>Updates>Recent Changes tab. Note to self, need to put better comments in for users to understand! [Jamie.Rees] - -- Added gravitar image. [Jamie.Rees] - -- Added a missing `await` for an HP AddArtist call. Added some more Trace logging. [smcpeck] - -- Added a missing `await` for an HP AddArtist call. Added some more Trace logging. [smcpeck] - -- Added some logging around API calls. [smcpeck] - -- Changed IEmbyAvailabilityChecker to use IEnumberables + checking actor search against Emby content + PR feedback. [smcpeck] - -- Changed actor searching to support non-actors too. [smcpeck] - -- Added a 10 second timer to refresh some new caching I put in. [smcpeck] - -- Added root folder and approving quality profiles in radarr #1065. [tidusjar] - -- Added some debugging code around the newsletter for Emby #1116. [tidusjar] - -- Added a TMDB Rate limiter for the newsletter. [tidusjar] - -- Added port check in wizard. also fixed favicon. [tidusjar] - -- Update Radarr placeholder. [d2dyno] - -- Added the user login for emby users #435. [tidusjar] - -- Added User Management support for Emby #435. [tidusjar] - -- Added emby to the sidebar #435. [tidusjar] - -- Added API endpoint for /actor/new/ to support searching for movies not already available/requested. [smcpeck] - -- Update ISSUE_TEMPLATE.md. [Jamie] - -- Update README.md. [SuperPotatoMen] - -- Update README.md. [SuperPotatoMen] - -- Update README.md. [SuperPotatoMen] - -### **Fixes** - -- Translation changes. [Jamie.Rees] - -- Syntax error. [tidusjar] - -- Fixed an issue where we were retrying the API call when the Plex users login creds were invalid. #1217. [tidusjar] - -- Slightly increased the wait time for the emby newsletter also fixed a potential error in the plex user checker. [Jamie.Rees] - -- Fixed an issue where we were not notifiying emby users. [Jamie.Rees] - -- Fixed the issue where the recent changes page was not showing the correct date. #1296. [Jamie.Rees] - -- Fixed #1252 (Show the correct user type on the management page for Plex Users) [Jamie.Rees] - -- Fixed the casting error #1292. [Jamie.Rees] - -- Fix test newletter not sending when empty. [Dhruv Bhavsar] - -- Quick fix for email false positive message. ISSUE: #1286. [Dhruv Bhavsar] - -- Fixes around the newsletter. We will now correctly show newly added shows and also newly added episodes. #1163. [tidusjar] - -- Fixed a sonarr deseralization error. [tidusjar] - -- Increased the delay for the Episode information api calls. #1163. [tidusjar] - -- Looks like we were overloading emby with out api calls. [tidusjar] - -- Fixed the root path escaping issue for Radarr too! [tidusjar] - -- Some small backend newsletter changes, we can now detect if there are any movies and/or tv shows, if there are none then we will no longer send out an empty newsletter. [Jamie.Rees] - -- Remoddeled the notificaiton settings to make it easier to add more. This is some techinical changes that no one except me will ever notice :( [Jamie.Rees] - -- Fixed #1234. [Jamie.Rees] - -- A fix to the about page and also started to rework the notification backend slightly to easily add more notifications. [Jamie.Rees] - -- Adding more logging into the Plex Cacher. [Jamie.Rees] - -- #1218 changed the text when we cannot display release notes for dev and EAP branches. [Jamie.Rees] - -- Fix for #1236. [SuperPotatoMen] - -- Tooltips. [Jamie.Rees] - -- #236. [Jamie.Rees] - -- #1102. [Jamie.Rees] - -- Done #1012. [Jamie.Rees] - -- Oops #1134. [Jamie.Rees] - -- Fixed #1121. [Jamie.Rees] - -- Fixed #1210. [Jamie.Rees] - -- Fixed typo #1134. [Jamie.Rees] - -- Fixed #1223. [Jamie.Rees] - -- Another newsletter fix attempt #1163 #1116. [tidusjar] - -- Fixup! Reset the branch on v2.1.0 tag to get to a shared state between dev and Master. [distaula] - -- Fixed a bug in the Plex Newsletter. [tidusjar] - -- Typo. [tidusjar] - -- Fixed around the newsletter and a small feature around the permissions/features (#1215) [Jamie] - -- Fixed #1189. [tidusjar] - -- Fixed #1195. [Jamie.Rees] - -- Fixed #1195. [Jamie.Rees] - -- Fixed #1192. [Jamie.Rees] - -- Fixed issue where we could get null rating keys on Plex. [tidusjar] - -- Needed to treat a 201 as success, too. + removed some commented out code. [Shaun McPeck] - -- Normalized spacing/tabs. [smcpeck] - -- Move local user login to be the first thing checked; renamed old Api variable to PlexApi now that Emby is in play. [smcpeck] - -- Remove all the polling/retry logic around HP requests. This was a problem do to not properly awaiting the initial AddArtist API call being sent to HP. Also fix SetAlbumStatus to use ReleaseId instead of MusicBrainsId (same fix previously applied to AddArtist). [smcpeck] - -- Restore checking of HTTP StatusCode on ApiRequests; remove checking of response.ErrorException. [smcpeck] - -- Reverted (for now) non-200 response handling; added some extra logging. [smcpeck] - -- Tweaked ApiRequest behavior on non-200 responses; think it was breaking login. :-" [smcpeck] - -- Only deserialize response payload in ApiRequest when StatusCode == 200. Will a default return value in other cases cause other issues? [smcpeck] - -- Headphones - added releaseID to generic RequestedModel and passing that through to HP request. Their API doesn't request via the MusicBrainzId. [smcpeck] - -- Fixed #1038. [tidusjar] - -- Fixed a slight issue where we could click the change folders button rather than the dropdown arrow #1189. [tidusjar] - -- Bunch of updater files. [tidusjar] - -- #1163 #117. [tidusjar] - -- Removed some unnecessary 'ConfigureAwait` uses. [smcpeck] - -- Remove meaningless html class from actor searching checkbox. [smcpeck] - -- Fixed an issue where we were not always showing movies from external programs. [tidusjar] - -- Remove extra delay when filtering out existing movies. [smcpeck] - -- Post merge build fixes. [smcpeck] - -- Fix. [tidusjar] - -- Fixed #1177. [tidusjar] - -- Fixed #1152. [tidusjar] - -- Fixed #1123. [tidusjar] - -- Fixed a bug when sending to radarr. [tidusjar] - -- Fixed #1133. [tidusjar] - -- Fixed issues img. [Jamie.Rees] - -- Stop Plex being enabled on the first time installing #1048. [Jamie.Rees] - -- The landing page now works for emby #435. [tidusjar] - -- Fixed #1104. [tidusjar] - -- Fixed #1090. [tidusjar] - -- Fixed #1103. [tidusjar] - -- Small changes. [tidusjar] - -- Break out Mass Email feature into its own tab, upgrade Font Awesome and clean up some comments. [dhruvb14] - -- Fix typo. [Travis Bybee] - -- Fixed #1066. [Jamie.Rees] - -- Fixed broken builds. [Jamie.Rees] - -- Fixed #1083. [Jamie.Rees] - -- #1049. [tidusjar] - -- Fixed #1071. [tidusjar] - -- Fixed #1048 #1081. [tidusjar] - -- #1074. [Jamie.Rees] - -- #1069. [Jamie.Rees] - -- Fix for #1068. [tidusjar] - -- Remove duplciate tv show status. [tidusjar] - -- Some request ui changes. [tidusjar] - -- Removed references to Plex. [tidusjar] - -- Removed plex from the scheduled jobs ui. [tidusjar] - -- First run of the newsletter set it to a test. [tidusjar] - -- Reworked the newsletter for Emby! Need to rework it for Plex and use the new way to do it. [tidusjar] - -- Fixed build. [tidusjar] - -- Fixed the mass email, it was only being set to users with the newsletter feature #358. [tidusjar] - -- Removed Plex Request from the notifications. [tidusjar] - -- Finish implementing mass email feature. [dhruvb14] - -- @tidusjar pointed out runtime error!! [dhruvb14] - -- Does not compile, need to get data from UI into nancy somehow and figure out why IMassEmail is not initializing. [dhruvb14] - -- Begin Implementing Mass Email Section. [dhruvb14] - -- Hide the auto update btn #236 Fixed where we were not populating the emby episodes #435. [tidusjar] - -- Fix Radarr labels. [d2dyno] - -- Fixed pace loader. [Jamie.Rees] - -- Check if Emby/Plex is enabled before starting the job. [Jamie.Rees] - -- Fixed #1036. [Jamie.Rees] - -- Fixed a typo and changed wording. [Torkil Liseth] - -- Fixed #1035. [Jamie.Rees] - -- Fix for #1026. [Jamie.Rees] - -- Fixed #1042. [tidusjar] - -- #435. [Jamie.Rees] - -- #435 Started the wizard. [Jamie.Rees] - -- Removed. [tidusjar] - -- Final Fixes. [dhruvb14] - -- Partial fix for broken HR tag's in Email... [dhruvb14] - -- DAMN! #435 that's a lot of code! [tidusjar] - -- Started adding Emby, Lots of backend work done. Need a few more services done and login and user management. #435. [tidusjar] - -- UI changes to add checkbox and support searching for only new matches via new API. [smcpeck] - -- REFACTOR: IAvailabilityChecker - changed arrays to IEnumerables. [smcpeck] - -- UI changes to consume actor searching API. [smcpeck] - -- API changes to allow for searching movies by actor. [smcpeck] - -- Enforcing async/await in synchronous methods that were marked async. [smcpeck] - - -## v2.1.0 (2017-01-31) - -### **New Features** - -- Update README.md. [Jamie] - -- Update .gitattributes. [Jamie] - -- Update README.md. [Jamie] - -- Update README.md. [Jamie] - -- Update README.md. [Jamie] - -- Update README.md. [Jamie] - -- Update appveyor.yml. [Jamie] - -- Added the new labels to the search. [tidusjar] - -- Added a switch to use the new search or not, just in case people do not like it. added a migration to turn on the new search. [Jamie.Rees] - -- Added a bunch of categories for tv search similar to what we have for movies. [Jamie.Rees] - -### **Fixes** - -- Fixed typos. [Haries Ramdhani] - -- Fix typo in readme. [tdorsey] - -- Fixed #985. [Jamie.Rees] - -- FIxed #978. [tidusjar] - -- Fixed the approval issue for #939. [tidusjar] - -- Some general improvements. [tidusjar] - -- Turned off migration for now. [tidusjar] - -- Fixed #998. [tidusjar] - -- Additional movie information. [Jamie.Rees] - -- Debug info around the notifications. [Jamie.Rees] - -- Small changes. [tidusjar] - -- Fixed #995. [tidusjar] - -- Fix for #978. [tidusjar] - -- Fixed #991. [tidusjar] - -- Fixed the login issue and pass Radarr the year #990. [Jamie.Rees] - -- More small tweaks around the username/alias. [Jamie.Rees] - -- Possible issue with the empty username. [Jamie.Rees] - -- Potential Fix for #985. [Jamie.Rees] - -- Small changed to the sidebar. [Jamie.Rees] - -- Small changes. [Jamie.Rees] - -- Done #627. [Jamie.Rees] - -- Finished #535 #445 #170. [tidusjar] - -- Fixed tests. [Jamie.Rees] - -- Started to add the specify Sonarr root folders. [Jamie.Rees] - -- Fixed #968. [Jamie.Rees] - -- Fixed #970. [Jamie.Rees] - -- Done #924. [Jamie.Rees] - -- Fixed. [Jamie.Rees] - -- Fixed #955. [Jamie.Rees] - -- #956. [Jamie.Rees] - -- Fixed #947. [Jamie.Rees] - -- #951. [tidusjar] - -- Finished #923 !!! [tidusjar] - -- More for #923. [Jamie.Rees] - -- Radarr integartion in progress #923. [tidusjar] - -- Fixed #940 don't show any shows without a tvdb id. [tidusjar] - -- Finished #739. [Jamie.Rees] - -- Initial impliementation of #739. [Jamie.Rees] - -- Improved the search UI and made it more consistant. Finished the Netflix API Part #884. [Jamie.Rees] - - -## v2.0.1 (2017-01-16) - -### **New Features** - -- Update appveyor.yml. [Jamie] - -- Added a netflix api. [Jamie.Rees] - -### **Fixes** - -- Fixed #934. [Jamie.Rees] - - -## v2.0 (2017-01-14) - -### **New Features** - -- Update ISSUE_TEMPLATE.md. [SuperPotatoMen] - -- Update ISSUE_TEMPLATE.md. [SuperPotatoMen] - -- Update ISSUE_TEMPLATE.md. [SuperPotatoMen] - -- Update README.md. [SuperPotatoMen] - -- Update README.md. [SuperPotatoMen] - -- Update ISSUE_TEMPLATE.md. [SuperPotatoMen] - -- Update ISSUE_TEMPLATE.md. [SuperPotatoMen] - -- Update README.md. [SuperPotatoMen] - -- Update README.md. [SuperPotatoMen] - -- Added the settings for #925 but need to apply the settings to the UI. [Jamie.Rees] - -- Changed the settings name from Plex Requests to Ombi. [Jamie.Rees] - -- Added support for Managed Users #811. [Jamie.Rees] - -- Change solution name in travis. [mhann] - -- Update ISSUE_TEMPLATE.md. [SuperPotatoMen] - -### **Fixes** - -- Finished #925. [Jamie.Rees] - -- Some TODO's. [Jamie.Rees] - -- Fixed #915. [Jamie.Rees] - -- Fixed the issue where notifications are not being sent to users with Aliases #912. [Jamie.Rees] - -- Fixed #891. [Jamie.Rees] - -- Fix indentation issue. [Marcus Hann] - -- Implement simple button. [Marcus Hann] - -- Plex Username Case Sensitivity Fix. [thegame3202] - -- Fixed #882. [Jamie.Rees] - -- Api changed again, so more fixes for #878. [Jamie.Rees] - -- Possible fix for #893. [Jamie.Rees] - -- Fixed #898. [Jamie.Rees] - -- Fixed #878. [TidusJar] - -- * userManagementController.js: fixed #881. [TidusJar] - -- More work on watcher, should all be good now. #878. [Jamie.Rees] - -- Delete PlexRequests.sln.DotSettings. [Jamie] - -- Fixed #862. [Jamie.Rees] - -- #399 and #398 finished. [Jamie.Rees] - -- More work on #399. [Jamie.Rees] - -- Finished #884. [Jamie.Rees] - -- More for #844. [Jamie.Rees] - -- Another #844. [Jamie.Rees] - -- Fixed a dependancy issue with #844. [Jamie.Rees] - -- Finished the main part of #844 just need testing. [Jamie.Rees] - -- Fixed #832. [Jamie.Rees] - -- Fixed build. [Jamie.Rees] - -- Fix tiny readme typo. [mhann] - -- Fixed #850 also started #844 (Wrote the API interaction) [Jamie.Rees] - -- #801 #292 done. [Jamie.Rees] - -- Should fix #841 #835 #810. [Jamie.Rees] - -- Fix small typo in ticket overview page. [mhann] - -- More work on the combined login. [Jamie.Rees] - -- Fixed db issue. [Jamie.Rees] - -- Name changes. [Jamie.Rees] - -- All Sln changes. [tidusjar] - -- Moved API Sln dir. [tidusjar] - -- Fixed build. [tidusjar] - -- Moved namespaces. [tidusjar] - -- Renamed zip. [tidusjar] - -- Product name change. [tidusjar] - - -## v1.10.1 (2016-12-17) - -### **Fixes** - -- #788 fixed! [tidusjar] - -- Fixed #788 and #791. [tidusjar] - -- #399 #398. [Jamie.Rees] - -- Fixed build. [Jamie.Rees] - -- Small refactorings. [Jamie.Rees] - -- #782. [Jamie.Rees] - -- #785. [Jamie.Rees] - - -## v1.10.0 (2016-12-15) - -### **New Features** - -- Update README.md. [Jamie] - -- Added optional launch args for the auto updater. [Jamie.Rees] - -- Update README.md. [Jamie] - -- Update README.md. [Jamie] - -- Update _Navbar.cshtml. [Jamie] - -- Update README.md. [Jamie] - -- Update _Navbar.cshtml. [Jamie] - -- Update README.md. [Jamie] - -- Added a new permission to bypass the request limit. [Jamie.Rees] - -- Update Version1100.cs. [SuperPotatoMen] - -- Added logging around the Newsletter #717. [Jamie.Rees] - -- Added missing migration. [tidusjar] - -- Added loading spinner. [Jamie.Rees] - -- Update UI.resx. [SuperPotatoMen] - -- Update Version1100.cs. [Jamie] - -- Update ISSUE_TEMPLATE.md. [SuperPotatoMen] - -### **Fixes** - -- Fixed an issue where the HTML in the newsletter was incorrect. [Jamie.Rees] - -- Fixed #201. [Jamie.Rees] - -- Fixed #720 and added better error handling around the migrations. [Jamie.Rees] - -- Fixed #769. [Jamie.Rees] - -- Write out the actual file version. [Jamie.Rees] - -- Error checking around GA. [TidusJar] - -- Fixed #761. [tidusjar] - -- Fixed #749 Fixed an issue where we were adding the read only permission when creating the admin. [tidusjar] - -- Fixed #757. [tidusjar] - -- Fixes around the server admin #754. [Jamie.Rees] - -- Removed the trace option from the UI, it is only accessible when appending the url with "?developer" #753. [Jamie.Rees] - -- Fixed an issue where the admin could not be updated. [Jamie.Rees] - -- Fixed #745. [Jamie.Rees] - -- Fixed #748. [Jamie.Rees] - -- Workaround for #748. [SuperPotatoMen] - -- Another attempt to fix #717. [tidusjar] - -- Fixed #744. [Jamie.Rees] - -- Tidied up the warnings. [Jamie.Rees] - -- Small bit of analytics. [Jamie.Rees] - -- Removed the whitelist. [Jamie.Rees] - -- Should fix #696 Fixed an issue with the scheduled jobs where it could use a different trigger if the order between the schedules and the triggers were in different positions in the array... Stupid me, ordering both arrays by the name now. [tidusjar] - -- Some better null object handling #731. [Jamie.Rees] - -- Small tweaks. [Jamie.Rees] - -- Fixed #728. [Jamie.Rees] - -- Fixed #727. [Jamie.Rees] - -- Fixed admin redirect issue. [tidusjar] - -- Fixed #718. [tidusjar] - -- Lots of small fixes and tweaks. [Jamie.Rees] - -- Tidied up some of the angular code, split the UI into it's own directives for easier maintainability. [Jamie.Rees] - -- Small tweaks to the Request Page. [Jamie.Rees] - -- Reverted the PR that may have caused #619. [Jamie.Rees] - -- Some small tweaks around #218 Just added the link to the settings and some angular improvements. [Jamie.Rees] - -- Test. [Jamie.Rees] - -- Attempt at fixing #686. [Jamie.Rees] - -- Finished #707. [Jamie.Rees] - -- Fixed #704. [Jamie.Rees] - -- Fixed #705. [Jamie.Rees] - -- Fixed #706. [Jamie.Rees] - -- #547. [Jamie.Rees] - -- Default tabs #304. [Jamie.Rees] - -- Fixed #703. [Jamie.Rees] - -- #233. [Jamie.Rees] - -- Done #678. [Jamie.Rees] - -- Fixed #670. [Jamie.Rees] - -- #456 Update all the requests when we identify that the username changes. [Jamie.Rees] - -- More user management. [Jamie.Rees] - -- #218. [Jamie.Rees] - -- Fixed build. [tidusjar] - -- Implimented the features #218. [tidusjar] - -- Use the user alias everywhere if it is set #218. [tidusjar] - -- Implimented auto approve permissions #218. [tidusjar] - -- A. [tidusjar] - -- Fixed an IOC issue. [tidusjar] - -- Fixed the issue with user management, needed to implement our own authentication provider. [Jamie.Rees] - -- Small changes including #666. [Jamie.Rees] - -- Done #679. [tidusjar] - -- Reduce the retry time. [Jamie.Rees] - -- Remove all references to the claims. [Jamie.Rees] - -- Lots of fixed and stuff. [Jamie.Rees] - -- Fixed potential crash #683. [Jamie.Rees] - -- Fixed #681. [Jamie.Rees] - -- More user management. [Jamie.Rees] - -- Finishing off the user management page #218 #359 #195. [Jamie.Rees] - -- Finished #646 and fixed #664. [Jamie.Rees] - -- Started on #646. Fixed #657. [Jamie.Rees] - -- Fixed #665. [Jamie.Rees] - -- Migrate users. [TidusJar] - -- Fixed build. [Jamie.Rees] - -- Convert the for to foreach for better readability. Still need to rework this area. [Jamie.Rees] - -- Final Tweaks #483. [Jamie.Rees] - -- Finished #483. [Jamie.Rees] - -- Finished the queue #483. [Jamie.Rees] - -- Started on the queue for requests #483 TV Requests with missing information has been completed. [Jamie.Rees] - -- Fixed build. [Jamie.Rees] - -- Finished the notification for the fault queue. [Jamie.Rees] - -- Finished #556. [Jamie.Rees] - -- Finished #633 (First part of the queuing) [Jamie.Rees] - -- Finished #659 #236 has been modified slightly. Needs testing on Different systems. [Jamie.Rees] - -- Almost finished #659. [Jamie.Rees] - -- Started on #483. [Jamie.Rees] - -- #544. [Jamie.Rees] - -- Fixed #656 and more work on #218. [Jamie.Rees] - -- Fixed some issues with the user management work. [TidusJar] - -- Fixed build issue. [TidusJar] - -- User perms. [Jamie.Rees] - - -## v1.9.7 (2016-11-02) - -### **New Features** - -- Update appveyor.yml. [Jamie] - -- Update appveyor.yml. [Jamie] - -### **Fixes** - -- Potential fix for #629. [TidusJar] - -- Fixed an issue to stop blatting over the base url. [tidusjar] - -- Fixed #643. [TidusJar] - -- Fixed #622. [TidusJar] - - -## v1.9.6 (2016-10-28) - -### **New Features** - -- Update appveyor.yml. [Jamie] - -### **Fixes** - -- Fixed #586. [Jamie.Rees] - -- Fixed #622. [Jamie.Rees] - -- Fixed #621. [Jamie.Rees] - - -## v1.9.5 (2016-10-27) - -### **New Features** - -- Added our own custom migrations, a lot easier to migrate DB versions now. [tidusjar] - -### **Fixes** - -- Bump version. [Jamie.Rees] - -- Small bit of work on the user claims. [Jamie.Rees] - -- Fix #612 again. [Jamie.Rees] - -- User management styling. [Jamie.Rees] - -- Fixed #608 and some other small stuff. [tidusjar] - -- More user mapping. [tidusjar] - -- Fixed #615. [tidusjar] - -- Fixed #610. [tidusjar] - -- User management stuff. [Jamie.Rees] - -- User management work. [Jamie.Rees] - -- Revert the TVSender to use the old code. [Jamie.Rees] - -- Fixed the view issue. [tidusjar] - -- S582: admin improvements part 2. [Jim MacKenzie] - -- Fix #612. [Jamie.Rees] - -- User management, migration and newsletter. [Jamie.Rees] - -- #602 recently added improvements. [tidusjar] - -- Revert "Sorting out the current state of migrations" [Jamie.Rees] - -- Sorting out the current state of migrations. [Jamie.Rees] - -- Marked as obsolete. [Jim MacKenzie] - -- Migration setup. [Jim MacKenzie] - -- Removed extra line breaks. [Jim MacKenzie] - -- Moved Newsletter Settings to its own page. [Jim MacKenzie] - -- Reverted TMDB package. [Jamie.Rees] - -- Remove DB Option. [Jamie.Rees] - -- Upgrade the movie DB package and fixed #370 To fix this I had to make another API call... It slows down the search... [tidusjar] - -- Lots of small fixes including #475. [tidusjar] - -- A better fix for #587. [tidusjar] - -- Fixed #553. [tidusjar] - -- #601. [Jamie.Rees] - -- More rework to use the Plex DB. [Jamie.Rees] - -- More work around using the PlexDatabase. [Jamie.Rees] - -- Plex DB. [Jamie.Rees] - -- Allow to process even know we had an error #578. [Jamie.Rees] - -- Fix boostrapper-datetimepicker imports (#586) [David Torosyan] - -- Potential work around for #587. [tidusjar] - -- Fixed #589. [tidusjar] - - -## v1.9.4 (2016-10-10) - -### **New Features** - -- Update appveyor.yml. [Jamie] - -- Added Paypalme options, no UI yet (#568) [Jim MacKenize] - -- Update appveyor.yml. [Jamie] - -- Update README.md. [Jamie] - -### **Fixes** - -- Reverted. [tidusjar] - -- Make sure it's enabled before sending the recently added. [tidusjar] - -- Moved the HR inside the table for TV Shows. [Jamie.Rees] - -- FIXED!!!!! YES BITCH! #550. [tidusjar] - -- Moved the horizontal rules inside the table row. [tidusjar] - - -## v1.9.3 (2016-10-09) - -### **New Features** - -- Added properties to disable tv requests for specific episodes or seasons and wired up to admin settings. [Matt McHughes] - -- Added different sonarr search commands. [tidusjar] - -### **Fixes** - -- Fixed #515. [tidusjar] - -- Fixed #561 and a small bit of work on #569. [tidusjar] - -- #569. [tidusjar] - -- Fixed case typo. [Matt McHughes] - -- Finished wiring tv request settings to tv search. [Matt McHughes] - -- WIP hide tv request options based on admin settings. [Matt McHughes] - -- Set meta charset to be utf-8. [Madeleine Schönemann] - -- F#552: updated labels text. [Jim MacKenize] - -- F#552: Re-design lables. [Jim MacKenzie] - -- Last correction.. Now the translation is ready to be used. [Michael Reber] - -- Forgot to correct two incorrect translations. [Michael Reber] - -- Correction of the German translation. [Michael Reber] - -- Notification improvements. [tidusjar] - -- #515. [tidusjar] - - -## v1.9.2 (2016-09-18) - -### **New Features** - -- Update appveyor.yml. [Jamie] - -- Update CouchPotatoCacher.cs. [Jamie] - -- Added some error handing around the GetMovie area #517. [tidusjar] - -- Added a version endpoint in "/api/version" #529. [tidusjar] - -### **Fixes** - -- Trying to fix the auto CP. [tidusjar] - -- Increase the notice message text box #527. [tidusjar] - -- #536 this should fix notification settings when it is being unsubscribed when testing. [tidusjar] - -- Improved how the TV search looks and feels. [tidusjar] - -- Fix for reverse proxy when using the wizard. [Devin Buhl] - -- Fixed #532. [tidusjar] - -- This should fix some issues with the episode requests #514. [tidusjar] - -- Small changes around existing series. [tidusjar] - -- Fixed #514 and the unit tests. [tidusjar] - -- If there is a bad password when changing it, we now inform the user. [tidusjar] - -- When logging out as admin remove the username from the session. [tidusjar] - -- Sorted out some of the UI for #18. [tidusjar] - -- Finished #18. [tidusjar] - - -## v1.9.1 (2016-08-30) - -### **New Features** - -- Update appveyor.yml. [Jamie] - -- Added french to the navbar. [tidusjar] - -- Changed the way we use the setTimeout function. Should fix #403 #491 #492. [tidusjar] - -- Change the redirection to use a relative uri redirect #473. [tidusjar] - -### **Fixes** - -- Fixed tests. [tidusjar] - -- Fixed #491 and added more logging around the email messages under the Info level. [tidusjar] - -- Finished #415. [tidusjar] - -- Fixed an issue where there were some JS errors on the landing page settings and stopped us being redirected to the login sometimes as an admin. [tidusjar] - -- Fixed #480. [tidusjar] - -- User management. [tidusjar] - -- Fixed #505. [tidusjar] - -- Append the application version to the end of our JS/CSS files. [tidusjar] - -- Fixed issue #487. [tidusjar] - -- Remove the datetime picker css from the main assets block and only load it on the pages it needs. #493. [tidusjar] - -- Redirect to search if we are already logged in #488. [tidusjar] - -- Fixed build. [tidusjar] - -- Fixed an issue where you could set the base url as requests #479. [tidusjar] - -- Working on the beta releases page and also the user management. [tidusjar] - -- User work. [tidusjar] - - -## v1.9.0 (2016-08-18) - -### **New Features** - -- Update the availability checker to search for TV Episodes. [tidusjar] - -- Changed the no TVMazeid message. [tidusjar] - -- Added an option to disable/enable the Plex episode cacher. [tidusjar] - -- Updated the episode cacher to have a minimum of 11 hours before it runs again. [tidusjar] - -- Added some useful analytical infomation around the wizard. [tidusjar] - -- Updated the German translations #402. [tidusjar] - -- Added some code to shrink the DB. reworked the search to speed it up. [tidusjar] - -- Change to use the GrandparentTitle rather than the thumbnail.... facepalm. [tidusjar] - -- Change the interval to hours! [tidusjar] - -- Added the transaction back into the DB. Do not run the episode cacher if it's been run in the last hour. [tidusjar] - -- Added logging. [tidusjar] - -- Changed the query slightly. [tidusjar] - -- Updated Newtonsoft.Json, Autofixture, Nlog and Dapper packages. [tidusjar] - -- Added the Sonarr check for episodes #254. [tidusjar] - -- Added unit tests. [tidusjar] - -- Added #436. [tidusjar] - -- Update build no. [tidusjar] - -- Updated translations for #402. [tidusjar] - -- Added a beta module. [tidusjar] - -- Added a custom debug root path provider, this means we do not have to recompile the views every time we make a view change. [tidusjar] - -- Update .gitignore. [Jamie] - -- Update appveyor.yml. [Jamie] - -- Added tests for the string hash. [tidusjar] - -- Added code to request the api key for CouchPotato. [tidusjar] - -- Added the file version to the layout. [tidusjar] - -- Update appveyor.yml. [Jamie] - -- Update appveyor.yml. [Jamie] - -- Added automation tests. [tidusjar] - -- Update appveyor.yml. [Jamie] - -- Updated packages. [tidusjar] - -- Updated Polly. [tidusjar] - -- Updated Fr, IT and NL translations #402. [tidusjar] - -- Changed the way the donate button works for #414. [tidusjar] - -- Added MediatR. [tidusjar] - -### **Fixes** - -- User management stuff. [tidusjar] - -- Fixed! [tidusjar] - -- Small amount of work on the user management. [tidusjar] - -- Fixed #466. [tidusjar] - -- Fixes. [tidusjar] - -- Made the episode request better. [tidusjar] - -- Removed commented out tests. [tidusjar] - -- Fixed the bad test after the merge. [tidusjar] - -- Reworked #466. [tidusjar] - -- More unit tests around the login and also the core Plex Checker. [tidusjar] - -- Potentially fixed the issue where we were requesting everything that was also available now. [tidusjar] - -- This should fix #466. [tidusjar] - -- Attempt at fixing a potential bug found from #466. [tidusjar] - -- #464 fixed. [tidusjar] - -- Fixed the build. [tidusjar] - -- Small improvements to the wizard. [tidusjar] - -- Always set the wizard to be true when editing the Plex Requests settings (Since the flag is not in the UI, a bool defaults to false). [tidusjar] - -- Tiny bit of more info. [tidusjar] - -- Finished #459. [tidusjar] - -- #459 is almost done. [tidusjar] - -- Modified the episode modal so that we are now resetting the button after a request. [tidusjar] - -- Commented out the transaction for now to debug it. [tidusjar] - -- Since we are multithreading, we should use a threadsafe type to store the episodes to prevent any threading or race conditions. [tidusjar] - -- Wrapped the bulk insert inside a transaction. [tidusjar] - -- Made the episode check parallel. [tidusjar] - -- Log out the GUID causing the issue. [tidusjar] - -- Fixed another test. [tidusjar] - -- Fixed tests. [tidusjar] - -- Got mostly everything working for #254 Ready for testing. [tidusjar] - -- Fixed issue with saving to db. [tidusjar] - -- Need to work out why the cacher is not working and where the datatype mismatch is. [tidusjar] - -- Don't delete first. [tidusjar] - -- Fix the log path issue #451. [tidusjar] - -- Dump an item. [tidusjar] - -- Small change with the return value in the batch insert. [tidusjar] - -- #254 Removed the cache, we are now storing the plex information into the database. [tidusjar] - -- Small change in the episode saver. [tidusjar] - -- Some small tweaks to improve the memory alloc. [tidusjar] - -- Short circuit when Plex hasn't been setup. Added Miniprofiler. [tidusjar] - -- Consolidate newtonsoft.json packages. [tidusjar] - -- Some performance improvements around the new TV stuff. [tidusjar] - -- Reworked the cacher, fixed the memory leak. No more logging within tight loops. [tidusjar] - -- Another null check. [tidusjar] - -- Some more changes. [tidusjar] - -- Some error handling. [tidusjar] - -- Check if the sonarr ep is monitored. [tidusjar] - -- Some logging. [tidusjar] - -- Small changes, we will actually see the episode cacher on the scheduled jobs page now. [tidusjar] - -- Work on the UI to show what episodes have been requested #254. [tidusjar] - -- Small fix. [tidusjar] - -- Fix the api change in #450. [tidusjar] - -- #254. [tidusjar] - -- Workaround for #440. [tidusjar] - -- Async async async improvements. [tidusjar] - -- Finished #266 Added a new cacher job to cache all episodes in Plex. [tidusjar] - -- Fixed #442. [tidusjar] - -- #254. [tidusjar] - -- Work around the sonarr bug #254. [tidusjar] - -- #254 having an issue with Sonarr. [tidusjar] - -- Small bit of work on #266. [tidusjar] - -- #254. [tidusjar] - -- Precheck and disable the episode boxes if we already have requested it. TODO check sonarr to see if it's already there. #254. [tidusjar] - -- Fixed broken build. [tidusjar] - -- More work for #254. [tidusjar] - -- More work on #254. [tidusjar] - -- Fixed the bug in #438 and added unit tests to make so we dont break it in the future. [tidusjar] - -- Some reason we had dupe translations. [tidusjar] - -- Rename SubDir to Base Url. [tidusjar] - -- Fix the exception in #440. [tidusjar] - -- Reworking the login page for #426. [tidusjar] - -- Fixed #438. [tidusjar] - -- Finished the auth stuff. [tidusjar] - -- Finished up the SMTP side of #429. [tidusjar] - -- #428 Added a message when the we cannot get a TVMaze ID. [tidusjar] - -- #254 MOSTLY DONE! At last, this took a while. [tidusjar] - -- Removed the other rootpath provider. [TidusJar] - -- Removed the other rootpath provider. [TidusJar] - -- Removed the other rootpath provider. [TidusJar] - -- Should fix #429. [TidusJar] - -- Done #135 We are including the application version number in the directory. [tidusjar] - -- #387 trim the spaces from the api key. Tidied up the setting models a bit. [tidusjar] - -- Wrapped the repo to catch Sqlite corrupt messages. [tidusjar] - -- Frontend and tv episodes api work for #254. [tidusjar] - -- #424. [tidusjar] - -- #359. [tidusjar] - -- Moved the plex auth token to the plex settings where it should belong. [tidusjar] - -- Small changes around the user management. [tidusjar] - -- Missed brace. [tidusjar] - -- Removed. [tidusjar] - -- Fixed issues from the merge. [tidusjar] - -- Stupid &$(*£ merge. [tidusjar] - -- Angular. [tidusjar] - -- Reworked the custom notifications... again. Need to figure out how to find the view to the model. [tidusjar] - -- Fixed #417. [tidusjar] - -- Removed NinjectConventions, we hadn't started to use it anyway. [tidusjar] - -- Fixed the way we will be using custom messages. [tidusjar] - -- Test checkin. [tidusjar] - -- Better handling for #388. [tidusjar] - -- Fixed #412. [tidusjar] - -- Fixed #413. [tidusjar] - -- Fixed #409. [tidusjar] - -- Trycatch around the availbility checker. [tidusjar] - -- WIP on notification resolver. [tidusjar] - -- Tidy. [tidusjar] - -- Plugged in MediatR. [tidusjar] - -- Moved over to using Ninject. [tidusjar] - - -## v1.8.4 (2016-06-30) - -### **Fixes** - -- Fixed the bug where we were auto approving everything. Added French language into the navigation bar. [tidusjar] - - -## v1.8.3 (2016-06-29) - -### **New Features** - -- Update README.md. [Jamie] - -- Update appveyor.yml. [Jamie] - -- Added some of the backend bits for #182. [tidusjar] - -- Updates for #243. [tidusjar] - -- Added Dutch language #243. [tidusjar] - -- Added languages #243. [tidusjar] - -- Added logging #350. [tidusjar] - -### **Fixes** - -- Small changes. [tidusjar] - -- Allow html in the notice message. [tidusjar] - -- Some more unit tests around the NotificationMessageResolver. [tidusjar] - -- Fixed a timing bug found the in build. Note, when working with time differences use TotalDays. [tidusjar] - -- More translations on the search page (Mainly the notification messages) #243. [tidusjar] - -- Fixed some warnings. [tidusjar] - -- CodeCleanup. [tidusjar] - -- Fixed a bit of a stupid bug in the resetter and added unit tests around it to make sure this never happens again. [tidusjar] - -- Fixed an issue where we didn't provide the correct response when clearing the logs. [tidusjar] - -- Made it so users that are in the whitelist do not have a request limit. [tidusjar] - -- Made it so the request limit doesn't apply to admin users. [tidusjar] - -- Fixed where a user could see the delete button on the issues page. [tidusjar] - -- Fixed some small issues and improved the navbar. [tidusjar] - -- Translated the Requested page #243. [tidusjar] - -- Finished #337. [tidusjar] - -- Some analytics. [tidusjar] - -- More translations for #243 and welcome text for #293. [tidusjar] - -- Small bit of work for #359. [tidusjar] - -- Finished #6. [tidusjar] - -- Analytics and fixes. [tidusjar] - -- Translated the search page #243. [tidusjar] - -- Implemented the different languages and added the ability to change cultures. #243. [tidusjar] - -- Started #243. [tidusjar] - -- Fixed #364. [tidusjar] - -- Some more useful analytical information. [tidusjar] - -- Generic try catch to fix #350. [tidusjar] - -- Slight changes, moved the donate button. [tidusjar] - -- Potential fix for #350. [tidusjar] - -- Better way of obtaining clean enum string. [Drewster727] - -- Fixed #362. [tidusjar] - - -## v1.8.2 (2016-06-22) - -### **New Features** - -- Update readme. [tidusjar] - -- Update appveyor.yml. [Jamie] - -### **Fixes** - -- Fixed a circular reference issue. [tidusjar] - -- Small changes around how we work with custom events in the analytics. [tidusjar] - -- Fixed #353 #354 #355. [tidusjar] - -- Null provider check for movies. [Drewster727] - -- Show request type in notifications #346 and fix an issue from previous commit for #345. [Drewster727] - -- Add an option to stop sending notifications for requests that don't require approval #345. [Drewster727] - - -## v1.8.1 (2016-06-21) - -### **New Features** - -- Update appveyor.yml. [Jamie] - -### **Fixes** - -- Fix obj ref error when scheduler runs (ProviderId is null?) [Drewster727] - -- Fix logic for obtaining a sonarr quality profile #340. [Drewster727] - - -## v1.8.0 (2016-06-21) - -### **New Features** - -- Update README.md. [Jamie] - -- Update README.md. [Jamie] - -- Update README.md. [Jamie] - -- Added the new advanced search into the search page too. [tidusjar] - -- Change the way we configure the IoC container in the bootstrapper, we are registering all the concrete instances on application start rather than on each web request. This should increase the performance per HTTP request. [tidusjar] - -- Updated nlog and fixed #295. [tidusjar] - -### **Fixes** - -- Workaround for #334. [Drewster727] - -- Create .gitattributes. [Jamie] - -- Fixes to the issues. [tidusjar] - -- Set the defaults for the landing page. [tidusjar] - -- Revert branch to 664dae2. [tidusjar] - -- Some unit tests for the issues. [tidusjar] - -- Tidied up the bootstrapper. [tidusjar] - -- Fix up landing page UI. [Drewster727] - -- Fixed CSS issue with the top arrow in the Plex theme. [tidusjar] - -- Small changes. [tidusjar] - -- Done #318. [tidusjar] - -- Fixed tests. [tidusjar] - -- #298 added some tests for the landing page. [tidusjar] - -- We are now only keeping the latest 1000 log records in the database. Delete everything else. [tidusjar] - -- Some analytic stuff. [tidusjar] - -- Capture the TVDBID when requesting. [tidusjar] - -- Attempting to improve #219. [tidusjar] - -- Just some more async changes. [tidusjar] - -- Small changes. [tidusjar] - -- More work on #298. Everything wired up. [tidusjar] - -- Fixed the issue on the landing page #298. [tidusjar] - -- #298 moved the content to the left a bit. [tidusjar] - -- Styling for #298 done, just need to wire up the model and do the actual status check. [tidusjar] - -- Bumped up the version number. [tidusjar] - -- Removed some DumpJson() from the trace logs. [tidusjar] - -- Small ui fix (100% width user/password fields to improve mobile experience) [Drewster727] - -- Landing page stuff #298. [tidusjar] - -- Datepicker UI fixes + small landing page UI fix. [Drewster727] - -- Removed a change that shoudn't have been commited. [tidusjar] - -- Fixed tests. [tidusjar] - -- More work for #298. [tidusjar] - -- #273 added for only available content on the search. [tidusjar] - -- Fixed #303 Looks like there was some incorrect business logic. [tidusjar] - -- Most of #273 done. [tidusjar] - -- Settings done for #298. [tidusjar] - -- Started #298. [tidusjar] - -- A crap tonne of work on #273. [tidusjar] - -- More work on #273. [tidusjar] - -- Reduced kept logs for 2 days. [tidusjar] - -- Fixed #300. [tidusjar] - -- #273. [tidusjar] - -- Fixed a bug with some users with the CP profiles. [tidusjar] - -- #273. [tidusjar] - -- Done the same for TV. [tidusjar] - -- Fixes #296. [tidusjar] - -- More for #273. [tidusjar] - -- Small changes. [tidusjar] - -- Revert "Small changes" [tidusjar] - -- Small changes. [tidusjar] - -- Finished #221 and added more async #278. [tidusjar] - -- Spelling mistake in the html! this fixes #264. [tidusjar] - -- More work on #273. [tidusjar] - -- Fixed #210. [tidusjar] - -- Started #273. [tidusjar] - - -## v1.7.5 (2016-05-29) - -### **New Features** - -- Update preview. [Jamie] - -- Updated dapper.contrib. Looks like there was a bug in the async methods. [tidusjar] - -- Updater wouldn't work when running a reverse proxy #236. [tidusjar] - -### **Fixes** - -- Bump build ver. [tidusjar] - -- Use HTTPS for the poster images, so there aren't any mixed content warnings when serving the application via an HTTPS reverse proxy. [Sean Callinan] - -- Removed static declarations. [tidusjar] - -- Fixed styling on modal. [tidusjar] - -- Made the search page all async goodness #278. [tidusjar] - -- Made the request module async #278. [tidusjar] - -- Started some dynamic scrolling. [tidusjar] - -- Stop dumping out the settings to the log. [tidusjar] - -- Made more async goodness. [tidusjar] - -- Made some of the searching async #278. [tidusjar] - -- Fixed #277. [tidusjar] - -- Reworked some tests. [tidusjar] - -- #26q make the auth users list taller. [Drewster727] - -- Fix 404 error. [Drewster727] - -- #262 make the auth users list taller. [Drewster727] - -- #221 delete requests per category. [Drewster727] - -- #256 #237 UI Improvements and consolidation. [Drewster727] - -- Fixed a bug in the user notification where if an admin wants to be notified they wouldn't be. [tidusjar] - -- Set the admin to have all claims. [tidusjar] - -- Fix null exception possibility in cp/sickrage cacher classes. [Drewster727] - -- Fixed #244. [tidusjar] - -- Fixed #240. [tidusjar] - -- Fixed #270. [tidusjar] - -- Fixed an issue where if you have only 1 plex friend it would not show in the list. [tidusjar] - - -## v1.7.4 (2016-05-25) - -### **New Features** - -- Update README.md. [Jamie] - -### **Fixes** - -- Fixed #252. [tidusjar] - -- Fixed #428. [tidusjar] - -- Version bump. [tidusjar] - -- Fixed tests. [tidusjar] - -- Fully fixed #239. [tidusjar] - -- We wan't updating the DB schema. [tidusjar] - - -## v1.7.3 (2016-05-25) - -### **Fixes** - -- Fixed the release build issue where we could not access the settings #239. [tidusjar] - - -## v1.7.2 (2016-05-25) - -### **Fixes** - -- Fixed a small bug where an exception would get thrown. [tidusjar] - -- Build version bump. [tidusjar] - -- Cleanup. [tidusjar] - -- Typo. [tidusjar] - -- Fixed #241. [tidusjar] - -- Fixed #239. [tidusjar] - -- Fixed #238. [tidusjar] - -- Small UI tweaks/improvements. [Drewster727] - - -## v1.7.1 (2016-05-24) - -### **New Features** - -- Update version. [tidusjar] - -### **Fixes** - -- Fixed an issue with the auth page when running with a reverse proxy. [tidusjar] - - -## v1.7 (2016-05-24) - -### **New Features** - -- Update appveyor.yml. [Jamie] - -- Update README.md. [Jamie] - -- Update README.md. [Jamie] - -- Added the ability to get the apikey from the api if you provide a correct username and password. Added more unit tests Added the ability to change a users password using the api refactored the Usermapper and made it unit testsable. [tidusjar] - -- Update. [tidusjar] - -- Added in an audit table. Since we are now allowing multiple users to change and modify things we need to audit this. [TidusJar] - -- Added the updater to the soloution and did a bit of starting code. [TidusJar] - -- Updated the claims so we can support more users. Added a user management section (not yet complete) Added the api to the solution and a api key in the settings (currently only gets the requests). [TidusJar] - -- Updated packages. [TidusJar] - -- Added a retry handler into the solution. We can now retry failed api requests. [TidusJar] - -- Update README.md. [Jamie] - -- Added Released propety to RequestViewModel. Added Released filter to the Requests page. [Chris Lees] - -- Added #27 to albums. [tidusjar] - -- Added the actual notification part of #27. [tidusjar] - -- Added the missing baseurl bit on the login page for #72. [tidusjar] - -- Added the 'enable user notifications' to the email settings view and model. [tidusjar] - -- Update README.md. [Jamie] - -### **Fixes** - -- Remove pointless test, change the default theme and fix a small bug. [tidusjar] - -- Fixed api. [tidusjar] - -- Finished #26. [tidusjar] - -- Plex theme. [tidusjar] - -- Implimented a theme changer, waiting for the Plex theme. [tidusjar] - -- Finished #222 #205. [tidusjar] - -- Started working on #26. [tidusjar] - -- Undid some small changes that was checked in by accident. [tidusjar] - -- #164 has been resolved. [tidusjar] - -- Resolved #224 , Removed the 'SSL' option from the email notification settings. We will now use the correct secure socket options (SSL/TLS) for your email host. [tidusjar] - -- Small changes. [tidusjar] - -- #27 fully finished. [tidusjar] - -- Fixed #215. [tidusjar] - -- Using Mailkit to fix #204. [tidusjar] - -- Color. [tidusjar] - -- Fully finished #27 just need to test it! [tidusjar] - -- Fixed test. [tidusjar] - -- Styling for #27. [tidusjar] - -- I think the auto updater is finished! #29. [tidusjar] - -- I think we have finished the main bulk of the auto updater #29. [tidusjar] - -- #222 #205 more ! Started getting the settings out. [tidusjar] - -- Removed the service locator from the base classes and added in some Api tests added all the tests back in! [tidusjar] - -- More work on the api and documentation #222 #205. [tidusjar] - -- Started documenting the API we now have swagger under ~/apidocs #222 #205. [tidusjar] - -- Api work for #205 Refactored how we check if the user has a valid api key Added POST request, PUT and DELTE. [tidusjar] - -- First pass of the updater working. #29. [tidusjar] - -- Removed SIGHUP from the termination list #220. [tidusjar] - -- Fixed. [tidusjar] - -- Missing. [tidusjar] - -- Missed out a file. [TidusJar] - -- And some more... [TidusJar] - -- Missed some files. [TidusJar] - -- A bit more work on switching to using user claims so we can support multiple users. [TidusJar] - -- Made the store backup clean up some of the older backups (> 7 days). [TidusJar] - -- More work on the user management. [TidusJar] - -- - Notifications will no longer be send to the admins if they request something. - Looks like we missed out adding the notifications to Music requests, so I added that in. [TidusJar] - -- - Improved the RetryHandler. - Made the tester buttons on the settings pages a bit more robust and added an indication when it's testing (spinner) [TidusJar] - -- Packages. [TidusJar] - -- Nm, [TidusJar] - -- Downgraded packages. [TidusJar] - -- Better handling for #202. [TidusJar] - -- Finished #208 and #202. [TidusJar] - -- This should help #202. [TidusJar] - -- Resolved #209. [TidusJar] - -- Finished #209. [TidusJar] - -- Slight adjustments to #189. [tidusjar] - -- - Added a visual indication on the UI to tell the admin there is a update available. - We are now also recording the last scheduled run in the database. [tidusjar] - -- Did the login bit on #185. [tidusjar] - -- Finished #186. [tidusjar] - -- Fixed #185. [tidusjar] - -- Fixed issue in #27 with albums. [tidusjar] - -- #27 added TV Search to the notification. [tidusjar] - -- Fixed bug. [tidusjar] - -- More work on #27 Added a new notify button to the search UI (Needs styling). Also fixed a bug where if the user could only see their own requests, if they search for something that has been requested, it will show as requested. [tidusjar] - -- Improved the startup of the application. We now properaly parse any args passed into the console. [tidusjar] - -- Additional cacher error handling + don't bother checking the requests when we don't get data back from plex. [Drewster727] - -- Remove old migration code and added new migration code. [tidusjar] - -- Stop the Cachers from bombing out when the response from the 3rd party api returns an exception or invalid response. #171. [tidusjar] - -- Increase the scheduler cache timeframe to avoid losing cache when the remote api endpoints go offline (due to a reboot or some other reason) -- if they're online, the cache will get refreshed every 10 minutes like normal. [Drewster727] - -- Fix the cacher by adding locking + extra logging in the plex checker + use a const key for scheduler caching time. [Drewster727] - -- Small changes. [tidusjar] - -- Switched out the schedulers, this seems to be a better implimentation to the previous and is easier to add new "jobs" in. [tidusjar] - -- Fixed #168. [tidusjar] - -- Fixed #162. [tidusjar] - -- Fix saving the log level. [Drewster727] - -- Set the max json length (fixes large json response errors) [Drewster727] - - -## v1.6.1 (2016-04-16) - -### **New Features** - -- Update README.md. [Jamie] - -- Added a url base. [tidusjar] - -- Change default logging. [tidusjar] - -- Added logging around SickRage. [tidusjar] - -### **Fixes** - -- Bump up the version number ready for the release. [tidusjar] - -- BaseUrl is finally finished! #72. [tidusjar] - -- #72 Login page done. [tidusjar] - -- More changes for the urlbase #72. [tidusjar] - -- Done the auth, cp, logs and sidebar for #72. [tidusjar] - -- Add an extra check when determining if a tv show is already available (also check if it starts with the show name returned from the tv db) [Drewster727] - -- Cache plex library data regardless of whether we have requests in the database or not. [Drewster727] - -- By default don't use a url base. [tidusjar] - -- Return empty array when obtaining queued IDs in sickrage cacher. [Drewster727] - -- Fixed a small bug in the SR cacher. [tidusjar] - -- Fixed when we do not have a base. [tidusjar] - -- More changes for #72. [tidusjar] - -- Fixed exception and all areas will now use the base url #72. [tidusjar] - -- Removed the test code from #72. [tidusjar] - -- Commented out the unit tests as they need to be reworked now. [tidusjar] - -- Finally fixed #72. [tidusjar] - -- Remove test code from plex api GetLibrary method. [Drewster727] - -- Finished up the caching TODO's. [tidusjar] - -- Kick off the schedulers once the web app has started (fixes api errors on start) [Drewster727] - -- Converted the UI back down to .NET 4.5.2. [tidusjar] - -- Fixed #154. [tidusjar] - -- Revert everything (except PlexRequests.UI) back to .NET 4.5.2 -- fixes incompatibilities with the latest version of mono (4.2.3.4) -- fixes notifications not working #152 #147 #141. [Drewster727] - -- #150 start caching plex media as well. refactored the availability checker. NEEDS TESTING. also, we need to make the Requests hit the plex api directly rather than hitting the cache as it does now. [Drewster727] - -- #150 split out the cache subscriptions to make sure they subscribe properly. [Drewster727] - -- #150 sonarr/sickrage cache checking. sickrage has a couple small items left. [Drewster727] - -- Fixed args. [tidusjar] - -- Fixed. [tidusjar] - -- Made the base better. [tidusjar] - -- Remove couchpotato api test code. [Drewster727] - -- Start the initial couchpotato cache call on a separate thread to keep the startup process quick. [Drewster727] - -- Add csproj with file changes from previous commit. [Drewster727] - -- Cache the couchpotato wanted list, update it on an interval, and use it to determine if a movie has been queued already. [Drewster727] - -- I think i've fixed an issue where SickRage reports Show not found. [tidusjar] - -- Set the default log level to info. #141. [tidusjar] - -- #125 refactor async task logic to work with mono. [Drewster727] - -- Fix search spinner sticking around after clearing search text + make the "Requested" and "Available" indicators in the search page different colors. [Drewster727] - -- #125 start indicating in the results if an item is already requested or available. [Drewster727] - -- #145 firefox css dsplay issue. [Drewster727] - -- Fixes for sonarr, we now display the error messages back to the user. [tidusjar] - -- Fixed #144. [tidusjar] - - -## v1.6.0 (2016-04-06) - -### **New Features** - -- Changed the build number. [tidusjar] - -- Update README.md. [Drew] - -- Update README.md. [Drew] - -- Update README.md. [Drew] - -- Update README.md. [Drew] - -- Changed the title to a contains but the artist still must match, [tidusjar] - -- Added unit tests to cover the new changes to the availability checker. [tidusjar] - -- Added the music check in the Plex Checker. [tidusjar] - -- Changed around the startup so we cache the profiles after the DB has been created. [tidusjar] - -- Updated where we update the request blobs schema change. [tidusjar] - -- Update SearchModule.cs. [Jamie] - -- Update README.md. [Jamie] - -- Update README.md. [Jamie] - -- Change the new columns type. [tidusjar] - -- Added a DBSchema so we have an easier way to update the DB. [tidusjar] - -- Added an issue template. [tidusjar] - -- Update README.md. [Jamie] - -- Added back the username into the Session when the admin logs in. This means they do not have to log in twice. [tidusjar] - -- Added happy path tests for the Checker. [tidusjar] - -- Added music to the search and requests page. [tidusjar] - -- Added a scroll to the top thingy and a bit more work on headphones. [tidusjar] - -- Added some tests and fixed the issue where the DB would get created in the wrong place depending on how you launched the application. [tidusjar] - -- Added the settings page for #32. [tidusjar] - -- Update README.md. [Drewster727] - -- Update README.md. [Drewster727] - -- Update README.md. [Drewster727] - -- Update README.md. [Drewster727] - -- Update README.md. [Drewster727] - -- Update appveyor.yml. [Jamie] - -### **Fixes** - -- Some final tweaks for #32. [tidusjar] - -- Fixed a bug where if we are the admin we didn't add the request to the db. [tidusjar] - -- Fixed an issue where we would add the Sickrage series but it would fail on adding the seasons. [tidusjar] - -- Properly account for future/past dates when humanizing with moment. [Drewster727] - -- Properly display release date on requests page. [Drewster727] - -- Add missing reference for release mode. [Drewster727] - -- #139 remove dependency and usage of humanize() - should help with cross-platform issues. start using moment.js. [Drewster727] - -- Fix selectors for music list on request page to get sorting working. [Drewster727] - -- Fixed the error #32. [tidusjar] - -- Fixed the logs page. [tidusjar] - -- Another attempt at filtering #32. [tidusjar] - -- A bit more error handling #32. [tidusjar] - -- Improved the availabilty check to include music results #32. [tidusjar] - -- Small changes for #32. [tidusjar] - -- A bit more logging for #32. [tidusjar] - -- More headphones #32 I am starting to hate headphones... Sometimes the artists and albums just randomly fail. [tidusjar] - -- #134 temporary workaround for this. [Drewster727] - -- Task.run for startup caching + fix admin module unit test failures. [Drewster727] - -- Cache injection, error handling and logging on startup, etc. [Drewster727] - -- Tweaks for #32. [tidusjar] - -- #132 auto-approve for admins. [Drewster727] - -- Finished the bulk work for Headphones. Needs testing #32. [tidusjar] - -- Made the album search 10x faster. We are now loading the images in a seperate call. #32. [tidusjar] - -- Add a reference to API Interfaces to fix the build. [tidusjar] - -- #114 start caching quality profiles. Set the cache on startup and when obtaining quality profiles in settings. [Drewster727] - -- Work for #32. [tidusjar] - -- #114 first pass at choosing quality profile when approving + focus search input by default and when switching tabs. [Drewster727] - -- #131 fix for default selected tab. [Drewster727] - -- Remove references to obsolete RequestedBy property + start setting the db schema to the app version, and check that in the future for migrations. [Drewster727] - -- Fixed async issue. [Shannon Barrett] - -- Updating SickRage api to verify Season List is up to date. [Shannon Barrett] - -- Work on showing the requests for #32. [tidusjar] - -- Got the search finished up for #32. [tidusjar] - -- Remove test/temp code in UserLoginModule. [Drewster727] - -- A bit more work on #32 started working on requesting it. The DB is a bit of an issue... [tidusjar] - -- Most of the UI work done for #32. [tidusjar] - -- Basic search working for #32. [tidusjar] - -- Mono datetime offset workaround. [Drewster727] - -- #122 store utc time in the databse + obtain timezone offset of the client upon login + offset times returned to client based on session offset. [Drewster727] - -- Method reference bug fix. [Drewster727] - -- Fix search focus z-index issue (hid suggestions options) [Drewster727] - -- Minor search UI adjustments. [Drewster727] - -- #55 first attempt at "suggestions" starting with "Comming Soon" and "In Theaters" [Drewster727] - -- #106 rename sorting options and polish the dropdown UI a bit. [Drewster727] - -- Started adding the api part for headphones #32. [tidusjar] - -- Upped the time of #123. [tidusjar] - -- First attempt at #123. [tidusjar] - -- We now do not show the text Requested By to the user, we also show a 'success' message instead of a warning when something has already been requested. [tidusjar] - -- Show a "no requests yet" message on the requests page (for each cateogory) [Drewster727] - -- Ignore items that are already available when approving in bulk, and simplify the checking + compile css. [Drewster727] - -- Add a better way to merge RequestedBy and RequestedUsers to avoid code duplication and simplify checks. [Drewster727] - -- Don't query the session as much in the modules, rely on a variable from the base class and store the username as needed. [Drewster727] - -- Show the requested by user from legacy request models. [Drewster727] - -- Only show requested by users to admins + start maintaining a list of users with each request. [Drewster727] - -- #96 fix up notification test feature. [Drewster727] - -- Fix the request page sort/approve button alignment. [Drewster727] - -- When pulling requests, set each to approved that is already available (so the UI avoids showing the approve option for already available content) [Drewster727] - -- Mono doesn't seem to have Tls1.2. Let's try TLS 1 #119. [tidusjar] - -- Specify a protocol type of TLS12. Looks like CP doesn't seem to like SSL3 (it is quite old now so understandable) #119. [tidusjar] - -- Made #85 better. [tidusjar] - -- Fixed the tests. [tidusjar] - -- Made the feedback from Sonarr better when Sonarr already has the series #85. [tidusjar] - -- An attempt to fix #108. [tidusjar] - -- Add some "no results" feedback to the searching + minor UI improvements. [Drewster727] - -- Fix notification tests. [Drewster727] - -- UI - increase icon size of nav menu (they were too small before) [Drewster727] - -- #96 Finished adding test functionality to notifications. [Drewster727] - -- #96 add the necessary back-end code to produce a test message for all notification types (still have to add the test buttons for pushbullet/pushover) [Drewster727] - -- #96 modify notifications interface/service to accept a non-type specific settings object. [Drewster727] - -- #96 Email notification test button (others to come) [Drewster727] - -- Minor UI adjustments. [Drewster727] - -- #84 provide an option in settings to resttrict users from viewing requests other than their own. [Drewster727] - -- #54 comma separated list of users who don't require approval + fix a couple request messages (include show title) [Drewster727] - -- Clean up the sorting option names. add a way to see which filter/sort is currently applied. [Drewster727] - -- Fix up the animations. seems to be related to the data-bound attribute causing the animtions not to fire on each .mix object. [Drewster727] - -- Move approve buttons to the tab content. [Drewster727] - -- Allow approving all requests by category. [Drewster727] - -- Fix up sorting on the request page. [Drewster727] - -- Add ubuntu/debian instructions. [Drewster727] - -- #86 - display movie/show title + year in request notifications. [Drewster727] - -- Show the movie/show title when requesting. [Drewster727] - - -## v1.5.2 (2016-03-26) - -### **Fixes** - -- Stoped users from spamming the request button. [tidusjar] - -- Fixed the logger no longer writing to the file. [tidusjar] - -- Fixed #97. [tidusjar] - - -## v1.5.1 (2016-03-26) - -### **New Features** - -- Update appveyor.yml. [Jamie] - -- Added logs to the sidebar. I'm an idiot. [tidusjar] - -### **Fixes** - -- Approve tv shows or movies. [Drewster727] - -- Fixed a bug where if you had auto approve it wouldn't notify you. [tidusjar] - - -## v1.5.0 (2016-03-25) - -### **New Features** - -- Updated version number for release. [tidusjar] - -- Updated the logic for handling specific seasons in Sonarr and Sickrage. [Shannon Barrett] - -- Updated the readme and added some icons to the navbar. [tidusjar] - -- Added the ability to sepcify a username in the email notification settings for external MTA's. We have had to add a new option called Email Sender because of this. #78. [tidusjar] - -- Update README.md. [Jamie] - -- Update README.md. [Jamie] - -- Added a notification model to the notifiers. Added the backend work for sending a notification for an issue report #75. [tidusjar] - -- Added a subdir to CP, SickRage, Sonarr and Plex #43. [tidusjar] - -### **Fixes** - -- And again. [tidusjar] - -- Made the check actually work. [tidusjar] - -- Finished up #68 and #62. [tidusjar] - -- Finished styling on the logger for now. #59. [tidusjar] - -- Fixed #69. [tidusjar] - -- Working on getting the Sonarr component to work correctly. [Shannon Barrett] - -- Fixes issue #62. [Shannon Barrett] - -- Refactored the Notification service to how it should have really been done in the first place. [tidusjar] - -- Fixed the build. [tidusjar] - -- Finished #49. [tidusjar] - -- Finished #57. [tidusjar] - -- Small changes around the filtering. [tidusjar] - -- Finished adding pushover support. #44. [tidusjar] - -- Resolved #75. [tidusjar] - -- Include DB changes. [tidusjar] - -- Done most on #59. [tidusjar] - -- Lowercase logs folder, because you know, linux. #59. [tidusjar] - -- Adding the imdb when requesting. [tidusjar] - -- Fixed an issue where the table didn't match the model. [tidusjar] - -- Improved the status page with the suggestion from #29. [tidusjar] - -- Hooked up most of #49 Just the validation messages need to be done. [tidusjar] - -- Fixed #74 and #64. [tidusjar] - -- Resolved #70. [tidusjar] - -- Finished #71. [tidusjar] - -- Got the filter working on both movie and tv #57. [tidusjar] - -- Started #57, currently there is a bug where the TV list won't filter. [tidusjar] - - -## v1.4.1 (2016-03-20) - -### **New Features** - -- Update appveyor.yml. [Jamie] - -- Update AvailabilityUpdateService.cs. [Jamie] - - -## v1.4.0 (2016-03-19) - -### **New Features** - -- Update README.md. [Jamie] - -- Update README.md. [Jamie] - -- Update README.md. [Jamie] - -- Update README.md. [Jamie] - -- Updated the build version ready for the next release. [tidusjar] - -- Added the api and settings page for Sickrage. Just need to do the tester and hook it up #40. [tidusjar] - -- Added the option to set a CP quality #38. [tidusjar] - -- Added the code to lookup the old requests and refresh them with new information from TVMaze. [tidusjar] - -- Update StatusCheckerTests.cs. [Jamie] - -- Update README.md. [Jamie] - -- Added TVMaze to the search. #21. [tidusjar] - -- Added migration code and cleaned up the DB. [tidusjar] - -- Updated the way we add requests. [tidusjar] - -- Updated the Dapper.Contrib package, it had a bug where it wasn't returning the correct Id from inserts. [tidusjar] - -### **Fixes** - -- This fixes #36. [tidusjar] - -- Should fix issue #36. [Shannon Barrett] - -- When we do a batch update we need to reset the cache. [tidusjar] - -- Fixed an issue where the default quality on Sickrage wouldn't work. [tidusjar] - -- Wow, that was a lot of work. - So, I have now finished #40. - Fixed a bug where we was not choosing the correct tv series (Because of TVMaze) - Fixed a bug when checking for plex titles - Fixed a bug where the wrong issue would clean on the UI (DB was correct) - Refactored how we send tv shows - And too many small changes to count. [tidusjar] - -- Fixed the new dependancy with the admin class tests. [tidusjar] - -- Back to what it was :( [tidusjar] - -- Another test for #37. [tidusjar] - -- This should fix #37. [Jamie Rees] - -- Catch the missing table exception when they have a new DB. [Jamie Rees] - -- Exploratory test for #37. [Jamie Rees] - -- Fixed #33 we now have SSL options for Sonarr and CP. [Jamie Rees] - -- Removed all the html from the new TVMaze api (for overview). Added tests to cover the html removal. updated Readme to remove TheTVDB. [Jamie Rees] - -- Fixed tests. [Jamie Rees] - -- Almost fully integrated TVMaze #21 and also improved the fix for #31. [Jamie Rees] - -- Should fix #28. [Shannon Barrett] - -- Fixed #16 and #30. [tidusjar] - -- Modified the adding of request to update the model with the added ID. [tidusjar] - -- Switched over to the new service. [tidusjar] - -- Fixed #25. [Jamie Rees] - - -## v1.3.0 (2016-03-17) - -### **New Features** - -- Added pushbullet to the sidebar. [Jamie Rees] - -- Updated build version for the next release. [Jamie Rees] - -- Updated readme link. [tidusjar] - -- Added ignore to static tests. [tidusjar] - -- Added Pushbullet notifications #8. [tidusjar] - -- Added first implimentation of the Notification Service #8 Added tests to cover the notification service. [tidusjar] - -- Added validation to the Email settings, also increased the availability checker from 2 minutes to 5. [tidusjar] - -### **Fixes** - -- Fixed #22. [Jamie Rees] - -- Started on #16, nothing is hooked up yet. [tidusjar] - -- Fixed tests. [tidusjar] - - -## v1.2.1 (2016-03-16) - -### **New Features** - -- Update Program.cs. [Jamie] - -- Update Program.cs. [Jamie] - -- Added back the reference. [tidusjar] - -### **Fixes** - -- Removed the email notification settings from the settings (for release 1.2.1) [Jamie Rees] - -- Fixed. [Jamie Rees] - -- Resolved #10. [tidusjar] - - -## v1.2.0 (2016-03-15) - -### **New Features** - -- Updated. [Jamie Rees] - -- Updated appveyor. [Jamie Rees] - -- Update appveyor.yml. [Jamie] - -- Added latest version code and view. Need to finish the view #11. [tidusjar] - -- Added test button to Plex. That's fixed #9. [tidusjar] - -- Added test sonarr button #9. [tidusjar] - -- Added more tests. [tidusjar] - -- Added a bunch of logging. [tidusjar] - -- Added the application tester for CP #9. [tidusjar] - -- Added settings page for #8. [tidusjar] - -- Added pace.js. [tidusjar] - -### **Fixes** - -- Finished the notes! Resolved #7. [Jamie Rees] - -- #12. [Jamie Rees] - -- #12. [Jamie Rees] - -- Finished the status page #11 and some more work to #12. [Jamie Rees] - -- Resolved #7. [tidusjar] - -- Small changes. [tidusjar] - -- Yeah... [tidusjar] - -- Fixed #5 and also added some tests to the availability checker. [tidusjar] - -- Started added tests. [Jamie Rees] - -- Fixed an issue where the issues text appears larger. [Jamie Rees] - - -## v1.1 (2016-03-13) - -### **New Features** - -- Update appveyor.yml. [Jamie] - -- Updated readme. [Jamie Rees] - -- Added the support for TV Series integrating with Sonarr. [Jamie Rees] - -- Added the functionality to pass a port through an argument. [tidusjar] - -- Added the code to get the quality profiles from Sonarr Started plugging that into the UI. [Jamie Rees] - -- Added the spinners #3. [tidusjar] - -- Added the functionality for the admin to clear the issues. [tidusjar] - -- Added the issues to the requests page. [tidusjar] - -- Added user logout method and unit tests to cover it. [tidusjar] - -- Added DeniedUsers to the view. [tidusjar] - -- Added the denied user check to the UserLoginModule. added a test case to cover it. [tidusjar] - -- Added a missing reference. [tidusjar] - -- Added first real test. [tidusjar] - -- Update README.md. [Jamie] - -- Added the latest version of nuget. [tidusjar] - -- Added travisyml. [tidusjar] - -- Added logging. [tidusjar] - -- Added missing files. [tidusjar] - -- Update README.md. [Jamie] - -- Added logging (Still WIP) [tidusjar] - -- Added favicon and also structured the HTML correctly. [tidusjar] - -- Updated the packages so everything is now with the correct framework (4.5.2) [tidusjar] - -- Added in deletion of requests. [tidusjar] - -- Added test code. [tidusjar] - -- Added dashboard. [tidusjar] - -- Added couchpotato page. [Jamie Rees] - -- Added readme to the project and updated it. [Jamie Rees] - -- Added helpers. [tidusjar] - -### **Fixes** - -- Bug fix, Couchpotato settings wouldn't show in release due to a Nancy bug. [Jamie Rees] - -- Small changes. [Jamie Rees] - -- First release, build 1.0.0. [Jamie Rees] - -- Removed the request limit since it's not currently being used. [Jamie Rees] - -- REmoved Sickbeared for the first release. [Jamie Rees] - -- Fixed #4 We now can manually set the status of a request. [tidusjar] - -- Made the pass in the port a bit more robust. [tidusjar] - -- Styling, Added the functionality for the Sonarr Profiles on the Admin page #2 resolved. [tidusjar] - -- Fixed a bug in the Login and added a unit test to cover that. Added a button to approve an individual request. Fixed some minor bugs in the request screen. [Jamie Rees] - -- Fixed the 'responsive' issue for the search and requests pages #3. [tidusjar] - -- Styling! #3. [tidusjar] - -- Navbar category now will follow you to various screens #3. [tidusjar] - -- Fixed bugs with the 'other' reporting issue and also the clear issues. [tidusjar] - -- We now are appending the users name to who wrote the comment. Rather than it being unknown. [tidusjar] - -- More work on submitting issues. [tidusjar] - -- More test changes. [tidusjar] - -- More tests to cover the login. [tidusjar] - -- Refactoring. [tidusjar] - -- Implimented the password part and authentication with Plex. [tidusjar] - -- Initial Use authentication is working. Need to do the password bit. [tidusjar] - -- Some error handling and ensure we are an admin to delete requests. [tidusjar] - -- Fixed the issue where the Release build would not show the admin screens! [tidusjar] - -- Fixes. [tidusjar] - -- Removed the DI part of the service. TinyIOC doesn't want to work with FluentScheduler. [tidusjar] - -- First pass at the plex update service. [tidusjar] - -- Small changes. [Jamie Rees] - -- Started to impliment the Plex checker. This will check plex every x minutes to see if there is any new content and then update the avalibility of the requests. [Jamie Rees] - -- Mre work. [Jamie Rees] - -- Few small changes, added plex settings. [Jamie Rees] - -- Making the configuration actually do something. Setting a default configuration if there is no DB. [Jamie Rees] - -- Remove post build. [Jamie Rees] - -- Small changes. [Jamie Rees] - -- MOre work. [Jamie Rees] - -- Fixed the issue when sending movies to CouchPotato. [Jamie Rees] - -- Add appveyor. [tidusjar] - -- Build it on 4.5. [tidusjar] - -- Upgraded .net to 4.6. [tidusjar] - -- Typo2. [tidusjar] - -- Typo. [tidusjar] - -- Another update. [tidusjar] - -- Fixed. [tidusjar] - -- More logging to figure out why the we cannot access the admin module in a release build. [tidusjar] - -- Firstpass integrating with CouchPotato. [tidusjar] - -- Some styling. [tidusjar] - -- Fixed the plex friends. Added some unit tests, moved the plex auth into it's own page. [tidusjar] - -- Fully switched the TV shows over to use the other provider. [Jamie Rees] - -- Renamed folders. [tidusjar] - -- Assembly updates. [tidusjar] - -- Moved the rest of the projects. [tidusjar] - -- Moved UI. [tidusjar] - -- Mass rename. [tidusjar] - -- Quick changes. [tidusjar] - -- Started switching the TV over to the new provider (TheTVDB). Currently TV search is partially broken. It will search but we are not mapping all of the details. [tidusjar] - -- Implimented the new TV show Provider (needed for Sonarr TheTvDB) [tidusjar] - -- Started the user auth. [tidusjar] - -- Some work on the requests page. [tidusjar] - -- Made the 'requested' better and made the remove look nicer. [tidusjar] - -- Cleaned up the program a tiny bit. [tidusjar] - -- Removed additional namespace. [tidusjar] - -- Fixed some db issues and added a preview. [Jamie Rees] - -- More work on the settings. [Jamie Rees] - -- Upgraded Json.Net and Nancy packages. [Jamie Rees] - -- Plex friends api. [Jamie Rees] - -- Enabled trace logs. [tidusjar] - -- Sql syntax issue fixed. [tidusjar] - -- Fixed release build. [tidusjar] - -- Small updates including assembly version. [tidusjar] - -- Work on the requests page mostly done. [tidusjar] - -- Work on the TV request. the `latest` parameter is not being passed into the requestTvshow. [tidusjar] - -- Missing file. [tidusjar] - -- Using the IoC container now. [tidusjar] - -- Some plex work. [Jamie Rees] - -- More work. [Jamie Rees] - -- Removed the setup code out of the startup, since we attemtp to connect to the DB before that. [Jamie Rees] - -- Some more work. Need to stop the form submitting on a request. [tidusjar] - -- Moved everything up a directory. [tidusjar] - -- Lots of work! [tidusjar] - -- Done most of the movie search work. [Jamie Rees] - -- First pass with RequestPlex. [tidusjar] - -- Initial commit. [Jamie] - - From e3adbcb51e5a43e733160a9889e31f3d8af47f44 Mon Sep 17 00:00:00 2001 From: Jamie Rees Date: Tue, 24 Apr 2018 14:05:18 +0100 Subject: [PATCH 32/37] Fixed the error from upgrading the hangfire package !wip --- 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 be78c5b11..fc80be838 100644 --- a/src/Ombi/Startup.cs +++ b/src/Ombi/Startup.cs @@ -134,7 +134,7 @@ namespace Ombi { x.UseSQLiteStorage(sqliteStorage); x.UseActivator(new IoCJobActivator(services.BuildServiceProvider())); - x.UseConsole(); + //x.UseConsole(); }); From 69e324c6701ce373c7562f9b6ef02f16e0f7277d Mon Sep 17 00:00:00 2001 From: Jamie Rees Date: Tue, 24 Apr 2018 14:23:07 +0100 Subject: [PATCH 33/37] Added Paging to the Movie Requests Page --- .../app/requests/movierequests.component.html | 4 +++- .../app/requests/movierequests.component.ts | 14 ++++++++------ src/Ombi/ClientApp/app/requests/requests.module.ts | 3 ++- src/Ombi/ClientApp/styles/base.scss | 4 ++++ 4 files changed, 17 insertions(+), 8 deletions(-) diff --git a/src/Ombi/ClientApp/app/requests/movierequests.component.html b/src/Ombi/ClientApp/app/requests/movierequests.component.html index 8eded8dba..19814b330 100644 --- a/src/Ombi/ClientApp/app/requests/movierequests.component.html +++ b/src/Ombi/ClientApp/app/requests/movierequests.component.html @@ -56,7 +56,7 @@
-
+
@@ -212,6 +212,8 @@
+ +
diff --git a/src/Ombi/ClientApp/app/requests/movierequests.component.ts b/src/Ombi/ClientApp/app/requests/movierequests.component.ts index e28b86eaa..bb6e17b7f 100644 --- a/src/Ombi/ClientApp/app/requests/movierequests.component.ts +++ b/src/Ombi/ClientApp/app/requests/movierequests.component.ts @@ -8,7 +8,7 @@ import { Subject } from "rxjs/Subject"; import { AuthService } from "../auth/auth.service"; import { NotificationService, RadarrService, RequestService } from "../services"; -import { FilterType, IFilter, IIssueCategory, IMovieRequests, IRadarrProfile, IRadarrRootFolder } from "../interfaces"; +import { FilterType, IFilter, IIssueCategory, IMovieRequests, IPagenator, IRadarrProfile, IRadarrRootFolder } from "../interfaces"; @Component({ selector: "movie-requests", @@ -65,8 +65,8 @@ export class MovieRequestsComponent implements OnInit { } public ngOnInit() { - this.amountToLoad = 100; - this.currentlyLoaded = 100; + this.amountToLoad = 10; + this.currentlyLoaded = 10; this.loadInit(); this.isAdmin = this.auth.hasRole("admin") || this.auth.hasRole("poweruser"); this.filter = { @@ -74,8 +74,10 @@ export class MovieRequestsComponent implements OnInit { statusFilter: FilterType.None}; } - public loadMore() { - this.loadRequests(this.amountToLoad, this.currentlyLoaded); + public paginate(event: IPagenator) { + const skipAmount = event.first; + + this.loadRequests(this.amountToLoad, skipAmount); } public search(text: any) { @@ -226,7 +228,7 @@ export class MovieRequestsComponent implements OnInit { if(!this.movieRequests) { this.movieRequests = []; } - this.movieRequests.push.apply(this.movieRequests, x); + this.movieRequests = x; this.currentlyLoaded = currentlyLoaded + amountToLoad; }); } diff --git a/src/Ombi/ClientApp/app/requests/requests.module.ts b/src/Ombi/ClientApp/app/requests/requests.module.ts index 7bddee71c..18be5ed25 100644 --- a/src/Ombi/ClientApp/app/requests/requests.module.ts +++ b/src/Ombi/ClientApp/app/requests/requests.module.ts @@ -6,7 +6,7 @@ import { OrderModule } from "ngx-order-pipe"; import { InfiniteScrollModule } from "ngx-infinite-scroll"; -import { ButtonModule, DialogModule } from "primeng/primeng"; +import { ButtonModule, DialogModule, PaginatorModule } from "primeng/primeng"; import { MovieRequestsComponent } from "./movierequests.component"; // Request import { RequestComponent } from "./request.component"; @@ -36,6 +36,7 @@ const routes: Routes = [ SharedModule, SidebarModule, OrderModule, + PaginatorModule, ], declarations: [ RequestComponent, diff --git a/src/Ombi/ClientApp/styles/base.scss b/src/Ombi/ClientApp/styles/base.scss index 3a5deb2a3..2f9b35982 100644 --- a/src/Ombi/ClientApp/styles/base.scss +++ b/src/Ombi/ClientApp/styles/base.scss @@ -959,3 +959,7 @@ a > h4:hover { width: 94%; } +.ui-state-active { + background-color: $primary-colour-outline $i; + color: black $i; +} \ No newline at end of file From 0b2e488e8f42f90e63065a6e174d48d9d3a3773b Mon Sep 17 00:00:00 2001 From: Jamie Rees Date: Tue, 24 Apr 2018 14:44:22 +0100 Subject: [PATCH 34/37] Added paging to the TV Requests page --- .../Engine/Interfaces/IMovieRequestEngine.cs | 1 + .../Engine/Interfaces/IRequestEngine.cs | 1 + src/Ombi.Core/Engine/MovieRequestEngine.cs | 13 +++++++++ src/Ombi.Core/Engine/TvRequestEngine.cs | 17 ++++++++++-- .../app/requests/movierequests.component.html | 2 +- .../app/requests/movierequests.component.ts | 2 ++ .../app/requests/tvrequests.component.html | 1 + .../app/requests/tvrequests.component.ts | 27 +++++++++---------- .../ClientApp/app/services/request.service.ts | 8 ++++++ src/Ombi/Controllers/RequestController.cs | 18 +++++++++++++ 10 files changed, 73 insertions(+), 17 deletions(-) diff --git a/src/Ombi.Core/Engine/Interfaces/IMovieRequestEngine.cs b/src/Ombi.Core/Engine/Interfaces/IMovieRequestEngine.cs index bfeb4fbe0..2f765fede 100644 --- a/src/Ombi.Core/Engine/Interfaces/IMovieRequestEngine.cs +++ b/src/Ombi.Core/Engine/Interfaces/IMovieRequestEngine.cs @@ -18,5 +18,6 @@ namespace Ombi.Core.Engine.Interfaces Task ApproveMovieById(int requestId); Task DenyMovieById(int modelId); Task> Filter(FilterViewModel vm); + } } \ No newline at end of file diff --git a/src/Ombi.Core/Engine/Interfaces/IRequestEngine.cs b/src/Ombi.Core/Engine/Interfaces/IRequestEngine.cs index bfb8be4e8..5dbf6e449 100644 --- a/src/Ombi.Core/Engine/Interfaces/IRequestEngine.cs +++ b/src/Ombi.Core/Engine/Interfaces/IRequestEngine.cs @@ -17,5 +17,6 @@ namespace Ombi.Core.Engine.Interfaces Task MarkUnavailable(int modelId); Task MarkAvailable(int modelId); + Task GetTotal(); } } \ No newline at end of file diff --git a/src/Ombi.Core/Engine/MovieRequestEngine.cs b/src/Ombi.Core/Engine/MovieRequestEngine.cs index f4b0ee48c..0e47a28f6 100644 --- a/src/Ombi.Core/Engine/MovieRequestEngine.cs +++ b/src/Ombi.Core/Engine/MovieRequestEngine.cs @@ -144,6 +144,19 @@ namespace Ombi.Core.Engine return allRequests; } + public async Task GetTotal() + { + var shouldHide = await HideFromOtherUsers(); + if (shouldHide.Hide) + { + return await MovieRepository.GetWithUser(shouldHide.UserId).CountAsync(); + } + else + { + return await MovieRepository.GetWithUser().CountAsync(); + } + } + /// /// Gets the requests. /// diff --git a/src/Ombi.Core/Engine/TvRequestEngine.cs b/src/Ombi.Core/Engine/TvRequestEngine.cs index 44eec3fd1..c3f1139bf 100644 --- a/src/Ombi.Core/Engine/TvRequestEngine.cs +++ b/src/Ombi.Core/Engine/TvRequestEngine.cs @@ -130,7 +130,7 @@ namespace Ombi.Core.Engine var newRequest = tvBuilder.CreateNewRequest(tv); return await AddRequest(newRequest.NewRequest); } - + public async Task> GetRequests(int count, int position) { var shouldHide = await HideFromOtherUsers(); @@ -280,7 +280,7 @@ namespace Ombi.Core.Engine results.Background = PosterPathHelper.FixBackgroundPath(request.Background); results.QualityOverride = request.QualityOverride; results.RootFolder = request.RootFolder; - + await TvRepository.Update(results); return results; } @@ -432,6 +432,19 @@ namespace Ombi.Core.Engine }; } + public async Task GetTotal() + { + var shouldHide = await HideFromOtherUsers(); + if (shouldHide.Hide) + { + return await TvRepository.Get(shouldHide.UserId).CountAsync(); + } + else + { + return await TvRepository.Get().CountAsync(); + } + } + private async Task AddExistingRequest(ChildRequests newRequest, TvRequests existingRequest) { // Add the child diff --git a/src/Ombi/ClientApp/app/requests/movierequests.component.html b/src/Ombi/ClientApp/app/requests/movierequests.component.html index 19814b330..2431b5477 100644 --- a/src/Ombi/ClientApp/app/requests/movierequests.component.html +++ b/src/Ombi/ClientApp/app/requests/movierequests.component.html @@ -213,7 +213,7 @@
- + diff --git a/src/Ombi/ClientApp/app/requests/movierequests.component.ts b/src/Ombi/ClientApp/app/requests/movierequests.component.ts index bb6e17b7f..3339b6941 100644 --- a/src/Ombi/ClientApp/app/requests/movierequests.component.ts +++ b/src/Ombi/ClientApp/app/requests/movierequests.component.ts @@ -39,6 +39,7 @@ export class MovieRequestsComponent implements OnInit { public order: string = "requestedDate"; public reverse = false; + public totalMovies: number = 100; private currentlyLoaded: number; private amountToLoad: number; @@ -269,6 +270,7 @@ export class MovieRequestsComponent implements OnInit { } private loadInit() { + this.requestService.getTotalMovies().subscribe(x => this.totalMovies = x); this.requestService.getMovieRequests(this.amountToLoad, 0) .subscribe(x => { this.movieRequests = x; diff --git a/src/Ombi/ClientApp/app/requests/tvrequests.component.html b/src/Ombi/ClientApp/app/requests/tvrequests.component.html index 9a3c4d186..c15166e96 100644 --- a/src/Ombi/ClientApp/app/requests/tvrequests.component.html +++ b/src/Ombi/ClientApp/app/requests/tvrequests.component.html @@ -120,6 +120,7 @@ + { - this.tvRequests = x; - this.currentlyLoaded = this.currentlyLoaded + this.amountToLoad; - }); + public paginate(event: IPagenator) { + const skipAmount = event.first; + + this.requestService.getTvRequestsTree(this.amountToLoad, skipAmount) + .subscribe(x => { + this.tvRequests = x; + this.currentlyLoaded = this.currentlyLoaded + this.amountToLoad; + }); } public search(text: any) { @@ -197,6 +195,7 @@ export class TvRequestsComponent implements OnInit { } private loadInit() { + this.requestService.getTotalTv().subscribe(x => this.totalTv = x); this.requestService.getTvRequestsTree(this.amountToLoad, 0) .subscribe(x => { this.tvRequests = x; diff --git a/src/Ombi/ClientApp/app/services/request.service.ts b/src/Ombi/ClientApp/app/services/request.service.ts index a2757427d..c06b39434 100644 --- a/src/Ombi/ClientApp/app/services/request.service.ts +++ b/src/Ombi/ClientApp/app/services/request.service.ts @@ -20,6 +20,14 @@ export class RequestService extends ServiceHelpers { return this.http.post(`${this.url}Movie/`, JSON.stringify(movie), {headers: this.headers}); } + public getTotalMovies(): Observable { + return this.http.get(`${this.url}Movie/total`, {headers: this.headers}); + } + + public getTotalTv(): Observable { + return this.http.get(`${this.url}tv/total`, {headers: this.headers}); + } + public requestTv(tv: ITvRequestViewModel): Observable { return this.http.post(`${this.url}TV/`, JSON.stringify(tv), {headers: this.headers}); } diff --git a/src/Ombi/Controllers/RequestController.cs b/src/Ombi/Controllers/RequestController.cs index 59bd75606..5b83a7981 100644 --- a/src/Ombi/Controllers/RequestController.cs +++ b/src/Ombi/Controllers/RequestController.cs @@ -37,6 +37,15 @@ namespace Ombi.Controllers return await MovieRequestEngine.GetRequests(count, position); } + /// + /// Gets the total amount of movie requests. + /// + [HttpGet("movie/total")] + public async Task GetTotalMovies() + { + return await MovieRequestEngine.GetTotal(); + } + /// /// Gets all movie requests. /// @@ -146,6 +155,15 @@ namespace Ombi.Controllers return await TvRequestEngine.GetRequestsTreeNode(count, position); } + /// + /// Gets the total amount of TV requests. + /// + [HttpGet("tv/total")] + public async Task GetTotalTV() + { + return await TvRequestEngine.GetTotal(); + } + /// /// Gets the tv requests. /// From d80c50a40a2333c19cfd1b9571624f24b4ab60c2 Mon Sep 17 00:00:00 2001 From: Jamie Rees Date: Tue, 24 Apr 2018 14:50:44 +0100 Subject: [PATCH 35/37] !wip changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 49d85aca7..e94945927 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ ### **New Features** +- Added paging to the TV Requests page. [Jamie Rees] + +- Added Paging to the Movie Requests Page. [Jamie Rees] + - Updated Mailkit dependancy. [Jamie Rees] - Update Hangfire, Newtonsoft and Swagger. [Jamie Rees] From 73340d4fb65a549617a9eb6c3249a41f84e059f5 Mon Sep 17 00:00:00 2001 From: Jamie Date: Tue, 24 Apr 2018 21:13:52 +0100 Subject: [PATCH 36/37] moved the Plex OAuth setting to the Authentication Settings !wip --- .../Settings/Models/AuthenticationSettings.cs | 1 + .../Settings/Models/External/PlexSettings.cs | 1 - src/Ombi/ClientApp/app/interfaces/ISettings.ts | 2 +- src/Ombi/ClientApp/app/login/login.component.html | 4 ++-- src/Ombi/ClientApp/app/login/login.component.ts | 1 - src/Ombi/ClientApp/app/services/settings.service.ts | 4 ---- .../authentication/authentication.component.html | 7 +++++++ .../authentication/authentication.component.ts | 1 + .../ClientApp/app/settings/plex/plex.component.html | 6 ------ src/Ombi/Controllers/SettingsController.cs | 10 ---------- 10 files changed, 12 insertions(+), 25 deletions(-) diff --git a/src/Ombi.Settings/Settings/Models/AuthenticationSettings.cs b/src/Ombi.Settings/Settings/Models/AuthenticationSettings.cs index 9ce5c72ac..f6736e7c5 100644 --- a/src/Ombi.Settings/Settings/Models/AuthenticationSettings.cs +++ b/src/Ombi.Settings/Settings/Models/AuthenticationSettings.cs @@ -12,5 +12,6 @@ namespace Ombi.Settings.Settings.Models public bool RequireLowercase { get; set; } public bool RequireNonAlphanumeric { get; set; } public bool RequireUppercase { get; set; } + public bool EnableOAuth { get; set; } // Plex OAuth } } \ No newline at end of file diff --git a/src/Ombi.Settings/Settings/Models/External/PlexSettings.cs b/src/Ombi.Settings/Settings/Models/External/PlexSettings.cs index a77b54a87..3faba3e42 100644 --- a/src/Ombi.Settings/Settings/Models/External/PlexSettings.cs +++ b/src/Ombi.Settings/Settings/Models/External/PlexSettings.cs @@ -6,7 +6,6 @@ namespace Ombi.Core.Settings.Models.External public sealed class PlexSettings : Ombi.Settings.Settings.Models.Settings { public bool Enable { get; set; } - public bool EnableOAuth { get; set; } public List Servers { get; set; } } diff --git a/src/Ombi/ClientApp/app/interfaces/ISettings.ts b/src/Ombi/ClientApp/app/interfaces/ISettings.ts index 234e0aa5b..bf429d6d5 100644 --- a/src/Ombi/ClientApp/app/interfaces/ISettings.ts +++ b/src/Ombi/ClientApp/app/interfaces/ISettings.ts @@ -43,7 +43,6 @@ export interface IEmbyServer extends IExternalSettings { export interface IPlexSettings extends ISettings { enable: boolean; - enableOAuth: boolean; servers: IPlexServer[]; } @@ -146,6 +145,7 @@ export interface IAuthenticationSettings extends ISettings { requiredLowercase: boolean; requireNonAlphanumeric: boolean; requireUppercase: boolean; + enableOAuth: boolean; } export interface IUserManagementSettings extends ISettings { diff --git a/src/Ombi/ClientApp/app/login/login.component.html b/src/Ombi/ClientApp/app/login/login.component.html index 40a5ef5a0..d87b7121e 100644 --- a/src/Ombi/ClientApp/app/login/login.component.html +++ b/src/Ombi/ClientApp/app/login/login.component.html @@ -17,7 +17,7 @@ include the remember me checkbox

-
+
@@ -41,7 +41,7 @@ include the remember me checkbox
-
+
+
+
+ + +
+
+