diff --git a/CHANGELOG.md b/CHANGELOG.md index ae8417bae..a611b5b99 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## (unreleased) +## v3.0.4817 (2019-10-15) ### **New Features** diff --git a/src/Ombi.Api/Request.cs b/src/Ombi.Api/Request.cs index fd888d0d2..918e189fe 100644 --- a/src/Ombi.Api/Request.cs +++ b/src/Ombi.Api/Request.cs @@ -93,24 +93,7 @@ namespace Ombi.Api public void AddQueryString(string key, string value) { if (string.IsNullOrEmpty(key) || string.IsNullOrEmpty(value)) return; - - var builder = new UriBuilder(FullUri); - var startingTag = string.Empty; - var hasQuery = false; - if (string.IsNullOrEmpty(builder.Query)) - { - startingTag = "?"; - } - else - { - hasQuery = true; - startingTag = builder.Query.Contains("?") ? "&" : "?"; - } - builder.Query = hasQuery - ? $"{builder.Query}{startingTag}{key}={value}" - : $"{startingTag}{key}={value}"; - - _modified = builder.Uri; + _modified = FullUri.AddQueryParameter(key, value); } public void AddJsonBody(object obj) diff --git a/src/Ombi.DependencyInjection/IocExtensions.cs b/src/Ombi.DependencyInjection/IocExtensions.cs index ec48e6e3d..a5b3935bb 100644 --- a/src/Ombi.DependencyInjection/IocExtensions.cs +++ b/src/Ombi.DependencyInjection/IocExtensions.cs @@ -206,7 +206,6 @@ namespace Ombi.DependencyInjection services.AddTransient(); services.AddTransient(); services.AddTransient(); - services.AddTransient(); services.AddTransient(); services.AddTransient(); services.AddTransient(); diff --git a/src/Ombi.Schedule/JobDataKeys.cs b/src/Ombi.Schedule/JobDataKeys.cs new file mode 100644 index 000000000..46d2dee2a --- /dev/null +++ b/src/Ombi.Schedule/JobDataKeys.cs @@ -0,0 +1,7 @@ +namespace Ombi.Schedule +{ + public class JobDataKeys + { + public const string RecentlyAddedSearch = "recentlyAddedSearch"; + } +} \ No newline at end of file diff --git a/src/Ombi.Schedule/JobSetup.cs b/src/Ombi.Schedule/JobSetup.cs deleted file mode 100644 index f0f53f128..000000000 --- a/src/Ombi.Schedule/JobSetup.cs +++ /dev/null @@ -1,117 +0,0 @@ -using System; -using Hangfire; -using Ombi.Core.Settings; -using Ombi.Schedule.Jobs; -using Ombi.Schedule.Jobs.Couchpotato; -using Ombi.Schedule.Jobs.Emby; -using Ombi.Schedule.Jobs.Lidarr; -using Ombi.Schedule.Jobs.Ombi; -using Ombi.Schedule.Jobs.Plex; -using Ombi.Schedule.Jobs.Plex.Interfaces; -using Ombi.Schedule.Jobs.Radarr; -using Ombi.Schedule.Jobs.SickRage; -using Ombi.Schedule.Jobs.Sonarr; -using Ombi.Settings.Settings.Models; - -namespace Ombi.Schedule -{ - public class JobSetup : IJobSetup, IDisposable - { - public JobSetup(IPlexContentSync plexContentSync, IRadarrSync radarrSync, - IOmbiAutomaticUpdater updater, IEmbyContentSync embySync, IPlexUserImporter userImporter, - IEmbyUserImporter embyUserImporter, ISonarrSync cache, ICouchPotatoSync cpCache, - ISettingsService jobsettings, ISickRageSync srSync, IRefreshMetadata refresh, - INewsletterJob newsletter, IPlexRecentlyAddedSync recentlyAddedPlex, ILidarrArtistSync artist, - IIssuesPurge purge, IResendFailedRequests resender, IMediaDatabaseRefresh dbRefresh) - { - _plexContentSync = plexContentSync; - _radarrSync = radarrSync; - _updater = updater; - _embyContentSync = embySync; - _plexUserImporter = userImporter; - _embyUserImporter = embyUserImporter; - _sonarrSync = cache; - _cpCache = cpCache; - _jobSettings = jobsettings; - _srSync = srSync; - _refreshMetadata = refresh; - _newsletter = newsletter; - _plexRecentlyAddedSync = recentlyAddedPlex; - _lidarrArtistSync = artist; - _issuesPurge = purge; - _resender = resender; - _mediaDatabaseRefresh = dbRefresh; - } - - private readonly IPlexContentSync _plexContentSync; - private readonly IPlexRecentlyAddedSync _plexRecentlyAddedSync; - private readonly IRadarrSync _radarrSync; - private readonly IOmbiAutomaticUpdater _updater; - private readonly IPlexUserImporter _plexUserImporter; - private readonly IEmbyContentSync _embyContentSync; - private readonly IEmbyUserImporter _embyUserImporter; - private readonly ISonarrSync _sonarrSync; - private readonly ICouchPotatoSync _cpCache; - private readonly ISickRageSync _srSync; - private readonly ISettingsService _jobSettings; - private readonly IRefreshMetadata _refreshMetadata; - private readonly INewsletterJob _newsletter; - private readonly ILidarrArtistSync _lidarrArtistSync; - private readonly IIssuesPurge _issuesPurge; - private readonly IResendFailedRequests _resender; - private readonly IMediaDatabaseRefresh _mediaDatabaseRefresh; - - public void Setup() - { - var s = _jobSettings.GetSettings(); - - // RecurringJob.AddOrUpdate(() => _embyContentSync.Start(), JobSettingsHelper.EmbyContent(s)); - // RecurringJob.AddOrUpdate(() => _sonarrSync.Start(), JobSettingsHelper.Sonarr(s)); - // RecurringJob.AddOrUpdate(() => _radarrSync.CacheContent(), JobSettingsHelper.Radarr(s)); - // //RecurringJob.AddOrUpdate(() => _plexContentSync.Execute(null), JobSettingsHelper.PlexContent(s)); - // //RecurringJob.AddOrUpdate(() => _plexRecentlyAddedSync.Start(), JobSettingsHelper.PlexRecentlyAdded(s)); - // RecurringJob.AddOrUpdate(() => _cpCache.Start(), JobSettingsHelper.CouchPotato(s)); - // RecurringJob.AddOrUpdate(() => _srSync.Start(), JobSettingsHelper.SickRageSync(s)); - // RecurringJob.AddOrUpdate(() => _refreshMetadata.Start(), JobSettingsHelper.RefreshMetadata(s)); - // RecurringJob.AddOrUpdate(() => _lidarrArtistSync.CacheContent(), JobSettingsHelper.LidarrArtistSync(s)); - // RecurringJob.AddOrUpdate(() => _issuesPurge.Start(), JobSettingsHelper.IssuePurge(s)); - - // RecurringJob.AddOrUpdate(() => _updater.Update(null), JobSettingsHelper.Updater(s)); - - // RecurringJob.AddOrUpdate(() => _embyUserImporter.Start(), JobSettingsHelper.UserImporter(s)); - // RecurringJob.AddOrUpdate(() => _plexUserImporter.Start(), JobSettingsHelper.UserImporter(s)); - // RecurringJob.AddOrUpdate(() => _newsletter.Start(), JobSettingsHelper.Newsletter(s)); - //// RecurringJob.AddOrUpdate(() => _resender.Start(), JobSettingsHelper.ResendFailedRequests(s)); - // RecurringJob.AddOrUpdate(() => _mediaDatabaseRefresh.Start(), JobSettingsHelper.MediaDatabaseRefresh(s)); - } - - private bool _disposed; - protected virtual void Dispose(bool disposing) - { - if (_disposed) - return; - - if (disposing) - { - _radarrSync?.Dispose(); - _updater?.Dispose(); - _plexUserImporter?.Dispose(); - _embyContentSync?.Dispose(); - _embyUserImporter?.Dispose(); - _sonarrSync?.Dispose(); - _cpCache?.Dispose(); - _srSync?.Dispose(); - _jobSettings?.Dispose(); - _refreshMetadata?.Dispose(); - _newsletter?.Dispose(); - } - _disposed = true; - } - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - } -} diff --git a/src/Ombi.Schedule/Jobs/Emby/EmbyAvaliabilityChecker.cs b/src/Ombi.Schedule/Jobs/Emby/EmbyAvaliabilityChecker.cs index 632542d34..18c4c28a7 100644 --- a/src/Ombi.Schedule/Jobs/Emby/EmbyAvaliabilityChecker.cs +++ b/src/Ombi.Schedule/Jobs/Emby/EmbyAvaliabilityChecker.cs @@ -36,6 +36,7 @@ using Ombi.Core.Notifications; using Ombi.Helpers; using Ombi.Hubs; using Ombi.Notifications.Models; +using Ombi.Schedule.Jobs.Ombi; using Ombi.Store.Entities; using Ombi.Store.Repository; using Ombi.Store.Repository.Requests; @@ -70,6 +71,8 @@ namespace Ombi.Schedule.Jobs.Emby await ProcessMovies(); await ProcessTv(); + await OmbiQuartz.TriggerJob(nameof(IRefreshMetadata), "System"); + await _notification.Clients.Clients(NotificationHub.AdminConnectionIds) .SendAsync(NotificationHub.NotificationEvent, "Emby Availability Checker Finished"); } diff --git a/src/Ombi.Schedule/Jobs/Emby/EmbyContentSync.cs b/src/Ombi.Schedule/Jobs/Emby/EmbyContentSync.cs index ee81892b8..6a58af097 100644 --- a/src/Ombi.Schedule/Jobs/Emby/EmbyContentSync.cs +++ b/src/Ombi.Schedule/Jobs/Emby/EmbyContentSync.cs @@ -67,7 +67,6 @@ namespace Ombi.Schedule.Jobs.Emby // Episodes await OmbiQuartz.TriggerJob(nameof(IEmbyEpisodeSync), "Emby"); - await OmbiQuartz.TriggerJob(nameof(IRefreshMetadata), "System"); } diff --git a/src/Ombi.Schedule/Jobs/Lidarr/LidarrAvailabilityChecker.cs b/src/Ombi.Schedule/Jobs/Lidarr/LidarrAvailabilityChecker.cs index 4dbf91215..a7706a433 100644 --- a/src/Ombi.Schedule/Jobs/Lidarr/LidarrAvailabilityChecker.cs +++ b/src/Ombi.Schedule/Jobs/Lidarr/LidarrAvailabilityChecker.cs @@ -51,7 +51,7 @@ namespace Ombi.Schedule.Jobs.Lidarr var cachedAlbum = await _cachedAlbums.FirstOrDefaultAsync(x => x.ForeignAlbumId.Equals(request.ForeignAlbumId)); if (cachedAlbum != null) { - if (cachedAlbum.FullyAvailable) + if (cachedAlbum.FullyAvailable) // ensure we have all tracks { request.Available = true; request.MarkedAsAvailable = DateTime.Now; diff --git a/src/Ombi.Schedule/Jobs/Ombi/Interfaces/IRefreshMetadata.cs b/src/Ombi.Schedule/Jobs/Ombi/Interfaces/IRefreshMetadata.cs index a7e7d5fdc..c29a80994 100644 --- a/src/Ombi.Schedule/Jobs/Ombi/Interfaces/IRefreshMetadata.cs +++ b/src/Ombi.Schedule/Jobs/Ombi/Interfaces/IRefreshMetadata.cs @@ -5,6 +5,5 @@ namespace Ombi.Schedule.Jobs.Ombi { public interface IRefreshMetadata : IBaseJob { - Task ProcessPlexServerContent(IEnumerable contentIds); } } \ No newline at end of file diff --git a/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs b/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs index aa88f03d7..5f1443046 100644 --- a/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs +++ b/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs @@ -728,7 +728,8 @@ namespace Ombi.Schedule.Jobs.Ombi (key, g) => new { SeasonNumber = key, - Episodes = g.ToList() + Episodes = g.ToList(), + EpisodeAirDate = tvInfo?.seasons?.Where(x => x.season_number == key)?.Select(x => x.air_date).FirstOrDefault() } ); @@ -738,7 +739,8 @@ namespace Ombi.Schedule.Jobs.Ombi { var orderedEpisodes = epInformation.Episodes.OrderBy(x => x.EpisodeNumber).ToList(); var episodeString = StringHelper.BuildEpisodeList(orderedEpisodes.Select(x => x.EpisodeNumber)); - finalsb.Append($"Season: {epInformation.SeasonNumber} - Episodes: {episodeString}"); + var episodeAirDate = epInformation.EpisodeAirDate; + finalsb.Append($"Season: {epInformation.SeasonNumber} - Episodes: {episodeString} {episodeAirDate}"); finalsb.Append("
"); } @@ -851,7 +853,8 @@ namespace Ombi.Schedule.Jobs.Ombi (key, g) => new { SeasonNumber = key, - Episodes = g.ToList() + Episodes = g.ToList(), + EpisodeAirDate = tvInfo?.seasons?.Where(x => x.season_number == key)?.Select(x => x.air_date).FirstOrDefault() } ); @@ -861,7 +864,8 @@ namespace Ombi.Schedule.Jobs.Ombi { var orderedEpisodes = epInformation.Episodes.OrderBy(x => x.EpisodeNumber).ToList(); var episodeString = StringHelper.BuildEpisodeList(orderedEpisodes.Select(x => x.EpisodeNumber)); - finalsb.Append($"Season: {epInformation.SeasonNumber} - Episodes: {episodeString}"); + var episodeAirDate = epInformation.EpisodeAirDate; + finalsb.Append($"Season: {epInformation.SeasonNumber} - Episodes: {episodeString} {episodeAirDate}"); finalsb.Append("
"); } diff --git a/src/Ombi.Schedule/Jobs/Ombi/RefreshMetadata.cs b/src/Ombi.Schedule/Jobs/Ombi/RefreshMetadata.cs index c50c63e9b..dd48e442d 100644 --- a/src/Ombi.Schedule/Jobs/Ombi/RefreshMetadata.cs +++ b/src/Ombi.Schedule/Jobs/Ombi/RefreshMetadata.cs @@ -61,7 +61,7 @@ namespace Ombi.Schedule.Jobs.Ombi { await StartPlex(); } - + var embySettings = await _embySettings.GetSettingsAsync(); if (embySettings.Enable) { @@ -81,44 +81,16 @@ namespace Ombi.Schedule.Jobs.Ombi .SendAsync(NotificationHub.NotificationEvent, "Metadata Refresh Finished"); } - public async Task ProcessPlexServerContent(IEnumerable contentIds) - { - _log.LogInformation("Starting the Metadata refresh from RecentlyAddedSync"); - var plexSettings = await _plexSettings.GetSettingsAsync(); - try - { - if (plexSettings.Enable) - { - await StartPlexWithKnownContent(contentIds); - } - } - catch (Exception e) - { - _log.LogError(e, "Exception when refreshing the Plex Metadata"); - throw; - } - } - - private async Task StartPlexWithKnownContent(IEnumerable contentids) - { - var everything = _plexRepo.GetAll().Where(x => contentids.Contains(x.Id)); - var allMovies = everything.Where(x => x.Type == PlexMediaTypeEntity.Movie); - await StartPlexMovies(allMovies); - - // Now Tv - var allTv = everything.Where(x => x.Type == PlexMediaTypeEntity.Show); - await StartPlexTv(allTv); - } - private async Task StartPlex() { + // Ensure we check that we have not linked this item to a request var allMovies = _plexRepo.GetAll().Where(x => - x.Type == PlexMediaTypeEntity.Movie && (!x.TheMovieDbId.HasValue() || !x.ImdbId.HasValue())); + x.Type == PlexMediaTypeEntity.Movie && !x.RequestId.HasValue && (!x.TheMovieDbId.HasValue() || !x.ImdbId.HasValue())); await StartPlexMovies(allMovies); // Now Tv var allTv = _plexRepo.GetAll().Where(x => - x.Type == PlexMediaTypeEntity.Show && (!x.TheMovieDbId.HasValue() || !x.ImdbId.HasValue() || !x.TvDbId.HasValue())); + x.Type == PlexMediaTypeEntity.Show && !x.RequestId.HasValue && (!x.TheMovieDbId.HasValue() || !x.ImdbId.HasValue() || !x.TvDbId.HasValue())); await StartPlexTv(allTv); } @@ -130,9 +102,13 @@ namespace Ombi.Schedule.Jobs.Ombi private async Task StartPlexTv(IQueryable allTv) { - var tvCount = 0; foreach (var show in allTv) - { + { + // Just double check there is no associated request id + if (show.RequestId.HasValue) + { + continue; + } var hasImdb = show.ImdbId.HasValue(); var hasTheMovieDb = show.TheMovieDbId.HasValue(); var hasTvDbId = show.TvDbId.HasValue(); @@ -156,21 +132,15 @@ namespace Ombi.Schedule.Jobs.Ombi show.TvDbId = id; _plexRepo.UpdateWithoutSave(show); } - tvCount++; - if (tvCount >= 75) - { - await _plexRepo.SaveChangesAsync(); - tvCount = 0; - } + await _plexRepo.SaveChangesAsync(); } - await _plexRepo.SaveChangesAsync(); } private async Task StartEmbyTv() { var allTv = _embyRepo.GetAll().Where(x => x.Type == EmbyMediaType.Series && (!x.TheMovieDbId.HasValue() || !x.ImdbId.HasValue() || !x.TvDbId.HasValue())); - var tvCount = 0; + foreach (var show in allTv) { var hasImdb = show.ImdbId.HasValue(); @@ -196,21 +166,20 @@ namespace Ombi.Schedule.Jobs.Ombi show.TvDbId = id; _embyRepo.UpdateWithoutSave(show); } - tvCount++; - if (tvCount >= 75) - { - await _embyRepo.SaveChangesAsync(); - tvCount = 0; - } + + await _embyRepo.SaveChangesAsync(); } - await _embyRepo.SaveChangesAsync(); } private async Task StartPlexMovies(IQueryable allMovies) { - int movieCount = 0; foreach (var movie in allMovies) { + // Just double check there is no associated request id + if (movie.RequestId.HasValue) + { + continue; + } var hasImdb = movie.ImdbId.HasValue(); var hasTheMovieDb = movie.TheMovieDbId.HasValue(); // Movies don't really use TheTvDb @@ -227,26 +196,19 @@ namespace Ombi.Schedule.Jobs.Ombi movie.TheMovieDbId = id; _plexRepo.UpdateWithoutSave(movie); } - movieCount++; - if (movieCount >= 75) - { - await _plexRepo.SaveChangesAsync(); - movieCount = 0; - } - } - await _plexRepo.SaveChangesAsync(); + await _plexRepo.SaveChangesAsync(); + } } private async Task StartEmbyMovies(EmbySettings settings) { var allMovies = _embyRepo.GetAll().Where(x => x.Type == EmbyMediaType.Movie && (!x.TheMovieDbId.HasValue() || !x.ImdbId.HasValue())); - int movieCount = 0; foreach (var movie in allMovies) { movie.ImdbId.HasValue(); - movie.TheMovieDbId.HasValue(); + movie.TheMovieDbId.HasValue(); // Movies don't really use TheTvDb // Check if it even has 1 ID @@ -255,6 +217,8 @@ namespace Ombi.Schedule.Jobs.Ombi // Ok this sucks, // The only think I can think that has happened is that we scanned Emby before Emby has got the metadata // So let's recheck emby to see if they have got the metadata now + // + // Yeah your right that does suck - Future Jamie _log.LogInformation($"Movie {movie.Title} does not have a ImdbId or TheMovieDbId, so rechecking emby"); foreach (var server in settings.Servers) { @@ -286,15 +250,10 @@ namespace Ombi.Schedule.Jobs.Ombi movie.TheMovieDbId = id; _embyRepo.UpdateWithoutSave(movie); } - movieCount++; - if (movieCount >= 75) - { - await _embyRepo.SaveChangesAsync(); - movieCount = 0; - } - } - await _embyRepo.SaveChangesAsync(); + await _embyRepo.SaveChangesAsync(); + + } } private async Task GetTheMovieDbId(bool hasTvDbId, bool hasImdb, string tvdbID, string imdbId, string title, bool movie) @@ -377,7 +336,7 @@ namespace Ombi.Schedule.Jobs.Ombi { var result = await _movieApi.GetTvExternals(id); - return result.tvdb_id.ToString(); + return result.tvdb_id.ToString(); } } diff --git a/src/Ombi.Schedule/Jobs/Plex/PlexAvailabilityChecker.cs b/src/Ombi.Schedule/Jobs/Plex/PlexAvailabilityChecker.cs index a608f2988..e7ad5d86c 100644 --- a/src/Ombi.Schedule/Jobs/Plex/PlexAvailabilityChecker.cs +++ b/src/Ombi.Schedule/Jobs/Plex/PlexAvailabilityChecker.cs @@ -55,6 +55,7 @@ namespace Ombi.Schedule.Jobs.Plex await _notification.Clients.Clients(NotificationHub.AdminConnectionIds) .SendAsync(NotificationHub.NotificationEvent, "Plex Availability Check Failed"); _log.LogError(e, "Exception thrown in Plex availbility checker"); + return; } await _notification.Clients.Clients(NotificationHub.AdminConnectionIds) @@ -84,7 +85,7 @@ namespace Ombi.Schedule.Jobs.Plex { useTvDb = true; } - + var tvDbId = child.ParentRequest.TvDbId; var imdbId = child.ParentRequest.ImdbId; IQueryable seriesEpisodes = null; @@ -92,7 +93,7 @@ namespace Ombi.Schedule.Jobs.Plex { seriesEpisodes = plexEpisodes.Where(x => x.Series.ImdbId == imdbId.ToString()); } - if (useTvDb && (seriesEpisodes == null || !seriesEpisodes.Any()) ) + if (useTvDb && (seriesEpisodes == null || !seriesEpisodes.Any())) { seriesEpisodes = plexEpisodes.Where(x => x.Series.TvDbId == tvDbId.ToString()); } @@ -106,8 +107,8 @@ namespace Ombi.Schedule.Jobs.Plex { // Let's try and match the series by name seriesEpisodes = plexEpisodes.Where(x => - x.Series.Title.Equals(child.Title, StringComparison.CurrentCultureIgnoreCase) && - x.Series.ReleaseYear == child.ParentRequest.ReleaseDate.Year.ToString()); + x.Series.Title.Equals(child.Title, StringComparison.InvariantCultureIgnoreCase) && + x.Series.ReleaseYear.Equals(child.ParentRequest.ReleaseDate.Year.ToString(), StringComparison.InvariantCultureIgnoreCase)); } @@ -134,17 +135,18 @@ namespace Ombi.Schedule.Jobs.Plex var allAvailable = child.SeasonRequests.All(x => x.Episodes.All(c => c.Available)); if (allAvailable) { - // We have fulfulled this request! + _log.LogInformation("[PAC] - Child request {0} is now available, sending notification", $"{child.Title} - {child.Id}"); + // We have ful-fulled this request! child.Available = true; child.MarkedAsAvailable = DateTime.Now; - _backgroundJobClient.Enqueue(() => _notificationService.Publish(new NotificationOptions + await _notificationService.Publish(new NotificationOptions { DateTime = DateTime.Now, NotificationType = NotificationType.RequestAvailable, RequestId = child.Id, RequestType = RequestType.TvShow, Recipient = child.RequestedUser.Email - })); + }); } } @@ -178,20 +180,22 @@ namespace Ombi.Schedule.Jobs.Plex movie.Available = true; movie.MarkedAsAvailable = DateTime.Now; - if (movie.Available) + item.RequestId = movie.Id; + + _log.LogInformation("[PAC] - Movie request {0} is now available, sending notification", $"{movie.Title} - {movie.Id}"); + await _notificationService.Publish(new NotificationOptions { - _backgroundJobClient.Enqueue(() => _notificationService.Publish(new NotificationOptions - { - DateTime = DateTime.Now, - NotificationType = NotificationType.RequestAvailable, - RequestId = movie.Id, - RequestType = RequestType.Movie, - Recipient = movie.RequestedUser != null ? movie.RequestedUser.Email : string.Empty - })); - } + DateTime = DateTime.Now, + NotificationType = NotificationType.RequestAvailable, + RequestId = movie.Id, + RequestType = RequestType.Movie, + Recipient = movie.RequestedUser != null ? movie.RequestedUser.Email : string.Empty + }); + } await _movieRepo.Save(); + await _repo.SaveChangesAsync(); } private bool _disposed; diff --git a/src/Ombi.Schedule/Jobs/Plex/PlexContentSync.cs b/src/Ombi.Schedule/Jobs/Plex/PlexContentSync.cs index b143b2610..f698a2dc3 100644 --- a/src/Ombi.Schedule/Jobs/Plex/PlexContentSync.cs +++ b/src/Ombi.Schedule/Jobs/Plex/PlexContentSync.cs @@ -72,7 +72,7 @@ namespace Ombi.Schedule.Jobs.Plex public async Task Execute(IJobExecutionContext context) { JobDataMap dataMap = context.JobDetail.JobDataMap; - var recentlyAddedSearch = dataMap.GetBooleanValueFromString("recentlyAddedSearch"); + var recentlyAddedSearch = dataMap.GetBooleanValueFromString(JobDataKeys.RecentlyAddedSearch); var plexSettings = await Plex.GetSettingsAsync(); if (!plexSettings.Enable) @@ -89,7 +89,9 @@ namespace Ombi.Schedule.Jobs.Plex return; } var processedContent = new ProcessedContent(); - Logger.LogInformation($"Starting Plex Content Cacher {(recentlyAddedSearch ? "Recently Added Scan" : "")}"); + Logger.LogInformation(recentlyAddedSearch + ? "Starting Plex Content Cacher Recently Added Scan" + : "Starting Plex Content Cacher"); try { if (recentlyAddedSearch) @@ -111,12 +113,13 @@ namespace Ombi.Schedule.Jobs.Plex if (!recentlyAddedSearch) { Logger.LogInformation("Starting EP Cacher"); - await OmbiQuartz.TriggerJob(nameof(IPlexEpisodeSync), "Plex"); } if ((processedContent?.HasProcessedContent ?? false) && recentlyAddedSearch) { + Logger.LogInformation("Kicking off Plex Availability Checker"); + await OmbiQuartz.TriggerJob(nameof(IPlexAvailabilityChecker), "Plex"); Logger.LogInformation("Starting Metadata refresh"); // Just check what we send it await OmbiQuartz.TriggerJob(nameof(IRefreshMetadata), "System"); diff --git a/src/Ombi.Schedule/OmbiScheduler.cs b/src/Ombi.Schedule/OmbiScheduler.cs index a1a7a3765..e1d136849 100644 --- a/src/Ombi.Schedule/OmbiScheduler.cs +++ b/src/Ombi.Schedule/OmbiScheduler.cs @@ -60,7 +60,7 @@ namespace Ombi.Schedule private static async Task AddSystem(JobSettings s) { - await OmbiQuartz.Instance.AddJob(nameof(IRefreshMetadata), "System", JobSettingsHelper.RefreshMetadata(s)); + await OmbiQuartz.Instance.AddJob(nameof(IRefreshMetadata), "System", null); await OmbiQuartz.Instance.AddJob(nameof(IIssuesPurge), "System", JobSettingsHelper.IssuePurge(s)); //OmbiQuartz.Instance.AddJob(nameof(IOmbiAutomaticUpdater), "System", JobSettingsHelper.Updater(s)); await OmbiQuartz.Instance.AddJob(nameof(INewsletterJob), "System", JobSettingsHelper.Newsletter(s)); @@ -82,7 +82,7 @@ namespace Ombi.Schedule private static async Task AddPlex(JobSettings s) { await OmbiQuartz.Instance.AddJob(nameof(IPlexContentSync), "Plex", JobSettingsHelper.PlexContent(s), new Dictionary { { "recentlyAddedSearch", "false" } }); - await OmbiQuartz.Instance.AddJob(nameof(IPlexContentSync) + "RecentlyAdded", "Plex", JobSettingsHelper.PlexRecentlyAdded(s), new Dictionary { { "recentlyAddedSearch", "true" } }); + await OmbiQuartz.Instance.AddJob(nameof(IPlexContentSync) + "RecentlyAdded", "Plex", JobSettingsHelper.PlexRecentlyAdded(s), new Dictionary { { JobDataKeys.RecentlyAddedSearch, "true" } }); await OmbiQuartz.Instance.AddJob(nameof(IPlexUserImporter), "Plex", JobSettingsHelper.UserImporter(s)); await OmbiQuartz.Instance.AddJob(nameof(IPlexEpisodeSync), "Plex", null); await OmbiQuartz.Instance.AddJob(nameof(IPlexAvailabilityChecker), "Plex", null); diff --git a/src/Ombi.Settings/Settings/Models/External/TheMovieDbSettings.cs b/src/Ombi.Settings/Settings/Models/External/TheMovieDbSettings.cs new file mode 100644 index 000000000..fe0c238d8 --- /dev/null +++ b/src/Ombi.Settings/Settings/Models/External/TheMovieDbSettings.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; + +namespace Ombi.Core.Settings.Models.External +{ + public sealed class TheMovieDbSettings : Ombi.Settings.Settings.Models.Settings + { + public bool ShowAdultMovies { get; set; } + + public List ExcludedKeywordIds { get; set; } + } +} diff --git a/src/Ombi.Store/Entities/PlexServerContent.cs b/src/Ombi.Store/Entities/PlexServerContent.cs index 17d8ceffb..f8e0e01d4 100644 --- a/src/Ombi.Store/Entities/PlexServerContent.cs +++ b/src/Ombi.Store/Entities/PlexServerContent.cs @@ -53,6 +53,8 @@ namespace Ombi.Store.Entities public DateTime AddedAt { get; set; } public string Quality { get; set; } + public int? RequestId { get; set; } + [NotMapped] public bool HasImdb => !string.IsNullOrEmpty(ImdbId); diff --git a/src/Ombi.Store/Migrations/20191016203035_RequestIdOnPlexContent.Designer.cs b/src/Ombi.Store/Migrations/20191016203035_RequestIdOnPlexContent.Designer.cs new file mode 100644 index 000000000..f0cd74378 --- /dev/null +++ b/src/Ombi.Store/Migrations/20191016203035_RequestIdOnPlexContent.Designer.cs @@ -0,0 +1,1216 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Ombi.Store.Context; + +namespace Ombi.Store.Migrations +{ + [DbContext(typeof(OmbiContext))] + [Migration("20191016203035_RequestIdOnPlexContent")] + partial class RequestIdOnPlexContent + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.2.2-servicing-10034"); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Name") + .HasMaxLength(256); + + b.Property("NormalizedName") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasName("RoleNameIndex"); + + b.ToTable("AspNetRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("RoleId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider"); + + b.Property("ProviderKey"); + + b.Property("ProviderDisplayName"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId"); + + b.Property("RoleId"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId"); + + b.Property("LoginProvider"); + + b.Property("Name"); + + b.Property("Value"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.ApplicationConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Type"); + + b.Property("Value"); + + b.HasKey("Id"); + + b.ToTable("ApplicationConfiguration"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Audit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AuditArea"); + + b.Property("AuditType"); + + b.Property("DateTime"); + + b.Property("Description"); + + b.Property("User"); + + b.HasKey("Id"); + + b.ToTable("Audit"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.CouchPotatoCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TheMovieDbId"); + + b.HasKey("Id"); + + b.ToTable("CouchPotatoCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("EmbyId") + .IsRequired(); + + b.Property("ImdbId"); + + b.Property("ProviderId"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.Property("Type"); + + b.Property("Url"); + + b.HasKey("Id"); + + b.ToTable("EmbyContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("EmbyId"); + + b.Property("EpisodeNumber"); + + b.Property("ImdbId"); + + b.Property("ParentId"); + + b.Property("ProviderId"); + + b.Property("SeasonNumber"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.ToTable("EmbyEpisode"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.GlobalSettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Content"); + + b.Property("SettingsName"); + + b.HasKey("Id"); + + b.ToTable("GlobalSettings"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.LidarrAlbumCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("ArtistId"); + + b.Property("ForeignAlbumId"); + + b.Property("Monitored"); + + b.Property("PercentOfTracks"); + + b.Property("ReleaseDate"); + + b.Property("Title"); + + b.Property("TrackCount"); + + b.HasKey("Id"); + + b.ToTable("LidarrAlbumCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.LidarrArtistCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ArtistId"); + + b.Property("ArtistName"); + + b.Property("ForeignArtistId"); + + b.Property("Monitored"); + + b.HasKey("Id"); + + b.ToTable("LidarrArtistCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationTemplates", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Agent"); + + b.Property("Enabled"); + + b.Property("Message"); + + b.Property("NotificationType"); + + b.Property("Subject"); + + b.HasKey("Id"); + + b.ToTable("NotificationTemplates"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationUserId", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("PlayerId"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("NotificationUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.OmbiUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AccessFailedCount"); + + b.Property("Alias"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Email") + .HasMaxLength(256); + + b.Property("EmailConfirmed"); + + b.Property("EmbyConnectUserId"); + + b.Property("EpisodeRequestLimit"); + + b.Property("LastLoggedIn"); + + b.Property("LockoutEnabled"); + + b.Property("LockoutEnd"); + + b.Property("MovieRequestLimit"); + + b.Property("MusicRequestLimit"); + + b.Property("NormalizedEmail") + .HasMaxLength(256); + + b.Property("NormalizedUserName") + .HasMaxLength(256); + + b.Property("PasswordHash"); + + b.Property("PhoneNumber"); + + b.Property("PhoneNumberConfirmed"); + + b.Property("ProviderUserId"); + + b.Property("SecurityStamp"); + + b.Property("TwoFactorEnabled"); + + b.Property("UserAccessToken"); + + b.Property("UserName") + .HasMaxLength(256); + + b.Property("UserType"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasName("UserNameIndex"); + + b.ToTable("AspNetUsers"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeNumber"); + + b.Property("GrandparentKey"); + + b.Property("Key"); + + b.Property("ParentKey"); + + b.Property("SeasonNumber"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("GrandparentKey"); + + b.ToTable("PlexEpisode"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ParentKey"); + + b.Property("PlexContentId"); + + b.Property("PlexServerContentId"); + + b.Property("SeasonKey"); + + b.Property("SeasonNumber"); + + b.HasKey("Id"); + + b.HasIndex("PlexServerContentId"); + + b.ToTable("PlexSeasonsContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexServerContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("ImdbId"); + + b.Property("Key"); + + b.Property("Quality"); + + b.Property("ReleaseYear"); + + b.Property("RequestId"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.Property("Type"); + + b.Property("Url"); + + b.HasKey("Id"); + + b.ToTable("PlexServerContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RadarrCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("HasFile"); + + b.Property("TheMovieDbId"); + + b.HasKey("Id"); + + b.ToTable("RadarrCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RecentlyAddedLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("AlbumId"); + + b.Property("ContentId"); + + b.Property("ContentType"); + + b.Property("EpisodeNumber"); + + b.Property("SeasonNumber"); + + b.Property("Type"); + + b.HasKey("Id"); + + b.ToTable("RecentlyAddedLog"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RequestQueue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Completed"); + + b.Property("Dts"); + + b.Property("Error"); + + b.Property("RequestId"); + + b.Property("RetryCount"); + + b.Property("Type"); + + b.HasKey("Id"); + + b.ToTable("RequestQueue"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RequestSubscription", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("RequestId"); + + b.Property("RequestType"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("RequestSubscription"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.AlbumRequest", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Approved"); + + b.Property("ArtistName"); + + b.Property("Available"); + + b.Property("Cover"); + + b.Property("Denied"); + + b.Property("DeniedReason"); + + b.Property("Disk"); + + b.Property("ForeignAlbumId"); + + b.Property("ForeignArtistId"); + + b.Property("MarkedAsApproved"); + + b.Property("MarkedAsAvailable"); + + b.Property("MarkedAsDenied"); + + b.Property("Rating"); + + b.Property("ReleaseDate"); + + b.Property("RequestType"); + + b.Property("RequestedByAlias"); + + b.Property("RequestedDate"); + + b.Property("RequestedUserId"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("AlbumRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Approved"); + + b.Property("Available"); + + b.Property("Denied"); + + b.Property("DeniedReason"); + + b.Property("IssueId"); + + b.Property("MarkedAsApproved"); + + b.Property("MarkedAsAvailable"); + + b.Property("MarkedAsDenied"); + + b.Property("ParentRequestId"); + + b.Property("RequestType"); + + b.Property("RequestedByAlias"); + + b.Property("RequestedDate"); + + b.Property("RequestedUserId"); + + b.Property("SeriesType"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("ParentRequestId"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("ChildRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueCategory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Value"); + + b.HasKey("Id"); + + b.ToTable("IssueCategory"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueComments", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Comment"); + + b.Property("Date"); + + b.Property("IssuesId"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("IssuesId"); + + b.HasIndex("UserId"); + + b.ToTable("IssueComments"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Description"); + + b.Property("IssueCategoryId"); + + b.Property("IssueId"); + + b.Property("ProviderId"); + + b.Property("RequestId"); + + b.Property("RequestType"); + + b.Property("ResovledDate"); + + b.Property("Status"); + + b.Property("Subject"); + + b.Property("Title"); + + b.Property("UserReportedId"); + + b.HasKey("Id"); + + b.HasIndex("IssueCategoryId"); + + b.HasIndex("IssueId"); + + b.HasIndex("UserReportedId"); + + b.ToTable("Issues"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Approved"); + + b.Property("Available"); + + b.Property("Background"); + + b.Property("Denied"); + + b.Property("DeniedReason"); + + b.Property("DigitalReleaseDate"); + + b.Property("ImdbId"); + + b.Property("IssueId"); + + b.Property("LangCode"); + + b.Property("MarkedAsApproved"); + + b.Property("MarkedAsAvailable"); + + b.Property("MarkedAsDenied"); + + b.Property("Overview"); + + b.Property("PosterPath"); + + b.Property("QualityOverride"); + + b.Property("ReleaseDate"); + + b.Property("RequestType"); + + b.Property("RequestedByAlias"); + + b.Property("RequestedDate"); + + b.Property("RequestedUserId"); + + b.Property("RootPathOverride"); + + b.Property("Status"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("MovieRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.RequestLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeCount"); + + b.Property("RequestDate"); + + b.Property("RequestId"); + + b.Property("RequestType"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("RequestLog"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.TvRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Background"); + + b.Property("ImdbId"); + + b.Property("Overview"); + + b.Property("PosterPath"); + + b.Property("QualityOverride"); + + b.Property("ReleaseDate"); + + b.Property("RootFolder"); + + b.Property("Status"); + + b.Property("Title"); + + b.Property("TotalSeasons"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("TvRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SickRageCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SickRageCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SickRageEpisodeCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeNumber"); + + b.Property("SeasonNumber"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SickRageEpisodeCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SonarrCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SonarrCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SonarrEpisodeCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeNumber"); + + b.Property("HasFile"); + + b.Property("SeasonNumber"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SonarrEpisodeCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Tokens", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Token"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Tokens"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.UserNotificationPreferences", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Agent"); + + b.Property("Enabled"); + + b.Property("UserId"); + + b.Property("Value"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("UserNotificationPreferences"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.UserQualityProfiles", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("RadarrQualityProfile"); + + b.Property("RadarrRootPath"); + + b.Property("SonarrQualityProfile"); + + b.Property("SonarrQualityProfileAnime"); + + b.Property("SonarrRootPath"); + + b.Property("SonarrRootPathAnime"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("UserQualityProfiles"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Votes", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Date"); + + b.Property("Deleted"); + + b.Property("RequestId"); + + b.Property("RequestType"); + + b.Property("UserId"); + + b.Property("VoteType"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Votes"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AirDate"); + + b.Property("Approved"); + + b.Property("Available"); + + b.Property("EpisodeNumber"); + + b.Property("Requested"); + + b.Property("SeasonId"); + + b.Property("Title"); + + b.Property("Url"); + + b.HasKey("Id"); + + b.HasIndex("SeasonId"); + + b.ToTable("EpisodeRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChildRequestId"); + + b.Property("SeasonNumber"); + + b.HasKey("Id"); + + b.HasIndex("ChildRequestId"); + + b.ToTable("SeasonRequests"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b => + { + b.HasOne("Ombi.Store.Entities.EmbyContent", "Series") + .WithMany("Episodes") + .HasForeignKey("ParentId") + .HasPrincipalKey("EmbyId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationUserId", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany("NotificationUserIds") + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b => + { + b.HasOne("Ombi.Store.Entities.PlexServerContent", "Series") + .WithMany("Episodes") + .HasForeignKey("GrandparentKey") + .HasPrincipalKey("Key") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b => + { + b.HasOne("Ombi.Store.Entities.PlexServerContent") + .WithMany("Seasons") + .HasForeignKey("PlexServerContentId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RequestSubscription", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.AlbumRequest", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b => + { + b.HasOne("Ombi.Store.Entities.Requests.TvRequests", "ParentRequest") + .WithMany("ChildRequests") + .HasForeignKey("ParentRequestId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueComments", b => + { + b.HasOne("Ombi.Store.Entities.Requests.Issues", "Issues") + .WithMany("Comments") + .HasForeignKey("IssuesId"); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b => + { + b.HasOne("Ombi.Store.Entities.Requests.IssueCategory", "IssueCategory") + .WithMany() + .HasForeignKey("IssueCategoryId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ombi.Store.Entities.Requests.ChildRequests") + .WithMany("Issues") + .HasForeignKey("IssueId"); + + b.HasOne("Ombi.Store.Entities.Requests.MovieRequests") + .WithMany("Issues") + .HasForeignKey("IssueId"); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "UserReported") + .WithMany() + .HasForeignKey("UserReportedId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.RequestLog", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Tokens", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.UserNotificationPreferences", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany("UserNotificationPreferences") + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.UserQualityProfiles", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Votes", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b => + { + b.HasOne("Ombi.Store.Repository.Requests.SeasonRequests", "Season") + .WithMany("Episodes") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b => + { + b.HasOne("Ombi.Store.Entities.Requests.ChildRequests", "ChildRequest") + .WithMany("SeasonRequests") + .HasForeignKey("ChildRequestId") + .OnDelete(DeleteBehavior.Cascade); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Ombi.Store/Migrations/20191016203035_RequestIdOnPlexContent.cs b/src/Ombi.Store/Migrations/20191016203035_RequestIdOnPlexContent.cs new file mode 100644 index 000000000..f197cdcdb --- /dev/null +++ b/src/Ombi.Store/Migrations/20191016203035_RequestIdOnPlexContent.cs @@ -0,0 +1,22 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Ombi.Store.Migrations +{ + public partial class RequestIdOnPlexContent : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "RequestId", + table: "PlexServerContent", + nullable: true); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "RequestId", + table: "PlexServerContent"); + } + } +} diff --git a/src/Ombi.Store/Migrations/External/20191018225833_PlexContentId.Designer.cs b/src/Ombi.Store/Migrations/External/20191018225833_PlexContentId.Designer.cs new file mode 100644 index 000000000..cba04a14c --- /dev/null +++ b/src/Ombi.Store/Migrations/External/20191018225833_PlexContentId.Designer.cs @@ -0,0 +1,314 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Ombi.Store.Context; + +namespace Ombi.Store.Migrations.External +{ + [DbContext(typeof(ExternalContext))] + [Migration("20191018225833_PlexContentId")] + partial class PlexContentId + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.2.2-servicing-10034"); + + modelBuilder.Entity("Ombi.Store.Entities.CouchPotatoCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TheMovieDbId"); + + b.HasKey("Id"); + + b.ToTable("CouchPotatoCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("EmbyId") + .IsRequired(); + + b.Property("ImdbId"); + + b.Property("ProviderId"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.Property("Type"); + + b.Property("Url"); + + b.HasKey("Id"); + + b.ToTable("EmbyContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("EmbyId"); + + b.Property("EpisodeNumber"); + + b.Property("ImdbId"); + + b.Property("ParentId"); + + b.Property("ProviderId"); + + b.Property("SeasonNumber"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.ToTable("EmbyEpisode"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.LidarrAlbumCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("ArtistId"); + + b.Property("ForeignAlbumId"); + + b.Property("Monitored"); + + b.Property("PercentOfTracks"); + + b.Property("ReleaseDate"); + + b.Property("Title"); + + b.Property("TrackCount"); + + b.HasKey("Id"); + + b.ToTable("LidarrAlbumCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.LidarrArtistCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ArtistId"); + + b.Property("ArtistName"); + + b.Property("ForeignArtistId"); + + b.Property("Monitored"); + + b.HasKey("Id"); + + b.ToTable("LidarrArtistCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeNumber"); + + b.Property("GrandparentKey"); + + b.Property("Key"); + + b.Property("ParentKey"); + + b.Property("SeasonNumber"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("GrandparentKey"); + + b.ToTable("PlexEpisode"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ParentKey"); + + b.Property("PlexContentId"); + + b.Property("PlexServerContentId"); + + b.Property("SeasonKey"); + + b.Property("SeasonNumber"); + + b.HasKey("Id"); + + b.HasIndex("PlexServerContentId"); + + b.ToTable("PlexSeasonsContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexServerContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("ImdbId"); + + b.Property("Key"); + + b.Property("Quality"); + + b.Property("ReleaseYear"); + + b.Property("RequestId"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.Property("Type"); + + b.Property("Url"); + + b.HasKey("Id"); + + b.ToTable("PlexServerContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RadarrCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("HasFile"); + + b.Property("TheMovieDbId"); + + b.HasKey("Id"); + + b.ToTable("RadarrCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SickRageCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SickRageCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SickRageEpisodeCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeNumber"); + + b.Property("SeasonNumber"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SickRageEpisodeCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SonarrCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SonarrCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SonarrEpisodeCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeNumber"); + + b.Property("HasFile"); + + b.Property("SeasonNumber"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SonarrEpisodeCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b => + { + b.HasOne("Ombi.Store.Entities.EmbyContent", "Series") + .WithMany("Episodes") + .HasForeignKey("ParentId") + .HasPrincipalKey("EmbyId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b => + { + b.HasOne("Ombi.Store.Entities.PlexServerContent", "Series") + .WithMany("Episodes") + .HasForeignKey("GrandparentKey") + .HasPrincipalKey("Key") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b => + { + b.HasOne("Ombi.Store.Entities.PlexServerContent") + .WithMany("Seasons") + .HasForeignKey("PlexServerContentId"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Ombi.Store/Migrations/External/20191018225833_PlexContentId.cs b/src/Ombi.Store/Migrations/External/20191018225833_PlexContentId.cs new file mode 100644 index 000000000..5d5e33dce --- /dev/null +++ b/src/Ombi.Store/Migrations/External/20191018225833_PlexContentId.cs @@ -0,0 +1,22 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Ombi.Store.Migrations.External +{ + public partial class PlexContentId : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "RequestId", + table: "PlexServerContent", + nullable: true); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "RequestId", + table: "PlexServerContent"); + } + } +} diff --git a/src/Ombi.Store/Migrations/External/ExternalContextModelSnapshot.cs b/src/Ombi.Store/Migrations/External/ExternalContextModelSnapshot.cs index 4e97b5514..77ef6b080 100644 --- a/src/Ombi.Store/Migrations/External/ExternalContextModelSnapshot.cs +++ b/src/Ombi.Store/Migrations/External/ExternalContextModelSnapshot.cs @@ -14,7 +14,7 @@ namespace Ombi.Store.Migrations.External { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "2.1.3-rtm-32065"); + .HasAnnotation("ProductVersion", "2.2.2-servicing-10034"); modelBuilder.Entity("Ombi.Store.Entities.CouchPotatoCache", b => { @@ -194,6 +194,8 @@ namespace Ombi.Store.Migrations.External b.Property("ReleaseYear"); + b.Property("RequestId"); + b.Property("TheMovieDbId"); b.Property("Title"); diff --git a/src/Ombi.Store/Migrations/OmbiContextModelSnapshot.cs b/src/Ombi.Store/Migrations/OmbiContextModelSnapshot.cs index 7e1907bed..7af47f592 100644 --- a/src/Ombi.Store/Migrations/OmbiContextModelSnapshot.cs +++ b/src/Ombi.Store/Migrations/OmbiContextModelSnapshot.cs @@ -455,6 +455,8 @@ namespace Ombi.Store.Migrations b.Property("ReleaseYear"); + b.Property("RequestId"); + b.Property("TheMovieDbId"); b.Property("Title"); diff --git a/src/Ombi.Store/Repository/BaseRepository.cs b/src/Ombi.Store/Repository/BaseRepository.cs index 82661e0c3..14b871a12 100644 --- a/src/Ombi.Store/Repository/BaseRepository.cs +++ b/src/Ombi.Store/Repository/BaseRepository.cs @@ -90,8 +90,8 @@ namespace Ombi.Store.Repository .WaitAndRetryAsync(new[] { TimeSpan.FromSeconds(1), - TimeSpan.FromSeconds(2), - TimeSpan.FromSeconds(3) + TimeSpan.FromSeconds(5), + TimeSpan.FromSeconds(10) }); var result = await policy.ExecuteAndCaptureAsync(async () => diff --git a/src/Ombi.TheMovieDbApi/IMovieDbApi.cs b/src/Ombi.TheMovieDbApi/IMovieDbApi.cs index a6976b7a0..c9e21c2ec 100644 --- a/src/Ombi.TheMovieDbApi/IMovieDbApi.cs +++ b/src/Ombi.TheMovieDbApi/IMovieDbApi.cs @@ -26,5 +26,7 @@ namespace Ombi.Api.TheMovieDb Task> DiscoverMovies(string langCode, int keywordId); Task GetFullMovieInfo(int movieId, CancellationToken cancellationToken, string langCode); Task GetCollection(string langCode, int collectionId, CancellationToken cancellationToken); + Task> SearchKeyword(string searchTerm); + Task GetKeyword(int keywordId); } } \ No newline at end of file diff --git a/src/Ombi.TheMovieDbApi/Models/Keyword.cs b/src/Ombi.TheMovieDbApi/Models/Keyword.cs new file mode 100644 index 000000000..770eebc94 --- /dev/null +++ b/src/Ombi.TheMovieDbApi/Models/Keyword.cs @@ -0,0 +1,13 @@ +using System.Runtime.Serialization; + +namespace Ombi.Api.TheMovieDb.Models +{ + public sealed class Keyword + { + [DataMember(Name = "id")] + public int Id { get; set; } + + [DataMember(Name = "name")] + public string Name { get; set; } + } +} diff --git a/src/Ombi.TheMovieDbApi/TheMovieDbApi.cs b/src/Ombi.TheMovieDbApi/TheMovieDbApi.cs index 68fdfb97a..c9deede55 100644 --- a/src/Ombi.TheMovieDbApi/TheMovieDbApi.cs +++ b/src/Ombi.TheMovieDbApi/TheMovieDbApi.cs @@ -1,31 +1,38 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; +using System.Linq; using System.Net; using System.Net.Http; using System.Threading; using System.Threading.Tasks; using AutoMapper; +using Nito.AsyncEx; using Ombi.Api.TheMovieDb.Models; +using Ombi.Core.Settings; +using Ombi.Core.Settings.Models.External; using Ombi.TheMovieDbApi.Models; namespace Ombi.Api.TheMovieDb { public class TheMovieDbApi : IMovieDbApi { - public TheMovieDbApi(IMapper mapper, IApi api) + public TheMovieDbApi(IMapper mapper, IApi api, ISettingsService settingsService) { Api = api; Mapper = mapper; + Settings = new AsyncLazy(() => settingsService.GetSettingsAsync()); } + private const string ApiToken = "b8eabaf5608b88d0298aa189dd90bf00"; + private const string BaseUri ="http://api.themoviedb.org/3/"; private IMapper Mapper { get; } - private readonly string ApiToken = "b8eabaf5608b88d0298aa189dd90bf00"; - private static readonly string BaseUri ="http://api.themoviedb.org/3/"; private IApi Api { get; } + private AsyncLazy Settings { get; } public async Task GetMovieInformation(int movieId) { var request = new Request($"movie/{movieId}", BaseUri, HttpMethod.Get); - request.FullUri = request.FullUri.AddQueryParameter("api_key", ApiToken); + request.AddQueryString("api_key", ApiToken); AddRetry(request); var result = await Api.Request(request); @@ -69,7 +76,7 @@ namespace Ombi.Api.TheMovieDb public async Task Find(string externalId, ExternalSource source) { var request = new Request($"find/{externalId}", BaseUri, HttpMethod.Get); - request.FullUri = request.FullUri.AddQueryParameter("api_key", ApiToken); + request.AddQueryString("api_key", ApiToken); AddRetry(request); request.AddQueryString("external_source", source.ToString()); @@ -80,9 +87,11 @@ namespace Ombi.Api.TheMovieDb public async Task> SearchByActor(string searchTerm, string langCode) { var request = new Request($"search/person", BaseUri, HttpMethod.Get); - request.FullUri = request.FullUri.AddQueryParameter("api_key", ApiToken); - request.FullUri = request.FullUri.AddQueryParameter("query", searchTerm); - request.FullUri = request.FullUri.AddQueryParameter("language", langCode); + request.AddQueryString("api_key", ApiToken); + request.AddQueryString("query", searchTerm); + request.AddQueryString("language", langCode); + var settings = await Settings; + request.AddQueryString("include_adult", settings.ShowAdultMovies.ToString().ToLower()); var result = await Api.Request>(request); return result; @@ -91,8 +100,8 @@ namespace Ombi.Api.TheMovieDb public async Task GetActorMovieCredits(int actorId, string langCode) { var request = new Request($"person/{actorId}/movie_credits", BaseUri, HttpMethod.Get); - request.FullUri = request.FullUri.AddQueryParameter("api_key", ApiToken); - request.FullUri = request.FullUri.AddQueryParameter("language", langCode); + request.AddQueryString("api_key", ApiToken); + request.AddQueryString("language", langCode); var result = await Api.Request(request); return result; @@ -101,8 +110,8 @@ namespace Ombi.Api.TheMovieDb public async Task> SearchTv(string searchTerm) { var request = new Request($"search/tv", BaseUri, HttpMethod.Get); - request.FullUri = request.FullUri.AddQueryParameter("api_key", ApiToken); - request.FullUri = request.FullUri.AddQueryParameter("query", searchTerm); + request.AddQueryString("api_key", ApiToken); + request.AddQueryString("query", searchTerm); AddRetry(request); var result = await Api.Request>(request); @@ -112,7 +121,7 @@ namespace Ombi.Api.TheMovieDb public async Task GetTvExternals(int theMovieDbId) { var request = new Request($"/tv/{theMovieDbId}/external_ids", BaseUri, HttpMethod.Get); - request.FullUri = request.FullUri.AddQueryParameter("api_key", ApiToken); + request.AddQueryString("api_key", ApiToken); AddRetry(request); return await Api.Request(request); @@ -121,9 +130,8 @@ namespace Ombi.Api.TheMovieDb public async Task> SimilarMovies(int movieId, string langCode) { var request = new Request($"movie/{movieId}/similar", BaseUri, HttpMethod.Get); - request.FullUri = request.FullUri.AddQueryParameter("api_key", ApiToken); - - request.FullUri = request.FullUri.AddQueryParameter("language", langCode); + request.AddQueryString("api_key", ApiToken); + request.AddQueryString("language", langCode); AddRetry(request); var result = await Api.Request>(request); @@ -133,81 +141,128 @@ namespace Ombi.Api.TheMovieDb public async Task GetMovieInformationWithExtraInfo(int movieId, string langCode = "en") { var request = new Request($"movie/{movieId}", BaseUri, HttpMethod.Get); - request.FullUri = request.FullUri.AddQueryParameter("api_key", ApiToken); - request.FullUri = request.FullUri.AddQueryParameter("append_to_response", "videos,release_dates"); - request.FullUri = request.FullUri.AddQueryParameter("language", langCode); + request.AddQueryString("api_key", ApiToken); + request.AddQueryString("append_to_response", "videos,release_dates"); + request.AddQueryString("language", langCode); AddRetry(request); var result = await Api.Request(request); return Mapper.Map(result); } - public async Task> SearchMovie(string searchTerm, int? year, string langageCode) + public async Task> SearchMovie(string searchTerm, int? year, string langCode) { var request = new Request($"search/movie", BaseUri, HttpMethod.Get); - request.FullUri = request.FullUri.AddQueryParameter("api_key", ApiToken); - request.FullUri = request.FullUri.AddQueryParameter("query", searchTerm); - request.FullUri = request.FullUri.AddQueryParameter("language", langageCode); + request.AddQueryString("api_key", ApiToken); + request.AddQueryString("query", searchTerm); + request.AddQueryString("language", langCode); if (year.HasValue && year.Value > 0) { - request.FullUri = request.FullUri.AddQueryParameter("year", year.Value.ToString()); + request.AddQueryString("year", year.Value.ToString()); } + + var settings = await Settings; + request.AddQueryString("include_adult", settings.ShowAdultMovies.ToString().ToLower()); + AddRetry(request); var result = await Api.Request>(request); return Mapper.Map>(result.results); } - public async Task> PopularMovies(string langageCode, int? page = null, CancellationToken cancellationToken = default(CancellationToken)) + /// + /// Maintains filter parity with /movie/popular. + /// + public async Task> PopularMovies(string langCode, int? page = null, CancellationToken cancellationToken = default(CancellationToken)) { - var request = new Request($"movie/popular", BaseUri, HttpMethod.Get); - request.FullUri = request.FullUri.AddQueryParameter("api_key", ApiToken); - request.FullUri = request.FullUri.AddQueryParameter("language", langageCode); + var request = new Request($"discover/movie", BaseUri, HttpMethod.Get); + request.AddQueryString("api_key", ApiToken); + request.AddQueryString("language", langCode); + request.AddQueryString("sort_by", "popularity.desc"); if (page != null) { - request.FullUri = request.FullUri.AddQueryParameter("page", page.ToString()); + request.AddQueryString("page", page.ToString()); } + await AddDiscoverMovieSettings(request); AddRetry(request); var result = await Api.Request>(request, cancellationToken); return Mapper.Map>(result.results); } - public async Task> TopRated(string langageCode, int? page = null) + /// + /// Maintains filter parity with /movie/top_rated. + /// + public async Task> TopRated(string langCode, int? page = null) { - var request = new Request($"movie/top_rated", BaseUri, HttpMethod.Get); - request.FullUri = request.FullUri.AddQueryParameter("api_key", ApiToken); - request.FullUri = request.FullUri.AddQueryParameter("language", langageCode); + var request = new Request($"discover/movie", BaseUri, HttpMethod.Get); + request.AddQueryString("api_key", ApiToken); + request.AddQueryString("language", langCode); + request.AddQueryString("sort_by", "vote_average.desc"); if (page != null) { - request.FullUri = request.FullUri.AddQueryParameter("page", page.ToString()); + request.AddQueryString("page", page.ToString()); } + + // `vote_count` consideration isn't explicitly documented, but using only the `sort_by` filter + // does not provide the same results as `/movie/top_rated`. This appears to be adequate enough + // to filter out extremely high-rated movies due to very little votes + request.AddQueryString("vote_count.gte", "250"); + + await AddDiscoverMovieSettings(request); AddRetry(request); var result = await Api.Request>(request); return Mapper.Map>(result.results); } - public async Task> Upcoming(string langageCode, int? page = null) + /// + /// Maintains filter parity with /movie/upcoming. + /// + public async Task> Upcoming(string langCode, int? page = null) { - var request = new Request($"movie/upcoming", BaseUri, HttpMethod.Get); - request.FullUri = request.FullUri.AddQueryParameter("api_key", ApiToken); - request.FullUri = request.FullUri.AddQueryParameter("language", langageCode); + var request = new Request($"discover/movie", BaseUri, HttpMethod.Get); + request.AddQueryString("api_key", ApiToken); + request.AddQueryString("language", langCode); + + // Release types "2 or 3" explicitly stated as used in docs + request.AddQueryString("with_release_type", "2|3"); + + // The date range being used in `/movie/upcoming` isn't documented, but we infer it is + // an offset from today based on the minimum and maximum date they provide in the output + var startDate = DateTime.Today.AddDays(7); + request.AddQueryString("release_date.gte", startDate.ToString("yyyy-MM-dd")); + request.AddQueryString("release_date.lte", startDate.AddDays(17).ToString("yyyy-MM-dd")); if (page != null) { - request.FullUri = request.FullUri.AddQueryParameter("page", page.ToString()); + request.AddQueryString("page", page.ToString()); } + await AddDiscoverMovieSettings(request); AddRetry(request); var result = await Api.Request>(request); return Mapper.Map>(result.results); } - public async Task> NowPlaying(string langageCode, int? page = null) + /// + /// Maintains filter parity with /movie/now_playing. + /// + public async Task> NowPlaying(string langCode, int? page = null) { - var request = new Request($"movie/now_playing", BaseUri, HttpMethod.Get); - request.FullUri = request.FullUri.AddQueryParameter("api_key", ApiToken); - request.FullUri = request.FullUri.AddQueryParameter("language", langageCode); + var request = new Request($"discover/movie", BaseUri, HttpMethod.Get); + request.AddQueryString("api_key", ApiToken); + request.AddQueryString("language", langCode); + + // Release types "2 or 3" explicitly stated as used in docs + request.AddQueryString("with_release_type", "2|3"); + + // The date range being used in `/movie/now_playing` isn't documented, but we infer it is + // an offset from today based on the minimum and maximum date they provide in the output + var today = DateTime.Today; + request.AddQueryString("release_date.gte", today.AddDays(-42).ToString("yyyy-MM-dd")); + request.AddQueryString("release_date.lte", today.AddDays(6).ToString("yyyy-MM-dd")); if (page != null) { - request.FullUri = request.FullUri.AddQueryParameter("page", page.ToString()); + request.AddQueryString("page", page.ToString()); } + + await AddDiscoverMovieSettings(request); AddRetry(request); var result = await Api.Request>(request); return Mapper.Map>(result.results); @@ -216,19 +271,51 @@ namespace Ombi.Api.TheMovieDb public async Task GetTVInfo(string themoviedbid) { var request = new Request($"/tv/{themoviedbid}", BaseUri, HttpMethod.Get); - request.FullUri = request.FullUri.AddQueryParameter("api_key", ApiToken); - request.FullUri = request.FullUri.AddQueryParameter("append_to_response", "external_ids"); + request.AddQueryString("api_key", ApiToken); + request.AddQueryString("append_to_response", "external_ids"); AddRetry(request); return await Api.Request(request); } + public async Task> SearchKeyword(string searchTerm) + { + var request = new Request("search/keyword", BaseUri, HttpMethod.Get); + request.AddQueryString("api_key", ApiToken); + request.AddQueryString("query", searchTerm); + AddRetry(request); + + var result = await Api.Request>(request); + return result.results ?? new List(); + } + + public async Task GetKeyword(int keywordId) + { + var request = new Request($"keyword/{keywordId}", BaseUri, HttpMethod.Get); + request.AddQueryString("api_key", ApiToken); + AddRetry(request); + + var keyword = await Api.Request(request); + return keyword == null || keyword.Id == 0 ? null : keyword; + } + + private async Task AddDiscoverMovieSettings(Request request) + { + var settings = await Settings; + request.AddQueryString("include_adult", settings.ShowAdultMovies.ToString().ToLower()); + if (settings.ExcludedKeywordIds?.Any() == true) + { + request.AddQueryString("without_keywords", string.Join(",", settings.ExcludedKeywordIds)); + } + } + + public async Task> MultiSearch(string searchTerm, string languageCode, CancellationToken cancellationToken) { var request = new Request("search/multi", BaseUri, HttpMethod.Get); - request.FullUri = request.FullUri.AddQueryParameter("api_key", ApiToken); - request.FullUri = request.FullUri.AddQueryParameter("language", languageCode); - request.FullUri = request.FullUri.AddQueryParameter("query", searchTerm); + request.AddQueryString("api_key", ApiToken); + request.AddQueryString("language", languageCode); + request.AddQueryString("query", searchTerm); var result = await Api.Request>(request, cancellationToken); return result; } diff --git a/src/Ombi/ClientApp/package.json b/src/Ombi/ClientApp/package.json index a9358e516..5567b840e 100644 --- a/src/Ombi/ClientApp/package.json +++ b/src/Ombi/ClientApp/package.json @@ -48,6 +48,7 @@ "moment": "^2.23.0", "ng2-cookies": "^1.0.12", "ngx-bootstrap": "^3.1.4", + "ngx-chips": "^2.1.0", "ngx-clipboard": "^12.1.0", "ngx-infinite-scroll": "^7.1.0", "ngx-moment": "^3.0.1", diff --git a/src/Ombi/ClientApp/src/app/app.component.html b/src/Ombi/ClientApp/src/app/app.component.html index 635693765..e356c06d2 100644 --- a/src/Ombi/ClientApp/src/app/app.component.html +++ b/src/Ombi/ClientApp/src/app/app.component.html @@ -127,6 +127,9 @@
  • English
  • +
  • + Español +
  • Français
  • @@ -139,9 +142,6 @@
  • Italiano
  • -
  • - Español -
  • Nederlands
  • @@ -157,6 +157,9 @@
  • Svenska
  • +
  • + Slovak +
  • diff --git a/src/Ombi/ClientApp/src/app/app.component.ts b/src/Ombi/ClientApp/src/app/app.component.ts index 732c94d47..06d18b394 100644 --- a/src/Ombi/ClientApp/src/app/app.component.ts +++ b/src/Ombi/ClientApp/src/app/app.component.ts @@ -59,7 +59,7 @@ export class AppComponent implements OnInit { // __webpack_public_path__ = window['base-href'] - this.translate.addLangs(["en", "de", "fr", "da", "es", "it", "nl", "sv", "no", "pl", "pt"]); + this.translate.addLangs(["en", "de", "fr", "da", "es", "it", "nl", "sk", "sv", "no", "pl", "pt"]); const selectedLang = this.storage.get("Language"); @@ -70,7 +70,7 @@ export class AppComponent implements OnInit { } else { // See if we can match the supported langs with the current browser lang const browserLang: string = translate.getBrowserLang(); - this.translate.use(browserLang.match(/en|fr|da|de|es|it|nl|sv|no|pl|pt/) ? browserLang : "en"); + this.translate.use(browserLang.match(/en|fr|da|de|es|it|nl|sk|sv|no|pl|pt/) ? browserLang : "en"); } } diff --git a/src/Ombi/ClientApp/src/app/interfaces/IMovieDb.ts b/src/Ombi/ClientApp/src/app/interfaces/IMovieDb.ts new file mode 100644 index 000000000..63443ae4c --- /dev/null +++ b/src/Ombi/ClientApp/src/app/interfaces/IMovieDb.ts @@ -0,0 +1,4 @@ +export interface IMovieDbKeyword { + id: number; + name: string; +} diff --git a/src/Ombi/ClientApp/src/app/interfaces/ISettings.ts b/src/Ombi/ClientApp/src/app/interfaces/ISettings.ts index bf000dbaa..3038debac 100644 --- a/src/Ombi/ClientApp/src/app/interfaces/ISettings.ts +++ b/src/Ombi/ClientApp/src/app/interfaces/ISettings.ts @@ -246,3 +246,8 @@ export interface IVoteSettings extends ISettings { musicVoteMax: number; tvShowVoteMax: number; } + +export interface ITheMovieDbSettings extends ISettings { + showAdultMovies: boolean; + excludedKeywordIds: number[]; +} diff --git a/src/Ombi/ClientApp/src/app/interfaces/index.ts b/src/Ombi/ClientApp/src/app/interfaces/index.ts index 25e73355d..8c0bf9cad 100644 --- a/src/Ombi/ClientApp/src/app/interfaces/index.ts +++ b/src/Ombi/ClientApp/src/app/interfaces/index.ts @@ -3,6 +3,7 @@ export * from "./ICouchPotato"; export * from "./IImages"; export * from "./IMediaServerStatus"; export * from "./INotificationSettings"; +export * from "./IMovieDb"; export * from "./IPlex"; export * from "./IRadarr"; export * from "./IRequestEngineResult"; diff --git a/src/Ombi/ClientApp/src/app/login/login.component.ts b/src/Ombi/ClientApp/src/app/login/login.component.ts index 534cd08e3..91d3acf5f 100644 --- a/src/Ombi/ClientApp/src/app/login/login.component.ts +++ b/src/Ombi/ClientApp/src/app/login/login.component.ts @@ -24,14 +24,6 @@ import { MatSnackBar } from "@angular/material"; }) export class LoginComponent implements OnDestroy, OnInit { - public get appName(): string { - if (this.customizationSettings.applicationName) { - return this.customizationSettings.applicationName; - } else { - return "Ombi"; - } - } - public form: FormGroup; public customizationSettings: ICustomizationSettings; public authenticationSettings: IAuthenticationSettings; diff --git a/src/Ombi/ClientApp/src/app/search/moviesearch.component.html b/src/Ombi/ClientApp/src/app/search/moviesearch.component.html index ed669e0f5..30507d004 100644 --- a/src/Ombi/ClientApp/src/app/search/moviesearch.component.html +++ b/src/Ombi/ClientApp/src/app/search/moviesearch.component.html @@ -1,30 +1,41 @@ 
    + +
    + + + +
    +
    +
    - +
    -
    - - - +
    +
    -
    + +
    + +
    +
    diff --git a/src/Ombi/ClientApp/src/app/search/search.component.scss b/src/Ombi/ClientApp/src/app/search/search.component.scss index b8339bd82..52ff70f0b 100644 --- a/src/Ombi/ClientApp/src/app/search/search.component.scss +++ b/src/Ombi/ClientApp/src/app/search/search.component.scss @@ -3,7 +3,7 @@ padding-top: 5% } .form-control-search { - width: 77%; + padding-right: 165px; } } diff --git a/src/Ombi/ClientApp/src/app/search/tvsearch.component.html b/src/Ombi/ClientApp/src/app/search/tvsearch.component.html index 5975abb0d..86ee7c24c 100644 --- a/src/Ombi/ClientApp/src/app/search/tvsearch.component.html +++ b/src/Ombi/ClientApp/src/app/search/tvsearch.component.html @@ -1,32 +1,44 @@ 
    diff --git a/src/Ombi/ClientApp/src/app/services/applications/index.ts b/src/Ombi/ClientApp/src/app/services/applications/index.ts index 295a53415..28edca151 100644 --- a/src/Ombi/ClientApp/src/app/services/applications/index.ts +++ b/src/Ombi/ClientApp/src/app/services/applications/index.ts @@ -7,3 +7,4 @@ export * from "./tester.service"; export * from "./plexoauth.service"; export * from "./plextv.service"; export * from "./lidarr.service"; +export * from "./themoviedb.service"; diff --git a/src/Ombi/ClientApp/src/app/services/applications/themoviedb.service.ts b/src/Ombi/ClientApp/src/app/services/applications/themoviedb.service.ts new file mode 100644 index 000000000..677034143 --- /dev/null +++ b/src/Ombi/ClientApp/src/app/services/applications/themoviedb.service.ts @@ -0,0 +1,25 @@ +import { APP_BASE_HREF } from "@angular/common"; +import { HttpClient, HttpErrorResponse, HttpParams } from "@angular/common/http"; +import { Injectable, Inject } from "@angular/core"; +import { empty, Observable, throwError } from "rxjs"; +import { catchError } from "rxjs/operators"; + +import { IMovieDbKeyword } from "../../interfaces"; +import { ServiceHelpers } from "../service.helpers"; + +@Injectable() +export class TheMovieDbService extends ServiceHelpers { + constructor(http: HttpClient, @Inject(APP_BASE_HREF) href:string) { + super(http, "/api/v1/TheMovieDb", href); + } + + public getKeywords(searchTerm: string): Observable { + const params = new HttpParams().set("searchTerm", searchTerm); + return this.http.get(`${this.url}/Keywords`, {headers: this.headers, params}); + } + + public getKeyword(keywordId: number): Observable { + return this.http.get(`${this.url}/Keywords/${keywordId}`, { headers: this.headers }) + .pipe(catchError((error: HttpErrorResponse) => error.status === 404 ? empty() : throwError(error))); + } +} diff --git a/src/Ombi/ClientApp/src/app/services/settings.service.ts b/src/Ombi/ClientApp/src/app/services/settings.service.ts index 184977838..fb50f3e89 100644 --- a/src/Ombi/ClientApp/src/app/services/settings.service.ts +++ b/src/Ombi/ClientApp/src/app/services/settings.service.ts @@ -32,6 +32,7 @@ import { ISlackNotificationSettings, ISonarrSettings, ITelegramNotifcationSettings, + ITheMovieDbSettings, IUpdateSettings, IUserManagementSettings, IVoteSettings, @@ -301,6 +302,14 @@ export class SettingsService extends ServiceHelpers { return this.http.post(`${this.url}/vote`, JSON.stringify(settings), {headers: this.headers}); } + public getTheMovieDbSettings(): Observable { + return this.http.get(`${this.url}/themoviedb`, {headers: this.headers}); + } + + public saveTheMovieDbSettings(settings: ITheMovieDbSettings) { + return this.http.post(`${this.url}/themoviedb`, JSON.stringify(settings), {headers: this.headers}); + } + public getNewsletterSettings(): Observable { return this.http.get(`${this.url}/notifications/newsletter`, {headers: this.headers}); } diff --git a/src/Ombi/ClientApp/src/app/settings/settings.module.ts b/src/Ombi/ClientApp/src/app/settings/settings.module.ts index 0420df48c..b137ba377 100644 --- a/src/Ombi/ClientApp/src/app/settings/settings.module.ts +++ b/src/Ombi/ClientApp/src/app/settings/settings.module.ts @@ -3,13 +3,14 @@ import { NgModule } from "@angular/core"; import { FormsModule, ReactiveFormsModule } from "@angular/forms"; import { RouterModule, Routes } from "@angular/router"; import { NgbAccordionModule, NgbModule } from "@ng-bootstrap/ng-bootstrap"; +import { TagInputModule } from "ngx-chips"; import { ClipboardModule } from "ngx-clipboard"; import { AuthGuard } from "../auth/auth.guard"; import { AuthService } from "../auth/auth.service"; import { CouchPotatoService, EmbyService, IssuesService, JobService, LidarrService, MobileService, NotificationMessageService, PlexService, RadarrService, - RequestRetryService, SonarrService, TesterService, ValidationService, SystemService, FileDownloadService, + RequestRetryService, SonarrService, TesterService, ValidationService, SystemService, FileDownloadService, TheMovieDbService } from "../services"; import { PipeModule } from "../pipes/pipe.module"; @@ -41,6 +42,7 @@ import { PlexComponent } from "./plex/plex.component"; import { RadarrComponent } from "./radarr/radarr.component"; import { SickRageComponent } from "./sickrage/sickrage.component"; import { SonarrComponent } from "./sonarr/sonarr.component"; +import { TheMovieDbComponent } from "./themoviedb/themoviedb.component"; import { UpdateComponent } from "./update/update.component"; import { UserManagementComponent } from "./usermanagement/usermanagement.component"; import { VoteComponent } from "./vote/vote.component"; @@ -84,6 +86,7 @@ const routes: Routes = [ { path: "Newsletter", component: NewsletterComponent, canActivate: [AuthGuard] }, { path: "Lidarr", component: LidarrComponent, canActivate: [AuthGuard] }, { path: "Vote", component: VoteComponent, canActivate: [AuthGuard] }, + { path: "TheMovieDb", component: TheMovieDbComponent, canActivate: [AuthGuard] }, { path: "FailedRequests", component: FailedRequestsComponent, canActivate: [AuthGuard] }, { path: "Logs", component: LogsComponent, canActivate: [AuthGuard] }, ]; @@ -102,6 +105,7 @@ const routes: Routes = [ NgbAccordionModule, AutoCompleteModule, CalendarModule, + TagInputModule, ClipboardModule, PipeModule, RadioButtonModule, @@ -142,6 +146,7 @@ const routes: Routes = [ NewsletterComponent, LidarrComponent, VoteComponent, + TheMovieDbComponent, FailedRequestsComponent, LogsComponent, ], @@ -166,7 +171,8 @@ const routes: Routes = [ RequestRetryService, HubService, SystemService, - FileDownloadService + FileDownloadService, + TheMovieDbService, ], }) diff --git a/src/Ombi/ClientApp/src/app/settings/settingsmenu.component.html b/src/Ombi/ClientApp/src/app/settings/settingsmenu.component.html index 0dc8b859a..41a5c5d66 100644 --- a/src/Ombi/ClientApp/src/app/settings/settingsmenu.component.html +++ b/src/Ombi/ClientApp/src/app/settings/settingsmenu.component.html @@ -8,6 +8,7 @@ + diff --git a/src/Ombi/ClientApp/src/app/settings/themoviedb/themoviedb.component.html b/src/Ombi/ClientApp/src/app/settings/themoviedb/themoviedb.component.html new file mode 100644 index 000000000..ccadc68ac --- /dev/null +++ b/src/Ombi/ClientApp/src/app/settings/themoviedb/themoviedb.component.html @@ -0,0 +1,50 @@ + + +
    + The Movie Database +
    +
    +
    + + +
    +
    + +
    + + + + + {{item.id}} +  ({{item.name}}) + + + + + + +
    + +
    +
    + +
    +
    +
    +
    \ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/settings/themoviedb/themoviedb.component.ts b/src/Ombi/ClientApp/src/app/settings/themoviedb/themoviedb.component.ts new file mode 100644 index 000000000..4fc9138a0 --- /dev/null +++ b/src/Ombi/ClientApp/src/app/settings/themoviedb/themoviedb.component.ts @@ -0,0 +1,71 @@ +import { Component, OnInit } from "@angular/core"; +import { empty, of } from "rxjs"; + +import { ITheMovieDbSettings } from "../../interfaces"; +import { NotificationService } from "../../services"; +import { SettingsService } from "../../services"; +import { TheMovieDbService } from "../../services"; + +interface IKeywordTag { + id: number; + name: string; + initial: boolean; +} + +@Component({ + templateUrl: "./themoviedb.component.html", +}) +export class TheMovieDbComponent implements OnInit { + + public settings: ITheMovieDbSettings; + public excludedKeywords: IKeywordTag[]; + + constructor(private settingsService: SettingsService, + private notificationService: NotificationService, + private tmdbService: TheMovieDbService) { } + + public ngOnInit() { + this.settingsService.getTheMovieDbSettings().subscribe(settings => { + this.settings = settings; + this.excludedKeywords = settings.excludedKeywordIds + ? settings.excludedKeywordIds.map(id => ({ + id, + name: "", + initial: true, + })) + : []; + }); + } + + public autocompleteKeyword = (text: string) => this.tmdbService.getKeywords(text); + + public onAddingKeyword = (tag: string | IKeywordTag) => { + if (typeof tag === "string") { + const id = Number(tag); + return isNaN(id) ? empty() : this.tmdbService.getKeyword(id); + } else { + return of(tag); + } + } + + public onKeywordSelect = (keyword: IKeywordTag) => { + if (keyword.initial) { + this.tmdbService.getKeyword(keyword.id) + .subscribe(k => { + keyword.name = k.name; + keyword.initial = false; + }); + } + } + + public save() { + this.settings.excludedKeywordIds = this.excludedKeywords.map(k => k.id); + this.settingsService.saveTheMovieDbSettings(this.settings).subscribe(x => { + if (x) { + this.notificationService.success("Successfully saved The Movie Database settings"); + } else { + this.notificationService.success("There was an error when saving The Movie Database settings"); + } + }); + } +} diff --git a/src/Ombi/ClientApp/yarn.lock b/src/Ombi/ClientApp/yarn.lock index c4dc3f0ec..a9e6911d9 100644 --- a/src/Ombi/ClientApp/yarn.lock +++ b/src/Ombi/ClientApp/yarn.lock @@ -3917,10 +3917,25 @@ ng2-cookies@^1.0.12: version "1.0.12" resolved "https://registry.yarnpkg.com/ng2-cookies/-/ng2-cookies-1.0.12.tgz#3f3e613e0137b0649b705c678074b4bd08149ccc" +ng2-material-dropdown@0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/ng2-material-dropdown/-/ng2-material-dropdown-0.11.0.tgz#27a402ef3cbdcaf6791ef4cfd4b257e31db7546f" + integrity sha512-wptBo09qKecY0QPTProAThrc4A3ajJTcHE9LTpCG5XZZUhXLBzhnGK8OW33TN8A+K/jqcs7OB74ppYJiqs3nhQ== + dependencies: + tslib "^1.9.0" + ngx-bootstrap@^3.1.4: version "3.1.4" resolved "https://registry.yarnpkg.com/ngx-bootstrap/-/ngx-bootstrap-3.1.4.tgz#5105c0227da3b51a1972d04efa1504a79474fd57" +ngx-chips@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/ngx-chips/-/ngx-chips-2.1.0.tgz#aa299bcf40dc3e1f6288bf1d29e2fdfe9a132ed3" + integrity sha512-OQV4dTfD3nXm5d2mGKUSgwOtJOaMnZ4F+lwXOtd7DWRSUne0JQWwoZNHdOpuS6saBGhqCPDAwq6KxdR5XSgZUQ== + dependencies: + ng2-material-dropdown "0.11.0" + tslib "^1.9.0" + ngx-clipboard@^12.1.0: version "12.1.0" resolved "https://registry.yarnpkg.com/ngx-clipboard/-/ngx-clipboard-12.1.0.tgz#41d10c9d031d5d6e854f8c21c85460c96685b10b" diff --git a/src/Ombi/Controllers/External/TheMovieDbController.cs b/src/Ombi/Controllers/External/TheMovieDbController.cs new file mode 100644 index 000000000..714831633 --- /dev/null +++ b/src/Ombi/Controllers/External/TheMovieDbController.cs @@ -0,0 +1,38 @@ +using Microsoft.AspNetCore.Mvc; +using Ombi.Api.TheMovieDb; +using Ombi.Api.TheMovieDb.Models; +using Ombi.Attributes; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Ombi.Controllers.External +{ + [Admin] + [ApiV1] + [Produces("application/json")] + public sealed class TheMovieDbController : Controller + { + public TheMovieDbController(IMovieDbApi tmdbApi) => TmdbApi = tmdbApi; + + private IMovieDbApi TmdbApi { get; } + + /// + /// Searches for keywords matching the specified term. + /// + /// The search term. + [HttpGet("Keywords")] + public async Task> GetKeywords([FromQuery]string searchTerm) => + await TmdbApi.SearchKeyword(searchTerm); + + /// + /// Gets the keyword matching the specified ID. + /// + /// The keyword ID. + [HttpGet("Keywords/{keywordId}")] + public async Task GetKeywords(int keywordId) + { + var keyword = await TmdbApi.GetKeyword(keywordId); + return keyword == null ? NotFound() : (IActionResult)Ok(keyword); + } + } +} diff --git a/src/Ombi/Controllers/V1/SettingsController.cs b/src/Ombi/Controllers/V1/SettingsController.cs index dd8f66a9e..c61a0d2ba 100644 --- a/src/Ombi/Controllers/V1/SettingsController.cs +++ b/src/Ombi/Controllers/V1/SettingsController.cs @@ -683,6 +683,25 @@ namespace Ombi.Controllers.V1 return vote.Enabled; } + /// + /// Save The Movie DB settings. + /// + /// The settings. + [HttpPost("themoviedb")] + public async Task TheMovieDbSettings([FromBody]TheMovieDbSettings settings) + { + return await Save(settings); + } + + /// + /// Get The Movie DB settings. + /// + [HttpGet("themoviedb")] + public async Task TheMovieDbSettings() + { + return await Get(); + } + /// /// Saves the email notification settings. /// diff --git a/src/Ombi/Startup.cs b/src/Ombi/Startup.cs index 8eff5486e..7f55c5a74 100644 --- a/src/Ombi/Startup.cs +++ b/src/Ombi/Startup.cs @@ -30,6 +30,7 @@ using Ombi.Store.Context; using Ombi.Store.Entities; using Ombi.Store.Repository; using Serilog; +using SQLitePCL; using System; using System.IO; using ILogger = Serilog.ILogger; @@ -101,8 +102,7 @@ namespace Ombi x.UseActivator(new IoCJobActivator(services.BuildServiceProvider())); }); - - SQLitePCL.raw.sqlite3_config(2); + SQLitePCL.raw.sqlite3_config(raw.SQLITE_CONFIG_MULTITHREAD); services.AddCors(o => o.AddPolicy("MyPolicy", builder => { diff --git a/src/Ombi/wwwroot/translations/sk.json b/src/Ombi/wwwroot/translations/sk.json new file mode 100644 index 000000000..d7c3e376a --- /dev/null +++ b/src/Ombi/wwwroot/translations/sk.json @@ -0,0 +1,186 @@ +{ + "Login": { + "SignInButton": "Prihláste sa", + "UsernamePlaceholder": "Užívateľské meno", + "PasswordPlaceholder": "Heslo", + "RememberMe": "Zapamätať prihlásenie", + "ForgottenPassword": "Zabudli ste heslo?", + "Errors": { + "IncorrectCredentials": "Nesprávne meno alebo heslo" + } + }, + "Common": { + "ContinueButton": "Pokračovať", + "Available": "Dostupné", + "PartiallyAvailable": "Čiastočne dostupné", + "Monitored": "Sledované", + "NotAvailable": "Nie je k dispozícii", + "ProcessingRequest": "Spracovávanie požiadavky", + "PendingApproval": "Čaká na schválenie", + "RequestDenied": "Požiadavka zamietnutá", + "NotRequested": "Nepožiadané", + "Requested": "Požiadané", + "Request": "Požiadať", + "Denied": "Zamietnuté", + "Approve": "Schválené", + "PartlyAvailable": "Čiastočne dostupné", + "Errors": { + "Validation": "Skontrolujte zadaný obsah" + } + }, + "PasswordReset": { + "EmailAddressPlaceholder": "Emailová adresa", + "ResetPasswordButton": "Nastaviť nové heslo" + }, + "LandingPage": { + "OnlineHeading": "Momentálne online", + "OnlineParagraph": "Mediálny server je momentálne online", + "PartiallyOnlineHeading": "Čiastočne online", + "PartiallyOnlineParagraph": "Mediálny server je momentálne online.", + "MultipleServersUnavailable": "Momntálne je {{serversUnavailable}} serverov offline z {{totalServers}}.", + "SingleServerUnavailable": "Momntálne je {{serversUnavailable}} server offline z {{totalServers}}.", + "OfflineHeading": "Momentálne offline", + "OfflineParagraph": "Mediálny server je momentálne offline.", + "CheckPageForUpdates": "Prezrite túto stránku pre aktualizácie." + }, + "NavigationBar": { + "Search": "Hľadať", + "Requests": "Požiadavky", + "UserManagement": "Správa užívateľov", + "Issues": "Problémy", + "Vote": "Hlasovať", + "Donate": "Prispieť!", + "DonateLibraryMaintainer": "Darovať správcovi knižnice", + "DonateTooltip": "Takto som presvedčil svoju manželku, aby mi umožnila tráviť svoj voľný čas vývojom Ombi ;)", + "UpdateAvailableTooltip": "K dispozícii je aktualizácia!", + "Settings": "Nastavenie", + "Welcome": "Vitaj {{username}}", + "UpdateDetails": "Aktualizovať údaje", + "Logout": "Odhlásiť sa", + "OpenMobileApp": "Otvoriť mobilnú aplikáciu", + "RecentlyAdded": "Nedávno pridané" + }, + "Search": { + "Title": "Hľadať", + "Paragraph": "Chcete sledovať niečo, čo v súčasnosti nie je k dispozícii? Žiadny problém. Vyhľadajte to nižšie a požiadajte o to!", + "MoviesTab": "Filmy", + "TvTab": "Seriály", + "MusicTab": "Hudba", + "Suggestions": "Návrhy", + "NoResults": "Ľutujeme, nenašli sme žiadne výsledky!", + "DigitalDate": "Online vydanie: {{date}}", + "TheatricalRelease": "Kino vydanie: {{date}}", + "ViewOnPlex": "Zobraziť na Plex", + "ViewOnEmby": "Zobraziť na Emby", + "RequestAdded": "Žiadosť o {{title}} bola úspešne pridaná", + "Similar": "Podobné", + "Refine": "Filtrovať", + "SearchBarPlaceholder": "Tu zadajte pre vyhľadávanie", + "Movies": { + "PopularMovies": "Populárne filmy", + "UpcomingMovies": "Očakávané filmy", + "TopRatedMovies": "Najlepšie hodnotené filmy", + "NowPlayingMovies": "Teraz prehrávané filmy", + "HomePage": "Úvodná stránka", + "Trailer": "Upútavka" + }, + "TvShows": { + "Popular": "Populárne", + "Trending": "Trendy", + "MostWatched": "Najsledovanejšie", + "MostAnticipated": "Najočakávanejšie", + "Results": "Výsledky", + "AirDate": "Dátum vysielania:", + "AllSeasons": "Všetky série", + "FirstSeason": "Prvá séria", + "LatestSeason": "Posledná séria", + "Select": "Vybrať ...", + "SubmitRequest": "Poslať žiadosť", + "Season": "Séria: {{seasonNumber}}", + "SelectAllInSeason": "Vybrať všetko v danej sérii {{seasonNumber}}" + } + }, + "Requests": { + "Title": "Požiadavky", + "Paragraph": "Nižšie nájdete svoje a všetky ďalšie žiadosti, ako aj stav ich sťahovania a schvaľovania.", + "MoviesTab": "Filmy", + "TvTab": "Seriály", + "MusicTab": "Hudba", + "RequestedBy": "Vyžiadané od:", + "Status": "Stav:", + "RequestStatus": "Stav požiadavky:", + "Denied": " Zamietnuté:", + "TheatricalRelease": "Kino vydanie: {{date}}", + "ReleaseDate": "Vydané: {{date}}", + "TheatricalReleaseSort": "Kino vydanie", + "DigitalRelease": "Online vydanie: {{date}}", + "RequestDate": "Dátum požiadavky:", + "QualityOverride": "Prepísanie kvality:", + "RootFolderOverride": "Prepísanie Root priečinku:", + "ChangeRootFolder": "Koreňový priečinok", + "ChangeQualityProfile": "Profil kvality", + "MarkUnavailable": "Označiť nedostupné", + "MarkAvailable": "Označiť dostupné", + "Remove": "Odstrániť", + "Deny": "Odmietnuť", + "Season": "Séria:", + "GridTitle": "Názov", + "AirDate": "Dátum vysielania", + "GridStatus": "Stav", + "ReportIssue": "Nahlásiť problém", + "Filter": "Filter", + "Sort": "Triediť", + "SeasonNumberHeading": "Séria: {seasonNumber}", + "SortTitleAsc": "Názov ▲", + "SortTitleDesc": "Názov ▼", + "SortRequestDateAsc": "Dátum požiadavky ▲", + "SortRequestDateDesc": "Dátum požiadavky ▼", + "SortStatusAsc": "Stav ▲", + "SortStatusDesc": "Stav ▼", + "Remaining": { + "Quota": "{{remaining}}/{{total}} zostávajúce žiadosti", + "NextDays": "Ďalšia žiadosť bude pridaná o {{time}} dní", + "NextHours": "Ďalšia žiadosť bude pridaná o {{time}} hodín", + "NextMinutes": "Ďalšia žiadosť bude pridaná o {{time}} minút", + "NextMinute": "Ďalšia žiadosť bude pridaná o {{time}} minútu" + } + }, + "Issues": { + "Title": "Problémy", + "PendingTitle": "Nevyriešené problémy", + "InProgressTitle": "Riešené problémy", + "ResolvedTitle": "Vyiešené problémy", + "ColumnTitle": "Názov", + "Category": "Kategória", + "Status": "Stav", + "Details": "Podrobnosti", + "Description": "Popis", + "NoComments": "Žiadne komentáre!", + "MarkInProgress": "Označiť ako prebiehajúce", + "MarkResolved": "Označiť ako vyriešené", + "SendMessageButton": "Odoslať", + "Subject": "Predmet", + "Comments": "Komentáre", + "WriteMessagePlaceholder": "Napíšte správu sem...", + "ReportedBy": "Nahlásené od" + }, + "Filter": { + "ClearFilter": "Vymazať filter", + "FilterHeaderAvailability": "Dostupnosť", + "FilterHeaderRequestStatus": "Stav", + "Approved": "Schválené", + "PendingApproval": "Čaká na schválenie" + }, + "UserManagment": { + "TvRemaining": "TV: {{remaining}}/{{total}} zostávajúce", + "MovieRemaining": "Filmy: {{remaining}}/{{total}} zostávajúce", + "MusicRemaining": "Hudba: {{remaining}}/{{total}} zostávajúce", + "TvDue": "TV: {{date}}", + "MovieDue": "Film: {{date}}", + "MusicDue": "Hudba: {{date}}" + }, + "Votes": { + "CompletedVotesTab": "Hlasované", + "VotesTab": "Potrebné hlasovanie" + } +}