From a4da4d0cc5b07eb9fbe339642816f0368d5a4aa0 Mon Sep 17 00:00:00 2001 From: Jamie Rees Date: Fri, 29 Jun 2018 21:46:08 +0100 Subject: [PATCH 01/26] Emby Improvements: Batch up the amount we get from the server. --- src/Ombi.Api.Emby/EmbyApi.cs | 28 +++++-- src/Ombi.Api.Emby/IEmbyApi.cs | 11 ++- .../Jobs/Emby/EmbyContentSync.cs | 73 ++++++++++++------- .../Jobs/Emby/EmbyEpisodeSync.cs | 60 ++++++++------- 4 files changed, 114 insertions(+), 58 deletions(-) diff --git a/src/Ombi.Api.Emby/EmbyApi.cs b/src/Ombi.Api.Emby/EmbyApi.cs index 3ac70c844..714ce2d28 100644 --- a/src/Ombi.Api.Emby/EmbyApi.cs +++ b/src/Ombi.Api.Emby/EmbyApi.cs @@ -99,9 +99,9 @@ namespace Ombi.Api.Emby return await Api.Request>(request); } - public async Task> GetAllMovies(string apiKey, string userId, string baseUri) + public async Task> GetAllMovies(string apiKey, int startIndex, int count, string userId, string baseUri) { - return await GetAll("Movie", apiKey, userId, baseUri, true); + return await GetAll("Movie", apiKey, userId, baseUri, true, startIndex, count); } public async Task> GetAllEpisodes(string apiKey, string userId, string baseUri) @@ -109,9 +109,9 @@ namespace Ombi.Api.Emby return await GetAll("Episode", apiKey, userId, baseUri); } - public async Task> GetAllShows(string apiKey, string userId, string baseUri) + public async Task> GetAllShows(string apiKey, int startIndex, int count, string userId, string baseUri) { - return await GetAll("Series", apiKey, userId, baseUri); + return await GetAll("Series", apiKey, userId, baseUri, false, startIndex, count); } public async Task GetSeriesInformation(string mediaId, string apiKey, string userId, string baseUrl) @@ -145,7 +145,25 @@ namespace Ombi.Api.Emby request.AddQueryString("IncludeItemTypes", type); request.AddQueryString("Fields", includeOverview ? "ProviderIds,Overview" : "ProviderIds"); - request.AddQueryString("VirtualItem","False"); + request.AddQueryString("VirtualItem", "False"); + + AddHeaders(request, apiKey); + + + var obj = await Api.Request>(request); + return obj; + } + private async Task> GetAll(string type, string apiKey, string userId, string baseUri, bool includeOverview, int startIndex, int count) + { + var request = new Request($"emby/users/{userId}/items", baseUri, HttpMethod.Get); + + request.AddQueryString("Recursive", true.ToString()); + request.AddQueryString("IncludeItemTypes", type); + request.AddQueryString("Fields", includeOverview ? "ProviderIds,Overview" : "ProviderIds"); + request.AddQueryString("startIndex", startIndex.ToString()); + request.AddQueryString("limit", count.ToString()); + + request.AddQueryString("VirtualItem", "False"); AddHeaders(request, apiKey); diff --git a/src/Ombi.Api.Emby/IEmbyApi.cs b/src/Ombi.Api.Emby/IEmbyApi.cs index 625ae3c13..88f14f24f 100644 --- a/src/Ombi.Api.Emby/IEmbyApi.cs +++ b/src/Ombi.Api.Emby/IEmbyApi.cs @@ -14,9 +14,14 @@ namespace Ombi.Api.Emby Task LogIn(string username, string password, string apiKey, string baseUri); Task LoginConnectUser(string username, string password); - Task> GetAllMovies(string apiKey, string userId, string baseUri); - Task> GetAllEpisodes(string apiKey, string userId, string baseUri); - Task> GetAllShows(string apiKey, string userId, string baseUri); + Task> GetAllMovies(string apiKey, int startIndex, int count, string userId, + string baseUri); + + Task> GetAllEpisodes(string apiKey, int startIndex, int count, string userId, + string baseUri); + + Task> GetAllShows(string apiKey, int startIndex, int count, string userId, + string baseUri); Task> GetCollection(string mediaId, string apiKey, string userId, string baseUrl); diff --git a/src/Ombi.Schedule/Jobs/Emby/EmbyContentSync.cs b/src/Ombi.Schedule/Jobs/Emby/EmbyContentSync.cs index af8125ab2..0e4b0a74f 100644 --- a/src/Ombi.Schedule/Jobs/Emby/EmbyContentSync.cs +++ b/src/Ombi.Schedule/Jobs/Emby/EmbyContentSync.cs @@ -71,37 +71,60 @@ namespace Ombi.Schedule.Jobs.Emby await _repo.ExecuteSql("DELETE FROM EmbyEpisode"); await _repo.ExecuteSql("DELETE FROM EmbyContent"); - var movies = await _api.GetAllMovies(server.ApiKey, server.AdministratorId, server.FullUri); + var movies = await _api.GetAllMovies(server.ApiKey,0, 200, server.AdministratorId, server.FullUri); + var totalCount = movies.TotalRecordCount; + var processed = 0; + var mediaToAdd = new HashSet(); - foreach (var movie in movies.Items) - { - // Regular movie - await ProcessMovies(movie, mediaToAdd); - } - // TV Time - var tv = await _api.GetAllShows(server.ApiKey, server.AdministratorId, server.FullUri); - foreach (var tvShow in tv.Items) - { - if (string.IsNullOrEmpty(tvShow.ProviderIds?.Tvdb)) + while (processed < totalCount) + { + foreach (var movie in movies.Items) { - Log.Error("Provider Id on tv {0} is null", tvShow.Name); - continue; + processed++; + // Regular movie + await ProcessMovies(movie, mediaToAdd); } - var existingTv = await _repo.GetByEmbyId(tvShow.Id); - if (existingTv == null) - mediaToAdd.Add(new EmbyContent + // Get the next batch + movies = await _api.GetAllMovies(server.ApiKey, processed + 1, 200, server.AdministratorId, server.FullUri); + await _repo.AddRange(mediaToAdd); + mediaToAdd.Clear(); + } + + // TV Time + var tv = await _api.GetAllShows(server.ApiKey, 0, 200, server.AdministratorId, server.FullUri); + var totalTv = tv.TotalRecordCount; + processed = 0; + while (processed < totalTv) + { + foreach (var tvShow in tv.Items) + { + processed++; + if (string.IsNullOrEmpty(tvShow.ProviderIds?.Tvdb)) { - TvDbId = tvShow.ProviderIds?.Tvdb, - ImdbId = tvShow.ProviderIds?.Imdb, - TheMovieDbId = tvShow.ProviderIds?.Tmdb, - Title = tvShow.Name, - Type = EmbyMediaType.Series, - EmbyId = tvShow.Id, - Url = EmbyHelper.GetEmbyMediaUrl(tvShow.Id), - AddedAt = DateTime.UtcNow - }); + Log.Error("Provider Id on tv {0} is null", tvShow.Name); + continue; + } + + var existingTv = await _repo.GetByEmbyId(tvShow.Id); + if (existingTv == null) + mediaToAdd.Add(new EmbyContent + { + TvDbId = tvShow.ProviderIds?.Tvdb, + ImdbId = tvShow.ProviderIds?.Imdb, + TheMovieDbId = tvShow.ProviderIds?.Tmdb, + Title = tvShow.Name, + Type = EmbyMediaType.Series, + EmbyId = tvShow.Id, + Url = EmbyHelper.GetEmbyMediaUrl(tvShow.Id), + AddedAt = DateTime.UtcNow + }); + } + // Get the next batch + tv = await _api.GetAllShows(server.ApiKey, processed + 1, 200, server.AdministratorId, server.FullUri); + await _repo.AddRange(mediaToAdd); + mediaToAdd.Clear(); } if (mediaToAdd.Any()) diff --git a/src/Ombi.Schedule/Jobs/Emby/EmbyEpisodeSync.cs b/src/Ombi.Schedule/Jobs/Emby/EmbyEpisodeSync.cs index b5ed6d443..dc486e6f9 100644 --- a/src/Ombi.Schedule/Jobs/Emby/EmbyEpisodeSync.cs +++ b/src/Ombi.Schedule/Jobs/Emby/EmbyEpisodeSync.cs @@ -73,37 +73,47 @@ namespace Ombi.Schedule.Jobs.Emby private async Task CacheEpisodes(EmbyServers server) { - var allEpisodes = await _api.GetAllEpisodes(server.ApiKey, server.AdministratorId, server.FullUri); + var allEpisodes = await _api.GetAllEpisodes(server.ApiKey, 0, 200, server.AdministratorId, server.FullUri); + var total = allEpisodes.TotalRecordCount; + var processed = 0; var epToAdd = new List(); - - foreach (var ep in allEpisodes.Items) + while (processed < total) { - // Let's make sure we have the parent request, stop those pesky forign key errors, - // Damn me having data integrity - var parent = await _repo.GetByEmbyId(ep.SeriesId); - if (parent == null) + foreach (var ep in allEpisodes.Items) { - _logger.LogInformation("The episode {0} does not relate to a series, so we cannot save this", ep.Name); - continue; - } + processed++; + // Let's make sure we have the parent request, stop those pesky forign key errors, + // Damn me having data integrity + var parent = await _repo.GetByEmbyId(ep.SeriesId); + if (parent == null) + { + _logger.LogInformation("The episode {0} does not relate to a series, so we cannot save this", + ep.Name); + continue; + } - var existingEpisode = await _repo.GetEpisodeByEmbyId(ep.Id); - if (existingEpisode == null) - { - // add it - epToAdd.Add(new EmbyEpisode + var existingEpisode = await _repo.GetEpisodeByEmbyId(ep.Id); + if (existingEpisode == null) { - EmbyId = ep.Id, - EpisodeNumber = ep.IndexNumber, - SeasonNumber = ep.ParentIndexNumber, - ParentId = ep.SeriesId, - TvDbId = ep.ProviderIds.Tvdb, - TheMovieDbId = ep.ProviderIds.Tmdb, - ImdbId = ep.ProviderIds.Imdb, - Title = ep.Name, - AddedAt = DateTime.UtcNow - }); + // add it + epToAdd.Add(new EmbyEpisode + { + EmbyId = ep.Id, + EpisodeNumber = ep.IndexNumber, + SeasonNumber = ep.ParentIndexNumber, + ParentId = ep.SeriesId, + TvDbId = ep.ProviderIds.Tvdb, + TheMovieDbId = ep.ProviderIds.Tmdb, + ImdbId = ep.ProviderIds.Imdb, + Title = ep.Name, + AddedAt = DateTime.UtcNow + }); + } } + + await _repo.AddRange(epToAdd); + epToAdd.Clear(); + allEpisodes = await _api.GetAllEpisodes(server.ApiKey, processed + 1, 200, server.AdministratorId, server.FullUri); } if (epToAdd.Any()) From 207d5c8d60ddfffd1b0a0b8b75ee1b92be6d82c9 Mon Sep 17 00:00:00 2001 From: Jamie Rees Date: Fri, 29 Jun 2018 21:55:36 +0100 Subject: [PATCH 02/26] Do not delete the Emby Information every time we run, let's keep the content now. --- .../Jobs/Emby/EmbyContentSync.cs | 21 ++++++++++++++++--- .../Jobs/Emby/EmbyEpisodeSync.cs | 8 +++++-- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/src/Ombi.Schedule/Jobs/Emby/EmbyContentSync.cs b/src/Ombi.Schedule/Jobs/Emby/EmbyContentSync.cs index 0e4b0a74f..4c11e7cc2 100644 --- a/src/Ombi.Schedule/Jobs/Emby/EmbyContentSync.cs +++ b/src/Ombi.Schedule/Jobs/Emby/EmbyContentSync.cs @@ -68,8 +68,8 @@ namespace Ombi.Schedule.Jobs.Emby if (!ValidateSettings(server)) return; - await _repo.ExecuteSql("DELETE FROM EmbyEpisode"); - await _repo.ExecuteSql("DELETE FROM EmbyContent"); + //await _repo.ExecuteSql("DELETE FROM EmbyEpisode"); + //await _repo.ExecuteSql("DELETE FROM EmbyContent"); var movies = await _api.GetAllMovies(server.ApiKey,0, 200, server.AdministratorId, server.FullUri); var totalCount = movies.TotalRecordCount; @@ -103,12 +103,14 @@ namespace Ombi.Schedule.Jobs.Emby processed++; if (string.IsNullOrEmpty(tvShow.ProviderIds?.Tvdb)) { - Log.Error("Provider Id on tv {0} is null", tvShow.Name); + _logger.LogInformation("Provider Id on tv {0} is null", tvShow.Name); continue; } var existingTv = await _repo.GetByEmbyId(tvShow.Id); if (existingTv == null) + { + _logger.LogDebug("Adding new TV Show {0}", tvShow.Name); mediaToAdd.Add(new EmbyContent { TvDbId = tvShow.ProviderIds?.Tvdb, @@ -120,6 +122,11 @@ namespace Ombi.Schedule.Jobs.Emby Url = EmbyHelper.GetEmbyMediaUrl(tvShow.Id), AddedAt = DateTime.UtcNow }); + } + else + { + _logger.LogDebug("We already have TV Show {0}", tvShow.Name); + } } // Get the next batch tv = await _api.GetAllShows(server.ApiKey, processed + 1, 200, server.AdministratorId, server.FullUri); @@ -137,6 +144,8 @@ namespace Ombi.Schedule.Jobs.Emby var existingMovie = await _repo.GetByEmbyId(movieInfo.Id); if (existingMovie == null) + { + _logger.LogDebug("Adding new movie {0}", movieInfo.Name); content.Add(new EmbyContent { ImdbId = movieInfo.ProviderIds.Imdb, @@ -147,6 +156,12 @@ namespace Ombi.Schedule.Jobs.Emby Url = EmbyHelper.GetEmbyMediaUrl(movieInfo.Id), AddedAt = DateTime.UtcNow, }); + } + else + { + // we have this + _logger.LogDebug("We already have movie {0}", movieInfo.Name); + } } private bool ValidateSettings(EmbyServers server) diff --git a/src/Ombi.Schedule/Jobs/Emby/EmbyEpisodeSync.cs b/src/Ombi.Schedule/Jobs/Emby/EmbyEpisodeSync.cs index dc486e6f9..b0f361a8c 100644 --- a/src/Ombi.Schedule/Jobs/Emby/EmbyEpisodeSync.cs +++ b/src/Ombi.Schedule/Jobs/Emby/EmbyEpisodeSync.cs @@ -76,7 +76,7 @@ namespace Ombi.Schedule.Jobs.Emby var allEpisodes = await _api.GetAllEpisodes(server.ApiKey, 0, 200, server.AdministratorId, server.FullUri); var total = allEpisodes.TotalRecordCount; var processed = 0; - var epToAdd = new List(); + var epToAdd = new HashSet(); while (processed < total) { foreach (var ep in allEpisodes.Items) @@ -93,8 +93,12 @@ namespace Ombi.Schedule.Jobs.Emby } var existingEpisode = await _repo.GetEpisodeByEmbyId(ep.Id); - if (existingEpisode == null) + // Make sure it's not in the hashset too + var existingInList = epToAdd.Any(x => x.EmbyId == ep.Id); + + if (existingEpisode == null && !existingInList) { + _logger.LogDebug("Adding new episode {0} to parent {1}", ep.Name, ep.SeriesName); // add it epToAdd.Add(new EmbyEpisode { From 075b3364308f9d7dbb292a197b92f60bd63f2ad3 Mon Sep 17 00:00:00 2001 From: Jamie Rees Date: Fri, 29 Jun 2018 22:13:53 +0100 Subject: [PATCH 03/26] More Emby improvements !wip --- src/Ombi.Api.Emby/EmbyApi.cs | 4 +- .../Jobs/Emby/EmbyContentSync.cs | 84 +++++++++++-------- .../Jobs/Emby/EmbyEpisodeSync.cs | 4 +- 3 files changed, 55 insertions(+), 37 deletions(-) diff --git a/src/Ombi.Api.Emby/EmbyApi.cs b/src/Ombi.Api.Emby/EmbyApi.cs index 714ce2d28..d94a42f90 100644 --- a/src/Ombi.Api.Emby/EmbyApi.cs +++ b/src/Ombi.Api.Emby/EmbyApi.cs @@ -104,9 +104,9 @@ namespace Ombi.Api.Emby return await GetAll("Movie", apiKey, userId, baseUri, true, startIndex, count); } - public async Task> GetAllEpisodes(string apiKey, string userId, string baseUri) + public async Task> GetAllEpisodes(string apiKey, int startIndex, int count, string userId, string baseUri) { - return await GetAll("Episode", apiKey, userId, baseUri); + return await GetAll("Episode", apiKey, userId, baseUri, false, startIndex, count); } public async Task> GetAllShows(string apiKey, int startIndex, int count, string userId, string baseUri) diff --git a/src/Ombi.Schedule/Jobs/Emby/EmbyContentSync.cs b/src/Ombi.Schedule/Jobs/Emby/EmbyContentSync.cs index 4c11e7cc2..03f61f4e4 100644 --- a/src/Ombi.Schedule/Jobs/Emby/EmbyContentSync.cs +++ b/src/Ombi.Schedule/Jobs/Emby/EmbyContentSync.cs @@ -71,65 +71,83 @@ namespace Ombi.Schedule.Jobs.Emby //await _repo.ExecuteSql("DELETE FROM EmbyEpisode"); //await _repo.ExecuteSql("DELETE FROM EmbyContent"); - var movies = await _api.GetAllMovies(server.ApiKey,0, 200, server.AdministratorId, server.FullUri); + var movies = await _api.GetAllMovies(server.ApiKey, 0, 200, server.AdministratorId, server.FullUri); var totalCount = movies.TotalRecordCount; - var processed = 0; + var processed = 1; var mediaToAdd = new HashSet(); while (processed < totalCount) { - foreach (var movie in movies.Items) + try { - processed++; - // Regular movie - await ProcessMovies(movie, mediaToAdd); + foreach (var movie in movies.Items) + { + processed++; + // Regular movie + await ProcessMovies(movie, mediaToAdd); + } + + // Get the next batch + movies = await _api.GetAllMovies(server.ApiKey, processed, 200, server.AdministratorId, server.FullUri); + await _repo.AddRange(mediaToAdd); + mediaToAdd.Clear(); } + catch (Exception) + { - // Get the next batch - movies = await _api.GetAllMovies(server.ApiKey, processed + 1, 200, server.AdministratorId, server.FullUri); - await _repo.AddRange(mediaToAdd); - mediaToAdd.Clear(); + throw; + } } // TV Time var tv = await _api.GetAllShows(server.ApiKey, 0, 200, server.AdministratorId, server.FullUri); var totalTv = tv.TotalRecordCount; - processed = 0; + processed = 1; while (processed < totalTv) { foreach (var tvShow in tv.Items) { - processed++; - if (string.IsNullOrEmpty(tvShow.ProviderIds?.Tvdb)) + try { - _logger.LogInformation("Provider Id on tv {0} is null", tvShow.Name); - continue; - } - var existingTv = await _repo.GetByEmbyId(tvShow.Id); - if (existingTv == null) - { - _logger.LogDebug("Adding new TV Show {0}", tvShow.Name); - mediaToAdd.Add(new EmbyContent + processed++; + if (string.IsNullOrEmpty(tvShow.ProviderIds?.Tvdb)) + { + _logger.LogInformation("Provider Id on tv {0} is null", tvShow.Name); + continue; + } + + var existingTv = await _repo.GetByEmbyId(tvShow.Id); + if (existingTv == null) + { + _logger.LogDebug("Adding new TV Show {0}", tvShow.Name); + mediaToAdd.Add(new EmbyContent + { + TvDbId = tvShow.ProviderIds?.Tvdb, + ImdbId = tvShow.ProviderIds?.Imdb, + TheMovieDbId = tvShow.ProviderIds?.Tmdb, + Title = tvShow.Name, + Type = EmbyMediaType.Series, + EmbyId = tvShow.Id, + Url = EmbyHelper.GetEmbyMediaUrl(tvShow.Id), + AddedAt = DateTime.UtcNow + }); + } + else { - TvDbId = tvShow.ProviderIds?.Tvdb, - ImdbId = tvShow.ProviderIds?.Imdb, - TheMovieDbId = tvShow.ProviderIds?.Tmdb, - Title = tvShow.Name, - Type = EmbyMediaType.Series, - EmbyId = tvShow.Id, - Url = EmbyHelper.GetEmbyMediaUrl(tvShow.Id), - AddedAt = DateTime.UtcNow - }); + _logger.LogDebug("We already have TV Show {0}", tvShow.Name); + } + } - else + catch (Exception) { - _logger.LogDebug("We already have TV Show {0}", tvShow.Name); + + throw; } } // Get the next batch - tv = await _api.GetAllShows(server.ApiKey, processed + 1, 200, server.AdministratorId, server.FullUri); + tv = await _api.GetAllShows(server.ApiKey, processed, 200, server.AdministratorId, server.FullUri); await _repo.AddRange(mediaToAdd); mediaToAdd.Clear(); } diff --git a/src/Ombi.Schedule/Jobs/Emby/EmbyEpisodeSync.cs b/src/Ombi.Schedule/Jobs/Emby/EmbyEpisodeSync.cs index b0f361a8c..f66ff89ab 100644 --- a/src/Ombi.Schedule/Jobs/Emby/EmbyEpisodeSync.cs +++ b/src/Ombi.Schedule/Jobs/Emby/EmbyEpisodeSync.cs @@ -75,7 +75,7 @@ namespace Ombi.Schedule.Jobs.Emby { var allEpisodes = await _api.GetAllEpisodes(server.ApiKey, 0, 200, server.AdministratorId, server.FullUri); var total = allEpisodes.TotalRecordCount; - var processed = 0; + var processed = 1; var epToAdd = new HashSet(); while (processed < total) { @@ -117,7 +117,7 @@ namespace Ombi.Schedule.Jobs.Emby await _repo.AddRange(epToAdd); epToAdd.Clear(); - allEpisodes = await _api.GetAllEpisodes(server.ApiKey, processed + 1, 200, server.AdministratorId, server.FullUri); + allEpisodes = await _api.GetAllEpisodes(server.ApiKey, processed, 200, server.AdministratorId, server.FullUri); } if (epToAdd.Any()) From fe590004d281f9c6e5ee9cd79abc1e295ff14c54 Mon Sep 17 00:00:00 2001 From: Jamie Rees Date: Sun, 1 Jul 2018 20:44:51 +0100 Subject: [PATCH 04/26] Fixed collection issues in Emby #2366 --- src/Ombi.Api.Emby/EmbyApi.cs | 8 +++-- src/Ombi.Api.Emby/IEmbyApi.cs | 4 +-- .../Jobs/Emby/EmbyContentSync.cs | 31 ++++++++++--------- src/Ombi/Startup.cs | 2 ++ 4 files changed, 27 insertions(+), 18 deletions(-) diff --git a/src/Ombi.Api.Emby/EmbyApi.cs b/src/Ombi.Api.Emby/EmbyApi.cs index d94a42f90..fcb989094 100644 --- a/src/Ombi.Api.Emby/EmbyApi.cs +++ b/src/Ombi.Api.Emby/EmbyApi.cs @@ -91,12 +91,16 @@ namespace Ombi.Api.Emby request.AddContentHeader("Content-Type", "application/json"); } - public async Task> GetCollection(string mediaId, string apiKey, string userId, string baseUrl) + public async Task> GetCollection(string mediaId, string apiKey, string userId, string baseUrl) { var request = new Request($"emby/users/{userId}/items?parentId={mediaId}", baseUrl, HttpMethod.Get); AddHeaders(request, apiKey); - return await Api.Request>(request); + request.AddQueryString("Fields", "ProviderIds,Overview"); + + request.AddQueryString("VirtualItem", "False"); + + return await Api.Request>(request); } public async Task> GetAllMovies(string apiKey, int startIndex, int count, string userId, string baseUri) diff --git a/src/Ombi.Api.Emby/IEmbyApi.cs b/src/Ombi.Api.Emby/IEmbyApi.cs index 88f14f24f..b4641ea5f 100644 --- a/src/Ombi.Api.Emby/IEmbyApi.cs +++ b/src/Ombi.Api.Emby/IEmbyApi.cs @@ -23,8 +23,8 @@ namespace Ombi.Api.Emby Task> GetAllShows(string apiKey, int startIndex, int count, string userId, string baseUri); - Task> GetCollection(string mediaId, string apiKey, string userId, - string baseUrl); + Task> GetCollection(string mediaId, + string apiKey, string userId, string baseUrl); Task GetSeriesInformation(string mediaId, string apiKey, string userId, string baseUrl); Task GetMovieInformation(string mediaId, string apiKey, string userId, string baseUrl); diff --git a/src/Ombi.Schedule/Jobs/Emby/EmbyContentSync.cs b/src/Ombi.Schedule/Jobs/Emby/EmbyContentSync.cs index 03f61f4e4..3e2e1a2e1 100644 --- a/src/Ombi.Schedule/Jobs/Emby/EmbyContentSync.cs +++ b/src/Ombi.Schedule/Jobs/Emby/EmbyContentSync.cs @@ -79,27 +79,30 @@ namespace Ombi.Schedule.Jobs.Emby while (processed < totalCount) { - try + foreach (var movie in movies.Items) { - foreach (var movie in movies.Items) + if (movie.Type.Equals("boxset", StringComparison.CurrentCultureIgnoreCase) && mediaToAdd.All(x => x.EmbyId != movie.Id)) { - processed++; - // Regular movie - await ProcessMovies(movie, mediaToAdd); + var movieInfo = + await _api.GetCollection(movie.Id, server.ApiKey, server.AdministratorId, server.FullUri); + foreach (var item in movieInfo.Items) + { + await ProcessMovies(item, mediaToAdd); + } } - - // Get the next batch - movies = await _api.GetAllMovies(server.ApiKey, processed, 200, server.AdministratorId, server.FullUri); - await _repo.AddRange(mediaToAdd); - mediaToAdd.Clear(); + processed++; + // Regular movie + await ProcessMovies(movie, mediaToAdd); } - catch (Exception) - { - throw; - } + // Get the next batch + movies = await _api.GetAllMovies(server.ApiKey, processed, 200, server.AdministratorId, server.FullUri); + await _repo.AddRange(mediaToAdd); + mediaToAdd.Clear(); + } + // TV Time var tv = await _api.GetAllShows(server.ApiKey, 0, 200, server.AdministratorId, server.FullUri); var totalTv = tv.TotalRecordCount; diff --git a/src/Ombi/Startup.cs b/src/Ombi/Startup.cs index 7e11cfe0e..9e7b1b290 100644 --- a/src/Ombi/Startup.cs +++ b/src/Ombi/Startup.cs @@ -5,6 +5,7 @@ using AutoMapper.EquivalencyExpression; using Hangfire; using Hangfire.Dashboard; using Hangfire.SQLite; +using Microsoft.ApplicationInsights.Extensibility; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.HttpOverrides; @@ -78,6 +79,7 @@ namespace Ombi // This method gets called by the runtime. Use this method to add services to the container. public IServiceProvider ConfigureServices(IServiceCollection services) { + TelemetryConfiguration.Active.DisableTelemetry = true; // Add framework services. services.AddDbContext(); From c55fc32c63bfc9947c3afb2cb6bac71d6beb7a2d Mon Sep 17 00:00:00 2001 From: Jamie Rees Date: Sun, 1 Jul 2018 22:16:12 +0100 Subject: [PATCH 05/26] Fixed Plex OAuth, should no longer show Insecure warning --- src/Ombi.Api.Plex/PlexApi.cs | 2 +- .../Authentication/PlexOAuthManager.cs | 13 ------- .../ClientApp/app/login/login.component.ts | 34 ++++++++++++++----- src/Ombi/Controllers/TokenController.cs | 4 +-- 4 files changed, 29 insertions(+), 24 deletions(-) diff --git a/src/Ombi.Api.Plex/PlexApi.cs b/src/Ombi.Api.Plex/PlexApi.cs index a16dee9ec..95c1c9d49 100644 --- a/src/Ombi.Api.Plex/PlexApi.cs +++ b/src/Ombi.Api.Plex/PlexApi.cs @@ -214,7 +214,7 @@ namespace Ombi.Api.Plex ? new Request($"Wizard/OAuth/{pinId}", applicationUrl, HttpMethod.Get) : new Request($"Login/OAuth/{pinId}", applicationUrl, HttpMethod.Get); - request.AddQueryString("forwardUrl", forwardUrl.FullUri.ToString()); + //request.AddQueryString("forwardUrl", forwardUrl.FullUri.ToString()); request.AddQueryString("pinID", pinId.ToString()); request.AddQueryString("code", code); request.AddQueryString("context[device][product]", "Ombi"); diff --git a/src/Ombi.Core/Authentication/PlexOAuthManager.cs b/src/Ombi.Core/Authentication/PlexOAuthManager.cs index 37ed7d2f7..887245579 100644 --- a/src/Ombi.Core/Authentication/PlexOAuthManager.cs +++ b/src/Ombi.Core/Authentication/PlexOAuthManager.cs @@ -34,19 +34,6 @@ namespace Ombi.Core.Authentication 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; } diff --git a/src/Ombi/ClientApp/app/login/login.component.ts b/src/Ombi/ClientApp/app/login/login.component.ts index 3447f84c3..e6386b8ce 100644 --- a/src/Ombi/ClientApp/app/login/login.component.ts +++ b/src/Ombi/ClientApp/app/login/login.component.ts @@ -40,6 +40,7 @@ export class LoginComponent implements OnDestroy, OnInit { } private timer: any; + private pinTimer: any; private errorBody: string; private errorValidation: string; @@ -124,18 +125,35 @@ export class LoginComponent implements OnDestroy, OnInit { public oauth() { this.authService.login({usePlexOAuth: true, password:"",rememberMe:true,username:""}).subscribe(x => { - if (window.frameElement) { - // in frame - window.open(x.url, "_blank"); - } else { - // not in frame - window.location.href = x.url; - } - }); + window.open(x.url, "_blank"); + this.pinTimer = setInterval(() => { + this.getPinResult(x.pinId); + }, 10000); + }); + + } + + public getPinResult(pinId: number) { + this.authService.oAuth(pinId).subscribe(x => { + if(x.access_token) { + localStorage.setItem("id_token", x.access_token); + + if (this.authService.loggedIn()) { + this.router.navigate(["search"]); + return; + } + } + + }, err => { + this.notify.error(err.statusText); + + this.router.navigate(["login"]); + }); } public ngOnDestroy() { clearInterval(this.timer); + clearInterval(this.pinTimer); } private cycleBackground() { diff --git a/src/Ombi/Controllers/TokenController.cs b/src/Ombi/Controllers/TokenController.cs index b45752af4..3d810d1d2 100644 --- a/src/Ombi/Controllers/TokenController.cs +++ b/src/Ombi/Controllers/TokenController.cs @@ -82,7 +82,7 @@ namespace Ombi.Controllers // Redirect them to Plex // 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, websiteAddress); @@ -93,7 +93,7 @@ namespace Ombi.Controllers error = "Application URL has not been set" }); } - return new JsonResult(new { url = url.ToString() }); + return new JsonResult(new { url = url.ToString(), pinId = pin.id }); } return new UnauthorizedResult(); From cbf331cd09409b58d51506c18e0874aaac747c46 Mon Sep 17 00:00:00 2001 From: Jamie Rees Date: Mon, 2 Jul 2018 08:06:43 +0100 Subject: [PATCH 06/26] Revert "Fixed Plex OAuth, should no longer show Insecure warning" This reverts commit c55fc32c63bfc9947c3afb2cb6bac71d6beb7a2d. --- src/Ombi.Api.Plex/PlexApi.cs | 2 +- .../Authentication/PlexOAuthManager.cs | 13 +++++++ .../ClientApp/app/login/login.component.ts | 34 +++++-------------- src/Ombi/Controllers/TokenController.cs | 4 +-- 4 files changed, 24 insertions(+), 29 deletions(-) diff --git a/src/Ombi.Api.Plex/PlexApi.cs b/src/Ombi.Api.Plex/PlexApi.cs index 95c1c9d49..a16dee9ec 100644 --- a/src/Ombi.Api.Plex/PlexApi.cs +++ b/src/Ombi.Api.Plex/PlexApi.cs @@ -214,7 +214,7 @@ namespace Ombi.Api.Plex ? new Request($"Wizard/OAuth/{pinId}", applicationUrl, HttpMethod.Get) : new Request($"Login/OAuth/{pinId}", applicationUrl, HttpMethod.Get); - //request.AddQueryString("forwardUrl", forwardUrl.FullUri.ToString()); + request.AddQueryString("forwardUrl", forwardUrl.FullUri.ToString()); request.AddQueryString("pinID", pinId.ToString()); request.AddQueryString("code", code); request.AddQueryString("context[device][product]", "Ombi"); diff --git a/src/Ombi.Core/Authentication/PlexOAuthManager.cs b/src/Ombi.Core/Authentication/PlexOAuthManager.cs index 887245579..37ed7d2f7 100644 --- a/src/Ombi.Core/Authentication/PlexOAuthManager.cs +++ b/src/Ombi.Core/Authentication/PlexOAuthManager.cs @@ -34,6 +34,19 @@ namespace Ombi.Core.Authentication 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; } diff --git a/src/Ombi/ClientApp/app/login/login.component.ts b/src/Ombi/ClientApp/app/login/login.component.ts index e6386b8ce..3447f84c3 100644 --- a/src/Ombi/ClientApp/app/login/login.component.ts +++ b/src/Ombi/ClientApp/app/login/login.component.ts @@ -40,7 +40,6 @@ export class LoginComponent implements OnDestroy, OnInit { } private timer: any; - private pinTimer: any; private errorBody: string; private errorValidation: string; @@ -125,35 +124,18 @@ export class LoginComponent implements OnDestroy, OnInit { public oauth() { this.authService.login({usePlexOAuth: true, password:"",rememberMe:true,username:""}).subscribe(x => { - window.open(x.url, "_blank"); - this.pinTimer = setInterval(() => { - this.getPinResult(x.pinId); - }, 10000); - }); - - } - - public getPinResult(pinId: number) { - this.authService.oAuth(pinId).subscribe(x => { - if(x.access_token) { - localStorage.setItem("id_token", x.access_token); - - if (this.authService.loggedIn()) { - this.router.navigate(["search"]); - return; - } - } - - }, err => { - this.notify.error(err.statusText); - - this.router.navigate(["login"]); - }); + if (window.frameElement) { + // in frame + window.open(x.url, "_blank"); + } else { + // not in frame + window.location.href = x.url; + } + }); } public ngOnDestroy() { clearInterval(this.timer); - clearInterval(this.pinTimer); } private cycleBackground() { diff --git a/src/Ombi/Controllers/TokenController.cs b/src/Ombi/Controllers/TokenController.cs index 3d810d1d2..b45752af4 100644 --- a/src/Ombi/Controllers/TokenController.cs +++ b/src/Ombi/Controllers/TokenController.cs @@ -82,7 +82,7 @@ namespace Ombi.Controllers // Redirect them to Plex // 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, websiteAddress); @@ -93,7 +93,7 @@ namespace Ombi.Controllers error = "Application URL has not been set" }); } - return new JsonResult(new { url = url.ToString(), pinId = pin.id }); + return new JsonResult(new { url = url.ToString() }); } return new UnauthorizedResult(); From 7bfc5ad63204cb2bb8d99c476a8d6eb2adf1ac1c Mon Sep 17 00:00:00 2001 From: Jamie Rees Date: Mon, 2 Jul 2018 08:44:32 +0100 Subject: [PATCH 07/26] Rework how we create the OAuth Pin !wip --- src/Ombi.Api.Plex/IPlexApi.cs | 2 +- src/Ombi.Api.Plex/PlexApi.cs | 80 ++++++++++++------- .../Authentication/PlexOAuthManager.cs | 6 +- src/Ombi.Core/IPlexOAuthManager.cs | 2 +- .../Settings/Models/External/PlexSettings.cs | 7 +- .../Controllers/External/PlexController.cs | 2 +- src/Ombi/Controllers/SettingsController.cs | 4 + 7 files changed, 69 insertions(+), 34 deletions(-) diff --git a/src/Ombi.Api.Plex/IPlexApi.cs b/src/Ombi.Api.Plex/IPlexApi.cs index cc61dfa5d..95aba2a63 100644 --- a/src/Ombi.Api.Plex/IPlexApi.cs +++ b/src/Ombi.Api.Plex/IPlexApi.cs @@ -24,6 +24,6 @@ namespace Ombi.Api.Plex Task GetRecentlyAdded(string authToken, string uri, string sectionId); Task CreatePin(); Task GetPin(int pinId); - Uri GetOAuthUrl(int pinId, string code, string applicationUrl, bool wizard); + Task GetOAuthUrl(int pinId, string code, string applicationUrl, bool wizard); } } \ No newline at end of file diff --git a/src/Ombi.Api.Plex/PlexApi.cs b/src/Ombi.Api.Plex/PlexApi.cs index a16dee9ec..a559bea07 100644 --- a/src/Ombi.Api.Plex/PlexApi.cs +++ b/src/Ombi.Api.Plex/PlexApi.cs @@ -16,14 +16,16 @@ namespace Ombi.Api.Plex { public class PlexApi : IPlexApi { - public PlexApi(IApi api, ISettingsService settings) + public PlexApi(IApi api, ISettingsService settings, ISettingsService p) { Api = api; _custom = settings; + _plexSettings = p; } private IApi Api { get; } private readonly ISettingsService _custom; + private readonly ISettingsService _plexSettings; private string _app; private string ApplicationName @@ -69,7 +71,7 @@ namespace Ombi.Api.Plex }; var request = new Request(SignInUri, string.Empty, HttpMethod.Post); - AddHeaders(request); + await AddHeaders(request); request.AddJsonBody(userModel); var obj = await Api.Request(request); @@ -80,14 +82,14 @@ namespace Ombi.Api.Plex public async Task GetStatus(string authToken, string uri) { var request = new Request(uri, string.Empty, HttpMethod.Get); - AddHeaders(request, authToken); + await AddHeaders(request, authToken); return await Api.Request(request); } public async Task GetAccount(string authToken) { var request = new Request(GetAccountUri, string.Empty, HttpMethod.Get); - AddHeaders(request, authToken); + await AddHeaders(request, authToken); return await Api.Request(request); } @@ -95,7 +97,7 @@ namespace Ombi.Api.Plex { var request = new Request(ServerUri, string.Empty, HttpMethod.Get, ContentType.Xml); - AddHeaders(request, authToken); + await AddHeaders(request, authToken); return await Api.Request(request); } @@ -103,14 +105,14 @@ namespace Ombi.Api.Plex public async Task GetLibrarySections(string authToken, string plexFullHost) { var request = new Request("library/sections", plexFullHost, HttpMethod.Get); - AddHeaders(request, authToken); + await AddHeaders(request, authToken); return await Api.Request(request); } public async Task GetLibrary(string authToken, string plexFullHost, string libraryId) { var request = new Request($"library/sections/{libraryId}/all", plexFullHost, HttpMethod.Get); - AddHeaders(request, authToken); + await AddHeaders(request, authToken); return await Api.Request(request); } @@ -128,21 +130,21 @@ namespace Ombi.Api.Plex public async Task GetEpisodeMetaData(string authToken, string plexFullHost, int ratingKey) { var request = new Request($"/library/metadata/{ratingKey}", plexFullHost, HttpMethod.Get); - AddHeaders(request, authToken); + await AddHeaders(request, authToken); return await Api.Request(request); } public async Task GetMetadata(string authToken, string plexFullHost, int itemId) { var request = new Request($"library/metadata/{itemId}", plexFullHost, HttpMethod.Get); - AddHeaders(request, authToken); + await AddHeaders(request, authToken); return await Api.Request(request); } public async Task GetSeasons(string authToken, string plexFullHost, int ratingKey) { var request = new Request($"library/metadata/{ratingKey}/children", plexFullHost, HttpMethod.Get); - AddHeaders(request, authToken); + await AddHeaders(request, authToken); return await Api.Request(request); } @@ -161,9 +163,9 @@ namespace Ombi.Api.Plex request.AddQueryString("type", "4"); AddLimitHeaders(request, start, retCount); - AddHeaders(request, authToken); + await AddHeaders(request, authToken); - return await Api.Request(request); + return await Api.Request(request); } /// @@ -174,8 +176,8 @@ namespace Ombi.Api.Plex /// public async Task GetUsers(string authToken) { - var request = new Request(string.Empty,FriendsUri, HttpMethod.Get, ContentType.Xml); - AddHeaders(request, authToken); + var request = new Request(string.Empty, FriendsUri, HttpMethod.Get, ContentType.Xml); + await AddHeaders(request, authToken); return await Api.Request(request); } @@ -183,7 +185,7 @@ namespace Ombi.Api.Plex public async Task GetRecentlyAdded(string authToken, string uri, string sectionId) { var request = new Request($"library/sections/{sectionId}/recentlyAdded", uri, HttpMethod.Get); - AddHeaders(request, authToken); + await AddHeaders(request, authToken); AddLimitHeaders(request, 0, 50); return await Api.Request(request); @@ -193,7 +195,7 @@ namespace Ombi.Api.Plex { var request = new Request($"api/v2/pins", "https://plex.tv/", HttpMethod.Post); request.AddQueryString("strong", "true"); - AddHeaders(request); + await AddHeaders(request); return await Api.Request(request); } @@ -201,17 +203,17 @@ namespace Ombi.Api.Plex public async Task GetPin(int pinId) { var request = new Request($"api/v2/pins/{pinId}", "https://plex.tv/", HttpMethod.Get); - AddHeaders(request); + await AddHeaders(request); return await Api.Request(request); } - public Uri GetOAuthUrl(int pinId, string code, string applicationUrl, bool wizard) + public async Task 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) + var request = new Request("auth#!?", "https://app.plex.tv", HttpMethod.Get); + await AddHeaders(request); + var forwardUrl = wizard + ? new Request($"Wizard/OAuth/{pinId}", applicationUrl, HttpMethod.Get) : new Request($"Login/OAuth/{pinId}", applicationUrl, HttpMethod.Get); request.AddQueryString("forwardUrl", forwardUrl.FullUri.ToString()); @@ -219,7 +221,13 @@ namespace Ombi.Api.Plex request.AddQueryString("code", code); request.AddQueryString("context[device][product]", "Ombi"); request.AddQueryString("context[device][environment]", "bundled"); - request.AddQueryString("clientID", $"OmbiV3"); + request.AddQueryString("context[device][layout]", "desktop"); + request.AddQueryString("context[device][platform]", "Web"); + request.AddQueryString("context[device][devuce]", "Ombi (Web)"); + + var s = await GetSettings(); + await CheckInstallId(s); + request.AddQueryString("clientID", s.InstallId.ToString("N")); if (request.FullUri.Fragment.Equals("#")) { @@ -238,21 +246,25 @@ namespace Ombi.Api.Plex /// /// /// - private void AddHeaders(Request request, string authToken) + private async Task AddHeaders(Request request, string authToken) { request.AddHeader("X-Plex-Token", authToken); - AddHeaders(request); + await AddHeaders(request); } /// /// Adds the main required headers to the Plex Request /// /// - private void AddHeaders(Request request) + private async Task AddHeaders(Request request) { - request.AddHeader("X-Plex-Client-Identifier", $"OmbiV3"); + var s = await GetSettings(); + await CheckInstallId(s); + request.AddHeader("X-Plex-Client-Identifier", s.InstallId.ToString("N")); request.AddHeader("X-Plex-Product", ApplicationName); request.AddHeader("X-Plex-Version", "3"); + request.AddHeader("X-Plex-Device", "Ombi (Web)"); + request.AddHeader("X-Plex-Platform", "Web"); request.AddContentHeader("Content-Type", request.ContentType == ContentType.Json ? "application/json" : "application/xml"); request.AddHeader("Accept", "application/json"); } @@ -262,5 +274,19 @@ namespace Ombi.Api.Plex request.AddHeader("X-Plex-Container-Start", from.ToString()); request.AddHeader("X-Plex-Container-Size", to.ToString()); } + private async Task CheckInstallId(PlexSettings s) + { + if (s.InstallId == null || s.InstallId == Guid.Empty) + { + s.InstallId = Guid.NewGuid(); + await _plexSettings.SaveSettingsAsync(s); + } + } + + private PlexSettings _settings; + private async Task GetSettings() + { + return _settings ?? (_settings = await _plexSettings.GetSettingsAsync()); + } } } diff --git a/src/Ombi.Core/Authentication/PlexOAuthManager.cs b/src/Ombi.Core/Authentication/PlexOAuthManager.cs index 37ed7d2f7..c10015f33 100644 --- a/src/Ombi.Core/Authentication/PlexOAuthManager.cs +++ b/src/Ombi.Core/Authentication/PlexOAuthManager.cs @@ -58,14 +58,14 @@ namespace Ombi.Core.Authentication public async Task GetOAuthUrl(int pinId, string code, string websiteAddress = null) { var settings = await _customizationSettingsService.GetSettingsAsync(); - var url = _api.GetOAuthUrl(pinId, code, settings.ApplicationUrl.IsNullOrEmpty() ? websiteAddress : settings.ApplicationUrl, false); + var url = await _api.GetOAuthUrl(pinId, code, settings.ApplicationUrl.IsNullOrEmpty() ? websiteAddress : settings.ApplicationUrl, false); return url; } - public Uri GetWizardOAuthUrl(int pinId, string code, string websiteAddress) + public async Task GetWizardOAuthUrl(int pinId, string code, string websiteAddress) { - var url = _api.GetOAuthUrl(pinId, code, websiteAddress, true); + var url = await _api.GetOAuthUrl(pinId, code, websiteAddress, true); return url; } } diff --git a/src/Ombi.Core/IPlexOAuthManager.cs b/src/Ombi.Core/IPlexOAuthManager.cs index 9c4f0582e..57a7cfc83 100644 --- a/src/Ombi.Core/IPlexOAuthManager.cs +++ b/src/Ombi.Core/IPlexOAuthManager.cs @@ -10,7 +10,7 @@ namespace Ombi.Core.Authentication Task GetAccessTokenFromPin(int pinId); Task RequestPin(); Task GetOAuthUrl(int pinId, string code, string websiteAddress = null); - Uri GetWizardOAuthUrl(int pinId, string code, string websiteAddress); + Task GetWizardOAuthUrl(int pinId, string code, string websiteAddress); Task GetAccount(string accessToken); } } \ 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 3faba3e42..8fc8111f7 100644 --- a/src/Ombi.Settings/Settings/Models/External/PlexSettings.cs +++ b/src/Ombi.Settings/Settings/Models/External/PlexSettings.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using Ombi.Settings.Settings.Models.External; namespace Ombi.Core.Settings.Models.External @@ -6,6 +7,10 @@ namespace Ombi.Core.Settings.Models.External public sealed class PlexSettings : Ombi.Settings.Settings.Models.Settings { public bool Enable { get; set; } + /// + /// This is the ClientId for OAuth + /// + public Guid InstallId { get; set; } public List Servers { get; set; } } diff --git a/src/Ombi/Controllers/External/PlexController.cs b/src/Ombi/Controllers/External/PlexController.cs index 4819ed8a0..3675040d3 100644 --- a/src/Ombi/Controllers/External/PlexController.cs +++ b/src/Ombi/Controllers/External/PlexController.cs @@ -195,7 +195,7 @@ namespace Ombi.Controllers.External else { var websiteAddress =$"{this.Request.Scheme}://{this.Request.Host}{this.Request.PathBase}"; - url = _plexOAuthManager.GetWizardOAuthUrl(pin.id, pin.code, websiteAddress); + url = await _plexOAuthManager.GetWizardOAuthUrl(pin.id, pin.code, websiteAddress); } if (url == null) diff --git a/src/Ombi/Controllers/SettingsController.cs b/src/Ombi/Controllers/SettingsController.cs index 895621ad3..fb0ec53e3 100644 --- a/src/Ombi/Controllers/SettingsController.cs +++ b/src/Ombi/Controllers/SettingsController.cs @@ -139,6 +139,10 @@ namespace Ombi.Controllers [HttpPost("plex")] public async Task PlexSettings([FromBody]PlexSettings plex) { + if (plex.InstallId == null || plex.InstallId == Guid.Empty) + { + plex.InstallId = Guid.NewGuid(); + } var result = await Save(plex); return result; } From 41da68b96182be4ad3ca473ffbd5a6593f591fb9 Mon Sep 17 00:00:00 2001 From: Jamie Date: Tue, 3 Jul 2018 12:33:21 +0100 Subject: [PATCH 08/26] Fixed the Plex OAuth warning --- src/Ombi.Api.Plex/IPlexApi.cs | 1 - src/Ombi.Api.Plex/PlexApi.cs | 15 ++-------- .../Authentication/PlexOAuthManager.cs | 6 ---- src/Ombi.Core/IPlexOAuthManager.cs | 2 -- src/Ombi/ClientApp/app/app.module.ts | 3 +- src/Ombi/ClientApp/app/auth/IUserLogin.ts | 5 +++- src/Ombi/ClientApp/app/interfaces/IPlex.ts | 10 +++++++ .../ClientApp/app/login/login.component.ts | 27 ++++++++++-------- .../app/services/applications/index.ts | 1 + .../app/services/applications/plex.service.ts | 6 ++-- .../services/applications/plextv.service.ts | 28 +++++++++++++++++++ .../app/services/settings.service.ts | 4 +++ .../app/wizard/plex/plex.component.ts | 25 +++++++++++------ .../Controllers/External/PlexController.cs | 13 ++++----- src/Ombi/Controllers/SettingsController.cs | 15 +++++++++- src/Ombi/Controllers/TokenController.cs | 6 ++-- src/Ombi/Models/PlexOAuthViewModel.cs | 10 +++++++ src/Ombi/Models/UserAuthModel.cs | 5 +++- src/Ombi/Startup.cs | 11 +++++++- 19 files changed, 134 insertions(+), 59 deletions(-) create mode 100644 src/Ombi/ClientApp/app/services/applications/plextv.service.ts create mode 100644 src/Ombi/Models/PlexOAuthViewModel.cs diff --git a/src/Ombi.Api.Plex/IPlexApi.cs b/src/Ombi.Api.Plex/IPlexApi.cs index 95aba2a63..2dd1a638f 100644 --- a/src/Ombi.Api.Plex/IPlexApi.cs +++ b/src/Ombi.Api.Plex/IPlexApi.cs @@ -22,7 +22,6 @@ 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); Task GetOAuthUrl(int pinId, string code, string applicationUrl, bool wizard); } diff --git a/src/Ombi.Api.Plex/PlexApi.cs b/src/Ombi.Api.Plex/PlexApi.cs index a559bea07..4276f6203 100644 --- a/src/Ombi.Api.Plex/PlexApi.cs +++ b/src/Ombi.Api.Plex/PlexApi.cs @@ -191,15 +191,6 @@ 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"); - await 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); @@ -210,7 +201,7 @@ namespace Ombi.Api.Plex public async Task GetOAuthUrl(int pinId, string code, string applicationUrl, bool wizard) { - var request = new Request("auth#!?", "https://app.plex.tv", HttpMethod.Get); + var request = new Request("auth#", "https://app.plex.tv", HttpMethod.Get); await AddHeaders(request); var forwardUrl = wizard ? new Request($"Wizard/OAuth/{pinId}", applicationUrl, HttpMethod.Get) @@ -219,11 +210,11 @@ namespace Ombi.Api.Plex 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][product]", ApplicationName); request.AddQueryString("context[device][environment]", "bundled"); request.AddQueryString("context[device][layout]", "desktop"); request.AddQueryString("context[device][platform]", "Web"); - request.AddQueryString("context[device][devuce]", "Ombi (Web)"); + request.AddQueryString("context[device][device]", "Ombi (Web)"); var s = await GetSettings(); await CheckInstallId(s); diff --git a/src/Ombi.Core/Authentication/PlexOAuthManager.cs b/src/Ombi.Core/Authentication/PlexOAuthManager.cs index c10015f33..803176d74 100644 --- a/src/Ombi.Core/Authentication/PlexOAuthManager.cs +++ b/src/Ombi.Core/Authentication/PlexOAuthManager.cs @@ -20,12 +20,6 @@ namespace Ombi.Core.Authentication 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); diff --git a/src/Ombi.Core/IPlexOAuthManager.cs b/src/Ombi.Core/IPlexOAuthManager.cs index 57a7cfc83..a5c0c44ff 100644 --- a/src/Ombi.Core/IPlexOAuthManager.cs +++ b/src/Ombi.Core/IPlexOAuthManager.cs @@ -1,14 +1,12 @@ 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, string websiteAddress = null); Task GetWizardOAuthUrl(int pinId, string code, string websiteAddress); Task GetAccount(string accessToken); diff --git a/src/Ombi/ClientApp/app/app.module.ts b/src/Ombi/ClientApp/app/app.module.ts index 1421b1117..257a07cd0 100644 --- a/src/Ombi/ClientApp/app/app.module.ts +++ b/src/Ombi/ClientApp/app/app.module.ts @@ -36,7 +36,7 @@ import { ImageService } from "./services"; import { LandingPageService } from "./services"; import { NotificationService } from "./services"; import { SettingsService } from "./services"; -import { IssuesService, JobService, StatusService } from "./services"; +import { IssuesService, JobService, PlexTvService, StatusService } from "./services"; const routes: Routes = [ { path: "*", component: PageNotFoundComponent }, @@ -133,6 +133,7 @@ export function HttpLoaderFactory(http: HttpClient, platformLocation: PlatformLo CookieService, JobService, IssuesService, + PlexTvService, ], bootstrap: [AppComponent], }) diff --git a/src/Ombi/ClientApp/app/auth/IUserLogin.ts b/src/Ombi/ClientApp/app/auth/IUserLogin.ts index 609055c8e..d0e4d374a 100644 --- a/src/Ombi/ClientApp/app/auth/IUserLogin.ts +++ b/src/Ombi/ClientApp/app/auth/IUserLogin.ts @@ -1,8 +1,11 @@ -export interface IUserLogin { +import { IPlexPin } from "../interfaces"; + +export interface IUserLogin { username: string; password: string; rememberMe: boolean; usePlexOAuth: boolean; + plexTvPin: IPlexPin; } export interface ILocalUser { diff --git a/src/Ombi/ClientApp/app/interfaces/IPlex.ts b/src/Ombi/ClientApp/app/interfaces/IPlex.ts index 823b80d32..ccc4e0300 100644 --- a/src/Ombi/ClientApp/app/interfaces/IPlex.ts +++ b/src/Ombi/ClientApp/app/interfaces/IPlex.ts @@ -2,6 +2,16 @@ user: IPlexUser; } +export interface IPlexPin { + id: number; + code: string; +} + +export interface IPlexOAuthViewModel { + wizard: boolean; + pin: IPlexPin; +} + export interface IPlexOAuthAccessToken { accessToken: string; } diff --git a/src/Ombi/ClientApp/app/login/login.component.ts b/src/Ombi/ClientApp/app/login/login.component.ts index 3447f84c3..7a2a605ce 100644 --- a/src/Ombi/ClientApp/app/login/login.component.ts +++ b/src/Ombi/ClientApp/app/login/login.component.ts @@ -6,7 +6,7 @@ import { TranslateService } from "@ngx-translate/core"; import { PlatformLocation } from "@angular/common"; import { AuthService } from "../auth/auth.service"; import { IAuthenticationSettings, ICustomizationSettings } from "../interfaces"; -import { NotificationService } from "../services"; +import { NotificationService, PlexTvService } from "../services"; import { SettingsService } from "../services"; import { StatusService } from "../services"; @@ -40,13 +40,14 @@ export class LoginComponent implements OnDestroy, OnInit { } private timer: any; + private clientId: string; private errorBody: string; private errorValidation: string; constructor(private authService: AuthService, private router: Router, private notify: NotificationService, private status: StatusService, private fb: FormBuilder, private settingsService: SettingsService, private images: ImageService, private sanitizer: DomSanitizer, - private route: ActivatedRoute, private location: PlatformLocation, private readonly translate: TranslateService) { + private route: ActivatedRoute, private location: PlatformLocation, private translate: TranslateService, private plexTv: PlexTvService) { this.route.params .subscribe((params: any) => { this.landingFlag = params.landing; @@ -78,6 +79,7 @@ export class LoginComponent implements OnDestroy, OnInit { public ngOnInit() { this.settingsService.getAuthentication().subscribe(x => this.authenticationSettings = x); + this.settingsService.getClientId().subscribe(x => this.clientId = x); this.settingsService.getCustomization().subscribe(x => this.customizationSettings = 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 + ")"); @@ -101,7 +103,7 @@ export class LoginComponent implements OnDestroy, OnInit { return; } const value = form.value; - const user = { password: value.password, username: value.username, rememberMe: value.rememberMe, usePlexOAuth: false }; + const user = { password: value.password, username: value.username, rememberMe: value.rememberMe, usePlexOAuth: false, plexTvPin: { id: 0, code: ""} }; this.authService.requiresPassword(user).subscribe(x => { if(x && this.authenticationSettings.allowNoPassword) { // Looks like this user requires a password @@ -123,14 +125,17 @@ export class LoginComponent implements OnDestroy, OnInit { } public oauth() { - this.authService.login({usePlexOAuth: true, password:"",rememberMe:true,username:""}).subscribe(x => { - if (window.frameElement) { - // in frame - window.open(x.url, "_blank"); - } else { - // not in frame - window.location.href = x.url; - } + this.plexTv.GetPin(this.clientId, this.appName).subscribe(pin => { + + this.authService.login({usePlexOAuth: true, password:"",rememberMe:true,username:"", plexTvPin: pin}).subscribe(x => { + if (window.frameElement) { + // in frame + window.open(x.url, "_blank"); + } else { + // not in frame + window.location.href = x.url; + } + }); }); } diff --git a/src/Ombi/ClientApp/app/services/applications/index.ts b/src/Ombi/ClientApp/app/services/applications/index.ts index 98c61cf04..9fe4a5403 100644 --- a/src/Ombi/ClientApp/app/services/applications/index.ts +++ b/src/Ombi/ClientApp/app/services/applications/index.ts @@ -5,3 +5,4 @@ export * from "./radarr.service"; export * from "./sonarr.service"; export * from "./tester.service"; export * from "./plexoauth.service"; +export * from "./plextv.service"; diff --git a/src/Ombi/ClientApp/app/services/applications/plex.service.ts b/src/Ombi/ClientApp/app/services/applications/plex.service.ts index 53fd31f9d..296f79ddb 100644 --- a/src/Ombi/ClientApp/app/services/applications/plex.service.ts +++ b/src/Ombi/ClientApp/app/services/applications/plex.service.ts @@ -6,7 +6,7 @@ import { Observable } from "rxjs/Rx"; import { ServiceHelpers } from "../service.helpers"; -import { IPlexAuthentication, IPlexLibResponse, IPlexServer, IPlexServerViewModel, IUsersModel } from "../../interfaces"; +import { IPlexAuthentication, IPlexLibResponse, IPlexOAuthViewModel,IPlexServer, IPlexServerViewModel, IUsersModel } from "../../interfaces"; @Injectable() export class PlexService extends ServiceHelpers { @@ -30,7 +30,7 @@ export class PlexService extends ServiceHelpers { 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}); + public oAuth(wizard: IPlexOAuthViewModel): Observable { + return this.http.post(`${this.url}oauth`, JSON.stringify(wizard), {headers: this.headers}); } } diff --git a/src/Ombi/ClientApp/app/services/applications/plextv.service.ts b/src/Ombi/ClientApp/app/services/applications/plextv.service.ts new file mode 100644 index 000000000..58a756007 --- /dev/null +++ b/src/Ombi/ClientApp/app/services/applications/plextv.service.ts @@ -0,0 +1,28 @@ +import { PlatformLocation } from "@angular/common"; +import { HttpClient, HttpHeaders } from "@angular/common/http"; +import { Injectable } from "@angular/core"; + +import { Observable } from "rxjs/Rx"; + +import { IPlexPin } from "../../interfaces"; + +@Injectable() +export class PlexTvService { + + constructor(private http: HttpClient, public platformLocation: PlatformLocation) { + + } + + public GetPin(clientId: string, applicationName: string): Observable { + const headers = new HttpHeaders({"Content-Type":"application/json", + "X-Plex-Client-Identifier": clientId, + "X-Plex-Product": applicationName, + "X-Plex-Version": "3", + "X-Plex-Device": "Ombi (Web)", + "X-Plex-Platform": "Web", + "Accept": "application/json", + }); + return this.http.post("https://plex.tv/api/v2/pins?strong=true", null, {headers}); + } + +} diff --git a/src/Ombi/ClientApp/app/services/settings.service.ts b/src/Ombi/ClientApp/app/services/settings.service.ts index 726c86d63..ff49e393c 100644 --- a/src/Ombi/ClientApp/app/services/settings.service.ts +++ b/src/Ombi/ClientApp/app/services/settings.service.ts @@ -95,6 +95,10 @@ export class SettingsService extends ServiceHelpers { return this.http.get(`${this.url}/Authentication`, {headers: this.headers}); } + public getClientId(): Observable { + return this.http.get(`${this.url}/clientid`, {headers: this.headers}); + } + public saveAuthentication(settings: IAuthenticationSettings): Observable { return this.http.post(`${this.url}/Authentication`, JSON.stringify(settings), {headers: this.headers}); } diff --git a/src/Ombi/ClientApp/app/wizard/plex/plex.component.ts b/src/Ombi/ClientApp/app/wizard/plex/plex.component.ts index 67bd672dc..81b5db0b4 100644 --- a/src/Ombi/ClientApp/app/wizard/plex/plex.component.ts +++ b/src/Ombi/ClientApp/app/wizard/plex/plex.component.ts @@ -1,20 +1,27 @@ -import { Component } from "@angular/core"; +import { Component, OnInit } from "@angular/core"; import { Router } from "@angular/router"; -import { PlexService } from "../../services"; +import { PlexService, PlexTvService, SettingsService } from "../../services"; import { IdentityService, NotificationService } from "../../services"; @Component({ templateUrl: "./plex.component.html", }) -export class PlexComponent { +export class PlexComponent implements OnInit { public login: string; public password: string; + private clientId: string; + constructor(private plexService: PlexService, private router: Router, private notificationService: NotificationService, - private identityService: IdentityService) { } + private identityService: IdentityService, private plexTv: PlexTvService, + private settingsService: SettingsService) { } + + public ngOnInit(): void { + this.settingsService.getClientId().subscribe(x => this.clientId = x); + } public requestAuthToken() { this.plexService.logIn(this.login, this.password).subscribe(x => { @@ -40,10 +47,12 @@ export class PlexComponent { } public oauth() { - this.plexService.oAuth(true).subscribe(x => { - if(x.url) { - window.location.href = x.url; - } + this.plexTv.GetPin(this.clientId, "Ombi").subscribe(pin => { + this.plexService.oAuth({wizard: true, pin}).subscribe(x => { + if(x.url) { + window.location.href = x.url; + } + }); }); } } diff --git a/src/Ombi/Controllers/External/PlexController.cs b/src/Ombi/Controllers/External/PlexController.cs index 3675040d3..6ea37e9cc 100644 --- a/src/Ombi/Controllers/External/PlexController.cs +++ b/src/Ombi/Controllers/External/PlexController.cs @@ -12,6 +12,7 @@ using Ombi.Core.Authentication; using Ombi.Core.Settings; using Ombi.Core.Settings.Models.External; using Ombi.Helpers; +using Ombi.Models; using Ombi.Models.External; namespace Ombi.Controllers.External @@ -177,25 +178,23 @@ namespace Ombi.Controllers.External return vm.DistinctBy(x => x.Id); } - [HttpGet("oauth/{wizard:bool}")] + [HttpPost("oauth")] [AllowAnonymous] - public async Task OAuth(bool wizard) + public async Task OAuth([FromBody]PlexOAuthViewModel 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) + if (!wizard.Wizard) { - url = await _plexOAuthManager.GetOAuthUrl(pin.id, pin.code); + url = await _plexOAuthManager.GetOAuthUrl(wizard.Pin.id, wizard.Pin.code); } else { var websiteAddress =$"{this.Request.Scheme}://{this.Request.Host}{this.Request.PathBase}"; - url = await _plexOAuthManager.GetWizardOAuthUrl(pin.id, pin.code, websiteAddress); + url = await _plexOAuthManager.GetWizardOAuthUrl(wizard.Pin.id, wizard.Pin.code, websiteAddress); } if (url == null) diff --git a/src/Ombi/Controllers/SettingsController.cs b/src/Ombi/Controllers/SettingsController.cs index fb0ec53e3..50938942b 100644 --- a/src/Ombi/Controllers/SettingsController.cs +++ b/src/Ombi/Controllers/SettingsController.cs @@ -127,10 +127,23 @@ namespace Ombi.Controllers public async Task PlexSettings() { var s = await Get(); - return s; } + [HttpGet("clientid")] + [AllowAnonymous] + public async Task GetClientId() + { + var s = await Get(); + if (s.InstallId == Guid.Empty) + { + s.InstallId = Guid.NewGuid(); + // Save it + await PlexSettings(s); + } + return s.InstallId.ToString("N"); + } + /// /// Save the Plex settings. /// diff --git a/src/Ombi/Controllers/TokenController.cs b/src/Ombi/Controllers/TokenController.cs index b45752af4..aad367dbe 100644 --- a/src/Ombi/Controllers/TokenController.cs +++ b/src/Ombi/Controllers/TokenController.cs @@ -80,12 +80,10 @@ namespace Ombi.Controllers { // Plex OAuth // Redirect them to Plex - // 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, websiteAddress); + var url = await _plexOAuthManager.GetOAuthUrl(model.PlexTvPin.id, model.PlexTvPin.code, websiteAddress); if (url == null) { return new JsonResult(new diff --git a/src/Ombi/Models/PlexOAuthViewModel.cs b/src/Ombi/Models/PlexOAuthViewModel.cs new file mode 100644 index 000000000..dfc2eda8f --- /dev/null +++ b/src/Ombi/Models/PlexOAuthViewModel.cs @@ -0,0 +1,10 @@ +using Ombi.Api.Plex.Models.OAuth; + +namespace Ombi.Models +{ + public class PlexOAuthViewModel + { + public bool Wizard { get; set; } + public OAuthPin Pin { get; set; } + } +} \ No newline at end of file diff --git a/src/Ombi/Models/UserAuthModel.cs b/src/Ombi/Models/UserAuthModel.cs index 046119821..5f240699b 100644 --- a/src/Ombi/Models/UserAuthModel.cs +++ b/src/Ombi/Models/UserAuthModel.cs @@ -1,4 +1,6 @@ -namespace Ombi.Models +using Ombi.Api.Plex.Models.OAuth; + +namespace Ombi.Models { public class UserAuthModel { @@ -7,5 +9,6 @@ public bool RememberMe { get; set; } public bool UsePlexAdminAccount { get; set; } public bool UsePlexOAuth { get; set; } + public OAuthPin PlexTvPin { get; set; } } } \ No newline at end of file diff --git a/src/Ombi/Startup.cs b/src/Ombi/Startup.cs index 9e7b1b290..b8a841592 100644 --- a/src/Ombi/Startup.cs +++ b/src/Ombi/Startup.cs @@ -129,6 +129,12 @@ namespace Ombi //x.UseConsole(); }); + services.AddCors(o => o.AddPolicy("MyPolicy", builder => + { + builder.AllowAnyOrigin() + .AllowAnyMethod() + .AllowAnyHeader(); + })); // Build the intermediate service provider return services.BuildServiceProvider(); @@ -211,13 +217,16 @@ namespace Ombi app.UseMiddleware(); app.UseMiddleware(); + app.UseCors("MyPolicy"); //app.ApiKeyMiddlewear(app.ApplicationServices); app.UseSwagger(); app.UseSwaggerUI(c => { c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1"); }); - + + + app.UseMvc(routes => { routes.MapRoute( From 5eaa5cf78ca26082ab56605c56e8a3768ac3f412 Mon Sep 17 00:00:00 2001 From: Jamie Rees Date: Tue, 3 Jul 2018 19:44:30 +0100 Subject: [PATCH 09/26] Fixed #2371 --- src/Ombi.Core/Senders/TvSender.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Ombi.Core/Senders/TvSender.cs b/src/Ombi.Core/Senders/TvSender.cs index 12124d270..2cccd6778 100644 --- a/src/Ombi.Core/Senders/TvSender.cs +++ b/src/Ombi.Core/Senders/TvSender.cs @@ -255,7 +255,12 @@ namespace Ombi.Core.Senders { // We have the same amount of requests as all of the episodes in the season. var existingSeason = - result.seasons.First(x => x.seasonNumber == season.SeasonNumber); + result.seasons.FirstOrDefault(x => x.seasonNumber == season.SeasonNumber); + if (existingSeason == null) + { + Logger.LogError("The sonarr ep count was the same as out request count, but could not match the season number {0}", season.SeasonNumber); + continue; + } existingSeason.monitored = true; seriesChanges = true; } From f339969258c777230e05b00ad02d1ee6682dbc76 Mon Sep 17 00:00:00 2001 From: Jamie Rees Date: Tue, 3 Jul 2018 20:56:32 +0100 Subject: [PATCH 10/26] Fixed the issue where episodes were not being marked as available in the search #2367 --- .../Rule/Rules/Search/EmbyAvailabilityRule.cs | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/Ombi.Core/Rule/Rules/Search/EmbyAvailabilityRule.cs b/src/Ombi.Core/Rule/Rules/Search/EmbyAvailabilityRule.cs index 4f2f56b28..b61efc8ac 100644 --- a/src/Ombi.Core/Rule/Rules/Search/EmbyAvailabilityRule.cs +++ b/src/Ombi.Core/Rule/Rules/Search/EmbyAvailabilityRule.cs @@ -1,4 +1,6 @@ -using System.Linq; +using System; +using System.Linq; +using System.Linq.Expressions; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using Ombi.Core.Models.Search; @@ -59,10 +61,19 @@ namespace Ombi.Core.Rule.Rules.Search { EmbyEpisode epExists = null; - epExists = await allEpisodes.FirstOrDefaultAsync(x => - x.EpisodeNumber == episode.EpisodeNumber && x.SeasonNumber == season.SeasonNumber && - x.Series.ProviderId == item.ProviderId.ToString()); - + if (item.HasImdb) + { + epExists = await allEpisodes.FirstOrDefaultAsync(e => e.EpisodeNumber == episode.EpisodeNumber && e.SeasonNumber == season.SeasonNumber + && e.ImdbId == item.ImdbId); + } if (item.HasTvDb) + { + epExists = await allEpisodes.FirstOrDefaultAsync(e => e.EpisodeNumber == episode.EpisodeNumber && e.SeasonNumber == season.SeasonNumber + && e.TvDbId == item.TvDbId); + } if (item.HasTheMovieDb) + { + epExists = await allEpisodes.FirstOrDefaultAsync(e => e.EpisodeNumber == episode.EpisodeNumber && e.SeasonNumber == season.SeasonNumber + && e.TheMovieDbId == item.TheMovieDbId); + } if (epExists != null) { From 73d444cde20a6435dba33e6d688f280e624435a9 Mon Sep 17 00:00:00 2001 From: Jamie Rees Date: Tue, 3 Jul 2018 21:15:23 +0100 Subject: [PATCH 11/26] Fixed the View On Emby URL since the Link changed #2368 --- src/Ombi.Helpers/EmbyHelper.cs | 2 +- .../20180703200952_EmbyUrlFix.Designer.cs | 981 ++++++++++++++++++ .../Migrations/20180703200952_EmbyUrlFix.cs | 20 + 3 files changed, 1002 insertions(+), 1 deletion(-) create mode 100644 src/Ombi.Store/Migrations/20180703200952_EmbyUrlFix.Designer.cs create mode 100644 src/Ombi.Store/Migrations/20180703200952_EmbyUrlFix.cs diff --git a/src/Ombi.Helpers/EmbyHelper.cs b/src/Ombi.Helpers/EmbyHelper.cs index cf6904a0c..567bcfe7e 100644 --- a/src/Ombi.Helpers/EmbyHelper.cs +++ b/src/Ombi.Helpers/EmbyHelper.cs @@ -10,7 +10,7 @@ namespace Ombi.Helpers public static string GetEmbyMediaUrl(string mediaId) { var url = - $"http://app.emby.media/itemdetails.html?id={mediaId}"; + $"http://app.emby.media/#!/itemdetails.html?id={mediaId}"; return url; } } diff --git a/src/Ombi.Store/Migrations/20180703200952_EmbyUrlFix.Designer.cs b/src/Ombi.Store/Migrations/20180703200952_EmbyUrlFix.Designer.cs new file mode 100644 index 000000000..cebb7d22e --- /dev/null +++ b/src/Ombi.Store/Migrations/20180703200952_EmbyUrlFix.Designer.cs @@ -0,0 +1,981 @@ +// +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage; +using Microsoft.EntityFrameworkCore.Storage.Internal; +using Ombi.Helpers; +using Ombi.Store.Context; +using Ombi.Store.Entities; +using Ombi.Store.Entities.Requests; +using System; + +namespace Ombi.Store.Migrations +{ + [DbContext(typeof(OmbiContext))] + [Migration("20180703200952_EmbyUrlFix")] + partial class EmbyUrlFix + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.0.3-rtm-10026"); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Name") + .HasMaxLength(256); + + b.Property("NormalizedName") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasName("RoleNameIndex"); + + b.ToTable("AspNetRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("RoleId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider"); + + b.Property("ProviderKey"); + + b.Property("ProviderDisplayName"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId"); + + b.Property("RoleId"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId"); + + b.Property("LoginProvider"); + + b.Property("Name"); + + b.Property("Value"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.ApplicationConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Type"); + + b.Property("Value"); + + b.HasKey("Id"); + + b.ToTable("ApplicationConfiguration"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Audit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AuditArea"); + + b.Property("AuditType"); + + b.Property("DateTime"); + + b.Property("Description"); + + b.Property("User"); + + b.HasKey("Id"); + + b.ToTable("Audit"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.CouchPotatoCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TheMovieDbId"); + + b.HasKey("Id"); + + b.ToTable("CouchPotatoCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("EmbyId") + .IsRequired(); + + b.Property("ImdbId"); + + b.Property("ProviderId"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.Property("Type"); + + b.Property("Url"); + + b.HasKey("Id"); + + b.ToTable("EmbyContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("EmbyId"); + + b.Property("EpisodeNumber"); + + b.Property("ImdbId"); + + b.Property("ParentId"); + + b.Property("ProviderId"); + + b.Property("SeasonNumber"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.ToTable("EmbyEpisode"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.GlobalSettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Content"); + + b.Property("SettingsName"); + + b.HasKey("Id"); + + b.ToTable("GlobalSettings"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationTemplates", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Agent"); + + b.Property("Enabled"); + + b.Property("Message"); + + b.Property("NotificationType"); + + b.Property("Subject"); + + b.HasKey("Id"); + + b.ToTable("NotificationTemplates"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationUserId", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("PlayerId"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("NotificationUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.OmbiUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AccessFailedCount"); + + b.Property("Alias"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Email") + .HasMaxLength(256); + + b.Property("EmailConfirmed"); + + b.Property("EmbyConnectUserId"); + + b.Property("EpisodeRequestLimit"); + + b.Property("LastLoggedIn"); + + b.Property("LockoutEnabled"); + + b.Property("LockoutEnd"); + + b.Property("MovieRequestLimit"); + + b.Property("NormalizedEmail") + .HasMaxLength(256); + + b.Property("NormalizedUserName") + .HasMaxLength(256); + + b.Property("PasswordHash"); + + b.Property("PhoneNumber"); + + b.Property("PhoneNumberConfirmed"); + + b.Property("ProviderUserId"); + + b.Property("SecurityStamp"); + + b.Property("TwoFactorEnabled"); + + b.Property("UserAccessToken"); + + b.Property("UserName") + .HasMaxLength(256); + + b.Property("UserType"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasName("UserNameIndex"); + + b.ToTable("AspNetUsers"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeNumber"); + + b.Property("GrandparentKey"); + + b.Property("Key"); + + b.Property("ParentKey"); + + b.Property("SeasonNumber"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("GrandparentKey"); + + b.ToTable("PlexEpisode"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ParentKey"); + + b.Property("PlexContentId"); + + b.Property("PlexServerContentId"); + + b.Property("SeasonKey"); + + b.Property("SeasonNumber"); + + b.HasKey("Id"); + + b.HasIndex("PlexServerContentId"); + + b.ToTable("PlexSeasonsContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexServerContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("ImdbId"); + + b.Property("Key"); + + b.Property("Quality"); + + b.Property("ReleaseYear"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.Property("Type"); + + b.Property("Url"); + + b.HasKey("Id"); + + b.ToTable("PlexServerContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RadarrCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("HasFile"); + + b.Property("TheMovieDbId"); + + b.HasKey("Id"); + + b.ToTable("RadarrCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RecentlyAddedLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("ContentId"); + + b.Property("ContentType"); + + b.Property("EpisodeNumber"); + + b.Property("SeasonNumber"); + + b.Property("Type"); + + b.HasKey("Id"); + + b.ToTable("RecentlyAddedLog"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Approved"); + + b.Property("Available"); + + b.Property("Denied"); + + b.Property("DeniedReason"); + + b.Property("IssueId"); + + b.Property("ParentRequestId"); + + b.Property("RequestType"); + + b.Property("RequestedDate"); + + b.Property("RequestedUserId"); + + b.Property("SeriesType"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("ParentRequestId"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("ChildRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueCategory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Value"); + + b.HasKey("Id"); + + b.ToTable("IssueCategory"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueComments", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Comment"); + + b.Property("Date"); + + b.Property("IssuesId"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("IssuesId"); + + b.HasIndex("UserId"); + + b.ToTable("IssueComments"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Description"); + + b.Property("IssueCategoryId"); + + b.Property("IssueId"); + + b.Property("ProviderId"); + + b.Property("RequestId"); + + b.Property("RequestType"); + + b.Property("ResovledDate"); + + b.Property("Status"); + + b.Property("Subject"); + + b.Property("Title"); + + b.Property("UserReportedId"); + + b.HasKey("Id"); + + b.HasIndex("IssueCategoryId"); + + b.HasIndex("IssueId"); + + b.HasIndex("UserReportedId"); + + b.ToTable("Issues"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Approved"); + + b.Property("Available"); + + b.Property("Background"); + + b.Property("Denied"); + + b.Property("DeniedReason"); + + b.Property("DigitalReleaseDate"); + + b.Property("ImdbId"); + + b.Property("IssueId"); + + b.Property("Overview"); + + b.Property("PosterPath"); + + b.Property("QualityOverride"); + + b.Property("ReleaseDate"); + + b.Property("RequestType"); + + b.Property("RequestedDate"); + + b.Property("RequestedUserId"); + + b.Property("RootPathOverride"); + + b.Property("Status"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("MovieRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.RequestLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeCount"); + + b.Property("RequestDate"); + + b.Property("RequestId"); + + b.Property("RequestType"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("RequestLog"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.TvRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Background"); + + b.Property("ImdbId"); + + b.Property("Overview"); + + b.Property("PosterPath"); + + b.Property("QualityOverride"); + + b.Property("ReleaseDate"); + + b.Property("RootFolder"); + + b.Property("Status"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("TvRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RequestSubscription", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("RequestId"); + + b.Property("RequestType"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("RequestSubscription"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SickRageCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SickRageCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SickRageEpisodeCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeNumber"); + + b.Property("SeasonNumber"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SickRageEpisodeCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SonarrCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SonarrCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SonarrEpisodeCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeNumber"); + + b.Property("HasFile"); + + b.Property("SeasonNumber"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SonarrEpisodeCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Tokens", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Token"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Tokens"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AirDate"); + + b.Property("Approved"); + + b.Property("Available"); + + b.Property("EpisodeNumber"); + + b.Property("Requested"); + + b.Property("SeasonId"); + + b.Property("Title"); + + b.Property("Url"); + + b.HasKey("Id"); + + b.HasIndex("SeasonId"); + + b.ToTable("EpisodeRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChildRequestId"); + + b.Property("SeasonNumber"); + + b.HasKey("Id"); + + b.HasIndex("ChildRequestId"); + + b.ToTable("SeasonRequests"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b => + { + b.HasOne("Ombi.Store.Entities.EmbyContent", "Series") + .WithMany("Episodes") + .HasForeignKey("ParentId") + .HasPrincipalKey("EmbyId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationUserId", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany("NotificationUserIds") + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b => + { + b.HasOne("Ombi.Store.Entities.PlexServerContent", "Series") + .WithMany("Episodes") + .HasForeignKey("GrandparentKey") + .HasPrincipalKey("Key") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b => + { + b.HasOne("Ombi.Store.Entities.PlexServerContent") + .WithMany("Seasons") + .HasForeignKey("PlexServerContentId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b => + { + b.HasOne("Ombi.Store.Entities.Requests.TvRequests", "ParentRequest") + .WithMany("ChildRequests") + .HasForeignKey("ParentRequestId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueComments", b => + { + b.HasOne("Ombi.Store.Entities.Requests.Issues", "Issues") + .WithMany("Comments") + .HasForeignKey("IssuesId"); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b => + { + b.HasOne("Ombi.Store.Entities.Requests.IssueCategory", "IssueCategory") + .WithMany() + .HasForeignKey("IssueCategoryId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ombi.Store.Entities.Requests.ChildRequests") + .WithMany("Issues") + .HasForeignKey("IssueId"); + + b.HasOne("Ombi.Store.Entities.Requests.MovieRequests") + .WithMany("Issues") + .HasForeignKey("IssueId"); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "UserReported") + .WithMany() + .HasForeignKey("UserReportedId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.RequestLog", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RequestSubscription", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Tokens", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b => + { + b.HasOne("Ombi.Store.Repository.Requests.SeasonRequests", "Season") + .WithMany("Episodes") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b => + { + b.HasOne("Ombi.Store.Entities.Requests.ChildRequests", "ChildRequest") + .WithMany("SeasonRequests") + .HasForeignKey("ChildRequestId") + .OnDelete(DeleteBehavior.Cascade); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Ombi.Store/Migrations/20180703200952_EmbyUrlFix.cs b/src/Ombi.Store/Migrations/20180703200952_EmbyUrlFix.cs new file mode 100644 index 000000000..97e714a65 --- /dev/null +++ b/src/Ombi.Store/Migrations/20180703200952_EmbyUrlFix.cs @@ -0,0 +1,20 @@ +using Microsoft.EntityFrameworkCore.Migrations; +using System; +using System.Collections.Generic; + +namespace Ombi.Store.Migrations +{ + public partial class EmbyUrlFix : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.Sql( + @"UPDATE EmbyContent SET Url = replace( Url, 'http://app.emby.media/itemdetails.html', 'http://app.emby.media/#!/itemdetails.html' ) WHERE Url LIKE 'http://app.emby.media/itemdetails.html%';"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + + } + } +} From c14f60370594bf47a91f927ec8efe20de9521128 Mon Sep 17 00:00:00 2001 From: Jamie Rees Date: Tue, 3 Jul 2018 21:21:32 +0100 Subject: [PATCH 12/26] Another attempt to fix #2366 --- src/Ombi.Schedule/Jobs/Emby/EmbyContentSync.cs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/Ombi.Schedule/Jobs/Emby/EmbyContentSync.cs b/src/Ombi.Schedule/Jobs/Emby/EmbyContentSync.cs index 3e2e1a2e1..7002eec41 100644 --- a/src/Ombi.Schedule/Jobs/Emby/EmbyContentSync.cs +++ b/src/Ombi.Schedule/Jobs/Emby/EmbyContentSync.cs @@ -81,7 +81,7 @@ namespace Ombi.Schedule.Jobs.Emby { foreach (var movie in movies.Items) { - if (movie.Type.Equals("boxset", StringComparison.CurrentCultureIgnoreCase) && mediaToAdd.All(x => x.EmbyId != movie.Id)) + if (movie.Type.Equals("boxset", StringComparison.InvariantCultureIgnoreCase)) { var movieInfo = await _api.GetCollection(movie.Id, server.ApiKey, server.AdministratorId, server.FullUri); @@ -89,10 +89,15 @@ namespace Ombi.Schedule.Jobs.Emby { await ProcessMovies(item, mediaToAdd); } + + processed++; + } + else + { + processed++; + // Regular movie + await ProcessMovies(movie, mediaToAdd); } - processed++; - // Regular movie - await ProcessMovies(movie, mediaToAdd); } // Get the next batch @@ -163,8 +168,8 @@ namespace Ombi.Schedule.Jobs.Emby { // Check if it exists var existingMovie = await _repo.GetByEmbyId(movieInfo.Id); - - if (existingMovie == null) + var alreadyGoingToAdd = content.Any(x => x.EmbyId == movieInfo.Id); + if (existingMovie == null && !alreadyGoingToAdd) { _logger.LogDebug("Adding new movie {0}", movieInfo.Name); content.Add(new EmbyContent From c6a362bf2ba2f465f1998501c0dd8a73e0a0d11c Mon Sep 17 00:00:00 2001 From: Jamie Rees Date: Wed, 4 Jul 2018 14:28:01 +0100 Subject: [PATCH 13/26] Added the ability to impersonate a user when using the API Key. This allows people to use the API and request as a certain user. #2363 --- src/Ombi/ApiKeyMiddlewear.cs | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/src/Ombi/ApiKeyMiddlewear.cs b/src/Ombi/ApiKeyMiddlewear.cs index f38317b3e..f3c956df4 100644 --- a/src/Ombi/ApiKeyMiddlewear.cs +++ b/src/Ombi/ApiKeyMiddlewear.cs @@ -94,9 +94,31 @@ namespace Ombi } else { - var identity = new GenericIdentity("API"); - var principal = new GenericPrincipal(identity, new[] { "Admin", "ApiUser" }); - context.User = principal; + // Check if we have a UserName header if so we can impersonate that user + if (context.Request.Headers.Keys.Contains("UserName", StringComparer.InvariantCultureIgnoreCase)) + { + var username = context.Request.Headers["UserName"].FirstOrDefault(); + var um = context.RequestServices.GetService(); + var user = await um.Users.FirstOrDefaultAsync(x => + x.UserName.Equals(username, StringComparison.InvariantCultureIgnoreCase)); + if (user == null) + { + context.Response.StatusCode = (int)HttpStatusCode.Unauthorized; + await context.Response.WriteAsync("Invalid User"); + await next.Invoke(context); + } + var roles = await um.GetRolesAsync(user); + var identity = new GenericIdentity(user.UserName); + var principal = new GenericPrincipal(identity, roles.ToArray()); + context.User = principal; + } + else + { + var identity = new GenericIdentity("API"); + var principal = new GenericPrincipal(identity, new[] { "Admin", "ApiUser" }); + context.User = principal; + } + await next.Invoke(context); } } From 3f729089c8380f88bf7c048031de749d55d122ac Mon Sep 17 00:00:00 2001 From: TidusJar Date: Wed, 4 Jul 2018 14:32:32 +0100 Subject: [PATCH 14/26] !wip changelog --- CHANGELOG.md | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5eb51423c..338c4a5b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,76 @@ # Changelog +## (unreleased) + +### **New Features** + +- Added the ability to impersonate a user when using the API Key. This allows people to use the API and request as a certain user. #2363. [Jamie Rees] + +- Added more background images and it will loop through the available ones. [Jamie Rees] + +- Added chunk hashing to resolve #2330. [Jamie Rees] + +- Added API at /api/v1/status/info to get branch and version information #2331. [Jamie Rees] + +- Update to .net 2.1.1. [Jamie] + +### **Fixes** + +- Another attempt to fix #2366. [Jamie Rees] + +- Fixed the Plex OAuth warning. [Jamie] + +- Revert "Fixed Plex OAuth, should no longer show Insecure warning" [Jamie Rees] + +- Fixed Plex OAuth, should no longer show Insecure warning. [Jamie Rees] + +- Fixed the View On Emby URL since the Link changed #2368. [Jamie Rees] + +- Fixed the issue where episodes were not being marked as available in the search #2367. [Jamie Rees] + +- Fixed #2371. [Jamie Rees] + +- Fixed collection issues in Emby #2366. [Jamie Rees] + +- Do not delete the Emby Information every time we run, let's keep the content now. [Jamie Rees] + +- Emby Improvements: Batch up the amount we get from the server. [Jamie Rees] + +- Log errors when they are uncaught. [Jamie Rees] + +- Fix unclosed table tags causing overflow #2322. [Anojh] + +- This should now fix #2350. [Jamie] + +- Improve the validation around the Application URL. [Jamie Rees] + +- Fixed #2341. [Jamie Rees] + +- Stop spamming errors when FanArt doesn't have the image. [Jamie Rees] + +- Fixed #2338. [Jamie Rees] + +- Removed some logging statements. [Jamie Rees] + +- Fixed the api key being case sensative #2350. [Jamie Rees] + +- Improved the Emby API #2230 Thanks Luke! [Jamie Rees] + +- Revert. [Jamie Rees] + +- Fixed a small error in the Mobile Notification Provider. [Jamie Rees] + +- Minor style tweaks. [Randall Bruder] + +- Downgrade to .net core 2.0. [Jamie Rees] + +- Downgrade Microsoft.AspNetCore.All package back to 2.0.8. [Jamie Rees] + +- Removed old code. [Jamie Rees] + +- Swap out the old way of validating the API key with a real middlewear this time. [Jamie Rees] + + ## v3.0.3421 (2018-06-23) ### **New Features** From acf604305bd846cf944d55796ee3ad46a792818d Mon Sep 17 00:00:00 2001 From: TidusJar Date: Wed, 4 Jul 2018 14:40:02 +0100 Subject: [PATCH 15/26] Fixed the issue where you could not delete a user #2365 --- src/Ombi/Controllers/IdentityController.cs | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/Ombi/Controllers/IdentityController.cs b/src/Ombi/Controllers/IdentityController.cs index e9fb17af6..38c2d1e94 100644 --- a/src/Ombi/Controllers/IdentityController.cs +++ b/src/Ombi/Controllers/IdentityController.cs @@ -57,7 +57,9 @@ namespace Ombi.Controllers ISettingsService settings, IRepository requestLog, IRepository issues, - IRepository issueComments) + IRepository issueComments, + IRepository notificationRepository, + IRepository subscriptionRepository) { UserManager = user; Mapper = mapper; @@ -75,6 +77,8 @@ namespace Ombi.Controllers _requestLogRepository = requestLog; _issueCommentsRepository = issueComments; OmbiSettings = ombiSettings; + _requestSubscriptionRepository = subscriptionRepository; + _notificationRepository = notificationRepository; } private OmbiUserManager UserManager { get; } @@ -93,6 +97,8 @@ namespace Ombi.Controllers private readonly IRepository _issuesRepository; private readonly IRepository _issueCommentsRepository; private readonly IRepository _requestLogRepository; + private readonly IRepository _notificationRepository; + private readonly IRepository _requestSubscriptionRepository; /// @@ -568,6 +574,19 @@ namespace Ombi.Controllers { await _issueCommentsRepository.DeleteRange(issueComments); } + + // Delete the Subscriptions and mobile notification ids + var subs = _requestSubscriptionRepository.GetAll().Where(x => x.UserId == userId); + var mobileIds = _notificationRepository.GetAll().Where(x => x.UserId == userId); + if (subs.Any()) + { + await _requestSubscriptionRepository.DeleteRange(subs); + } + + if (mobileIds.Any()) + { + await _notificationRepository.DeleteRange(mobileIds); + } var result = await UserManager.DeleteAsync(userToDelete); if (result.Succeeded) From 50c4e76894a203ca343f474befd05d50e8cbb696 Mon Sep 17 00:00:00 2001 From: TidusJar Date: Thu, 5 Jul 2018 20:47:31 +0100 Subject: [PATCH 16/26] Fixed #2367 --- src/Ombi.Core/Rule/Rules/Search/EmbyAvailabilityRule.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Ombi.Core/Rule/Rules/Search/EmbyAvailabilityRule.cs b/src/Ombi.Core/Rule/Rules/Search/EmbyAvailabilityRule.cs index b61efc8ac..486de9ea8 100644 --- a/src/Ombi.Core/Rule/Rules/Search/EmbyAvailabilityRule.cs +++ b/src/Ombi.Core/Rule/Rules/Search/EmbyAvailabilityRule.cs @@ -65,11 +65,11 @@ namespace Ombi.Core.Rule.Rules.Search { epExists = await allEpisodes.FirstOrDefaultAsync(e => e.EpisodeNumber == episode.EpisodeNumber && e.SeasonNumber == season.SeasonNumber && e.ImdbId == item.ImdbId); - } if (item.HasTvDb) + } if (item.HasTvDb && epExists == null) { epExists = await allEpisodes.FirstOrDefaultAsync(e => e.EpisodeNumber == episode.EpisodeNumber && e.SeasonNumber == season.SeasonNumber - && e.TvDbId == item.TvDbId); - } if (item.HasTheMovieDb) + && e.Series.TvDbId == item.TvDbId); + } if (item.HasTheMovieDb && epExists == null) { epExists = await allEpisodes.FirstOrDefaultAsync(e => e.EpisodeNumber == episode.EpisodeNumber && e.SeasonNumber == season.SeasonNumber && e.TheMovieDbId == item.TheMovieDbId); From 185c87ab40d9b4e4409e0e38b728a0f354fecb64 Mon Sep 17 00:00:00 2001 From: TidusJar Date: Fri, 6 Jul 2018 08:36:36 +0100 Subject: [PATCH 17/26] Updated the Emby availability checker to bring it more in line with what we do with Plex. --- .../Jobs/Emby/EmbyAvaliabilityChecker.cs | 45 +++++++++++++++---- .../Jobs/Plex/PlexAvailabilityChecker.cs | 7 ++- 2 files changed, 43 insertions(+), 9 deletions(-) diff --git a/src/Ombi.Schedule/Jobs/Emby/EmbyAvaliabilityChecker.cs b/src/Ombi.Schedule/Jobs/Emby/EmbyAvaliabilityChecker.cs index 59b5adc96..ec675ebd8 100644 --- a/src/Ombi.Schedule/Jobs/Emby/EmbyAvaliabilityChecker.cs +++ b/src/Ombi.Schedule/Jobs/Emby/EmbyAvaliabilityChecker.cs @@ -121,24 +121,53 @@ namespace Ombi.Schedule.Jobs.Emby foreach (var child in tv) { - IQueryable seriesEpisodes; - if (child.ParentRequest.TvDbId > 0) + + var useImdb = false; + var useTvDb = false; + if (child.ParentRequest.ImdbId.HasValue()) + { + useImdb = true; + } + + if (child.ParentRequest.TvDbId.ToString().HasValue()) { - seriesEpisodes = embyEpisodes.Where(x => x.Series.TvDbId == child.ParentRequest.TvDbId.ToString()); + useTvDb = true; } - else if(child.ParentRequest.ImdbId.HasValue()) + + var tvDbId = child.ParentRequest.TvDbId; + var imdbId = child.ParentRequest.ImdbId; + IQueryable seriesEpisodes = null; + if (useImdb) { - seriesEpisodes = embyEpisodes.Where(x => x.Series.ImdbId == child.ParentRequest.ImdbId); + seriesEpisodes = embyEpisodes.Where(x => x.Series.ImdbId == imdbId.ToString()); } - else + + if (useTvDb && (seriesEpisodes == null || !seriesEpisodes.Any())) + { + seriesEpisodes = embyEpisodes.Where(x => x.Series.TvDbId == tvDbId.ToString()); + } + + if (seriesEpisodes == null) { continue; } + if (!seriesEpisodes.Any()) + { + // Let's try and match the series by name + seriesEpisodes = embyEpisodes.Where(x => + x.Series.Title.Equals(child.Title, StringComparison.CurrentCultureIgnoreCase)); + } + foreach (var season in child.SeasonRequests) { foreach (var episode in season.Episodes) { + if (episode.Available) + { + continue; + } + var foundEp = await seriesEpisodes.FirstOrDefaultAsync( x => x.EpisodeNumber == episode.EpisodeNumber && x.SeasonNumber == episode.Season.SeasonNumber); @@ -160,9 +189,9 @@ namespace Ombi.Schedule.Jobs.Emby { DateTime = DateTime.Now, NotificationType = NotificationType.RequestAvailable, - RequestId = child.ParentRequestId, + RequestId = child.Id, RequestType = RequestType.TvShow, - Recipient = child.RequestedUser.Email, + Recipient = child.RequestedUser.Email })); } } diff --git a/src/Ombi.Schedule/Jobs/Plex/PlexAvailabilityChecker.cs b/src/Ombi.Schedule/Jobs/Plex/PlexAvailabilityChecker.cs index 586bd75e9..9978b7e2b 100644 --- a/src/Ombi.Schedule/Jobs/Plex/PlexAvailabilityChecker.cs +++ b/src/Ombi.Schedule/Jobs/Plex/PlexAvailabilityChecker.cs @@ -79,11 +79,16 @@ namespace Ombi.Schedule.Jobs.Plex { seriesEpisodes = plexEpisodes.Where(x => x.Series.ImdbId == imdbId.ToString()); } - if (useTvDb) + if (useTvDb && (seriesEpisodes == null || !seriesEpisodes.Any()) ) { seriesEpisodes = plexEpisodes.Where(x => x.Series.TvDbId == tvDbId.ToString()); } + if (seriesEpisodes == null) + { + continue; + } + if (!seriesEpisodes.Any()) { // Let's try and match the series by name From 3d2010d059769e15fd23fc66d4310976ee20c698 Mon Sep 17 00:00:00 2001 From: Anojh Date: Mon, 9 Jul 2018 11:21:43 -0700 Subject: [PATCH 18/26] fix #2322 caused by continue statement inside try catch block --- src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs | 85 ++++++++++---------- 1 file changed, 43 insertions(+), 42 deletions(-) diff --git a/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs b/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs index 8f88a4a18..6e89d167e 100644 --- a/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs +++ b/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs @@ -492,41 +492,42 @@ namespace Ombi.Schedule.Jobs.Ombi var orderedTv = series.OrderByDescending(x => x.AddedAt); foreach (var t in orderedTv) { - try + if (!t.HasTvDb) { - if (!t.HasTvDb) + // We may need to use themoviedb for the imdbid or their own id to get info + if (t.HasTheMovieDb) { - // We may need to use themoviedb for the imdbid or their own id to get info - if (t.HasTheMovieDb) + int.TryParse(t.TheMovieDbId, out var movieId); + var externals = await _movieApi.GetTvExternals(movieId); + if (externals == null || externals.tvdb_id <= 0) { - int.TryParse(t.TheMovieDbId, out var movieId); - var externals = await _movieApi.GetTvExternals(movieId); - if (externals == null || externals.tvdb_id <= 0) - { - continue; - } - t.TvDbId = externals.tvdb_id.ToString(); + continue; } - // WE could check the below but we need to get the moviedb and then perform the above, let the metadata job figure this out. - //else if(t.HasImdb) - //{ - // // Check the imdbid - // var externals = await _movieApi.Find(t.ImdbId, ExternalSource.imdb_id); - // if (externals?.tv_results == null || externals.tv_results.Length <= 0) - // { - // continue; - // } - // t.TvDbId = externals.tv_results.FirstOrDefault()..ToString(); - //} - + t.TvDbId = externals.tvdb_id.ToString(); } + // WE could check the below but we need to get the moviedb and then perform the above, let the metadata job figure this out. + //else if(t.HasImdb) + //{ + // // Check the imdbid + // var externals = await _movieApi.Find(t.ImdbId, ExternalSource.imdb_id); + // if (externals?.tv_results == null || externals.tv_results.Length <= 0) + // { + // continue; + // } + // t.TvDbId = externals.tv_results.FirstOrDefault()..ToString(); + //} - int.TryParse(t.TvDbId, out var tvdbId); - var info = await _tvApi.ShowLookupByTheTvDbId(tvdbId); - if (info == null) - { - continue; - } + } + + int.TryParse(t.TvDbId, out var tvdbId); + var info = await _tvApi.ShowLookupByTheTvDbId(tvdbId); + if (info == null) + { + continue; + } + + try + { var banner = info.image?.original; if (!string.IsNullOrEmpty(banner)) { @@ -588,7 +589,7 @@ namespace Ombi.Schedule.Jobs.Ombi { AddGenres(sb, $"Genres: {string.Join(", ", info.genres.Select(x => x.ToString()).ToArray())}"); } - + } catch (Exception e) { @@ -676,20 +677,20 @@ namespace Ombi.Schedule.Jobs.Ombi var orderedTv = series.OrderByDescending(x => x.AddedAt); foreach (var t in orderedTv) { - try + if (!t.TvDbId.HasValue()) { - if (!t.TvDbId.HasValue()) - { - continue; - } + continue; + } - int.TryParse(t.TvDbId, out var tvdbId); - var info = await _tvApi.ShowLookupByTheTvDbId(tvdbId); - if (info == null) - { - continue; - } + int.TryParse(t.TvDbId, out var tvdbId); + var info = await _tvApi.ShowLookupByTheTvDbId(tvdbId); + if (info == null) + { + continue; + } + try + { var banner = info.image?.original; if (!string.IsNullOrEmpty(banner)) { @@ -752,7 +753,7 @@ namespace Ombi.Schedule.Jobs.Ombi { AddGenres(sb, $"Genres: {string.Join(", ", info.genres.Select(x => x.ToString()).ToArray())}"); } - + } catch (Exception e) { From cc0d6056c1bf80110c0ad27b86b221d83f023709 Mon Sep 17 00:00:00 2001 From: TidusJar Date: Wed, 18 Jul 2018 12:32:31 +0100 Subject: [PATCH 19/26] !wip changelog --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 338c4a5b2..fb91d9d96 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ ### **New Features** +- Updated the Emby availability checker to bring it more in line with what we do with Plex. [TidusJar] + - Added the ability to impersonate a user when using the API Key. This allows people to use the API and request as a certain user. #2363. [Jamie Rees] - Added more background images and it will loop through the available ones. [Jamie Rees] @@ -16,6 +18,12 @@ ### **Fixes** +- Fix #2322 caused by continue statement inside try catch block. [Anojh] + +- Fixed #2367. [TidusJar] + +- Fixed the issue where you could not delete a user #2365. [TidusJar] + - Another attempt to fix #2366. [Jamie Rees] - Fixed the Plex OAuth warning. [Jamie] From ec628e9217cb0a1cf8e02c3af93c7b7ef66079d8 Mon Sep 17 00:00:00 2001 From: Jamie Date: Sat, 21 Jul 2018 21:13:56 +0100 Subject: [PATCH 20/26] Updated to 2.1.1 --- src/Ombi.Api.Radarr/Ombi.Api.Radarr.csproj | 2 +- src/Ombi.Api.Service/Ombi.Api.Service.csproj | 2 +- src/Ombi.Api/Ombi.Api.csproj | 2 +- src/Ombi.Core.Tests/Ombi.Core.Tests.csproj | 4 ++-- src/Ombi.Core/Ombi.Core.csproj | 6 +++--- .../Ombi.DependencyInjection.csproj | 6 +++--- src/Ombi.Helpers/Ombi.Helpers.csproj | 4 ++-- .../Ombi.Notifications.Tests.csproj | 4 ++-- .../Ombi.Schedule.Tests.csproj | 6 +++--- src/Ombi.Settings/Ombi.Settings.csproj | 2 +- src/Ombi.Store/Ombi.Store.csproj | 8 ++++---- src/Ombi.Tests/Ombi.Tests.csproj | 4 ++-- src/Ombi.Updater/Ombi.Updater.csproj | 18 +++++++++--------- src/Ombi/Ombi.csproj | 10 +++++----- 14 files changed, 39 insertions(+), 39 deletions(-) diff --git a/src/Ombi.Api.Radarr/Ombi.Api.Radarr.csproj b/src/Ombi.Api.Radarr/Ombi.Api.Radarr.csproj index d2b605337..0c615f301 100644 --- a/src/Ombi.Api.Radarr/Ombi.Api.Radarr.csproj +++ b/src/Ombi.Api.Radarr/Ombi.Api.Radarr.csproj @@ -9,7 +9,7 @@ - + diff --git a/src/Ombi.Api.Service/Ombi.Api.Service.csproj b/src/Ombi.Api.Service/Ombi.Api.Service.csproj index 67f37c80d..8cbddd874 100644 --- a/src/Ombi.Api.Service/Ombi.Api.Service.csproj +++ b/src/Ombi.Api.Service/Ombi.Api.Service.csproj @@ -11,7 +11,7 @@ - + diff --git a/src/Ombi.Api/Ombi.Api.csproj b/src/Ombi.Api/Ombi.Api.csproj index 32fc60bb6..804512945 100644 --- a/src/Ombi.Api/Ombi.Api.csproj +++ b/src/Ombi.Api/Ombi.Api.csproj @@ -9,7 +9,7 @@ - + diff --git a/src/Ombi.Core.Tests/Ombi.Core.Tests.csproj b/src/Ombi.Core.Tests/Ombi.Core.Tests.csproj index db98876df..8f0abee8f 100644 --- a/src/Ombi.Core.Tests/Ombi.Core.Tests.csproj +++ b/src/Ombi.Core.Tests/Ombi.Core.Tests.csproj @@ -1,7 +1,7 @@  - netcoreapp2.0 + netcoreapp2.1 @@ -9,7 +9,7 @@ - + diff --git a/src/Ombi.Core/Ombi.Core.csproj b/src/Ombi.Core/Ombi.Core.csproj index 2037113bd..104db24fc 100644 --- a/src/Ombi.Core/Ombi.Core.csproj +++ b/src/Ombi.Core/Ombi.Core.csproj @@ -12,9 +12,9 @@ - - - + + + diff --git a/src/Ombi.DependencyInjection/Ombi.DependencyInjection.csproj b/src/Ombi.DependencyInjection/Ombi.DependencyInjection.csproj index 675f6461b..2e7f984a7 100644 --- a/src/Ombi.DependencyInjection/Ombi.DependencyInjection.csproj +++ b/src/Ombi.DependencyInjection/Ombi.DependencyInjection.csproj @@ -9,9 +9,9 @@ - - - + + + diff --git a/src/Ombi.Helpers/Ombi.Helpers.csproj b/src/Ombi.Helpers/Ombi.Helpers.csproj index 12c6fecc4..e94afc816 100644 --- a/src/Ombi.Helpers/Ombi.Helpers.csproj +++ b/src/Ombi.Helpers/Ombi.Helpers.csproj @@ -10,8 +10,8 @@ - - + + diff --git a/src/Ombi.Notifications.Tests/Ombi.Notifications.Tests.csproj b/src/Ombi.Notifications.Tests/Ombi.Notifications.Tests.csproj index 339986ef5..de1c55533 100644 --- a/src/Ombi.Notifications.Tests/Ombi.Notifications.Tests.csproj +++ b/src/Ombi.Notifications.Tests/Ombi.Notifications.Tests.csproj @@ -1,14 +1,14 @@  - netcoreapp2.0 + netcoreapp2.1 - + diff --git a/src/Ombi.Schedule.Tests/Ombi.Schedule.Tests.csproj b/src/Ombi.Schedule.Tests/Ombi.Schedule.Tests.csproj index bdfbd60e6..a124f01bd 100644 --- a/src/Ombi.Schedule.Tests/Ombi.Schedule.Tests.csproj +++ b/src/Ombi.Schedule.Tests/Ombi.Schedule.Tests.csproj @@ -1,16 +1,16 @@ - netcoreapp2.0 + netcoreapp2.1 - + - + diff --git a/src/Ombi.Settings/Ombi.Settings.csproj b/src/Ombi.Settings/Ombi.Settings.csproj index 3cb56cb07..19a415a47 100644 --- a/src/Ombi.Settings/Ombi.Settings.csproj +++ b/src/Ombi.Settings/Ombi.Settings.csproj @@ -9,7 +9,7 @@ - + diff --git a/src/Ombi.Store/Ombi.Store.csproj b/src/Ombi.Store/Ombi.Store.csproj index 703bbf672..2ceb78424 100644 --- a/src/Ombi.Store/Ombi.Store.csproj +++ b/src/Ombi.Store/Ombi.Store.csproj @@ -10,10 +10,10 @@ - - - - + + + + diff --git a/src/Ombi.Tests/Ombi.Tests.csproj b/src/Ombi.Tests/Ombi.Tests.csproj index 6296d9e8c..95eb09353 100644 --- a/src/Ombi.Tests/Ombi.Tests.csproj +++ b/src/Ombi.Tests/Ombi.Tests.csproj @@ -7,12 +7,12 @@ - + - + diff --git a/src/Ombi.Updater/Ombi.Updater.csproj b/src/Ombi.Updater/Ombi.Updater.csproj index 3a55a7040..07fb92d81 100644 --- a/src/Ombi.Updater/Ombi.Updater.csproj +++ b/src/Ombi.Updater/Ombi.Updater.csproj @@ -3,7 +3,7 @@ Exe win10-x64;win10-x86;osx-x64;ubuntu-x64;debian.8-x64;centos.7-x64;linux-x64;linux-arm;linux-arm64; - netcoreapp2.0 + netcoreapp2.1 3.0.0.0 3.0.0.0 @@ -12,14 +12,14 @@ - - - - - - - - + + + + + + + + diff --git a/src/Ombi/Ombi.csproj b/src/Ombi/Ombi.csproj index 8b9e00c45..06fc02b81 100644 --- a/src/Ombi/Ombi.csproj +++ b/src/Ombi/Ombi.csproj @@ -1,7 +1,7 @@  - netcoreapp2.0 + netcoreapp2.1 win10-x64;win10-x86;osx-x64;ubuntu-x64;debian.8-x64;centos.7-x64;linux-x64;linux-arm;linux-arm64; false Latest @@ -67,10 +67,10 @@ - - - - + + + + From a5435fe15d25675b1ef89d02179e796a9d8707dd Mon Sep 17 00:00:00 2001 From: Jamie Date: Thu, 26 Jul 2018 15:35:42 +0100 Subject: [PATCH 21/26] Upgraded to .net 2.1.2 (Includes security fixes) --- appveyor.yml | 10 +++++----- build.cake | 2 +- src/Ombi/Ombi.csproj | 2 +- src/Ombi/Startup.cs | 2 -- src/Ombi/StartupExtensions.cs | 13 +------------ 5 files changed, 8 insertions(+), 21 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 39d67b91d..862993a21 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -15,19 +15,19 @@ test: off after_build: - cmd: >- - appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.0\windows.zip" + appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.1\windows.zip" - appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.0\osx.tar.gz" + appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.1\osx.tar.gz" - appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.0\linux.tar.gz" + appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.1\linux.tar.gz" - appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.0\linux-arm.tar.gz" + appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.1\linux-arm.tar.gz" - appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.0\windows-32bit.zip" + appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.1\windows-32bit.zip" # appveyor PushArtifact "%APPVEYOR_BUILD_FOLDER%\src\Ombi\bin\Release\netcoreapp2.0\linux-arm64.tar.gz" diff --git a/build.cake b/build.cake index a497c5f77..d200eac98 100644 --- a/build.cake +++ b/build.cake @@ -26,7 +26,7 @@ var csProj = "./src/Ombi/Ombi.csproj"; // Path to the project.csproj var solutionFile = "Ombi.sln"; // Solution file if needed GitVersion versionInfo = null; -var frameworkVer = "netcoreapp2.0"; +var frameworkVer = "netcoreapp2.1"; var buildSettings = new DotNetCoreBuildSettings { diff --git a/src/Ombi/Ombi.csproj b/src/Ombi/Ombi.csproj index 06fc02b81..f2adbe506 100644 --- a/src/Ombi/Ombi.csproj +++ b/src/Ombi/Ombi.csproj @@ -67,7 +67,7 @@ - + diff --git a/src/Ombi/Startup.cs b/src/Ombi/Startup.cs index b8a841592..42503a5c6 100644 --- a/src/Ombi/Startup.cs +++ b/src/Ombi/Startup.cs @@ -5,7 +5,6 @@ using AutoMapper.EquivalencyExpression; using Hangfire; using Hangfire.Dashboard; using Hangfire.SQLite; -using Microsoft.ApplicationInsights.Extensibility; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.HttpOverrides; @@ -79,7 +78,6 @@ namespace Ombi // This method gets called by the runtime. Use this method to add services to the container. public IServiceProvider ConfigureServices(IServiceCollection services) { - TelemetryConfiguration.Active.DisableTelemetry = true; // Add framework services. services.AddDbContext(); diff --git a/src/Ombi/StartupExtensions.cs b/src/Ombi/StartupExtensions.cs index a2c44e7f3..0304ae48d 100644 --- a/src/Ombi/StartupExtensions.cs +++ b/src/Ombi/StartupExtensions.cs @@ -1,25 +1,14 @@ using System; using System.IO; -using System.Linq; -using System.Net; using System.Reflection; -using System.Security.Principal; using System.Text; -using System.Threading.Tasks; using Microsoft.AspNetCore.Authentication.JwtBearer; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.PlatformAbstractions; using Microsoft.IdentityModel.Tokens; using Ombi.Config; -using Ombi.Core.Authentication; -using Ombi.Core.Settings; using Ombi.Helpers; using Ombi.Models.Identity; -using Ombi.Settings.Settings.Models; using Swashbuckle.AspNetCore.Swagger; namespace Ombi @@ -45,7 +34,7 @@ namespace Ombi } }); c.CustomSchemaIds(x => x.FullName); - var basePath = PlatformServices.Default.Application.ApplicationBasePath; + var basePath = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location); var xmlPath = Path.Combine(basePath, "Swagger.xml"); try { From f5f05db4f1e465413efe4e9aa8642f431b7edd76 Mon Sep 17 00:00:00 2001 From: Jamie Date: Thu, 26 Jul 2018 15:38:03 +0100 Subject: [PATCH 22/26] Updated Polly !wip --- src/Ombi.Api/Ombi.Api.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ombi.Api/Ombi.Api.csproj b/src/Ombi.Api/Ombi.Api.csproj index 804512945..a37c128fb 100644 --- a/src/Ombi.Api/Ombi.Api.csproj +++ b/src/Ombi.Api/Ombi.Api.csproj @@ -11,7 +11,7 @@ - + From efd1d6e08a285f173b4ebd39c2990ffdb6f9ba98 Mon Sep 17 00:00:00 2001 From: Jamie Date: Thu, 26 Jul 2018 15:53:06 +0100 Subject: [PATCH 23/26] Updated swagger --- src/Ombi/Ombi.csproj | 2 +- src/Ombi/StartupExtensions.cs | 54 ++++++++++++++++++++++++++++------- 2 files changed, 45 insertions(+), 11 deletions(-) diff --git a/src/Ombi/Ombi.csproj b/src/Ombi/Ombi.csproj index f2adbe506..9212b3f79 100644 --- a/src/Ombi/Ombi.csproj +++ b/src/Ombi/Ombi.csproj @@ -78,7 +78,7 @@ - + diff --git a/src/Ombi/StartupExtensions.cs b/src/Ombi/StartupExtensions.cs index 0304ae48d..d81227ff4 100644 --- a/src/Ombi/StartupExtensions.cs +++ b/src/Ombi/StartupExtensions.cs @@ -1,8 +1,10 @@ using System; +using System.Collections.Generic; using System.IO; using System.Reflection; using System.Text; using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.AspNetCore.Mvc.ApiExplorer; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.IdentityModel.Tokens; @@ -10,9 +12,27 @@ using Ombi.Config; using Ombi.Helpers; using Ombi.Models.Identity; using Swashbuckle.AspNetCore.Swagger; +using Swashbuckle.AspNetCore.SwaggerGen; namespace Ombi { + public class AddRequiredHeaderParameter : IOperationFilter + { + + public void Apply(Operation operation, OperationFilterContext context) + { + if (operation.Parameters == null) + operation.Parameters = new List(); + + operation.Parameters.Add(new NonBodyParameter + { + Name = "ApiKey", + In = "header", + Type = "apiKey", + + }); + } + } public static class StartupExtensions { public static void AddSwagger(this IServiceCollection services) @@ -24,16 +44,36 @@ namespace Ombi { Version = "v1", Title = "Ombi Api", - Description = "The API for Ombi, most of these calls require an auth token that you can get from calling POST:\"/api/v1/token\" with the body of: \n {\n\"username\":\"YOURUSERNAME\",\n\"password\":\"YOURPASSWORD\"\n} \n" + - "You can then use the returned token in the JWT Token field e.g. \"Bearer Token123xxff\"", Contact = new Contact { - Email = "tidusjar@gmail.com", Name = "Jamie Rees", Url = "https://www.ombi.io/" } }); + var security = new Dictionary> + { + //{"Bearer", new string[] { }}, + {"ApiKey", new string[] { }}, + }; + + //c.AddSecurityDefinition("Bearer", new ApiKeyScheme + //{ + // Description = "JWT Authorization header using the Bearer scheme. Example: \"Authorization: Bearer {token}\"", + // Name = "Authorization", + // In = "header", + // Type = "apiKey" + //}); + + c.AddSecurityDefinition("ApiKey", new ApiKeyScheme + { + Description = "API Key provided by Ombi. Example: \"ApiKey: {token}\"", + Name = "ApiKey", + In = "header", + Type = "apiKey" + }); + c.AddSecurityRequirement(security); c.CustomSchemaIds(x => x.FullName); + c.OperationFilter(); var basePath = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location); var xmlPath = Path.Combine(basePath, "Swagger.xml"); try @@ -44,13 +84,7 @@ namespace Ombi { Console.WriteLine(e); } - c.AddSecurityDefinition("Bearer", new JwtBearer - { - Description = "JWT Authorization header using the Bearer scheme. Example: \"Authorization: Bearer {token}\"", - Name = "Authorization", - In = "header", - Type = "apiKey", - }); + c.OperationFilter(); c.DescribeAllParametersInCamelCase(); From 503afd6c744a948ec65e415025e0f1502fafd947 Mon Sep 17 00:00:00 2001 From: Jamie Date: Thu, 26 Jul 2018 15:54:14 +0100 Subject: [PATCH 24/26] updated mailkit !wip --- src/Ombi.Notifications/Ombi.Notifications.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ombi.Notifications/Ombi.Notifications.csproj b/src/Ombi.Notifications/Ombi.Notifications.csproj index 46a64072e..2b5c95154 100644 --- a/src/Ombi.Notifications/Ombi.Notifications.csproj +++ b/src/Ombi.Notifications/Ombi.Notifications.csproj @@ -10,7 +10,7 @@ - + From e172c94225fc8befd5cec5c1b11cf0798593312e Mon Sep 17 00:00:00 2001 From: Jamie Date: Thu, 26 Jul 2018 16:05:36 +0100 Subject: [PATCH 25/26] Fixed where you couldn't bulk edit the limits to 0 #2318 --- .../ClientApp/app/usermanagement/usermanagement.component.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Ombi/ClientApp/app/usermanagement/usermanagement.component.ts b/src/Ombi/ClientApp/app/usermanagement/usermanagement.component.ts index 74ad37e12..1e1109ea4 100644 --- a/src/Ombi/ClientApp/app/usermanagement/usermanagement.component.ts +++ b/src/Ombi/ClientApp/app/usermanagement/usermanagement.component.ts @@ -80,10 +80,10 @@ export class UserManagementComponent implements OnInit { if(anyRoles) { x.claims = this.availableClaims; } - if(this.bulkEpisodeLimit && this.bulkEpisodeLimit > 0) { + if(this.bulkEpisodeLimit) { x.episodeRequestLimit = this.bulkEpisodeLimit; } - if(this.bulkMovieLimit && this.bulkMovieLimit > 0) { + if(this.bulkMovieLimit) { x.movieRequestLimit = this.bulkMovieLimit; } this.identityService.updateUser(x).subscribe(y => { From 02adcdadc79526897fca13e3c1b88849de54e123 Mon Sep 17 00:00:00 2001 From: Jamie Date: Thu, 26 Jul 2018 16:07:26 +0100 Subject: [PATCH 26/26] Fixed the text for #2370 --- src/Ombi.Store/Context/OmbiContext.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ombi.Store/Context/OmbiContext.cs b/src/Ombi.Store/Context/OmbiContext.cs index e9937509e..8d6036877 100644 --- a/src/Ombi.Store/Context/OmbiContext.cs +++ b/src/Ombi.Store/Context/OmbiContext.cs @@ -183,7 +183,7 @@ namespace Ombi.Store.Context notificationToAdd = new NotificationTemplates { NotificationType = notificationType, - Message = "Hello! You {Title} on {ApplicationName}! This is now available! :)", + Message = "Hello! Your request for {Title} on {ApplicationName}! This is now available! :)", Subject = "{ApplicationName}: {Title} is now available!", Agent = agent, Enabled = true,