diff --git a/.azuredevops/pipelines/templates/publish-os-steps.yml b/.azuredevops/pipelines/templates/publish-os-steps.yml index 2f63fa4a4..cfb14e579 100644 --- a/.azuredevops/pipelines/templates/publish-os-steps.yml +++ b/.azuredevops/pipelines/templates/publish-os-steps.yml @@ -5,19 +5,19 @@ steps: packageType: 'sdk' version: '5.x' -- task: DotNetCoreInstaller@1 - displayName: 'Use .NET Core sdk for versioning' - inputs: - packageType: 'sdk' - version: '3.1.x' +# - task: DotNetCoreInstaller@1 +# displayName: 'Use .NET Core sdk for versioning' +# inputs: +# packageType: 'sdk' +# version: '3.1.x' -- task: PowerShell@2 - displayName: 'Set Version' - inputs: - targetType: 'inline' - script: | - dotnet tool install -g dotnet-setversion - setversion -r $(BuildVersion) +# - task: PowerShell@2 +# displayName: 'Set Version' +# inputs: +# targetType: 'inline' +# script: | +# dotnet tool install -g dotnet-setversion +# setversion -r $(BuildVersion) - task: DotNetCoreCLI@2 displayName: 'publish $(runtime)' diff --git a/Tools/nuget.exe b/Tools/nuget.exe deleted file mode 100644 index 9f8781de0..000000000 Binary files a/Tools/nuget.exe and /dev/null differ diff --git a/src/Ombi.Api/OmbiHttpClient.cs b/src/Ombi.Api/OmbiHttpClient.cs index 0b00664be..978476cf7 100644 --- a/src/Ombi.Api/OmbiHttpClient.cs +++ b/src/Ombi.Api/OmbiHttpClient.cs @@ -86,12 +86,16 @@ namespace Ombi.Api _handler = await GetHandler(); } _client = new HttpClient(_handler); - _client.DefaultRequestHeaders.Add("User-Agent",$"Ombi/{_runtimeVersion} (https://ombi.io/)"); + _client.DefaultRequestHeaders.Add("User-Agent", $"Ombi/{_runtimeVersion} (https://ombi.io/)"); } } private async Task GetHandler() { + if (_cache == null) + { + return new HttpClientHandler(); + } var settings = await _cache.GetOrAdd(CacheKeys.OmbiSettings, async () => await _settings.GetSettingsAsync(), DateTime.Now.AddHours(1)); if (settings.IgnoreCertificateErrors) { diff --git a/src/Ombi.Core/Engine/BaseMediaEngine.cs b/src/Ombi.Core/Engine/BaseMediaEngine.cs index 66e60767a..d2e18d2b2 100644 --- a/src/Ombi.Core/Engine/BaseMediaEngine.cs +++ b/src/Ombi.Core/Engine/BaseMediaEngine.cs @@ -69,8 +69,8 @@ namespace Ombi.Core.Engine { var allResults = await TvRepository.Get().ToListAsync(); - var distinctResults = allResults.DistinctBy(x => x.TvDbId); - _dbTv = distinctResults.ToDictionary(x => x.TvDbId); + var distinctResults = allResults.DistinctBy(x => x.ExternalProviderId); + _dbTv = distinctResults.ToDictionary(x => x.ExternalProviderId); _cacheTime = now; } return _dbTv; diff --git a/src/Ombi.Core/Engine/Demo/DemoTvSearchEngine.cs b/src/Ombi.Core/Engine/Demo/DemoTvSearchEngine.cs index 343fb400a..2c91a458f 100644 --- a/src/Ombi.Core/Engine/Demo/DemoTvSearchEngine.cs +++ b/src/Ombi.Core/Engine/Demo/DemoTvSearchEngine.cs @@ -28,7 +28,7 @@ namespace Ombi.Core.Engine.Demo ITraktApi trakt, IRuleEvaluator r, OmbiUserManager um, ICacheService memCache, ISettingsService s, IRepository sub, IOptions lists, IImageService imageService, ISettingsService custom) - : base(identity, service, tvMaze, mapper, trakt, r, um, custom, memCache, s, sub, imageService) + : base(identity, service, tvMaze, mapper, trakt, r, um, custom, memCache, s, sub, imageService, null) { _demoLists = lists.Value; } diff --git a/src/Ombi.Core/Engine/Interfaces/ITvRequestEngine.cs b/src/Ombi.Core/Engine/Interfaces/ITvRequestEngine.cs index 3a4207d9b..c93403b3e 100644 --- a/src/Ombi.Core/Engine/Interfaces/ITvRequestEngine.cs +++ b/src/Ombi.Core/Engine/Interfaces/ITvRequestEngine.cs @@ -13,6 +13,7 @@ namespace Ombi.Core.Engine.Interfaces Task RemoveTvRequest(int requestId); Task GetTvRequest(int requestId); Task RequestTvShow(TvRequestViewModel tv); + Task RequestTvShow(TvRequestViewModelV2 tv); Task DenyChildRequest(int requestId, string reason); Task> GetRequestsLite(int count, int position, OrderFilterModel type); Task> SearchTvRequest(string search); diff --git a/src/Ombi.Core/Engine/Interfaces/ITvSearchEngine.cs b/src/Ombi.Core/Engine/Interfaces/ITvSearchEngine.cs index b2afb4c6f..ef2373294 100644 --- a/src/Ombi.Core/Engine/Interfaces/ITvSearchEngine.cs +++ b/src/Ombi.Core/Engine/Interfaces/ITvSearchEngine.cs @@ -1,5 +1,6 @@ using Ombi.Core.Models.Search; using System.Collections.Generic; +using System.Threading; using System.Threading.Tasks; namespace Ombi.Core.Engine.Interfaces @@ -7,7 +8,7 @@ namespace Ombi.Core.Engine.Interfaces public interface ITvSearchEngine { Task> Search(string searchTerm); - Task GetShowInformation(int tvdbid); + Task GetShowInformation(string movieDbId, CancellationToken token); Task> Popular(); Task> Popular(int currentlyLoaded, int amountToLoad, bool includeImages = false); Task> Anticipated(); diff --git a/src/Ombi.Core/Engine/Interfaces/ITvSearchEngineV2.cs b/src/Ombi.Core/Engine/Interfaces/ITvSearchEngineV2.cs index d2201825f..c1cf2027a 100644 --- a/src/Ombi.Core/Engine/Interfaces/ITvSearchEngineV2.cs +++ b/src/Ombi.Core/Engine/Interfaces/ITvSearchEngineV2.cs @@ -7,8 +7,8 @@ namespace Ombi.Core { public interface ITVSearchEngineV2 { - Task GetShowInformation(int tvdbid); - Task GetShowByRequest(int requestId); - Task> GetStreamInformation(int tvDbId, int tvMazeId, CancellationToken cancellationToken); + Task GetShowInformation(string tvdbid, CancellationToken token); + Task GetShowByRequest(int requestId, CancellationToken token); + Task> GetStreamInformation(int movieDbId, CancellationToken cancellationToken); } } \ No newline at end of file diff --git a/src/Ombi.Core/Engine/MovieSearchEngine.cs b/src/Ombi.Core/Engine/MovieSearchEngine.cs index cda8676fd..11bcfbfa2 100644 --- a/src/Ombi.Core/Engine/MovieSearchEngine.cs +++ b/src/Ombi.Core/Engine/MovieSearchEngine.cs @@ -189,7 +189,7 @@ namespace Ombi.Core.Engine } protected async Task> TransformMovieResultsToResponse( - IEnumerable movies) + IEnumerable movies) { var viewMovies = new List(); foreach (var movie in movies) @@ -244,7 +244,7 @@ namespace Ombi.Core.Engine } } - private async Task ProcessSingleMovie(MovieSearchResult movie) + private async Task ProcessSingleMovie(MovieDbSearchResult movie) { var viewMovie = Mapper.Map(movie); return await ProcessSingleMovie(viewMovie); diff --git a/src/Ombi.Core/Engine/TvRequestEngine.cs b/src/Ombi.Core/Engine/TvRequestEngine.cs index b06c99d49..30d6c4e79 100644 --- a/src/Ombi.Core/Engine/TvRequestEngine.cs +++ b/src/Ombi.Core/Engine/TvRequestEngine.cs @@ -148,6 +148,106 @@ namespace Ombi.Core.Engine return await AddRequest(newRequest.NewRequest, tv.RequestOnBehalf); } + public async Task RequestTvShow(TvRequestViewModelV2 tv) + { + var user = await GetUser(); + var canRequestOnBehalf = false; + + if (tv.RequestOnBehalf.HasValue()) + { + canRequestOnBehalf = await UserManager.IsInRoleAsync(user, OmbiRoles.PowerUser) || await UserManager.IsInRoleAsync(user, OmbiRoles.Admin); + + if (!canRequestOnBehalf) + { + return new RequestEngineResult + { + Result = false, + Message = "You do not have the correct permissions to request on behalf of users!", + ErrorMessage = $"You do not have the correct permissions to request on behalf of users!" + }; + } + } + + var tvBuilder = new TvShowRequestBuilderV2(MovieDbApi); + (await tvBuilder + .GetShowInfo(tv.TheMovieDbId)) + .CreateTvList(tv) + .CreateChild(tv, canRequestOnBehalf ? tv.RequestOnBehalf : user.Id); + + await tvBuilder.BuildEpisodes(tv); + + var ruleResults = await RunRequestRules(tvBuilder.ChildRequest); + var results = ruleResults as RuleResult[] ?? ruleResults.ToArray(); + if (results.Any(x => !x.Success)) + { + return new RequestEngineResult + { + ErrorMessage = results.FirstOrDefault(x => !string.IsNullOrEmpty(x.Message)).Message + }; + } + + // Check if we have auto approved the request, if we have then mark the episodes as approved + if (tvBuilder.ChildRequest.Approved) + { + foreach (var seasons in tvBuilder.ChildRequest.SeasonRequests) + { + foreach (var ep in seasons.Episodes) + { + ep.Approved = true; + ep.Requested = true; + } + } + } + + var existingRequest = await TvRepository.Get().FirstOrDefaultAsync(x => x.ExternalProviderId == tv.TheMovieDbId); + if (existingRequest != null) + { + // Remove requests we already have, we just want new ones + foreach (var existingSeason in existingRequest.ChildRequests) + foreach (var existing in existingSeason.SeasonRequests) + { + var newChild = tvBuilder.ChildRequest.SeasonRequests.FirstOrDefault(x => x.SeasonNumber == existing.SeasonNumber); + if (newChild != null) + { + // We have some requests in this season... + // Let's find the episodes. + foreach (var existingEp in existing.Episodes) + { + var duplicateEpisode = newChild.Episodes.FirstOrDefault(x => x.EpisodeNumber == existingEp.EpisodeNumber); + if (duplicateEpisode != null) + { + // Remove it. + newChild.Episodes.Remove(duplicateEpisode); + } + } + if (!newChild.Episodes.Any()) + { + // We may have removed all episodes + tvBuilder.ChildRequest.SeasonRequests.Remove(newChild); + } + } + } + + // Remove the ID since this is a new child + // This was a TVDBID for the request rules to run + tvBuilder.ChildRequest.Id = 0; + if (!tvBuilder.ChildRequest.SeasonRequests.Any()) + { + // Looks like we have removed them all! They were all duplicates... + return new RequestEngineResult + { + Result = false, + ErrorMessage = "This has already been requested" + }; + } + return await AddExistingRequest(tvBuilder.ChildRequest, existingRequest, tv.RequestOnBehalf); + } + + // This is a new request + var newRequest = tvBuilder.CreateNewRequest(tv); + return await AddRequest(newRequest.NewRequest, tv.RequestOnBehalf); + } + public async Task> GetRequests(int count, int position, OrderFilterModel type) { var shouldHide = await HideFromOtherUsers(); diff --git a/src/Ombi.Core/Engine/TvSearchEngine.cs b/src/Ombi.Core/Engine/TvSearchEngine.cs index 433cacee0..156c76d6e 100644 --- a/src/Ombi.Core/Engine/TvSearchEngine.cs +++ b/src/Ombi.Core/Engine/TvSearchEngine.cs @@ -6,7 +6,6 @@ using Ombi.Core.Engine.Interfaces; using Ombi.Core.Models.Requests; using Ombi.Core.Models.Search; using Ombi.Core.Settings; -using Ombi.Core.Settings.Models.External; using Ombi.Store.Repository; using System; @@ -16,12 +15,13 @@ using System.Security.Principal; using System.Threading.Tasks; using Ombi.Core.Rule.Interfaces; using Ombi.Store.Repository.Requests; -using Microsoft.Extensions.Caching.Memory; using Ombi.Core.Authentication; using Ombi.Helpers; using Ombi.Settings.Settings.Models; using Ombi.Store.Entities; -using TraktSharp.Entities; +using Ombi.Api.TheMovieDb; +using Ombi.Api.TheMovieDb.Models; +using System.Threading; namespace Ombi.Core.Engine { @@ -29,13 +29,16 @@ namespace Ombi.Core.Engine { private readonly ISettingsService _customizationSettings; private readonly IImageService _imageService; + private readonly IMovieDbApi _theMovieDbApi; public TvSearchEngine(IPrincipal identity, IRequestServiceMain service, ITvMazeApi tvMaze, IMapper mapper, ITraktApi trakt, IRuleEvaluator r, OmbiUserManager um, ISettingsService customizationSettings, - ICacheService memCache, ISettingsService s, IRepository sub, IImageService imageService) + ICacheService memCache, ISettingsService s, IRepository sub, IImageService imageService, + IMovieDbApi theMovieDbApi) : base(identity, service, r, um, memCache, s, sub) { _imageService = imageService; + _theMovieDbApi = theMovieDbApi; TvMazeApi = tvMaze; Mapper = mapper; TraktApi = trakt; @@ -48,18 +51,18 @@ namespace Ombi.Core.Engine public async Task> Search(string searchTerm) { - var searchResult = await TvMazeApi.Search(searchTerm); + var searchResult = await _theMovieDbApi.SearchTv(searchTerm); if (searchResult != null) { var retVal = new List(); - foreach (var tvMazeSearch in searchResult) + foreach (var result in searchResult) { - if (tvMazeSearch.show.externals == null || !(tvMazeSearch.show.externals?.thetvdb.HasValue ?? false)) - { - continue; - } - var mappedResult = await ProcessResult(tvMazeSearch, false); + //if (tvMazeSearch.show.externals == null || !(tvMazeSearch.show.externals?.thetvdb.HasValue ?? false)) + //{ + // continue; + //} + var mappedResult = await ProcessResult(result, false); if (mappedResult == null) { continue; @@ -71,58 +74,64 @@ namespace Ombi.Core.Engine return null; } - public async Task GetShowInformation(int tvdbid) + public async Task GetShowInformation(string theMovieDbId, CancellationToken token) { - var show = await Cache.GetOrAdd(nameof(GetShowInformation) + tvdbid, - async () => await TvMazeApi.ShowLookupByTheTvDbId(tvdbid), DateTime.Now.AddHours(12)); + var show = await Cache.GetOrAdd(nameof(GetShowInformation) + theMovieDbId, + async () => await _theMovieDbApi.GetTVInfo(theMovieDbId), DateTime.Now.AddHours(12)); if (show == null) { // We don't have enough information return null; } - var episodes = await Cache.GetOrAdd("TvMazeEpisodeLookup" + show.id, - async () => await TvMazeApi.EpisodeLookup(show.id), DateTime.Now.AddHours(12)); - if (episodes == null || !episodes.Any()) - { - // We don't have enough information - return null; - } + //var episodes = await Cache.GetOrAdd("TvMazeEpisodeLookup" + show.id, + // async () => await TvMazeApi.EpisodeLookup(show.id), DateTime.Now.AddHours(12)); + //if (episodes == null || !episodes.Any()) + //{ + // // We don't have enough information + // return null; + //} var mapped = Mapper.Map(show); - foreach (var e in episodes) + foreach(var tvSeason in show.seasons) { - var season = mapped.SeasonRequests.FirstOrDefault(x => x.SeasonNumber == e.season); - if (season == null) - { - var newSeason = new SeasonRequests - { - SeasonNumber = e.season, - Episodes = new List() - }; - newSeason.Episodes.Add(new EpisodeRequests - { - Url = e.url.ToHttpsUrl(), - Title = e.name, - AirDate = e.airstamp.HasValue() ? DateTime.Parse(e.airstamp) : DateTime.MinValue, - EpisodeNumber = e.number, + var seasonEpisodes = (await _theMovieDbApi.GetSeasonEpisodes(show.id, tvSeason.season_number, token)); - }); - mapped.SeasonRequests.Add(newSeason); - } - else + foreach (var episode in seasonEpisodes.episodes) { - // We already have the season, so just add the episode - season.Episodes.Add(new EpisodeRequests + var season = mapped.SeasonRequests.FirstOrDefault(x => x.SeasonNumber == episode.season_number); + if (season == null) { - Url = e.url.ToHttpsUrl(), - Title = e.name, - AirDate = e.airstamp.HasValue() ? DateTime.Parse(e.airstamp) : DateTime.MinValue, - EpisodeNumber = e.number, - }); + var newSeason = new SeasonRequests + { + SeasonNumber = episode.season_number, + Episodes = new List() + }; + newSeason.Episodes.Add(new EpisodeRequests + { + //Url = episode...ToHttpsUrl(), + Title = episode.name, + AirDate = episode.air_date.HasValue() ? DateTime.Parse(episode.air_date) : DateTime.MinValue, + EpisodeNumber = episode.episode_number, + + }); + mapped.SeasonRequests.Add(newSeason); + } + else + { + // We already have the season, so just add the episode + season.Episodes.Add(new EpisodeRequests + { + //Url = e.url.ToHttpsUrl(), + Title = episode.name, + AirDate = episode.air_date.HasValue() ? DateTime.Parse(episode.air_date) : DateTime.MinValue, + EpisodeNumber = episode.episode_number, + }); + } } } + return await ProcessResult(mapped, false); } @@ -135,12 +144,14 @@ namespace Ombi.Core.Engine public async Task> Popular(int currentlyLoaded, int amountToLoad, bool includeImages = false) { + var langCode = await DefaultLanguageCode(null); + var pages = PaginationHelper.GetNextPages(currentlyLoaded, amountToLoad, ResultLimit); - var results = new List(); + var results = new List(); foreach (var pagesToLoad in pages) { - var apiResult = await Cache.GetOrAdd(nameof(Popular) + pagesToLoad.Page, - async () => await TraktApi.GetPopularShows(pagesToLoad.Page, ResultLimit), DateTime.Now.AddHours(12)); + var apiResult = await Cache.GetOrAdd(nameof(Popular) + langCode + pagesToLoad.Page, + async () => await _theMovieDbApi.PopularTv(langCode, pagesToLoad.Page), DateTime.Now.AddHours(12)); results.AddRange(apiResult.Skip(pagesToLoad.Skip).Take(pagesToLoad.Take)); } @@ -158,12 +169,14 @@ namespace Ombi.Core.Engine public async Task> Anticipated(int currentlyLoaded, int amountToLoad) { + var langCode = await DefaultLanguageCode(null); + var pages = PaginationHelper.GetNextPages(currentlyLoaded, amountToLoad, ResultLimit); - var results = new List(); + var results = new List(); foreach (var pagesToLoad in pages) { - var apiResult = await Cache.GetOrAdd(nameof(Anticipated) + pagesToLoad.Page, - async () => await TraktApi.GetAnticipatedShows(pagesToLoad.Page, ResultLimit), DateTime.Now.AddHours(12)); + var apiResult = await Cache.GetOrAdd(nameof(Anticipated) + langCode + pagesToLoad.Page, + async () => await _theMovieDbApi.UpcomingTv(langCode, pagesToLoad.Page), DateTime.Now.AddHours(12)); results.AddRange(apiResult.Skip(pagesToLoad.Skip).Take(pagesToLoad.Take)); } var processed = ProcessResults(results); @@ -180,12 +193,14 @@ namespace Ombi.Core.Engine public async Task> Trending(int currentlyLoaded, int amountToLoad) { + var langCode = await DefaultLanguageCode(null); + var pages = PaginationHelper.GetNextPages(currentlyLoaded, amountToLoad, ResultLimit); - var results = new List(); + var results = new List(); foreach (var pagesToLoad in pages) { - var apiResult = await Cache.GetOrAdd(nameof(Trending) + pagesToLoad.Page, - async () => await TraktApi.GetTrendingShows(pagesToLoad.Page, ResultLimit), DateTime.Now.AddHours(12)); + var apiResult = await Cache.GetOrAdd(nameof(Trending) + langCode + pagesToLoad.Page, + async () => await _theMovieDbApi.TopRatedTv(langCode, pagesToLoad.Page), DateTime.Now.AddHours(12)); results.AddRange(apiResult.Skip(pagesToLoad.Skip).Take(pagesToLoad.Take)); } var processed = ProcessResults(results); @@ -220,14 +235,15 @@ namespace Ombi.Core.Engine { return null; } - item.TheTvDbId = item.Id.ToString(); - if (includeImages) - { - if (item.TheTvDbId.HasValue()) - { - item.BackdropPath = await _imageService.GetTvBackground(item.TheTvDbId); - } - } + item.TheMovieDbId = item.Id.ToString(); + //item.TheTvDbId = item.Id.ToString(); + //if (includeImages) + //{ + // if (item.TheTvDbId.HasValue()) + // { + // item.BackdropPath = await _imageService.GetTvBackground(item.TheTvDbId); + // } + //} await RunSearchRules(item); diff --git a/src/Ombi.Core/Engine/V2/CalendarEngine.cs b/src/Ombi.Core/Engine/V2/CalendarEngine.cs index 4a5814e1a..b7ecc8ec6 100644 --- a/src/Ombi.Core/Engine/V2/CalendarEngine.cs +++ b/src/Ombi.Core/Engine/V2/CalendarEngine.cs @@ -47,7 +47,7 @@ namespace Ombi.Core.Engine.V2 new ExtraParams { Overview = e.Season?.ChildRequest?.ParentRequest?.Overview ?? string.Empty, - ProviderId = e.Season?.ChildRequest?.ParentRequest?.TvDbId ?? 0, + ProviderId = e.Season?.ChildRequest?.ParentRequest?.ExternalProviderId ?? 0, Type = RequestType.TvShow, ReleaseDate = e.AirDate, RequestStatus = e.RequestStatus diff --git a/src/Ombi.Core/Engine/V2/MovieSearchEngineV2.cs b/src/Ombi.Core/Engine/V2/MovieSearchEngineV2.cs index dc009371a..cb8ec0d3b 100644 --- a/src/Ombi.Core/Engine/V2/MovieSearchEngineV2.cs +++ b/src/Ombi.Core/Engine/V2/MovieSearchEngineV2.cs @@ -127,7 +127,7 @@ namespace Ombi.Core.Engine.V2 var pages = PaginationHelper.GetNextPages(currentlyLoaded, toLoad, _theMovieDbMaxPageItems); - var results = new List(); + var results = new List(); foreach (var pagesToLoad in pages) { var apiResult = await Cache.GetOrAdd(nameof(PopularMovies) + pagesToLoad.Page + langCode, @@ -161,7 +161,7 @@ namespace Ombi.Core.Engine.V2 var pages = PaginationHelper.GetNextPages(currentPosition, amountToLoad, _theMovieDbMaxPageItems); - var results = new List(); + var results = new List(); foreach (var pagesToLoad in pages) { var apiResult = await Cache.GetOrAdd(nameof(TopRatedMovies) + pagesToLoad.Page + langCode, @@ -177,7 +177,7 @@ namespace Ombi.Core.Engine.V2 var pages = PaginationHelper.GetNextPages(currentPosition, amountToLoad, _theMovieDbMaxPageItems); - var results = new List(); + var results = new List(); foreach (var pagesToLoad in pages) { var apiResult = await Cache.GetOrAdd(nameof(NowPlayingMovies) + pagesToLoad.Page + langCode, @@ -213,7 +213,7 @@ namespace Ombi.Core.Engine.V2 var pages = PaginationHelper.GetNextPages(currentPosition, amountToLoad, _theMovieDbMaxPageItems); - var results = new List(); + var results = new List(); foreach (var pagesToLoad in pages) { var apiResult = await Cache.GetOrAdd(nameof(UpcomingMovies) + pagesToLoad.Page + langCode, @@ -270,7 +270,7 @@ namespace Ombi.Core.Engine.V2 } protected async Task> TransformMovieResultsToResponse( - IEnumerable movies) + IEnumerable movies) { var settings = await _customizationSettings.GetSettingsAsync(); var viewMovies = new List(); @@ -286,7 +286,7 @@ namespace Ombi.Core.Engine.V2 return viewMovies; } - private async Task ProcessSingleMovie(MovieSearchResult movie) + private async Task ProcessSingleMovie(MovieDbSearchResult movie) { var viewMovie = Mapper.Map(movie); return await ProcessSingleMovie(viewMovie); diff --git a/src/Ombi.Core/Engine/V2/TvSearchEngineV2.cs b/src/Ombi.Core/Engine/V2/TvSearchEngineV2.cs index 29ea01879..5335fb0b7 100644 --- a/src/Ombi.Core/Engine/V2/TvSearchEngineV2.cs +++ b/src/Ombi.Core/Engine/V2/TvSearchEngineV2.cs @@ -43,89 +43,70 @@ namespace Ombi.Core.Engine.V2 } - public async Task GetShowByRequest(int requestId) + public async Task GetShowByRequest(int requestId, CancellationToken token) { var request = await RequestService.TvRequestService.Get().FirstOrDefaultAsync(x => x.Id == requestId); - return await GetShowInformation(request.TvDbId); + return await GetShowInformation(request.ExternalProviderId.ToString(), token); // TODO } - public async Task GetShowInformation(int tvdbid) + public async Task GetShowInformation(string tvdbid, CancellationToken token) { - var tvdbshow = await Cache.GetOrAdd(nameof(GetShowInformation) + tvdbid, - async () => await _tvMaze.ShowLookupByTheTvDbId(tvdbid), DateTime.Now.AddHours(12)); - if (tvdbshow == null) - { - return null; - } - var show = await Cache.GetOrAdd("GetTvFullInformation" + tvdbshow.id, - async () => await _tvMaze.GetTvFullInformation(tvdbshow.id), DateTime.Now.AddHours(12)); + var show = await Cache.GetOrAdd(nameof(GetShowInformation) + tvdbid, + async () => await _movieApi.GetTVInfo(tvdbid), DateTime.Now.AddHours(12)); if (show == null) { // We don't have enough information return null; } - // Setup the task so we can get the data later on if we have a IMDBID - Task traktInfoTask = null; - if (show.externals?.imdb.HasValue() ?? false) - { - traktInfoTask = Cache.GetOrAdd("GetExtendedTvInfoTrakt" + show.externals?.imdb, - () => _traktApi.GetTvExtendedInfo(show.externals?.imdb), DateTime.Now.AddHours(12)); - } - var mapped = _mapper.Map(show); - foreach (var e in show._embedded?.episodes ?? new Api.TvMaze.Models.V2.Episode[0]) - { - var season = mapped.SeasonRequests.FirstOrDefault(x => x.SeasonNumber == e.season); - if (season == null) - { - var newSeason = new SeasonRequests - { - SeasonNumber = e.season, - Episodes = new List() - }; - newSeason.Episodes.Add(new EpisodeRequests - { - Url = e.url.ToHttpsUrl(), - Title = e.name, - AirDate = e.airstamp, - EpisodeNumber = e.number, - }); - mapped.SeasonRequests.Add(newSeason); - } - else + foreach (var tvSeason in show.seasons.Where(x => x.season_number != 0)) // skip the first season + { + var seasonEpisodes = (await _movieApi.GetSeasonEpisodes(show.id, tvSeason.season_number, token)); + + foreach (var episode in seasonEpisodes.episodes) { - // We already have the season, so just add the episode - season.Episodes.Add(new EpisodeRequests + var season = mapped.SeasonRequests.FirstOrDefault(x => x.SeasonNumber == episode.season_number); + if (season == null) { - Url = e.url.ToHttpsUrl(), - Title = e.name, - AirDate = e.airstamp, - EpisodeNumber = e.number, - }); + var newSeason = new SeasonRequests + { + SeasonNumber = episode.season_number, + Overview = tvSeason.overview, + Episodes = new List() + }; + newSeason.Episodes.Add(new EpisodeRequests + { + //Url = episode...ToHttpsUrl(), + Title = episode.name, + AirDate = episode.air_date.HasValue() ? DateTime.Parse(episode.air_date) : DateTime.MinValue, + EpisodeNumber = episode.episode_number, + + }); + mapped.SeasonRequests.Add(newSeason); + } + else + { + // We already have the season, so just add the episode + season.Episodes.Add(new EpisodeRequests + { + //Url = e.url.ToHttpsUrl(), + Title = episode.name, + AirDate = episode.air_date.HasValue() ? DateTime.Parse(episode.air_date) : DateTime.MinValue, + EpisodeNumber = episode.episode_number, + }); + } } } - return await ProcessResult(mapped, traktInfoTask); + + return await ProcessResult(mapped); } - public async Task> GetStreamInformation(int tvDbId, int tvMazeId, CancellationToken cancellationToken) + public async Task> GetStreamInformation(int movieDbId, CancellationToken cancellationToken) { - var tvdbshow = await Cache.GetOrAdd(nameof(GetShowInformation) + tvMazeId, - async () => await _tvMaze.ShowLookupByTheTvDbId(tvMazeId), DateTime.Now.AddHours(12)); - if (tvdbshow == null) - { - return null; - } - - /// this is a best effort guess since TV maze do not provide the TheMovieDbId - var movieDbResults = await _movieApi.SearchTv(tvdbshow.name, tvdbshow.premiered.Substring(0, 4)); - var potential = movieDbResults.FirstOrDefault(); - tvDbId = potential.Id; - // end guess - - var providers = await _movieApi.GetTvWatchProviders(tvDbId, cancellationToken); + var providers = await _movieApi.GetTvWatchProviders(movieDbId, cancellationToken); var results = await GetUserWatchProvider(providers); var data = new List(); @@ -158,9 +139,9 @@ namespace Ombi.Core.Engine.V2 return _mapper.Map(tvMazeSearch); } - private async Task ProcessResult(SearchFullInfoTvShowViewModel item, Task showInfoTask) + private async Task ProcessResult(SearchFullInfoTvShowViewModel item) { - item.TheTvDbId = item.Id.ToString(); + item.TheMovieDbId = item.Id.ToString(); var oldModel = _mapper.Map(item); await RunSearchRules(oldModel); @@ -179,18 +160,9 @@ namespace Ombi.Core.Engine.V2 item.Images.Medium = item.Images.Medium.ToHttpsUrl(); } - if (item.Cast?.Any() ?? false) - { - foreach (var cast in item.Cast) - { - if (!string.IsNullOrEmpty(cast.Character?.Image?.Medium)) - { - cast.Character.Image.Medium = cast.Character?.Image?.Medium.ToHttpsUrl(); - } - } - } - - return await GetExtraInfo(showInfoTask, item); + + return item; + //return await GetExtraInfo(showInfoTask, item); } private async Task GetExtraInfo(Task showInfoTask, SearchFullInfoTvShowViewModel model) diff --git a/src/Ombi.Core/Helpers/TvShowRequestBuilder.cs b/src/Ombi.Core/Helpers/TvShowRequestBuilder.cs index 8ccbed8fb..39667eaca 100644 --- a/src/Ombi.Core/Helpers/TvShowRequestBuilder.cs +++ b/src/Ombi.Core/Helpers/TvShowRequestBuilder.cs @@ -35,14 +35,17 @@ namespace Ombi.Core.Helpers public TvRequests NewRequest { get; protected set; } protected TvMazeShow ShowInfo { get; set; } protected List Results { get; set; } + protected TvSearchResult TheMovieDbRecord { get; set; } public async Task GetShowInfo(int id) { ShowInfo = await TvApi.ShowLookupByTheTvDbId(id); Results = await MovieDbApi.SearchTv(ShowInfo.name); - foreach (TvSearchResult result in Results) { + foreach (TvSearchResult result in Results) + { if (result.Name.Equals(ShowInfo.name, StringComparison.InvariantCultureIgnoreCase)) - { + { + TheMovieDbRecord = result; var showIds = await MovieDbApi.GetTvExternals(result.Id); ShowInfo.externals.imdb = showIds.imdb_id; BackdropPath = result.BackdropPath; @@ -59,7 +62,7 @@ namespace Ombi.Core.Helpers return this; } - + public TvShowRequestBuilder CreateChild(TvRequestViewModel model, string userId) { ChildRequest = new ChildRequests @@ -240,6 +243,7 @@ namespace Ombi.Core.Helpers PosterPath = PosterPath, Title = ShowInfo.name, ReleaseDate = FirstAir, + ExternalProviderId = TheMovieDbRecord.Id, Status = ShowInfo.status, ImdbId = ShowInfo.externals?.imdb ?? string.Empty, TvDbId = tv.TvDbId, diff --git a/src/Ombi.Core/Helpers/TvShowRequestBuilderV2.cs b/src/Ombi.Core/Helpers/TvShowRequestBuilderV2.cs new file mode 100644 index 000000000..e6724b482 --- /dev/null +++ b/src/Ombi.Core/Helpers/TvShowRequestBuilderV2.cs @@ -0,0 +1,247 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Ombi.Api.TheMovieDb; +using Ombi.Api.TheMovieDb.Models; +using Ombi.Core.Models.Requests; +using Ombi.Store.Entities; +using Ombi.Store.Entities.Requests; +using Ombi.Store.Repository.Requests; +using System.Threading; + +namespace Ombi.Core.Helpers +{ + public class TvShowRequestBuilderV2 + { + + public TvShowRequestBuilderV2(IMovieDbApi movApi) + { + MovieDbApi = movApi; + } + + private IMovieDbApi MovieDbApi { get; } + + public ChildRequests ChildRequest { get; set; } + public List TvRequests { get; protected set; } + public string PosterPath { get; protected set; } + public string BackdropPath { get; protected set; } + public DateTime FirstAir { get; protected set; } + public TvRequests NewRequest { get; protected set; } + protected TvInfo TheMovieDbRecord { get; set; } + + public async Task GetShowInfo(int id) + { + TheMovieDbRecord = await MovieDbApi.GetTVInfo(id.ToString()); + BackdropPath = TheMovieDbRecord.Images?.Backdrops?.OrderBy(x => x.VoteCount).ThenBy(x => x.VoteAverage).FirstOrDefault()?.FilePath; ; + + DateTime.TryParse(TheMovieDbRecord.first_air_date, out var dt); + + FirstAir = dt; + + // For some reason the poster path is always http + PosterPath = TheMovieDbRecord.Images?.Posters?.OrderBy(x => x.VoteCount).ThenBy(x => x.VoteAverage).FirstOrDefault()?.FilePath; + + return this; + } + + public TvShowRequestBuilderV2 CreateChild(TvRequestViewModelV2 model, string userId) + { + var animationGenre = TheMovieDbRecord.genres?.Any(s => s.name.Equals("Animation", StringComparison.InvariantCultureIgnoreCase)) ?? false; + var animeKeyword = TheMovieDbRecord.Keywords?.KeywordsValue?.Any(s => s.Name.Equals("Anime", StringComparison.InvariantCultureIgnoreCase)) ?? false; + ChildRequest = new ChildRequests + { + Id = model.TheMovieDbId, // This is set to 0 after the request rules have run, the request rules needs it to identify the request + RequestType = RequestType.TvShow, + RequestedDate = DateTime.UtcNow, + Approved = false, + RequestedUserId = userId, + SeasonRequests = new List(), + Title = TheMovieDbRecord.name, + ReleaseYear = FirstAir, + RequestedByAlias = model.RequestedByAlias, + SeriesType = animationGenre && animeKeyword ? SeriesType.Anime : SeriesType.Standard + }; + + return this; + } + + public TvShowRequestBuilderV2 CreateTvList(TvRequestViewModelV2 tv) + { + TvRequests = new List(); + // Only have the TV requests we actually requested and not everything + foreach (var season in tv.Seasons) + { + if (season.Episodes.Any()) + { + TvRequests.Add(season); + } + } + + return this; + } + + + public async Task BuildEpisodes(TvRequestViewModelV2 tv) + { + var allEpisodes = new List(); + + foreach (var season in TheMovieDbRecord.seasons) + { + var seasonEpisodes = await MovieDbApi.GetSeasonEpisodes(TheMovieDbRecord.id, season.season_number, CancellationToken.None); + allEpisodes.AddRange(seasonEpisodes.episodes); + } + + if (tv.RequestAll) + { + foreach (var ep in allEpisodes) + { + var season = ChildRequest.SeasonRequests.FirstOrDefault(x => x.SeasonNumber == ep.season_number); + if (season == null) + { + ChildRequest.SeasonRequests.Add(new SeasonRequests + { + Episodes = new List{ + new EpisodeRequests + { + EpisodeNumber = ep.episode_number, + AirDate = FormatDate(ep.air_date), + Title = ep.name, + } + }, + SeasonNumber = ep.season_number, + }); + } + else + { + season.Episodes.Add(new EpisodeRequests + { + EpisodeNumber = ep.episode_number, + AirDate = FormatDate(ep.air_date), + Title = ep.name, + }); + } + } + + } + else if (tv.LatestSeason) + { + var latest = allEpisodes.OrderByDescending(x => x.season_number).FirstOrDefault(); + var episodesRequests = new List(); + foreach (var ep in allEpisodes) + { + if (ep.season_number == latest.season_number) + { + episodesRequests.Add(new EpisodeRequests + { + EpisodeNumber = ep.episode_number, + AirDate = FormatDate(ep.air_date), + Title = ep.name, + }); + } + } + ChildRequest.SeasonRequests.Add(new SeasonRequests + { + Episodes = episodesRequests, + SeasonNumber = latest.season_number, + }); + } + else if (tv.FirstSeason) + { + var first = allEpisodes.OrderBy(x => x.season_number).FirstOrDefault(); + var episodesRequests = new List(); + foreach (var ep in allEpisodes) + { + if (ep.season_number == first.season_number) + { + episodesRequests.Add(new EpisodeRequests + { + EpisodeNumber = ep.episode_number, + AirDate = FormatDate(ep.air_date), + Title = ep.name, + }); + } + } + ChildRequest.SeasonRequests.Add(new SeasonRequests + { + Episodes = episodesRequests, + SeasonNumber = first.season_number, + }); + } + else + { + // It's a custom request + var seasonRequests = new List(); + foreach (var ep in allEpisodes) + { + var existingSeasonRequest = seasonRequests.FirstOrDefault(x => x.SeasonNumber == ep.season_number); + if (existingSeasonRequest != null) + { + var requestedSeason = tv.Seasons.FirstOrDefault(x => x.SeasonNumber == ep.season_number); + var requestedEpisode = requestedSeason?.Episodes?.Any(x => x.EpisodeNumber == ep.episode_number) ?? false; + if (requestedSeason != null && requestedEpisode) + { + // We already have this, let's just add the episodes to it + existingSeasonRequest.Episodes.Add(new EpisodeRequests + { + EpisodeNumber = ep.episode_number, + AirDate = FormatDate(ep.air_date), + Title = ep.name, + }); + } + } + else + { + var newRequest = new SeasonRequests { SeasonNumber = ep.season_number }; + var requestedSeason = tv.Seasons.FirstOrDefault(x => x.SeasonNumber == ep.season_number); + var requestedEpisode = requestedSeason?.Episodes?.Any(x => x.EpisodeNumber == ep.episode_number) ?? false; + if (requestedSeason != null && requestedEpisode) + { + newRequest.Episodes.Add(new EpisodeRequests + { + EpisodeNumber = ep.episode_number, + AirDate = FormatDate(ep.air_date), + Title = ep.name, + }); + seasonRequests.Add(newRequest); + } + } + } + + foreach (var s in seasonRequests) + { + ChildRequest.SeasonRequests.Add(s); + } + } + return this; + } + + + public TvShowRequestBuilderV2 CreateNewRequest(TvRequestViewModelV2 tv) + { + int.TryParse(TheMovieDbRecord.ExternalIds?.TvDbId, out var tvdbId); + NewRequest = new TvRequests + { + Overview = TheMovieDbRecord.overview, + PosterPath = PosterPath, + Title = TheMovieDbRecord.name, + ReleaseDate = FirstAir, + ExternalProviderId = TheMovieDbRecord.id, + Status = TheMovieDbRecord.status, + ImdbId = TheMovieDbRecord.ExternalIds?.ImdbId ?? string.Empty, + TvDbId = tvdbId, + ChildRequests = new List(), + TotalSeasons = tv.Seasons.Count(), + Background = BackdropPath + }; + NewRequest.ChildRequests.Add(ChildRequest); + + return this; + } + + private DateTime FormatDate(string date) + { + return string.IsNullOrEmpty(date) ? DateTime.MinValue : DateTime.Parse(date); + } + } +} \ No newline at end of file diff --git a/src/Ombi.Core/Models/Requests/TvRequestViewModel.cs b/src/Ombi.Core/Models/Requests/TvRequestViewModel.cs index 15349462b..95eb77735 100644 --- a/src/Ombi.Core/Models/Requests/TvRequestViewModel.cs +++ b/src/Ombi.Core/Models/Requests/TvRequestViewModel.cs @@ -3,17 +3,9 @@ using Newtonsoft.Json; namespace Ombi.Core.Models.Requests { - public class TvRequestViewModel + public class TvRequestViewModel : TvRequestViewModelBase { - public bool RequestAll { get; set; } - public bool LatestSeason { get; set; } - public bool FirstSeason { get; set; } public int TvDbId { get; set; } - public List Seasons { get; set; } = new List(); - [JsonIgnore] - public string RequestedByAlias { get; set; } - - public string RequestOnBehalf { get; set; } } public class SeasonsViewModel @@ -27,4 +19,16 @@ namespace Ombi.Core.Models.Requests public int EpisodeNumber { get; set; } } + + public class TvRequestViewModelBase + { + public bool RequestAll { get; set; } + public bool LatestSeason { get; set; } + public bool FirstSeason { get; set; } + public List Seasons { get; set; } = new List(); + [JsonIgnore] + public string RequestedByAlias { get; set; } + + public string RequestOnBehalf { get; set; } + } } \ No newline at end of file diff --git a/src/Ombi.Core/Models/Requests/TvRequestViewModelV2.cs b/src/Ombi.Core/Models/Requests/TvRequestViewModelV2.cs new file mode 100644 index 000000000..d967993ec --- /dev/null +++ b/src/Ombi.Core/Models/Requests/TvRequestViewModelV2.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace Ombi.Core.Models.Requests +{ + public class TvRequestViewModelV2 : TvRequestViewModelBase + { + public int TheMovieDbId { get; set; } + } +} \ No newline at end of file diff --git a/src/Ombi.Core/Models/Search/V2/SearchFullInfoTvShowViewModel.cs b/src/Ombi.Core/Models/Search/V2/SearchFullInfoTvShowViewModel.cs index 8533bb2d7..480a1273b 100644 --- a/src/Ombi.Core/Models/Search/V2/SearchFullInfoTvShowViewModel.cs +++ b/src/Ombi.Core/Models/Search/V2/SearchFullInfoTvShowViewModel.cs @@ -14,7 +14,7 @@ namespace Ombi.Core.Models.Search.V2 public string FirstAired { get; set; } public string NetworkId { get; set; } public string Runtime { get; set; } - public List Genre { get; set; } + public GenreViewModel[] Genres { get; set; } public string Overview { get; set; } public int LastUpdated { get; set; } public string AirsDayOfWeek { get; set; } @@ -24,23 +24,12 @@ namespace Ombi.Core.Models.Search.V2 public NetworkViewModel Network { get; set; } public Images Images { get; set; } public List Cast { get; set; } - public List Crew { get; set; } + public List Crew { get; set; } public string Certification { get; set; } - - /// - /// This is used from the Trakt API - /// - /// - /// The trailer. - /// + public string Tagline { get; set; } + public Keywords Keywords { get; set; } + public ExternalIds ExternalIds { get; set; } public string Trailer { get; set; } - - /// - /// This is used from the Trakt API - /// - /// - /// The trailer. - /// public string Homepage { get; set; } public List SeasonRequests { get; set; } = new List(); @@ -66,7 +55,7 @@ namespace Ombi.Core.Models.Search.V2 { public int Id { get; set; } public string Name { get; set; } - public Country Country { get; set; } + public string Country { get; set; } } public class Country @@ -84,8 +73,9 @@ namespace Ombi.Core.Models.Search.V2 public class CastViewModel { - public PersonViewModel Person { get; set; } - public CharacterViewModel Character { get; set; } + public string Person { get; set; } + public string Character { get; set; } + public string Image { get; set; } public bool Self { get; set; } public bool Voice { get; set; } } @@ -95,7 +85,7 @@ namespace Ombi.Core.Models.Search.V2 public int Id { get; set; } public string Url { get; set; } public string Name { get; set; } - public Images Image { get; set; } + public string Image { get; set; } } public class CharacterViewModel @@ -106,9 +96,4 @@ namespace Ombi.Core.Models.Search.V2 public Images Image { get; set; } } - public class CrewViewModel - { - public string Type { get; set; } - public PersonViewModel Person { get; set; } - } } \ No newline at end of file diff --git a/src/Ombi.Core/Rule/Rules/Request/ExistingPlexRequestRule.cs b/src/Ombi.Core/Rule/Rules/Request/ExistingPlexRequestRule.cs index 9d4ab30ef..927ebf1be 100644 --- a/src/Ombi.Core/Rule/Rules/Request/ExistingPlexRequestRule.cs +++ b/src/Ombi.Core/Rule/Rules/Request/ExistingPlexRequestRule.cs @@ -32,8 +32,8 @@ namespace Ombi.Core.Rule.Rules.Request var tvContent = _plexContent.GetAll().Include(x => x.Episodes).Where(x => x.Type == PlexMediaTypeEntity.Show); // We need to do a check on the TVDBId - var anyTvDbMatches = await tvContent.FirstOrDefaultAsync(x => x.TvDbId.Length > 0 && x.TvDbId == tvRequest.Id.ToString()); // the Id on the child is the tvdbid at this point - if (anyTvDbMatches == null) + var anyMovieDbMatches = await tvContent.FirstOrDefaultAsync(x => x.TheMovieDbId.Length > 0 && x.TheMovieDbId == tvRequest.Id.ToString()); + if (anyMovieDbMatches == null) { // So we do not have a TVDB Id, that really sucks. // Let's try and match on the title and year of the show @@ -50,7 +50,7 @@ namespace Ombi.Core.Rule.Rules.Request return Success(); } // looks like we have a match on the TVDbID - return CheckExistingContent(tvRequest, anyTvDbMatches); + return CheckExistingContent(tvRequest, anyMovieDbMatches); } return Success(); } diff --git a/src/Ombi.Core/Rule/Rules/Request/ExistingTVRequestRule.cs b/src/Ombi.Core/Rule/Rules/Request/ExistingTVRequestRule.cs index 9942ece63..02546d356 100644 --- a/src/Ombi.Core/Rule/Rules/Request/ExistingTVRequestRule.cs +++ b/src/Ombi.Core/Rule/Rules/Request/ExistingTVRequestRule.cs @@ -28,7 +28,7 @@ namespace Ombi.Core.Rule.Rules.Request { var tv = (ChildRequests) obj; var tvRequests = Tv.GetChild(); - var currentRequest = await tvRequests.FirstOrDefaultAsync(x => x.ParentRequest.TvDbId == tv.Id); // the Id on the child is the tvdbid at this point + var currentRequest = await tvRequests.FirstOrDefaultAsync(x => x.ParentRequest.ExternalProviderId == tv.Id); // the Id on the child is the TheMovieDb at this point if (currentRequest == null) { return Success(); diff --git a/src/Ombi.Core/Rule/Rules/SonarrCacheRule.cs b/src/Ombi.Core/Rule/Rules/SonarrCacheRule.cs index ca7ccb6ce..ddd8cd983 100644 --- a/src/Ombi.Core/Rule/Rules/SonarrCacheRule.cs +++ b/src/Ombi.Core/Rule/Rules/SonarrCacheRule.cs @@ -22,7 +22,7 @@ namespace Ombi.Core.Rule.Rules if (obj.RequestType == RequestType.TvShow) { var vm = (ChildRequests) obj; - var result = await _ctx.SonarrCache.FirstOrDefaultAsync(x => x.TvDbId == vm.Id); + var result = await _ctx.SonarrCache.FirstOrDefaultAsync(x => x.TvDbId == vm.Id); // TODO lookup the external provider in the sonarr sync to use themoviedb if (result != null) { if (vm.SeasonRequests.Any()) @@ -54,7 +54,7 @@ namespace Ombi.Core.Rule.Rules { var vm = (SearchTvShowViewModel) obj; // Check if it's in Radarr - var result = await _ctx.SonarrCache.FirstOrDefaultAsync(x => x.TvDbId == vm.Id); + var result = await _ctx.SonarrCache.FirstOrDefaultAsync(x => x.TvDbId.ToString() == vm.TheTvDbId); if (result != null) { vm.Approved = true; @@ -69,7 +69,7 @@ namespace Ombi.Core.Rule.Rules // Check if we have it var monitoredInSonarr = await sonarrEpisodes.FirstOrDefaultAsync(x => x.EpisodeNumber == ep.EpisodeNumber && x.SeasonNumber == season.SeasonNumber - && x.TvDbId == vm.Id); + && x.TvDbId.ToString() == vm.TheTvDbId); if (monitoredInSonarr != null) { ep.Approved = true; diff --git a/src/Ombi.Core/Senders/TvSender.cs b/src/Ombi.Core/Senders/TvSender.cs index 91c38428a..5d5c48555 100644 --- a/src/Ombi.Core/Senders/TvSender.cs +++ b/src/Ombi.Core/Senders/TvSender.cs @@ -140,7 +140,7 @@ namespace Ombi.Core.Senders private async Task SendToDogNzb(ChildRequests model, DogNzbSettings settings) { - var id = model.ParentRequest.TvDbId; + var id = model.ParentRequest.ExternalProviderId; return await DogNzbApi.AddTvShow(settings.ApiKey, id.ToString()); } diff --git a/src/Ombi.Mapping/Profiles/MovieProfile.cs b/src/Ombi.Mapping/Profiles/MovieProfile.cs index 7027f3aed..00d50fdc5 100644 --- a/src/Ombi.Mapping/Profiles/MovieProfile.cs +++ b/src/Ombi.Mapping/Profiles/MovieProfile.cs @@ -14,7 +14,7 @@ namespace Ombi.Mapping.Profiles { public MovieProfile() { - CreateMap() + CreateMap() .ForMember(dest => dest.Adult, opts => opts.MapFrom(src => src.adult)) .ForMember(dest => dest.BackdropPath, opts => opts.MapFrom(src => src.backdrop_path)) .ForMember(dest => dest.Id, opts => opts.MapFrom(src => src.id)) @@ -24,7 +24,7 @@ namespace Ombi.Mapping.Profiles .ForMember(dest => dest.Popularity, opts => opts.MapFrom(src => src.popularity)) .ForMember(dest => dest.PosterPath, opts => opts.MapFrom(src => src.poster_path)) .ForMember(dest => dest.ReleaseDate, opts => opts.MapFrom(src => src.release_date)) - .ForMember(dest => dest.Title, opts => opts.MapFrom(src => src.title)) + .ForMember(dest => dest.Title, opts => opts.MapFrom(src => string.IsNullOrEmpty(src.title) ? src.name : src.title)) .ForMember(dest => dest.Video, opts => opts.MapFrom(src => src.video)) .ForMember(dest => dest.VoteAverage, opts => opts.MapFrom(src => src.vote_average)) .ForMember(dest => dest.VoteCount, opts => opts.MapFrom(src => src.vote_count)); @@ -75,7 +75,7 @@ namespace Ombi.Mapping.Profiles CreateMap(); - CreateMap().ReverseMap(); + CreateMap().ReverseMap(); CreateMap().ReverseMap(); CreateMap().ReverseMap(); diff --git a/src/Ombi.Mapping/Profiles/TvProfile.cs b/src/Ombi.Mapping/Profiles/TvProfile.cs index 0e1378d95..55f5ccf9e 100644 --- a/src/Ombi.Mapping/Profiles/TvProfile.cs +++ b/src/Ombi.Mapping/Profiles/TvProfile.cs @@ -1,6 +1,7 @@ using System; using System.Globalization; using AutoMapper; +using Ombi.Api.TheMovieDb.Models; using Ombi.Api.TvMaze.Models; using Ombi.Core.Models.Search; using Ombi.Helpers; @@ -27,6 +28,15 @@ namespace Ombi.Mapping.Profiles .ForMember(dest => dest.Banner, opts => opts.MapFrom(src => !string.IsNullOrEmpty(src.show.image.medium) ? src.show.image.medium.ToHttpsUrl() : string.Empty)) .ForMember(dest => dest.Status, opts => opts.MapFrom(src => src.show.status)); + CreateMap() + .ForMember(dest => dest.Id, opts => opts.MapFrom(src => src.Id)) + .ForMember(dest => dest.TheMovieDbId, opts => opts.MapFrom(src => src.Id.ToString())) + .ForMember(dest => dest.FirstAired, opts => opts.MapFrom(src => src.ReleaseDate)) + .ForMember(dest => dest.Overview, opts => opts.MapFrom(src => src.Overview)) + .ForMember(dest => dest.Rating, opts => opts.MapFrom(src => src.VoteAverage.ToString(CultureInfo.CurrentUICulture))) + .ForMember(dest => dest.Title, opts => opts.MapFrom(src => src.Name)) + .ForMember(dest => dest.Banner, opts => opts.MapFrom(src => !string.IsNullOrEmpty(src.BackdropPath) ? src.BackdropPath.ToHttpsUrl() : src.PosterPath)); + CreateMap() .ForMember(dest => dest.Id, opts => opts.MapFrom(src => src.externals.thetvdb)) .ForMember(dest => dest.FirstAired, opts => opts.MapFrom(src => src.premiered)) @@ -56,10 +66,27 @@ namespace Ombi.Mapping.Profiles .ForMember(dest => dest.Runtime, opts => opts.MapFrom(src => src.Runtime.ToString())) .ForMember(dest => dest.Title, opts => opts.MapFrom(src => src.Title)) .ForMember(dest => dest.Status, opts => opts.MapFrom(src => TraktEnumHelper.GetDescription(src.Status))) - .ForMember(dest => dest.Trailer, + .ForMember(dest => dest.Trailer, opts => opts.MapFrom(src => src.Trailer != null ? src.Trailer.ToString().ToHttpsUrl() : string.Empty)) - .ForMember(dest => dest.Homepage, + .ForMember(dest => dest.Homepage, opts => opts.MapFrom(src => src.Homepage != null ? src.Homepage.ToString().ToHttpsUrl() : string.Empty)); + + + CreateMap() + .ForMember(dest => dest.Id, opts => opts.MapFrom(src => src.Id)) + .ForMember(dest => dest.FirstAired, opts => opts.MapFrom(src => src.ReleaseDate)) + //.ForMember(dest => dest.ImdbId, opts => opts.MapFrom(src => src.Ids.Imdb)) + //.ForMember(dest => dest.Network, opts => opts.MapFrom(src => src.Network)) + .ForMember(dest => dest.Overview, opts => opts.MapFrom(src => src.Overview)) + .ForMember(dest => dest.Rating, opts => opts.MapFrom(src => src.VoteAverage.ToString())) + .ForMember(dest => dest.BackdropPath, opts => opts.MapFrom(src => src.PosterPath)) + //.ForMember(dest => dest.Runtime, opts => opts.MapFrom(src => src.Runtime.ToString())) + .ForMember(dest => dest.Title, opts => opts.MapFrom(src => src.Title)); + //.ForMember(dest => dest.Status, opts => opts.MapFrom(src => TraktEnumHelper.GetDescription(src.Status))) + //.ForMember(dest => dest.Trailer, + // opts => opts.MapFrom(src => src.Trailer != null ? src.Trailer.ToString().ToHttpsUrl() : string.Empty)) + //.ForMember(dest => dest.Homepage, + // opts => opts.MapFrom(src => src.Homepage != null ? src.Homepage.ToString().ToHttpsUrl() : string.Empty)); } } } \ No newline at end of file diff --git a/src/Ombi.Mapping/Profiles/TvProfileV2.cs b/src/Ombi.Mapping/Profiles/TvProfileV2.cs index 5ee0645fd..83b75189c 100644 --- a/src/Ombi.Mapping/Profiles/TvProfileV2.cs +++ b/src/Ombi.Mapping/Profiles/TvProfileV2.cs @@ -1,5 +1,8 @@ -using System.Globalization; +using System; +using System.Globalization; +using System.Linq; using AutoMapper; +using Ombi.Api.TheMovieDb.Models; using Ombi.Api.TvMaze.Models.V2; using Ombi.Core.Models.Search; using Ombi.Core.Models.Search.V2; @@ -24,7 +27,7 @@ namespace Ombi.Mapping.Profiles .ForMember(dest => dest.SeriesId, opts => opts.MapFrom(src => src.id)) .ForMember(dest => dest.Title, opts => opts.MapFrom(src => src.name)) .ForMember(dest => dest.Network, opts => opts.MapFrom(src => src.network)) - .ForMember(dest => dest.Images, opts => opts.MapFrom(src => src.image)) + //.ForMember(dest => dest.Images, opts => opts.MapFrom(src => src.image)) .ForMember(dest => dest.Cast, opts => opts.MapFrom(src => src._embedded.cast)) .ForMember(dest => dest.Crew, opts => opts.MapFrom(src => src._embedded.crew)) .ForMember(dest => dest.Banner, @@ -34,19 +37,65 @@ namespace Ombi.Mapping.Profiles : string.Empty)) .ForMember(dest => dest.Status, opts => opts.MapFrom(src => src.status)); - CreateMap() + + + CreateMap() + .ForMember(dest => dest.Id, opts => opts.MapFrom(src => src.id)) + .ForMember(dest => dest.FirstAired, opts => opts.MapFrom(src => src.first_air_date)) + .ForMember(dest => dest.ImdbId, opts => opts.MapFrom(src => src.ExternalIds.ImdbId)) + .ForMember(dest => dest.TheTvDbId, opts => opts.MapFrom(src => src.ExternalIds.TvDbId)) + .ForMember(dest => dest.Network, opts => opts.MapFrom(src => src.networks.FirstOrDefault())) + .ForMember(dest => dest.NetworkId, opts => opts.MapFrom(src => src.networks.FirstOrDefault().id)) + .ForMember(dest => dest.Overview, opts => opts.MapFrom(src => src.overview)) + .ForMember(dest => dest.Rating, + opts => opts.MapFrom(src => src.vote_average.ToString(CultureInfo.CurrentUICulture))) + //.ForMember(dest => dest.Runtime, opts => opts.MapFrom(src => src.runtime.ToString())) + .ForMember(dest => dest.SeriesId, opts => opts.MapFrom(src => src.id)) + .ForMember(dest => dest.Title, opts => opts.MapFrom(src => src.name)) + //.ForMember(dest => dest.Network, opts => opts.MapFrom(src => src.network)) + .ForMember(dest => dest.Images, opts => opts.MapFrom(src => src.Images)) + .ForMember(dest => dest.Cast, opts => opts.MapFrom(src => src.Credits.cast)) + .ForMember(dest => dest.Crew, opts => opts.MapFrom(src => src.Credits.crew)) + .ForMember(dest => dest.Banner, opts => opts.MapFrom(src => GetBanner(src.Images))) + .ForMember(dest => dest.Genres, opts => opts.MapFrom(src => src.genres)) + .ForMember(dest => dest.Keywords, opts => opts.MapFrom(src => src.Keywords)) + .ForMember(dest => dest.Tagline, opts => opts.MapFrom(src => src.tagline)) + .ForMember(dest => dest.ExternalIds, opts => opts.MapFrom(src => src.ExternalIds)) + .ForMember(dest => dest.Trailer, opts => opts.MapFrom(src => GetTrailer(src.Videos))) + .ForMember(dest => dest.Runtime, opts => opts.MapFrom(src => src.episode_run_time.FirstOrDefault())) + .ForMember(dest => dest.Status, opts => opts.MapFrom(src => src.status)); + + CreateMap().ReverseMap(); + CreateMap() + .ForMember(dest => dest.Original, opts => opts.MapFrom(src => src.Posters.OrderBy(x => x.VoteCount).ThenBy(x => x.VoteAverage).FirstOrDefault().FilePath)); + + + CreateMap().ReverseMap(); + CreateMap().ReverseMap(); + + + CreateMap() + .ForMember(dest => dest.id, opts => opts.MapFrom(src => src.id)) + .ForMember(dest => dest.name, opts => opts.MapFrom(src => src.name)); + + CreateMap() .ForMember(dest => dest.Id, opts => opts.MapFrom(src => src.id)) .ForMember(dest => dest.Country, opts => opts.MapFrom(src => src.country)) .ForMember(dest => dest.Name, opts => opts.MapFrom(src => src.name)); + CreateMap() + .ForMember(dest => dest.Id, opts => opts.MapFrom(src => src.id)) + .ForMember(dest => dest.Country, opts => opts.MapFrom(src => src.origin_country)) + .ForMember(dest => dest.Name, opts => opts.MapFrom(src => src.name)); + CreateMap() .ForMember(dest => dest.Name, opts => opts.MapFrom(src => src.name)) .ForMember(dest => dest.Code, opts => opts.MapFrom(src => src.code)) .ForMember(dest => dest.Timezone, opts => opts.MapFrom(src => src.timezone)); - CreateMap() - .ForMember(dest => dest.Medium, opts => opts.MapFrom(src => src.medium.ToHttpsUrl())) - .ForMember(dest => dest.Original, opts => opts.MapFrom(src => src.original.ToHttpsUrl())); + //CreateMap() + // .ForMember(dest => dest.Medium, opts => opts.MapFrom(src => src.medium.ToHttpsUrl())) + // .ForMember(dest => dest.Original, opts => opts.MapFrom(src => src.original.ToHttpsUrl())); CreateMap() .ForMember(dest => dest.Character, opts => opts.MapFrom(src => src.character)) @@ -54,15 +103,25 @@ namespace Ombi.Mapping.Profiles .ForMember(dest => dest.Voice, opts => opts.MapFrom(src => src.voice)) .ForMember(dest => dest.Self, opts => opts.MapFrom(src => src.self)); + CreateMap() + .ForMember(dest => dest.Character, opts => opts.MapFrom(src => src.character)) + .ForMember(dest => dest.Person, opts => opts.MapFrom(src => src.name)) + .ForMember(dest => dest.Image, opts => opts.MapFrom(src => src.profile_path)); + CreateMap() .ForMember(dest => dest.Id, opts => opts.MapFrom(src => src.id)) .ForMember(dest => dest.Name, opts => opts.MapFrom(src => src.name)) .ForMember(dest => dest.Image, opts => opts.MapFrom(src => src.image)) .ForMember(dest => dest.Url, opts => opts.MapFrom(src => src.url.ToHttpsUrl())); - CreateMap() - .ForMember(dest => dest.Person, opts => opts.MapFrom(src => src.person)) - .ForMember(dest => dest.Type, opts => opts.MapFrom(src => src.type)); + //CreateMap() + // .ForMember(dest => dest.Person, opts => opts.MapFrom(src => src.person)) + // .ForMember(dest => dest.Type, opts => opts.MapFrom(src => src.type)); + + CreateMap() + .ForMember(dest => dest.Id, opts => opts.MapFrom(src => src.id)) + .ForMember(dest => dest.Image, opts => opts.MapFrom(src => src.profile_path)) + .ForMember(dest => dest.Name, opts => opts.MapFrom(src => src.name)); CreateMap() .ForMember(dest => dest.Person, opts => opts.MapFrom(src => src.person)) @@ -78,5 +137,27 @@ namespace Ombi.Mapping.Profiles CreateMap().ReverseMap(); } + + private string GetBanner(Api.TheMovieDb.Models.Images images) + { + var hasBackdrop = images?.Backdrops?.Any(); + if (hasBackdrop ?? false) + { + return images.Backdrops?.OrderBy(x => x.VoteCount).ThenBy(x => x.VoteAverage).Select(x => x.FilePath).FirstOrDefault(); + } + else if (images != null) + { + return images.Posters?.OrderBy(x => x.VoteCount).ThenBy(x => x.VoteAverage).Select(x => x.FilePath).FirstOrDefault(); + } + else + { + return string.Empty; + } + } + + private string GetTrailer(Api.TheMovieDb.Models.Videos trailer) + { + return trailer?.results?.FirstOrDefault(x => x.type.Equals("trailer", StringComparison.InvariantCultureIgnoreCase))?.key ?? null; + } } } \ No newline at end of file diff --git a/src/Ombi.Notifications.Templates/EmailBasicTemplate.cs b/src/Ombi.Notifications.Templates/EmailBasicTemplate.cs index b8d76e10d..15ec9b286 100644 --- a/src/Ombi.Notifications.Templates/EmailBasicTemplate.cs +++ b/src/Ombi.Notifications.Templates/EmailBasicTemplate.cs @@ -13,7 +13,7 @@ namespace Ombi.Notifications.Templates if (string.IsNullOrEmpty(_templateLocation)) { #if DEBUG - _templateLocation = Path.Combine(Directory.GetCurrentDirectory(), "bin", "Debug", "netcoreapp3.1", "Templates", + _templateLocation = Path.Combine(Directory.GetCurrentDirectory(), "bin", "Debug", "net5.0", "Templates", "BasicTemplate.html"); #else _templateLocation = Path.Combine(Directory.GetCurrentDirectory(), "Templates","BasicTemplate.html"); diff --git a/src/Ombi.Notifications/NotificationMessageCurlys.cs b/src/Ombi.Notifications/NotificationMessageCurlys.cs index dcf49138d..54e85080b 100644 --- a/src/Ombi.Notifications/NotificationMessageCurlys.cs +++ b/src/Ombi.Notifications/NotificationMessageCurlys.cs @@ -131,7 +131,7 @@ namespace Ombi.Notifications { LoadIssues(opts); RequestId = req?.Id.ToString(); - ProviderId = req?.ParentRequest?.TvDbId.ToString() ?? string.Empty; + ProviderId = req?.ParentRequest?.ExternalProviderId.ToString() ?? string.Empty; string title; if (req == null) { diff --git a/src/Ombi.Schedule/Jobs/Plex/PlexContentSync.cs b/src/Ombi.Schedule/Jobs/Plex/PlexContentSync.cs index f554e428d..8333eed56 100644 --- a/src/Ombi.Schedule/Jobs/Plex/PlexContentSync.cs +++ b/src/Ombi.Schedule/Jobs/Plex/PlexContentSync.cs @@ -34,6 +34,8 @@ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using Ombi.Api.Plex; using Ombi.Api.Plex.Models; +using Ombi.Api.TheMovieDb; +using Ombi.Api.TheMovieDb.Models; using Ombi.Core.Settings; using Ombi.Core.Settings.Models.External; using Ombi.Helpers; @@ -49,8 +51,9 @@ namespace Ombi.Schedule.Jobs.Plex { public class PlexContentSync : IPlexContentSync { + private readonly IMovieDbApi _movieApi; public PlexContentSync(ISettingsService plex, IPlexApi plexApi, ILogger logger, IPlexContentRepository repo, - IPlexEpisodeSync epsiodeSync, IHubContext hub) + IPlexEpisodeSync epsiodeSync, IHubContext hub, IMovieDbApi movieDbApi) { Plex = plex; PlexApi = plexApi; @@ -58,6 +61,7 @@ namespace Ombi.Schedule.Jobs.Plex Repo = repo; EpisodeSync = epsiodeSync; Notification = hub; + _movieApi = movieDbApi; Plex.ClearCache(); } @@ -418,7 +422,7 @@ namespace Ombi.Schedule.Jobs.Plex { var showMetadata = await PlexApi.GetMetadata(servers.PlexAuthToken, servers.FullUri, existingContent.Key); - GetProviderIds(showMetadata, existingContent); + await GetProviderIds(showMetadata, existingContent); await Repo.Update(existingContent); } @@ -531,7 +535,7 @@ namespace Ombi.Schedule.Jobs.Plex Url = PlexHelper.GetPlexMediaUrl(servers.MachineIdentifier, show.ratingKey), Seasons = new List() }; - GetProviderIds(showMetadata, item); + await GetProviderIds(showMetadata, item); // Let's just double check to make sure we do not have it now we have some id's var existingImdb = false; @@ -573,7 +577,7 @@ namespace Ombi.Schedule.Jobs.Plex } } - private static void GetProviderIds(PlexMetadata showMetadata, PlexServerContent existingContent) + private async Task GetProviderIds(PlexMetadata showMetadata, PlexServerContent existingContent) { var metadata = showMetadata.MediaContainer.Metadata.FirstOrDefault(); var guids = new List @@ -601,6 +605,13 @@ namespace Ombi.Schedule.Jobs.Plex if (providerIds.TheTvDb.HasValue()) { + // Lookup TheMovieDbId + var findResult = await _movieApi.Find(providerIds.TheTvDb, ExternalSource.tvdb_id); + var tvResult = findResult.tv_results.FirstOrDefault(); + if (tvResult != null) + { + existingContent.TheMovieDbId = tvResult.id.ToString(); + } existingContent.TvDbId = providerIds.TheTvDb; } } @@ -683,6 +694,7 @@ namespace Ombi.Schedule.Jobs.Plex } private bool _disposed; + protected virtual void Dispose(bool disposing) { if (_disposed) diff --git a/src/Ombi.Settings/Settings/Models/OmbiSettings.cs b/src/Ombi.Settings/Settings/Models/OmbiSettings.cs index 11812e439..36b540026 100644 --- a/src/Ombi.Settings/Settings/Models/OmbiSettings.cs +++ b/src/Ombi.Settings/Settings/Models/OmbiSettings.cs @@ -4,7 +4,6 @@ { public string BaseUrl { get; set; } public bool CollectAnalyticData { get; set; } - public bool Set { get; set; } public bool Wizard { get; set; } public string ApiKey { get; set; } public bool IgnoreCertificateErrors { get; set; } @@ -14,5 +13,9 @@ public string DefaultLanguageCode { get; set; } = "en"; public bool AutoDeleteAvailableRequests { get; set; } public int AutoDeleteAfterDays { get; set; } + + //INTERNAL + public bool HasMigratedOldTvDbData { get; set; } + public bool Set { get; set; } } } \ No newline at end of file diff --git a/src/Ombi.Store/Entities/Requests/SeasonRequests.cs b/src/Ombi.Store/Entities/Requests/SeasonRequests.cs index 8a69a4767..8a9fabf08 100644 --- a/src/Ombi.Store/Entities/Requests/SeasonRequests.cs +++ b/src/Ombi.Store/Entities/Requests/SeasonRequests.cs @@ -11,6 +11,7 @@ namespace Ombi.Store.Repository.Requests public class SeasonRequests : Entity { public int SeasonNumber { get; set; } + public string Overview { get; set; } public List Episodes { get; set; } = new List(); public int ChildRequestId { get; set; } diff --git a/src/Ombi.Store/Entities/Requests/TvRequests.cs b/src/Ombi.Store/Entities/Requests/TvRequests.cs index 5e33c016f..9ba54c58f 100644 --- a/src/Ombi.Store/Entities/Requests/TvRequests.cs +++ b/src/Ombi.Store/Entities/Requests/TvRequests.cs @@ -6,7 +6,13 @@ namespace Ombi.Store.Entities.Requests { public class TvRequests : Entity { + /// + /// No longer used since moved away from TvMaze + /// public int TvDbId { get; set; } + + // TheMovieDbId + public int ExternalProviderId { get; set; } public string ImdbId { get; set; } public int? QualityOverride { get; set; } public int? RootFolder { get; set; } diff --git a/src/Ombi.Store/Migrations/OmbiMySql/20210305151743_TvRequestProviderId.Designer.cs b/src/Ombi.Store/Migrations/OmbiMySql/20210305151743_TvRequestProviderId.Designer.cs new file mode 100644 index 000000000..5621af456 --- /dev/null +++ b/src/Ombi.Store/Migrations/OmbiMySql/20210305151743_TvRequestProviderId.Designer.cs @@ -0,0 +1,1232 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Ombi.Store.Context.MySql; + +namespace Ombi.Store.Migrations.OmbiMySql +{ + [DbContext(typeof(OmbiMySqlContext))] + [Migration("20210305151743_TvRequestProviderId")] + partial class TvRequestProviderId + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("Relational:MaxIdentifierLength", 64) + .HasAnnotation("ProductVersion", "5.0.1"); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("varchar(255)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("AspNetRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("ProviderKey") + .HasColumnType("varchar(255)"); + + b.Property("ProviderDisplayName") + .HasColumnType("longtext"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("varchar(255)"); + + b.Property("RoleId") + .HasColumnType("varchar(255)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("varchar(255)"); + + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("Name") + .HasColumnType("varchar(255)"); + + b.Property("Value") + .HasColumnType("longtext"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Audit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("AuditArea") + .HasColumnType("int"); + + b.Property("AuditType") + .HasColumnType("int"); + + b.Property("DateTime") + .HasColumnType("datetime(6)"); + + b.Property("Description") + .HasColumnType("longtext"); + + b.Property("User") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("Audit"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.MobileDevices", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("AddedAt") + .HasColumnType("datetime(6)"); + + b.Property("Token") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("MobileDevices"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationTemplates", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Agent") + .HasColumnType("int"); + + b.Property("Enabled") + .HasColumnType("tinyint(1)"); + + b.Property("Message") + .HasColumnType("longtext"); + + b.Property("NotificationType") + .HasColumnType("int"); + + b.Property("Subject") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("NotificationTemplates"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationUserId", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("AddedAt") + .HasColumnType("datetime(6)"); + + b.Property("PlayerId") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("NotificationUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.OmbiUser", b => + { + b.Property("Id") + .HasColumnType("varchar(255)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("Alias") + .HasColumnType("longtext"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("EpisodeRequestLimit") + .HasColumnType("int"); + + b.Property("Language") + .HasColumnType("longtext"); + + b.Property("LastLoggedIn") + .HasColumnType("datetime(6)"); + + b.Property("LockoutEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("LockoutEnd") + .HasColumnType("datetime(6)"); + + b.Property("MovieRequestLimit") + .HasColumnType("int"); + + b.Property("MusicRequestLimit") + .HasColumnType("int"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("longtext"); + + b.Property("PhoneNumber") + .HasColumnType("longtext"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("ProviderUserId") + .HasColumnType("longtext"); + + b.Property("SecurityStamp") + .HasColumnType("longtext"); + + b.Property("StreamingCountry") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("TwoFactorEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("UserAccessToken") + .HasColumnType("longtext"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("UserType") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.ToTable("AspNetUsers"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RecentlyAddedLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("AddedAt") + .HasColumnType("datetime(6)"); + + b.Property("AlbumId") + .HasColumnType("longtext"); + + b.Property("ContentId") + .HasColumnType("int"); + + b.Property("ContentType") + .HasColumnType("int"); + + b.Property("EpisodeNumber") + .HasColumnType("int"); + + b.Property("SeasonNumber") + .HasColumnType("int"); + + b.Property("Type") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("RecentlyAddedLog"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RequestQueue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Completed") + .HasColumnType("datetime(6)"); + + b.Property("Dts") + .HasColumnType("datetime(6)"); + + b.Property("Error") + .HasColumnType("longtext"); + + b.Property("RequestId") + .HasColumnType("int"); + + b.Property("RetryCount") + .HasColumnType("int"); + + b.Property("Type") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("RequestQueue"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RequestSubscription", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("RequestId") + .HasColumnType("int"); + + b.Property("RequestType") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("RequestSubscription"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.AlbumRequest", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Approved") + .HasColumnType("tinyint(1)"); + + b.Property("ArtistName") + .HasColumnType("longtext"); + + b.Property("Available") + .HasColumnType("tinyint(1)"); + + b.Property("Cover") + .HasColumnType("longtext"); + + b.Property("Denied") + .HasColumnType("tinyint(1)"); + + b.Property("DeniedReason") + .HasColumnType("longtext"); + + b.Property("Disk") + .HasColumnType("longtext"); + + b.Property("ForeignAlbumId") + .HasColumnType("longtext"); + + b.Property("ForeignArtistId") + .HasColumnType("longtext"); + + b.Property("MarkedAsApproved") + .HasColumnType("datetime(6)"); + + b.Property("MarkedAsAvailable") + .HasColumnType("datetime(6)"); + + b.Property("MarkedAsDenied") + .HasColumnType("datetime(6)"); + + b.Property("Rating") + .HasColumnType("decimal(65,30)"); + + b.Property("ReleaseDate") + .HasColumnType("datetime(6)"); + + b.Property("RequestType") + .HasColumnType("int"); + + b.Property("RequestedByAlias") + .HasColumnType("longtext"); + + b.Property("RequestedDate") + .HasColumnType("datetime(6)"); + + b.Property("RequestedUserId") + .HasColumnType("varchar(255)"); + + b.Property("Title") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("AlbumRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Approved") + .HasColumnType("tinyint(1)"); + + b.Property("Available") + .HasColumnType("tinyint(1)"); + + b.Property("Denied") + .HasColumnType("tinyint(1)"); + + b.Property("DeniedReason") + .HasColumnType("longtext"); + + b.Property("IssueId") + .HasColumnType("int"); + + b.Property("MarkedAsApproved") + .HasColumnType("datetime(6)"); + + b.Property("MarkedAsAvailable") + .HasColumnType("datetime(6)"); + + b.Property("MarkedAsDenied") + .HasColumnType("datetime(6)"); + + b.Property("ParentRequestId") + .HasColumnType("int"); + + b.Property("RequestType") + .HasColumnType("int"); + + b.Property("RequestedByAlias") + .HasColumnType("longtext"); + + b.Property("RequestedDate") + .HasColumnType("datetime(6)"); + + b.Property("RequestedUserId") + .HasColumnType("varchar(255)"); + + b.Property("SeriesType") + .HasColumnType("int"); + + b.Property("Title") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("ParentRequestId"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("ChildRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueCategory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Value") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("IssueCategory"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueComments", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Comment") + .HasColumnType("longtext"); + + b.Property("Date") + .HasColumnType("datetime(6)"); + + b.Property("IssuesId") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("IssuesId"); + + b.HasIndex("UserId"); + + b.ToTable("IssueComments"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("CreatedDate") + .HasColumnType("datetime(6)"); + + b.Property("Description") + .HasColumnType("longtext"); + + b.Property("IssueCategoryId") + .HasColumnType("int"); + + b.Property("IssueId") + .HasColumnType("int"); + + b.Property("ProviderId") + .HasColumnType("longtext"); + + b.Property("RequestId") + .HasColumnType("int"); + + b.Property("RequestType") + .HasColumnType("int"); + + b.Property("ResovledDate") + .HasColumnType("datetime(6)"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("Subject") + .HasColumnType("longtext"); + + b.Property("Title") + .HasColumnType("longtext"); + + b.Property("UserReportedId") + .HasColumnType("varchar(255)"); + + 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() + .HasColumnType("int"); + + b.Property("Approved") + .HasColumnType("tinyint(1)"); + + b.Property("Available") + .HasColumnType("tinyint(1)"); + + b.Property("Background") + .HasColumnType("longtext"); + + b.Property("Denied") + .HasColumnType("tinyint(1)"); + + b.Property("DeniedReason") + .HasColumnType("longtext"); + + b.Property("DigitalReleaseDate") + .HasColumnType("datetime(6)"); + + b.Property("ImdbId") + .HasColumnType("longtext"); + + b.Property("IssueId") + .HasColumnType("int"); + + b.Property("LangCode") + .HasColumnType("longtext"); + + b.Property("MarkedAsApproved") + .HasColumnType("datetime(6)"); + + b.Property("MarkedAsAvailable") + .HasColumnType("datetime(6)"); + + b.Property("MarkedAsDenied") + .HasColumnType("datetime(6)"); + + b.Property("Overview") + .HasColumnType("longtext"); + + b.Property("PosterPath") + .HasColumnType("longtext"); + + b.Property("QualityOverride") + .HasColumnType("int"); + + b.Property("ReleaseDate") + .HasColumnType("datetime(6)"); + + b.Property("RequestType") + .HasColumnType("int"); + + b.Property("RequestedByAlias") + .HasColumnType("longtext"); + + b.Property("RequestedDate") + .HasColumnType("datetime(6)"); + + b.Property("RequestedUserId") + .HasColumnType("varchar(255)"); + + b.Property("RootPathOverride") + .HasColumnType("int"); + + b.Property("Status") + .HasColumnType("longtext"); + + b.Property("TheMovieDbId") + .HasColumnType("int"); + + b.Property("Title") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("MovieRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.RequestLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("EpisodeCount") + .HasColumnType("int"); + + b.Property("RequestDate") + .HasColumnType("datetime(6)"); + + b.Property("RequestId") + .HasColumnType("int"); + + b.Property("RequestType") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("RequestLog"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.TvRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Background") + .HasColumnType("longtext"); + + b.Property("ExternalProviderId") + .HasColumnType("int"); + + b.Property("ImdbId") + .HasColumnType("longtext"); + + b.Property("Overview") + .HasColumnType("longtext"); + + b.Property("PosterPath") + .HasColumnType("longtext"); + + b.Property("QualityOverride") + .HasColumnType("int"); + + b.Property("ReleaseDate") + .HasColumnType("datetime(6)"); + + b.Property("RootFolder") + .HasColumnType("int"); + + b.Property("Status") + .HasColumnType("longtext"); + + b.Property("Title") + .HasColumnType("longtext"); + + b.Property("TotalSeasons") + .HasColumnType("int"); + + b.Property("TvDbId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("TvRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Tokens", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Token") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Tokens"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.UserNotificationPreferences", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Agent") + .HasColumnType("int"); + + b.Property("Enabled") + .HasColumnType("tinyint(1)"); + + b.Property("UserId") + .HasColumnType("varchar(255)"); + + b.Property("Value") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("UserNotificationPreferences"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.UserQualityProfiles", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("RadarrQualityProfile") + .HasColumnType("int"); + + b.Property("RadarrRootPath") + .HasColumnType("int"); + + b.Property("SonarrQualityProfile") + .HasColumnType("int"); + + b.Property("SonarrQualityProfileAnime") + .HasColumnType("int"); + + b.Property("SonarrRootPath") + .HasColumnType("int"); + + b.Property("SonarrRootPathAnime") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("UserQualityProfiles"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Votes", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Date") + .HasColumnType("datetime(6)"); + + b.Property("Deleted") + .HasColumnType("tinyint(1)"); + + b.Property("RequestId") + .HasColumnType("int"); + + b.Property("RequestType") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("varchar(255)"); + + b.Property("VoteType") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Votes"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("AirDate") + .HasColumnType("datetime(6)"); + + b.Property("Approved") + .HasColumnType("tinyint(1)"); + + b.Property("Available") + .HasColumnType("tinyint(1)"); + + b.Property("EpisodeNumber") + .HasColumnType("int"); + + b.Property("Requested") + .HasColumnType("tinyint(1)"); + + b.Property("SeasonId") + .HasColumnType("int"); + + b.Property("Title") + .HasColumnType("longtext"); + + b.Property("Url") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("SeasonId"); + + b.ToTable("EpisodeRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("ChildRequestId") + .HasColumnType("int"); + + b.Property("Overview") + .HasColumnType("longtext"); + + b.Property("SeasonNumber") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("ChildRequestId"); + + b.ToTable("SeasonRequests"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Ombi.Store.Entities.OmbiUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Ombi.Store.Entities.MobileDevices", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationUserId", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany("NotificationUserIds") + .HasForeignKey("UserId"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RequestSubscription", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.AlbumRequest", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + + b.Navigation("RequestedUser"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b => + { + b.HasOne("Ombi.Store.Entities.Requests.TvRequests", "ParentRequest") + .WithMany("ChildRequests") + .HasForeignKey("ParentRequestId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + + b.Navigation("ParentRequest"); + + b.Navigation("RequestedUser"); + }); + + 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"); + + b.Navigation("Issues"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b => + { + b.HasOne("Ombi.Store.Entities.Requests.IssueCategory", "IssueCategory") + .WithMany() + .HasForeignKey("IssueCategoryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Ombi.Store.Entities.Requests.ChildRequests", null) + .WithMany("Issues") + .HasForeignKey("IssueId"); + + b.HasOne("Ombi.Store.Entities.Requests.MovieRequests", null) + .WithMany("Issues") + .HasForeignKey("IssueId"); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "UserReported") + .WithMany() + .HasForeignKey("UserReportedId"); + + b.Navigation("IssueCategory"); + + b.Navigation("UserReported"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + + b.Navigation("RequestedUser"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.RequestLog", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Tokens", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.UserNotificationPreferences", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany("UserNotificationPreferences") + .HasForeignKey("UserId"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.UserQualityProfiles", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Votes", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b => + { + b.HasOne("Ombi.Store.Repository.Requests.SeasonRequests", "Season") + .WithMany("Episodes") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Season"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b => + { + b.HasOne("Ombi.Store.Entities.Requests.ChildRequests", "ChildRequest") + .WithMany("SeasonRequests") + .HasForeignKey("ChildRequestId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ChildRequest"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.OmbiUser", b => + { + b.Navigation("NotificationUserIds"); + + b.Navigation("UserNotificationPreferences"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b => + { + b.Navigation("Issues"); + + b.Navigation("SeasonRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b => + { + b.Navigation("Comments"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b => + { + b.Navigation("Issues"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.TvRequests", b => + { + b.Navigation("ChildRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b => + { + b.Navigation("Episodes"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Ombi.Store/Migrations/OmbiMySql/20210305151743_TvRequestProviderId.cs b/src/Ombi.Store/Migrations/OmbiMySql/20210305151743_TvRequestProviderId.cs new file mode 100644 index 000000000..a5a0a2b00 --- /dev/null +++ b/src/Ombi.Store/Migrations/OmbiMySql/20210305151743_TvRequestProviderId.cs @@ -0,0 +1,52 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Ombi.Store.Migrations.OmbiMySql +{ + public partial class TvRequestProviderId : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "ExternalProviderId", + table: "TvRequests", + type: "int", + nullable: false, + defaultValue: 0); + + migrationBuilder.AddColumn( + name: "Overview", + table: "SeasonRequests", + type: "longtext", + nullable: true); + + migrationBuilder.AlterColumn( + name: "StreamingCountry", + table: "AspNetUsers", + type: "longtext", + nullable: false, + defaultValue: "", + oldClrType: typeof(string), + oldType: "longtext", + oldNullable: true); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "ExternalProviderId", + table: "TvRequests"); + + migrationBuilder.DropColumn( + name: "Overview", + table: "SeasonRequests"); + + migrationBuilder.AlterColumn( + name: "StreamingCountry", + table: "AspNetUsers", + type: "longtext", + nullable: true, + oldClrType: typeof(string), + oldType: "longtext"); + } + } +} diff --git a/src/Ombi.Store/Migrations/OmbiMySql/OmbiMySqlContextModelSnapshot.cs b/src/Ombi.Store/Migrations/OmbiMySql/OmbiMySqlContextModelSnapshot.cs index 38487dc9f..f127b6c23 100644 --- a/src/Ombi.Store/Migrations/OmbiMySql/OmbiMySqlContextModelSnapshot.cs +++ b/src/Ombi.Store/Migrations/OmbiMySql/OmbiMySqlContextModelSnapshot.cs @@ -308,6 +308,7 @@ namespace Ombi.Store.Migrations.OmbiMySql .HasColumnType("longtext"); b.Property("StreamingCountry") + .IsRequired() .HasColumnType("longtext"); b.Property("TwoFactorEnabled") @@ -764,6 +765,9 @@ namespace Ombi.Store.Migrations.OmbiMySql b.Property("Background") .HasColumnType("longtext"); + b.Property("ExternalProviderId") + .HasColumnType("int"); + b.Property("ImdbId") .HasColumnType("longtext"); @@ -954,6 +958,9 @@ namespace Ombi.Store.Migrations.OmbiMySql b.Property("ChildRequestId") .HasColumnType("int"); + b.Property("Overview") + .HasColumnType("longtext"); + b.Property("SeasonNumber") .HasColumnType("int"); diff --git a/src/Ombi.Store/Migrations/OmbiSqlite/20210305151854_TvRequestProviderId.Designer.cs b/src/Ombi.Store/Migrations/OmbiSqlite/20210305151854_TvRequestProviderId.Designer.cs new file mode 100644 index 000000000..20aedc05c --- /dev/null +++ b/src/Ombi.Store/Migrations/OmbiSqlite/20210305151854_TvRequestProviderId.Designer.cs @@ -0,0 +1,1231 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Ombi.Store.Context.Sqlite; + +namespace Ombi.Store.Migrations.OmbiSqlite +{ + [DbContext(typeof(OmbiSqliteContext))] + [Migration("20210305151854_TvRequestProviderId")] + partial class TvRequestProviderId + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "5.0.1"); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("TEXT"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("AspNetRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ClaimType") + .HasColumnType("TEXT"); + + b.Property("ClaimValue") + .HasColumnType("TEXT"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ClaimType") + .HasColumnType("TEXT"); + + b.Property("ClaimValue") + .HasColumnType("TEXT"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("TEXT"); + + b.Property("ProviderKey") + .HasColumnType("TEXT"); + + b.Property("ProviderDisplayName") + .HasColumnType("TEXT"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("RoleId") + .HasColumnType("TEXT"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("LoginProvider") + .HasColumnType("TEXT"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Audit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AuditArea") + .HasColumnType("INTEGER"); + + b.Property("AuditType") + .HasColumnType("INTEGER"); + + b.Property("DateTime") + .HasColumnType("TEXT"); + + b.Property("Description") + .HasColumnType("TEXT"); + + b.Property("User") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Audit"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.MobileDevices", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AddedAt") + .HasColumnType("TEXT"); + + b.Property("Token") + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("MobileDevices"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationTemplates", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Agent") + .HasColumnType("INTEGER"); + + b.Property("Enabled") + .HasColumnType("INTEGER"); + + b.Property("Message") + .HasColumnType("TEXT"); + + b.Property("NotificationType") + .HasColumnType("INTEGER"); + + b.Property("Subject") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("NotificationTemplates"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationUserId", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AddedAt") + .HasColumnType("TEXT"); + + b.Property("PlayerId") + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("NotificationUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.OmbiUser", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("AccessFailedCount") + .HasColumnType("INTEGER"); + + b.Property("Alias") + .HasColumnType("TEXT"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("TEXT"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("EmailConfirmed") + .HasColumnType("INTEGER"); + + b.Property("EpisodeRequestLimit") + .HasColumnType("INTEGER"); + + b.Property("Language") + .HasColumnType("TEXT"); + + b.Property("LastLoggedIn") + .HasColumnType("TEXT"); + + b.Property("LockoutEnabled") + .HasColumnType("INTEGER"); + + b.Property("LockoutEnd") + .HasColumnType("TEXT"); + + b.Property("MovieRequestLimit") + .HasColumnType("INTEGER"); + + b.Property("MusicRequestLimit") + .HasColumnType("INTEGER"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("PasswordHash") + .HasColumnType("TEXT"); + + b.Property("PhoneNumber") + .HasColumnType("TEXT"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("INTEGER"); + + b.Property("ProviderUserId") + .HasColumnType("TEXT"); + + b.Property("SecurityStamp") + .HasColumnType("TEXT"); + + b.Property("StreamingCountry") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("TwoFactorEnabled") + .HasColumnType("INTEGER"); + + b.Property("UserAccessToken") + .HasColumnType("TEXT"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("UserType") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.ToTable("AspNetUsers"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RecentlyAddedLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AddedAt") + .HasColumnType("TEXT"); + + b.Property("AlbumId") + .HasColumnType("TEXT"); + + b.Property("ContentId") + .HasColumnType("INTEGER"); + + b.Property("ContentType") + .HasColumnType("INTEGER"); + + b.Property("EpisodeNumber") + .HasColumnType("INTEGER"); + + b.Property("SeasonNumber") + .HasColumnType("INTEGER"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("RecentlyAddedLog"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RequestQueue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Completed") + .HasColumnType("TEXT"); + + b.Property("Dts") + .HasColumnType("TEXT"); + + b.Property("Error") + .HasColumnType("TEXT"); + + b.Property("RequestId") + .HasColumnType("INTEGER"); + + b.Property("RetryCount") + .HasColumnType("INTEGER"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("RequestQueue"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RequestSubscription", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("RequestId") + .HasColumnType("INTEGER"); + + b.Property("RequestType") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("RequestSubscription"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.AlbumRequest", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Approved") + .HasColumnType("INTEGER"); + + b.Property("ArtistName") + .HasColumnType("TEXT"); + + b.Property("Available") + .HasColumnType("INTEGER"); + + b.Property("Cover") + .HasColumnType("TEXT"); + + b.Property("Denied") + .HasColumnType("INTEGER"); + + b.Property("DeniedReason") + .HasColumnType("TEXT"); + + b.Property("Disk") + .HasColumnType("TEXT"); + + b.Property("ForeignAlbumId") + .HasColumnType("TEXT"); + + b.Property("ForeignArtistId") + .HasColumnType("TEXT"); + + b.Property("MarkedAsApproved") + .HasColumnType("TEXT"); + + b.Property("MarkedAsAvailable") + .HasColumnType("TEXT"); + + b.Property("MarkedAsDenied") + .HasColumnType("TEXT"); + + b.Property("Rating") + .HasColumnType("TEXT"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("RequestType") + .HasColumnType("INTEGER"); + + b.Property("RequestedByAlias") + .HasColumnType("TEXT"); + + b.Property("RequestedDate") + .HasColumnType("TEXT"); + + b.Property("RequestedUserId") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("AlbumRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Approved") + .HasColumnType("INTEGER"); + + b.Property("Available") + .HasColumnType("INTEGER"); + + b.Property("Denied") + .HasColumnType("INTEGER"); + + b.Property("DeniedReason") + .HasColumnType("TEXT"); + + b.Property("IssueId") + .HasColumnType("INTEGER"); + + b.Property("MarkedAsApproved") + .HasColumnType("TEXT"); + + b.Property("MarkedAsAvailable") + .HasColumnType("TEXT"); + + b.Property("MarkedAsDenied") + .HasColumnType("TEXT"); + + b.Property("ParentRequestId") + .HasColumnType("INTEGER"); + + b.Property("RequestType") + .HasColumnType("INTEGER"); + + b.Property("RequestedByAlias") + .HasColumnType("TEXT"); + + b.Property("RequestedDate") + .HasColumnType("TEXT"); + + b.Property("RequestedUserId") + .HasColumnType("TEXT"); + + b.Property("SeriesType") + .HasColumnType("INTEGER"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ParentRequestId"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("ChildRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueCategory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("IssueCategory"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueComments", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Comment") + .HasColumnType("TEXT"); + + b.Property("Date") + .HasColumnType("TEXT"); + + b.Property("IssuesId") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("IssuesId"); + + b.HasIndex("UserId"); + + b.ToTable("IssueComments"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CreatedDate") + .HasColumnType("TEXT"); + + b.Property("Description") + .HasColumnType("TEXT"); + + b.Property("IssueCategoryId") + .HasColumnType("INTEGER"); + + b.Property("IssueId") + .HasColumnType("INTEGER"); + + b.Property("ProviderId") + .HasColumnType("TEXT"); + + b.Property("RequestId") + .HasColumnType("INTEGER"); + + b.Property("RequestType") + .HasColumnType("INTEGER"); + + b.Property("ResovledDate") + .HasColumnType("TEXT"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("Subject") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("UserReportedId") + .HasColumnType("TEXT"); + + 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() + .HasColumnType("INTEGER"); + + b.Property("Approved") + .HasColumnType("INTEGER"); + + b.Property("Available") + .HasColumnType("INTEGER"); + + b.Property("Background") + .HasColumnType("TEXT"); + + b.Property("Denied") + .HasColumnType("INTEGER"); + + b.Property("DeniedReason") + .HasColumnType("TEXT"); + + b.Property("DigitalReleaseDate") + .HasColumnType("TEXT"); + + b.Property("ImdbId") + .HasColumnType("TEXT"); + + b.Property("IssueId") + .HasColumnType("INTEGER"); + + b.Property("LangCode") + .HasColumnType("TEXT"); + + b.Property("MarkedAsApproved") + .HasColumnType("TEXT"); + + b.Property("MarkedAsAvailable") + .HasColumnType("TEXT"); + + b.Property("MarkedAsDenied") + .HasColumnType("TEXT"); + + b.Property("Overview") + .HasColumnType("TEXT"); + + b.Property("PosterPath") + .HasColumnType("TEXT"); + + b.Property("QualityOverride") + .HasColumnType("INTEGER"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("RequestType") + .HasColumnType("INTEGER"); + + b.Property("RequestedByAlias") + .HasColumnType("TEXT"); + + b.Property("RequestedDate") + .HasColumnType("TEXT"); + + b.Property("RequestedUserId") + .HasColumnType("TEXT"); + + b.Property("RootPathOverride") + .HasColumnType("INTEGER"); + + b.Property("Status") + .HasColumnType("TEXT"); + + b.Property("TheMovieDbId") + .HasColumnType("INTEGER"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("MovieRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.RequestLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("EpisodeCount") + .HasColumnType("INTEGER"); + + b.Property("RequestDate") + .HasColumnType("TEXT"); + + b.Property("RequestId") + .HasColumnType("INTEGER"); + + b.Property("RequestType") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("RequestLog"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.TvRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Background") + .HasColumnType("TEXT"); + + b.Property("ExternalProviderId") + .HasColumnType("INTEGER"); + + b.Property("ImdbId") + .HasColumnType("TEXT"); + + b.Property("Overview") + .HasColumnType("TEXT"); + + b.Property("PosterPath") + .HasColumnType("TEXT"); + + b.Property("QualityOverride") + .HasColumnType("INTEGER"); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("RootFolder") + .HasColumnType("INTEGER"); + + b.Property("Status") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("TotalSeasons") + .HasColumnType("INTEGER"); + + b.Property("TvDbId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("TvRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Tokens", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Token") + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Tokens"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.UserNotificationPreferences", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Agent") + .HasColumnType("INTEGER"); + + b.Property("Enabled") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("UserNotificationPreferences"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.UserQualityProfiles", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("RadarrQualityProfile") + .HasColumnType("INTEGER"); + + b.Property("RadarrRootPath") + .HasColumnType("INTEGER"); + + b.Property("SonarrQualityProfile") + .HasColumnType("INTEGER"); + + b.Property("SonarrQualityProfileAnime") + .HasColumnType("INTEGER"); + + b.Property("SonarrRootPath") + .HasColumnType("INTEGER"); + + b.Property("SonarrRootPathAnime") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("UserQualityProfiles"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Votes", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Date") + .HasColumnType("TEXT"); + + b.Property("Deleted") + .HasColumnType("INTEGER"); + + b.Property("RequestId") + .HasColumnType("INTEGER"); + + b.Property("RequestType") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("VoteType") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Votes"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AirDate") + .HasColumnType("TEXT"); + + b.Property("Approved") + .HasColumnType("INTEGER"); + + b.Property("Available") + .HasColumnType("INTEGER"); + + b.Property("EpisodeNumber") + .HasColumnType("INTEGER"); + + b.Property("Requested") + .HasColumnType("INTEGER"); + + b.Property("SeasonId") + .HasColumnType("INTEGER"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("Url") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("SeasonId"); + + b.ToTable("EpisodeRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ChildRequestId") + .HasColumnType("INTEGER"); + + b.Property("Overview") + .HasColumnType("TEXT"); + + b.Property("SeasonNumber") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ChildRequestId"); + + b.ToTable("SeasonRequests"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Ombi.Store.Entities.OmbiUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Ombi.Store.Entities.MobileDevices", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationUserId", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany("NotificationUserIds") + .HasForeignKey("UserId"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RequestSubscription", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.AlbumRequest", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + + b.Navigation("RequestedUser"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b => + { + b.HasOne("Ombi.Store.Entities.Requests.TvRequests", "ParentRequest") + .WithMany("ChildRequests") + .HasForeignKey("ParentRequestId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + + b.Navigation("ParentRequest"); + + b.Navigation("RequestedUser"); + }); + + 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"); + + b.Navigation("Issues"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b => + { + b.HasOne("Ombi.Store.Entities.Requests.IssueCategory", "IssueCategory") + .WithMany() + .HasForeignKey("IssueCategoryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Ombi.Store.Entities.Requests.ChildRequests", null) + .WithMany("Issues") + .HasForeignKey("IssueId"); + + b.HasOne("Ombi.Store.Entities.Requests.MovieRequests", null) + .WithMany("Issues") + .HasForeignKey("IssueId"); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "UserReported") + .WithMany() + .HasForeignKey("UserReportedId"); + + b.Navigation("IssueCategory"); + + b.Navigation("UserReported"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + + b.Navigation("RequestedUser"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.RequestLog", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Tokens", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.UserNotificationPreferences", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany("UserNotificationPreferences") + .HasForeignKey("UserId"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.UserQualityProfiles", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Votes", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b => + { + b.HasOne("Ombi.Store.Repository.Requests.SeasonRequests", "Season") + .WithMany("Episodes") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Season"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b => + { + b.HasOne("Ombi.Store.Entities.Requests.ChildRequests", "ChildRequest") + .WithMany("SeasonRequests") + .HasForeignKey("ChildRequestId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ChildRequest"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.OmbiUser", b => + { + b.Navigation("NotificationUserIds"); + + b.Navigation("UserNotificationPreferences"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b => + { + b.Navigation("Issues"); + + b.Navigation("SeasonRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b => + { + b.Navigation("Comments"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b => + { + b.Navigation("Issues"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.TvRequests", b => + { + b.Navigation("ChildRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b => + { + b.Navigation("Episodes"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Ombi.Store/Migrations/OmbiSqlite/20210305151854_TvRequestProviderId.cs b/src/Ombi.Store/Migrations/OmbiSqlite/20210305151854_TvRequestProviderId.cs new file mode 100644 index 000000000..a3185b1a1 --- /dev/null +++ b/src/Ombi.Store/Migrations/OmbiSqlite/20210305151854_TvRequestProviderId.cs @@ -0,0 +1,34 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Ombi.Store.Migrations.OmbiSqlite +{ + public partial class TvRequestProviderId : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "ExternalProviderId", + table: "TvRequests", + type: "INTEGER", + nullable: false, + defaultValue: 0); + + migrationBuilder.AddColumn( + name: "Overview", + table: "SeasonRequests", + type: "TEXT", + nullable: true); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "ExternalProviderId", + table: "TvRequests"); + + migrationBuilder.DropColumn( + name: "Overview", + table: "SeasonRequests"); + } + } +} diff --git a/src/Ombi.Store/Migrations/OmbiSqlite/OmbiSqliteContextModelSnapshot.cs b/src/Ombi.Store/Migrations/OmbiSqlite/OmbiSqliteContextModelSnapshot.cs index 6a00fe29b..144ca4613 100644 --- a/src/Ombi.Store/Migrations/OmbiSqlite/OmbiSqliteContextModelSnapshot.cs +++ b/src/Ombi.Store/Migrations/OmbiSqlite/OmbiSqliteContextModelSnapshot.cs @@ -764,6 +764,9 @@ namespace Ombi.Store.Migrations.OmbiSqlite b.Property("Background") .HasColumnType("TEXT"); + b.Property("ExternalProviderId") + .HasColumnType("INTEGER"); + b.Property("ImdbId") .HasColumnType("TEXT"); @@ -954,6 +957,9 @@ namespace Ombi.Store.Migrations.OmbiSqlite b.Property("ChildRequestId") .HasColumnType("INTEGER"); + b.Property("Overview") + .HasColumnType("TEXT"); + b.Property("SeasonNumber") .HasColumnType("INTEGER"); diff --git a/src/Ombi.Store/Repository/Requests/ITvRequestRepository.cs b/src/Ombi.Store/Repository/Requests/ITvRequestRepository.cs index 396ec9dfb..d7106c086 100644 --- a/src/Ombi.Store/Repository/Requests/ITvRequestRepository.cs +++ b/src/Ombi.Store/Repository/Requests/ITvRequestRepository.cs @@ -16,7 +16,7 @@ namespace Ombi.Store.Repository.Requests IQueryable Get(string userId); IQueryable GetLite(string userId); Task GetRequestAsync(int tvDbId); - TvRequests GetRequest(int tvDbId); + TvRequests GetRequest(int theMovieDbId); Task Update(TvRequests request); Task UpdateChild(ChildRequests request); IQueryable GetChild(); diff --git a/src/Ombi.Store/Repository/Requests/TvRequestRepository.cs b/src/Ombi.Store/Repository/Requests/TvRequestRepository.cs index a5a7406ab..0a72b1b32 100644 --- a/src/Ombi.Store/Repository/Requests/TvRequestRepository.cs +++ b/src/Ombi.Store/Repository/Requests/TvRequestRepository.cs @@ -20,7 +20,7 @@ namespace Ombi.Store.Repository.Requests public async Task GetRequestAsync(int tvDbId) { - return await Db.TvRequests.Where(x => x.TvDbId == tvDbId) + return await Db.TvRequests.Where(x => x.ExternalProviderId == tvDbId) .Include(x => x.ChildRequests) .ThenInclude(x => x.RequestedUser) .Include(x => x.ChildRequests) @@ -29,9 +29,9 @@ namespace Ombi.Store.Repository.Requests .FirstOrDefaultAsync(); } - public TvRequests GetRequest(int tvDbId) + public TvRequests GetRequest(int theMovieDbId) { - return Db.TvRequests.Where(x => x.TvDbId == tvDbId).AsSplitQuery() + return Db.TvRequests.Where(x => x.ExternalProviderId == theMovieDbId).AsSplitQuery() .Include(x => x.ChildRequests) .ThenInclude(x => x.RequestedUser) .Include(x => x.ChildRequests) diff --git a/src/Ombi.TheMovieDbApi/IMovieDbApi.cs b/src/Ombi.TheMovieDbApi/IMovieDbApi.cs index 1b35381ad..ea2465c1d 100644 --- a/src/Ombi.TheMovieDbApi/IMovieDbApi.cs +++ b/src/Ombi.TheMovieDbApi/IMovieDbApi.cs @@ -10,16 +10,20 @@ namespace Ombi.Api.TheMovieDb { Task GetMovieInformation(int movieId); Task GetMovieInformationWithExtraInfo(int movieId, string langCode = "en"); - Task> NowPlaying(string languageCode, int? page = null); - Task> PopularMovies(string languageCode, int? page = null, CancellationToken cancellationToken = default(CancellationToken)); - Task> SearchMovie(string searchTerm, int? year, string languageCode); + Task> NowPlaying(string languageCode, int? page = null); + Task> PopularMovies(string languageCode, int? page = null, CancellationToken cancellationToken = default(CancellationToken)); + Task> PopularTv(string langCode, int? page = null, CancellationToken cancellationToken = default(CancellationToken)); + Task> SearchMovie(string searchTerm, int? year, string languageCode); Task> SearchTv(string searchTerm, string year = default); - Task> TopRated(string languageCode, int? page = null); - Task> Upcoming(string languageCode, int? page = null); - Task> SimilarMovies(int movieId, string langCode); + Task> TopRated(string languageCode, int? page = null); + Task> Upcoming(string languageCode, int? page = null); + Task> TopRatedTv(string languageCode, int? page = null); + Task> UpcomingTv(string languageCode, int? page = null); + Task> SimilarMovies(int movieId, string langCode); Task Find(string externalId, ExternalSource source); - Task GetTvExternals(int theMovieDbId); - Task GetTVInfo(string themoviedbid); + Task GetTvExternals(int theMovieDbId); + Task GetSeasonEpisodes(int theMovieDbId, int seasonNumber, CancellationToken token, string langCode = "en"); + Task GetTVInfo(string themoviedbid, string langCode = "en"); Task> SearchByActor(string searchTerm, string langCode); Task GetActorMovieCredits(int actorId, string langCode); Task> MultiSearch(string searchTerm, string languageCode, CancellationToken cancellationToken); diff --git a/src/Ombi.TheMovieDbApi/Models/FindResult.cs b/src/Ombi.TheMovieDbApi/Models/FindResult.cs index f76fca564..07020d866 100644 --- a/src/Ombi.TheMovieDbApi/Models/FindResult.cs +++ b/src/Ombi.TheMovieDbApi/Models/FindResult.cs @@ -6,7 +6,12 @@ public object[] person_results { get; set; } public TvResults[] tv_results { get; set; } public object[] tv_episode_results { get; set; } - public object[] tv_season_results { get; set; } + public FindSeasonResults[] tv_season_results { get; set; } + } + + public class FindSeasonResults + { + public int show_id { get; set; } } public class Movie_Results diff --git a/src/Ombi.TheMovieDbApi/Models/FullMovieInfo.cs b/src/Ombi.TheMovieDbApi/Models/FullMovieInfo.cs index 3149ed5a1..1dc39299a 100644 --- a/src/Ombi.TheMovieDbApi/Models/FullMovieInfo.cs +++ b/src/Ombi.TheMovieDbApi/Models/FullMovieInfo.cs @@ -87,6 +87,10 @@ namespace Ombi.Api.TheMovieDb.Models { [JsonProperty("file_path")] public string FilePath { get; set; } + [JsonProperty("vote_count")] + public int VoteCount { get; set; } + [JsonProperty("vote_average")] + public double VoteAverage { get; set; } } public class Keywords @@ -128,6 +132,8 @@ namespace Ombi.Api.TheMovieDb.Models public string InstagramId { get; set; } [JsonProperty("twitter_id")] public string TwitterId { get; set; } + [JsonProperty("tvdb_id")] + public string TvDbId { get; set; } } public class Credits diff --git a/src/Ombi.TheMovieDbApi/Models/MovieSearchResult.cs b/src/Ombi.TheMovieDbApi/Models/MovieDbSearchResult.cs similarity index 94% rename from src/Ombi.TheMovieDbApi/Models/MovieSearchResult.cs rename to src/Ombi.TheMovieDbApi/Models/MovieDbSearchResult.cs index 8503cd7f7..0e4a8852f 100644 --- a/src/Ombi.TheMovieDbApi/Models/MovieSearchResult.cs +++ b/src/Ombi.TheMovieDbApi/Models/MovieDbSearchResult.cs @@ -1,6 +1,6 @@ namespace Ombi.Api.TheMovieDb.Models { - public class MovieSearchResult + public class MovieDbSearchResult { public string PosterPath { get; set; } public bool Adult { get; set; } diff --git a/src/Ombi.TheMovieDbApi/Models/SeasonDetails.cs b/src/Ombi.TheMovieDbApi/Models/SeasonDetails.cs new file mode 100644 index 000000000..eb36bdd87 --- /dev/null +++ b/src/Ombi.TheMovieDbApi/Models/SeasonDetails.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Ombi.Api.TheMovieDb.Models +{ + public class SeasonDetails + { + public string _id { get; set; } + public string air_date { get; set; } + public Episode[] episodes { get; set; } + public string name { get; set; } + public string overview { get; set; } + public int id { get; set; } + public string poster_path { get; set; } + public int season_number { get; set; } + } + + public class Episode + { + public string air_date { get; set; } + public int episode_number { get; set; } + public Crew[] crew { get; set; } + public Guest_Stars[] guest_stars { get; set; } + public int id { get; set; } + public string name { get; set; } + public string overview { get; set; } + public string production_code { get; set; } + public int season_number { get; set; } + public string still_path { get; set; } + public float vote_average { get; set; } + public int vote_count { get; set; } + } + + + + public class Guest_Stars + { + public string character { get; set; } + public string credit_id { get; set; } + public int order { get; set; } + public bool adult { get; set; } + public int gender { get; set; } + public int id { get; set; } + public string known_for_department { get; set; } + public string name { get; set; } + public string original_name { get; set; } + public float popularity { get; set; } + public string profile_path { get; set; } + } + +} diff --git a/src/Ombi.TheMovieDbApi/Models/TvInfo.cs b/src/Ombi.TheMovieDbApi/Models/TvInfo.cs index f1070d658..024d6f42b 100644 --- a/src/Ombi.TheMovieDbApi/Models/TvInfo.cs +++ b/src/Ombi.TheMovieDbApi/Models/TvInfo.cs @@ -18,6 +18,7 @@ namespace Ombi.Api.TheMovieDb.Models public Network[] networks { get; set; } public int number_of_episodes { get; set; } public int number_of_seasons { get; set; } + public string tagline { get; set; } public string[] origin_country { get; set; } public string original_language { get; set; } public string original_name { get; set; } @@ -30,7 +31,21 @@ namespace Ombi.Api.TheMovieDb.Models public string type { get; set; } public float vote_average { get; set; } public int vote_count { get; set; } - [JsonProperty("external_ids")] public TvExternalIds TvExternalIds { get; set; } + [JsonProperty("videos")] + public Videos Videos { get; set; } + [JsonProperty("credits")] + public Credits Credits { get; set; } + [JsonProperty("similar")] + public Similar Similar { get; set; } + [JsonProperty("recommendations")] + public Recommendations Recommendations { get; set; } + [JsonProperty("external_ids")] + public ExternalIds ExternalIds { get; set; } + [JsonProperty("keywords")] + public Keywords Keywords { get; set; } + [JsonProperty("images")] + public Images Images { get; set; } + } public class Created_By diff --git a/src/Ombi.TheMovieDbApi/TheMovieDbApi.cs b/src/Ombi.TheMovieDbApi/TheMovieDbApi.cs index f270ad3b2..74c849d59 100644 --- a/src/Ombi.TheMovieDbApi/TheMovieDbApi.cs +++ b/src/Ombi.TheMovieDbApi/TheMovieDbApi.cs @@ -132,7 +132,7 @@ namespace Ombi.Api.TheMovieDb return await Api.Request(request); } - public async Task> SimilarMovies(int movieId, string langCode) + public async Task> SimilarMovies(int movieId, string langCode) { var request = new Request($"movie/{movieId}/similar", BaseUri, HttpMethod.Get); request.AddQueryString("api_key", ApiToken); @@ -140,7 +140,7 @@ namespace Ombi.Api.TheMovieDb AddRetry(request); var result = await Api.Request>(request); - return Mapper.Map>(result.results); + return Mapper.Map>(result.results); } public async Task GetMovieInformationWithExtraInfo(int movieId, string langCode = "en") @@ -154,7 +154,7 @@ namespace Ombi.Api.TheMovieDb return Mapper.Map(result); } - public async Task> SearchMovie(string searchTerm, int? year, string langCode) + public async Task> SearchMovie(string searchTerm, int? year, string langCode) { var request = new Request($"search/movie", BaseUri, HttpMethod.Get); request.AddQueryString("api_key", ApiToken); @@ -171,15 +171,25 @@ namespace Ombi.Api.TheMovieDb AddRetry(request); var result = await Api.Request>(request); - return Mapper.Map>(result.results); + return Mapper.Map>(result.results); } /// /// Maintains filter parity with /movie/popular. /// - public async Task> PopularMovies(string langCode, int? page = null, CancellationToken cancellationToken = default(CancellationToken)) + public async Task> PopularMovies(string langCode, int? page = null, CancellationToken cancellationToken = default(CancellationToken)) { - var request = new Request($"discover/movie", BaseUri, HttpMethod.Get); + return await Popular("movie", langCode, page, cancellationToken); + } + + public async Task> PopularTv(string langCode, int? page = null, CancellationToken cancellationToken = default(CancellationToken)) + { + return await Popular("tv", langCode, page, cancellationToken); + } + + public async Task> Popular(string type, string langCode, int? page = null, CancellationToken cancellationToken = default(CancellationToken)) + { + var request = new Request($"discover/{type}", BaseUri, HttpMethod.Get); request.AddQueryString("api_key", ApiToken); request.AddQueryString("language", langCode); request.AddQueryString("sort_by", "popularity.desc"); @@ -187,18 +197,28 @@ namespace Ombi.Api.TheMovieDb { request.AddQueryString("page", page.ToString()); } - await AddDiscoverMovieSettings(request); + await AddDiscoverSettings(request); AddRetry(request); var result = await Api.Request>(request, cancellationToken); - return Mapper.Map>(result.results); + return Mapper.Map>(result.results); + } + + public Task> TopRated(string langCode, int? page = null) + { + return TopRated("movie", langCode, page); + } + + public Task> TopRatedTv(string langCode, int? page = null) + { + return TopRated("tv", langCode, page); } /// /// Maintains filter parity with /movie/top_rated. /// - public async Task> TopRated(string langCode, int? page = null) + private async Task> TopRated(string type, string langCode, int? page = null) { - var request = new Request($"discover/movie", BaseUri, HttpMethod.Get); + var request = new Request($"discover/{type}", BaseUri, HttpMethod.Get); request.AddQueryString("api_key", ApiToken); request.AddQueryString("language", langCode); request.AddQueryString("sort_by", "vote_average.desc"); @@ -212,18 +232,27 @@ namespace Ombi.Api.TheMovieDb // to filter out extremely high-rated movies due to very little votes request.AddQueryString("vote_count.gte", "250"); - await AddDiscoverMovieSettings(request); + await AddDiscoverSettings(request); AddRetry(request); var result = await Api.Request>(request); - return Mapper.Map>(result.results); + return Mapper.Map>(result.results); + } + + public Task> Upcoming(string langCode, int? page = null) + { + return Upcoming("movie", langCode, page); + } + public Task> UpcomingTv(string langCode, int? page = null) + { + return Upcoming("tv", langCode, page); } /// /// Maintains filter parity with /movie/upcoming. /// - public async Task> Upcoming(string langCode, int? page = null) + private async Task> Upcoming(string type, string langCode, int? page = null) { - var request = new Request($"discover/movie", BaseUri, HttpMethod.Get); + var request = new Request($"discover/{type}", BaseUri, HttpMethod.Get); request.AddQueryString("api_key", ApiToken); request.AddQueryString("language", langCode); @@ -239,16 +268,16 @@ namespace Ombi.Api.TheMovieDb { request.AddQueryString("page", page.ToString()); } - await AddDiscoverMovieSettings(request); + await AddDiscoverSettings(request); AddRetry(request); var result = await Api.Request>(request); - return Mapper.Map>(result.results); + return Mapper.Map>(result.results); } /// /// Maintains filter parity with /movie/now_playing. /// - public async Task> NowPlaying(string langCode, int? page = null) + public async Task> NowPlaying(string langCode, int? page = null) { var request = new Request($"discover/movie", BaseUri, HttpMethod.Get); request.AddQueryString("api_key", ApiToken); @@ -267,22 +296,33 @@ namespace Ombi.Api.TheMovieDb request.AddQueryString("page", page.ToString()); } - await AddDiscoverMovieSettings(request); + await AddDiscoverSettings(request); AddRetry(request); var result = await Api.Request>(request); - return Mapper.Map>(result.results); + return Mapper.Map>(result.results); } - public async Task GetTVInfo(string themoviedbid) + public async Task GetTVInfo(string themoviedbid, string langCode = "en") { var request = new Request($"/tv/{themoviedbid}", BaseUri, HttpMethod.Get); request.AddQueryString("api_key", ApiToken); - request.AddQueryString("append_to_response", "external_ids"); + request.AddQueryString("language", langCode); + request.AddQueryString("append_to_response", "videos,credits,similar,recommendations,external_ids,keywords,images"); AddRetry(request); return await Api.Request(request); } + public async Task GetSeasonEpisodes(int theMovieDbId, int seasonNumber, CancellationToken token, string langCode = "en") + { + var request = new Request($"/tv/{theMovieDbId}/season/{seasonNumber}", BaseUri, HttpMethod.Get); + request.AddQueryString("api_key", ApiToken); + request.AddQueryString("language", langCode); + AddRetry(request); + + return await Api.Request(request, token); + } + public async Task> SearchKeyword(string searchTerm) { var request = new Request("search/keyword", BaseUri, HttpMethod.Get); @@ -330,7 +370,7 @@ namespace Ombi.Api.TheMovieDb return Api.Request(request, token); } - private async Task AddDiscoverMovieSettings(Request request) + private async Task AddDiscoverSettings(Request request) { var settings = await Settings; request.AddQueryString("include_adult", settings.ShowAdultMovies.ToString().ToLower()); diff --git a/src/Ombi.sln b/src/Ombi.sln index 70324f967..01bc0643e 100644 --- a/src/Ombi.sln +++ b/src/Ombi.sln @@ -7,6 +7,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ombi", "Ombi\Ombi.csproj", EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{9D30CCF8-A115-4EB7-A34D-07780D752789}" ProjectSection(SolutionItems) = preProject + .editorconfig = .editorconfig ..\.azuredevops\pipelines\templates\build-steps.yml = ..\.azuredevops\pipelines\templates\build-steps.yml ..\build.cake = ..\build.cake ..\CHANGELOG.md = ..\CHANGELOG.md @@ -121,7 +122,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ombi.Api.Webhook", "Ombi.Ap EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ombi.Api.CloudService", "Ombi.Api.CloudService\Ombi.Api.CloudService.csproj", "{5DE40A66-B369-469E-8626-ECE23D9D8034}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ombi.Api.RottenTomatoes", "Ombi.Api.RottenTomatoes\Ombi.Api.RottenTomatoes.csproj", "{8F19C701-7881-4BC7-8BBA-B068A6B954AD}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ombi.Api.RottenTomatoes", "Ombi.Api.RottenTomatoes\Ombi.Api.RottenTomatoes.csproj", "{8F19C701-7881-4BC7-8BBA-B068A6B954AD}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/src/Ombi/ClientApp/src/app/discover/components/card/discover-card.component.ts b/src/Ombi/ClientApp/src/app/discover/components/card/discover-card.component.ts index 1bb4f4359..e94789dcb 100644 --- a/src/Ombi/ClientApp/src/app/discover/components/card/discover-card.component.ts +++ b/src/Ombi/ClientApp/src/app/discover/components/card/discover-card.component.ts @@ -29,7 +29,8 @@ export class DiscoverCardComponent implements OnInit { public ngOnInit() { if (this.result.type == RequestType.tvShow) { - this.getExtraTvInfo(); + this.fullyLoaded = true; + // this.getExtraTvInfo(); } if (this.result.type == RequestType.movie) { this.getExtraMovieInfo(); @@ -44,15 +45,15 @@ export class DiscoverCardComponent implements OnInit { } public async getExtraTvInfo() { - if (this.result.tvMovieDb) { - this.tvSearchResult = await this.searchService.getTvInfoWithMovieDbId(+this.result.id); - } else { - this.tvSearchResult = await this.searchService.getTvInfo(+this.result.id); - } - if (!this.tvSearchResult || this.tvSearchResult?.status.length > 0 && this.tvSearchResult?.status === "404") { - this.hide = true; - return; - } + // if (this.result.tvMovieDb) { + // this.tvSearchResult = await this.searchService.getTvInfoWithMovieDbId(+this.result.id); + // } else { + // this.tvSearchResult = await this.searchService.getTvInfo(+this.result.id); + // } + // if (!this.tvSearchResult || this.tvSearchResult?.status.length > 0 && this.tvSearchResult?.status === "404") { + // this.hide = true; + // return; + // } this.setTvDefaults(this.tvSearchResult); this.updateTvItem(this.tvSearchResult); diff --git a/src/Ombi/ClientApp/src/app/discover/components/carousel-list/carousel-list.component.ts b/src/Ombi/ClientApp/src/app/discover/components/carousel-list/carousel-list.component.ts index c3909512e..eeb4821c3 100644 --- a/src/Ombi/ClientApp/src/app/discover/components/carousel-list/carousel-list.component.ts +++ b/src/Ombi/ClientApp/src/app/discover/components/carousel-list/carousel-list.component.ts @@ -289,7 +289,7 @@ export class CarouselListComponent implements OnInit { this.tvShows.forEach(m => { tempResults.push({ available: m.available, - posterPath: "../../../images/default_tv_poster.png", + posterPath: m.backdropPath ? `https://image.tmdb.org/t/p/w500/${m.backdropPath}` : "../../../images/default_tv_poster.png", requested: m.requested, title: m.title, type: RequestType.tvShow, diff --git a/src/Ombi/ClientApp/src/app/interfaces/IRequestModel.ts b/src/Ombi/ClientApp/src/app/interfaces/IRequestModel.ts index 1499c8b88..495563c36 100644 --- a/src/Ombi/ClientApp/src/app/interfaces/IRequestModel.ts +++ b/src/Ombi/ClientApp/src/app/interfaces/IRequestModel.ts @@ -100,6 +100,7 @@ export interface IBaseRequest { export interface ITvRequests { id: number; imdbId: string; + externalProviderId: number; rootFolder: number; overview: string; title: string; @@ -111,7 +112,8 @@ export interface ITvRequests { qualityOverride: number; background: any; totalSeasons: number; - tvDbId: number; + tvDbId: number; // NO LONGER USED + open: boolean; // THIS IS FOR THE UI // For UI display @@ -146,6 +148,7 @@ export enum OrderType { export interface INewSeasonRequests { id: number; + overview: string; seasonNumber: number; episodes: IEpisodesRequests[]; seasonAvailable: boolean; diff --git a/src/Ombi/ClientApp/src/app/interfaces/ISearchTvResult.ts b/src/Ombi/ClientApp/src/app/interfaces/ISearchTvResult.ts index 78438a079..2a5b3742e 100644 --- a/src/Ombi/ClientApp/src/app/interfaces/ISearchTvResult.ts +++ b/src/Ombi/ClientApp/src/app/interfaces/ISearchTvResult.ts @@ -36,16 +36,21 @@ export interface ISearchTvResult { subscribed: boolean; showSubscribe: boolean; fullyAvailable: boolean; + backdropPath: string; partlyAvailable: boolean; background: any; open: boolean; // THIS IS FOR THE UI } -export interface ITvRequestViewModel { +export interface ITvRequestViewModelV2 extends ITvRequestViewModelBase { + theMovieDbId: number; +} + + +export interface ITvRequestViewModelBase { requestAll: boolean; firstSeason: boolean; latestSeason: boolean; - tvDbId: number; seasons: ISeasonsViewModel[]; requestOnBehalf: string | undefined; } diff --git a/src/Ombi/ClientApp/src/app/interfaces/ISearchTvResultV2.ts b/src/Ombi/ClientApp/src/app/interfaces/ISearchTvResultV2.ts index ef48adc21..8ea7bacc5 100644 --- a/src/Ombi/ClientApp/src/app/interfaces/ISearchTvResultV2.ts +++ b/src/Ombi/ClientApp/src/app/interfaces/ISearchTvResultV2.ts @@ -1,4 +1,5 @@ import { INewSeasonRequests, RequestType } from "./IRequestModel"; +import { IExternalIds, IGenresViewModel, IKeywords } from "./ISearchMovieResultV2"; export interface ISearchTvResultV2 { id: number; @@ -11,7 +12,7 @@ export interface ISearchTvResultV2 { firstAired: string; networkId: string; runtime: string; - genre: string[]; + genres: IGenresViewModel[], overview: string; lastUpdated: number; airsDayOfWeek: string; @@ -21,7 +22,6 @@ export interface ISearchTvResultV2 { siteRating: number; trailer: string; homepage: string; - certification: string; seasonRequests: INewSeasonRequests[]; requestAll: boolean; approved: boolean; @@ -30,6 +30,7 @@ export interface ISearchTvResultV2 { plexUrl: string; embyUrl: string; jellyfinUrl: string; + tagline: string; quality: string; firstSeason: boolean; latestSeason: boolean; @@ -38,8 +39,10 @@ export interface ISearchTvResultV2 { showSubscribe: boolean; fullyAvailable: boolean; partlyAvailable: boolean; + externalIds: IExternalIds; network: INetwork; images: IImagesV2; + keywords: IKeywords; cast: ICast[]; crew: ICrew[]; requestId: number; diff --git a/src/Ombi/ClientApp/src/app/media-details/components/shared/cast-carousel/cast-carousel.component.html b/src/Ombi/ClientApp/src/app/media-details/components/shared/cast-carousel/cast-carousel.component.html index 4a986a684..dac5bdc04 100644 --- a/src/Ombi/ClientApp/src/app/media-details/components/shared/cast-carousel/cast-carousel.component.html +++ b/src/Ombi/ClientApp/src/app/media-details/components/shared/cast-carousel/cast-carousel.component.html @@ -8,19 +8,18 @@ - - + +
{{item.name}} - {{item.person.name}} + {{item.person}}
- {{item.character}} - {{item.character.name}} + {{item.character}}
diff --git a/src/Ombi/ClientApp/src/app/media-details/components/shared/social-icons/social-icons.component.html b/src/Ombi/ClientApp/src/app/media-details/components/shared/social-icons/social-icons.component.html index c49b52e1c..7e2caf4f8 100644 --- a/src/Ombi/ClientApp/src/app/media-details/components/shared/social-icons/social-icons.component.html +++ b/src/Ombi/ClientApp/src/app/media-details/components/shared/social-icons/social-icons.component.html @@ -1,8 +1,9 @@