mirror of
https://github.com/Ombi-app/Ombi.git
synced 2025-07-16 02:02:55 -07:00
Merged develop with V4
This commit is contained in:
commit
b07833e0e8
48 changed files with 2333 additions and 346 deletions
|
@ -1,6 +1,6 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
## (unreleased)
|
## v3.0.4817 (2019-10-15)
|
||||||
|
|
||||||
### **New Features**
|
### **New Features**
|
||||||
|
|
||||||
|
|
|
@ -93,24 +93,7 @@ namespace Ombi.Api
|
||||||
public void AddQueryString(string key, string value)
|
public void AddQueryString(string key, string value)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(key) || string.IsNullOrEmpty(value)) return;
|
if (string.IsNullOrEmpty(key) || string.IsNullOrEmpty(value)) return;
|
||||||
|
_modified = FullUri.AddQueryParameter(key, value);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddJsonBody(object obj)
|
public void AddJsonBody(object obj)
|
||||||
|
|
|
@ -206,7 +206,6 @@ namespace Ombi.DependencyInjection
|
||||||
services.AddTransient<IEmbyAvaliabilityChecker, EmbyAvaliabilityChecker>();
|
services.AddTransient<IEmbyAvaliabilityChecker, EmbyAvaliabilityChecker>();
|
||||||
services.AddTransient<IPlexEpisodeSync, PlexEpisodeSync>();
|
services.AddTransient<IPlexEpisodeSync, PlexEpisodeSync>();
|
||||||
services.AddTransient<IPlexAvailabilityChecker, PlexAvailabilityChecker>();
|
services.AddTransient<IPlexAvailabilityChecker, PlexAvailabilityChecker>();
|
||||||
services.AddTransient<IJobSetup, JobSetup>();
|
|
||||||
services.AddTransient<IRadarrSync, RadarrSync>();
|
services.AddTransient<IRadarrSync, RadarrSync>();
|
||||||
services.AddTransient<ISonarrSync, SonarrSync>();
|
services.AddTransient<ISonarrSync, SonarrSync>();
|
||||||
services.AddTransient<IOmbiAutomaticUpdater, OmbiAutomaticUpdater>();
|
services.AddTransient<IOmbiAutomaticUpdater, OmbiAutomaticUpdater>();
|
||||||
|
|
7
src/Ombi.Schedule/JobDataKeys.cs
Normal file
7
src/Ombi.Schedule/JobDataKeys.cs
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
namespace Ombi.Schedule
|
||||||
|
{
|
||||||
|
public class JobDataKeys
|
||||||
|
{
|
||||||
|
public const string RecentlyAddedSearch = "recentlyAddedSearch";
|
||||||
|
}
|
||||||
|
}
|
|
@ -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> 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> _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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -36,6 +36,7 @@ using Ombi.Core.Notifications;
|
||||||
using Ombi.Helpers;
|
using Ombi.Helpers;
|
||||||
using Ombi.Hubs;
|
using Ombi.Hubs;
|
||||||
using Ombi.Notifications.Models;
|
using Ombi.Notifications.Models;
|
||||||
|
using Ombi.Schedule.Jobs.Ombi;
|
||||||
using Ombi.Store.Entities;
|
using Ombi.Store.Entities;
|
||||||
using Ombi.Store.Repository;
|
using Ombi.Store.Repository;
|
||||||
using Ombi.Store.Repository.Requests;
|
using Ombi.Store.Repository.Requests;
|
||||||
|
@ -70,6 +71,8 @@ namespace Ombi.Schedule.Jobs.Emby
|
||||||
await ProcessMovies();
|
await ProcessMovies();
|
||||||
await ProcessTv();
|
await ProcessTv();
|
||||||
|
|
||||||
|
await OmbiQuartz.TriggerJob(nameof(IRefreshMetadata), "System");
|
||||||
|
|
||||||
await _notification.Clients.Clients(NotificationHub.AdminConnectionIds)
|
await _notification.Clients.Clients(NotificationHub.AdminConnectionIds)
|
||||||
.SendAsync(NotificationHub.NotificationEvent, "Emby Availability Checker Finished");
|
.SendAsync(NotificationHub.NotificationEvent, "Emby Availability Checker Finished");
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,7 +67,6 @@ namespace Ombi.Schedule.Jobs.Emby
|
||||||
// Episodes
|
// Episodes
|
||||||
|
|
||||||
await OmbiQuartz.TriggerJob(nameof(IEmbyEpisodeSync), "Emby");
|
await OmbiQuartz.TriggerJob(nameof(IEmbyEpisodeSync), "Emby");
|
||||||
await OmbiQuartz.TriggerJob(nameof(IRefreshMetadata), "System");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -51,7 +51,7 @@ namespace Ombi.Schedule.Jobs.Lidarr
|
||||||
var cachedAlbum = await _cachedAlbums.FirstOrDefaultAsync(x => x.ForeignAlbumId.Equals(request.ForeignAlbumId));
|
var cachedAlbum = await _cachedAlbums.FirstOrDefaultAsync(x => x.ForeignAlbumId.Equals(request.ForeignAlbumId));
|
||||||
if (cachedAlbum != null)
|
if (cachedAlbum != null)
|
||||||
{
|
{
|
||||||
if (cachedAlbum.FullyAvailable)
|
if (cachedAlbum.FullyAvailable) // ensure we have all tracks
|
||||||
{
|
{
|
||||||
request.Available = true;
|
request.Available = true;
|
||||||
request.MarkedAsAvailable = DateTime.Now;
|
request.MarkedAsAvailable = DateTime.Now;
|
||||||
|
|
|
@ -5,6 +5,5 @@ namespace Ombi.Schedule.Jobs.Ombi
|
||||||
{
|
{
|
||||||
public interface IRefreshMetadata : IBaseJob
|
public interface IRefreshMetadata : IBaseJob
|
||||||
{
|
{
|
||||||
Task ProcessPlexServerContent(IEnumerable<int> contentIds);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -728,7 +728,8 @@ namespace Ombi.Schedule.Jobs.Ombi
|
||||||
(key, g) => new
|
(key, g) => new
|
||||||
{
|
{
|
||||||
SeasonNumber = key,
|
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 orderedEpisodes = epInformation.Episodes.OrderBy(x => x.EpisodeNumber).ToList();
|
||||||
var episodeString = StringHelper.BuildEpisodeList(orderedEpisodes.Select(x => x.EpisodeNumber));
|
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("<br />");
|
finalsb.Append("<br />");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -851,7 +853,8 @@ namespace Ombi.Schedule.Jobs.Ombi
|
||||||
(key, g) => new
|
(key, g) => new
|
||||||
{
|
{
|
||||||
SeasonNumber = key,
|
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 orderedEpisodes = epInformation.Episodes.OrderBy(x => x.EpisodeNumber).ToList();
|
||||||
var episodeString = StringHelper.BuildEpisodeList(orderedEpisodes.Select(x => x.EpisodeNumber));
|
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("<br />");
|
finalsb.Append("<br />");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -61,7 +61,7 @@ namespace Ombi.Schedule.Jobs.Ombi
|
||||||
{
|
{
|
||||||
await StartPlex();
|
await StartPlex();
|
||||||
}
|
}
|
||||||
|
|
||||||
var embySettings = await _embySettings.GetSettingsAsync();
|
var embySettings = await _embySettings.GetSettingsAsync();
|
||||||
if (embySettings.Enable)
|
if (embySettings.Enable)
|
||||||
{
|
{
|
||||||
|
@ -81,44 +81,16 @@ namespace Ombi.Schedule.Jobs.Ombi
|
||||||
.SendAsync(NotificationHub.NotificationEvent, "Metadata Refresh Finished");
|
.SendAsync(NotificationHub.NotificationEvent, "Metadata Refresh Finished");
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task ProcessPlexServerContent(IEnumerable<int> 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<int> 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()
|
private async Task StartPlex()
|
||||||
{
|
{
|
||||||
|
// Ensure we check that we have not linked this item to a request
|
||||||
var allMovies = _plexRepo.GetAll().Where(x =>
|
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);
|
await StartPlexMovies(allMovies);
|
||||||
|
|
||||||
// Now Tv
|
// Now Tv
|
||||||
var allTv = _plexRepo.GetAll().Where(x =>
|
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);
|
await StartPlexTv(allTv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,9 +102,13 @@ namespace Ombi.Schedule.Jobs.Ombi
|
||||||
|
|
||||||
private async Task StartPlexTv(IQueryable<PlexServerContent> allTv)
|
private async Task StartPlexTv(IQueryable<PlexServerContent> allTv)
|
||||||
{
|
{
|
||||||
var tvCount = 0;
|
|
||||||
foreach (var show in allTv)
|
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 hasImdb = show.ImdbId.HasValue();
|
||||||
var hasTheMovieDb = show.TheMovieDbId.HasValue();
|
var hasTheMovieDb = show.TheMovieDbId.HasValue();
|
||||||
var hasTvDbId = show.TvDbId.HasValue();
|
var hasTvDbId = show.TvDbId.HasValue();
|
||||||
|
@ -156,21 +132,15 @@ namespace Ombi.Schedule.Jobs.Ombi
|
||||||
show.TvDbId = id;
|
show.TvDbId = id;
|
||||||
_plexRepo.UpdateWithoutSave(show);
|
_plexRepo.UpdateWithoutSave(show);
|
||||||
}
|
}
|
||||||
tvCount++;
|
await _plexRepo.SaveChangesAsync();
|
||||||
if (tvCount >= 75)
|
|
||||||
{
|
|
||||||
await _plexRepo.SaveChangesAsync();
|
|
||||||
tvCount = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
await _plexRepo.SaveChangesAsync();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task StartEmbyTv()
|
private async Task StartEmbyTv()
|
||||||
{
|
{
|
||||||
var allTv = _embyRepo.GetAll().Where(x =>
|
var allTv = _embyRepo.GetAll().Where(x =>
|
||||||
x.Type == EmbyMediaType.Series && (!x.TheMovieDbId.HasValue() || !x.ImdbId.HasValue() || !x.TvDbId.HasValue()));
|
x.Type == EmbyMediaType.Series && (!x.TheMovieDbId.HasValue() || !x.ImdbId.HasValue() || !x.TvDbId.HasValue()));
|
||||||
var tvCount = 0;
|
|
||||||
foreach (var show in allTv)
|
foreach (var show in allTv)
|
||||||
{
|
{
|
||||||
var hasImdb = show.ImdbId.HasValue();
|
var hasImdb = show.ImdbId.HasValue();
|
||||||
|
@ -196,21 +166,20 @@ namespace Ombi.Schedule.Jobs.Ombi
|
||||||
show.TvDbId = id;
|
show.TvDbId = id;
|
||||||
_embyRepo.UpdateWithoutSave(show);
|
_embyRepo.UpdateWithoutSave(show);
|
||||||
}
|
}
|
||||||
tvCount++;
|
|
||||||
if (tvCount >= 75)
|
await _embyRepo.SaveChangesAsync();
|
||||||
{
|
|
||||||
await _embyRepo.SaveChangesAsync();
|
|
||||||
tvCount = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
await _embyRepo.SaveChangesAsync();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task StartPlexMovies(IQueryable<PlexServerContent> allMovies)
|
private async Task StartPlexMovies(IQueryable<PlexServerContent> allMovies)
|
||||||
{
|
{
|
||||||
int movieCount = 0;
|
|
||||||
foreach (var movie in allMovies)
|
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 hasImdb = movie.ImdbId.HasValue();
|
||||||
var hasTheMovieDb = movie.TheMovieDbId.HasValue();
|
var hasTheMovieDb = movie.TheMovieDbId.HasValue();
|
||||||
// Movies don't really use TheTvDb
|
// Movies don't really use TheTvDb
|
||||||
|
@ -227,26 +196,19 @@ namespace Ombi.Schedule.Jobs.Ombi
|
||||||
movie.TheMovieDbId = id;
|
movie.TheMovieDbId = id;
|
||||||
_plexRepo.UpdateWithoutSave(movie);
|
_plexRepo.UpdateWithoutSave(movie);
|
||||||
}
|
}
|
||||||
movieCount++;
|
|
||||||
if (movieCount >= 75)
|
|
||||||
{
|
|
||||||
await _plexRepo.SaveChangesAsync();
|
|
||||||
movieCount = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await _plexRepo.SaveChangesAsync();
|
await _plexRepo.SaveChangesAsync();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task StartEmbyMovies(EmbySettings settings)
|
private async Task StartEmbyMovies(EmbySettings settings)
|
||||||
{
|
{
|
||||||
var allMovies = _embyRepo.GetAll().Where(x =>
|
var allMovies = _embyRepo.GetAll().Where(x =>
|
||||||
x.Type == EmbyMediaType.Movie && (!x.TheMovieDbId.HasValue() || !x.ImdbId.HasValue()));
|
x.Type == EmbyMediaType.Movie && (!x.TheMovieDbId.HasValue() || !x.ImdbId.HasValue()));
|
||||||
int movieCount = 0;
|
|
||||||
foreach (var movie in allMovies)
|
foreach (var movie in allMovies)
|
||||||
{
|
{
|
||||||
movie.ImdbId.HasValue();
|
movie.ImdbId.HasValue();
|
||||||
movie.TheMovieDbId.HasValue();
|
movie.TheMovieDbId.HasValue();
|
||||||
// Movies don't really use TheTvDb
|
// Movies don't really use TheTvDb
|
||||||
|
|
||||||
// Check if it even has 1 ID
|
// Check if it even has 1 ID
|
||||||
|
@ -255,6 +217,8 @@ namespace Ombi.Schedule.Jobs.Ombi
|
||||||
// Ok this sucks,
|
// Ok this sucks,
|
||||||
// The only think I can think that has happened is that we scanned Emby before Emby has got the metadata
|
// 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
|
// 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");
|
_log.LogInformation($"Movie {movie.Title} does not have a ImdbId or TheMovieDbId, so rechecking emby");
|
||||||
foreach (var server in settings.Servers)
|
foreach (var server in settings.Servers)
|
||||||
{
|
{
|
||||||
|
@ -286,15 +250,10 @@ namespace Ombi.Schedule.Jobs.Ombi
|
||||||
movie.TheMovieDbId = id;
|
movie.TheMovieDbId = id;
|
||||||
_embyRepo.UpdateWithoutSave(movie);
|
_embyRepo.UpdateWithoutSave(movie);
|
||||||
}
|
}
|
||||||
movieCount++;
|
|
||||||
if (movieCount >= 75)
|
|
||||||
{
|
|
||||||
await _embyRepo.SaveChangesAsync();
|
|
||||||
movieCount = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await _embyRepo.SaveChangesAsync();
|
await _embyRepo.SaveChangesAsync();
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<string> GetTheMovieDbId(bool hasTvDbId, bool hasImdb, string tvdbID, string imdbId, string title, bool movie)
|
private async Task<string> 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);
|
var result = await _movieApi.GetTvExternals(id);
|
||||||
|
|
||||||
return result.tvdb_id.ToString();
|
return result.tvdb_id.ToString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -55,6 +55,7 @@ namespace Ombi.Schedule.Jobs.Plex
|
||||||
await _notification.Clients.Clients(NotificationHub.AdminConnectionIds)
|
await _notification.Clients.Clients(NotificationHub.AdminConnectionIds)
|
||||||
.SendAsync(NotificationHub.NotificationEvent, "Plex Availability Check Failed");
|
.SendAsync(NotificationHub.NotificationEvent, "Plex Availability Check Failed");
|
||||||
_log.LogError(e, "Exception thrown in Plex availbility checker");
|
_log.LogError(e, "Exception thrown in Plex availbility checker");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await _notification.Clients.Clients(NotificationHub.AdminConnectionIds)
|
await _notification.Clients.Clients(NotificationHub.AdminConnectionIds)
|
||||||
|
@ -84,7 +85,7 @@ namespace Ombi.Schedule.Jobs.Plex
|
||||||
{
|
{
|
||||||
useTvDb = true;
|
useTvDb = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
var tvDbId = child.ParentRequest.TvDbId;
|
var tvDbId = child.ParentRequest.TvDbId;
|
||||||
var imdbId = child.ParentRequest.ImdbId;
|
var imdbId = child.ParentRequest.ImdbId;
|
||||||
IQueryable<PlexEpisode> seriesEpisodes = null;
|
IQueryable<PlexEpisode> seriesEpisodes = null;
|
||||||
|
@ -92,7 +93,7 @@ namespace Ombi.Schedule.Jobs.Plex
|
||||||
{
|
{
|
||||||
seriesEpisodes = plexEpisodes.Where(x => x.Series.ImdbId == imdbId.ToString());
|
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());
|
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
|
// Let's try and match the series by name
|
||||||
seriesEpisodes = plexEpisodes.Where(x =>
|
seriesEpisodes = plexEpisodes.Where(x =>
|
||||||
x.Series.Title.Equals(child.Title, StringComparison.CurrentCultureIgnoreCase) &&
|
x.Series.Title.Equals(child.Title, StringComparison.InvariantCultureIgnoreCase) &&
|
||||||
x.Series.ReleaseYear == child.ParentRequest.ReleaseDate.Year.ToString());
|
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));
|
var allAvailable = child.SeasonRequests.All(x => x.Episodes.All(c => c.Available));
|
||||||
if (allAvailable)
|
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.Available = true;
|
||||||
child.MarkedAsAvailable = DateTime.Now;
|
child.MarkedAsAvailable = DateTime.Now;
|
||||||
_backgroundJobClient.Enqueue(() => _notificationService.Publish(new NotificationOptions
|
await _notificationService.Publish(new NotificationOptions
|
||||||
{
|
{
|
||||||
DateTime = DateTime.Now,
|
DateTime = DateTime.Now,
|
||||||
NotificationType = NotificationType.RequestAvailable,
|
NotificationType = NotificationType.RequestAvailable,
|
||||||
RequestId = child.Id,
|
RequestId = child.Id,
|
||||||
RequestType = RequestType.TvShow,
|
RequestType = RequestType.TvShow,
|
||||||
Recipient = child.RequestedUser.Email
|
Recipient = child.RequestedUser.Email
|
||||||
}));
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -178,20 +180,22 @@ namespace Ombi.Schedule.Jobs.Plex
|
||||||
|
|
||||||
movie.Available = true;
|
movie.Available = true;
|
||||||
movie.MarkedAsAvailable = DateTime.Now;
|
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,
|
||||||
DateTime = DateTime.Now,
|
RequestId = movie.Id,
|
||||||
NotificationType = NotificationType.RequestAvailable,
|
RequestType = RequestType.Movie,
|
||||||
RequestId = movie.Id,
|
Recipient = movie.RequestedUser != null ? movie.RequestedUser.Email : string.Empty
|
||||||
RequestType = RequestType.Movie,
|
});
|
||||||
Recipient = movie.RequestedUser != null ? movie.RequestedUser.Email : string.Empty
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await _movieRepo.Save();
|
await _movieRepo.Save();
|
||||||
|
await _repo.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool _disposed;
|
private bool _disposed;
|
||||||
|
|
|
@ -72,7 +72,7 @@ namespace Ombi.Schedule.Jobs.Plex
|
||||||
public async Task Execute(IJobExecutionContext context)
|
public async Task Execute(IJobExecutionContext context)
|
||||||
{
|
{
|
||||||
JobDataMap dataMap = context.JobDetail.JobDataMap;
|
JobDataMap dataMap = context.JobDetail.JobDataMap;
|
||||||
var recentlyAddedSearch = dataMap.GetBooleanValueFromString("recentlyAddedSearch");
|
var recentlyAddedSearch = dataMap.GetBooleanValueFromString(JobDataKeys.RecentlyAddedSearch);
|
||||||
|
|
||||||
var plexSettings = await Plex.GetSettingsAsync();
|
var plexSettings = await Plex.GetSettingsAsync();
|
||||||
if (!plexSettings.Enable)
|
if (!plexSettings.Enable)
|
||||||
|
@ -89,7 +89,9 @@ namespace Ombi.Schedule.Jobs.Plex
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var processedContent = new ProcessedContent();
|
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
|
try
|
||||||
{
|
{
|
||||||
if (recentlyAddedSearch)
|
if (recentlyAddedSearch)
|
||||||
|
@ -111,12 +113,13 @@ namespace Ombi.Schedule.Jobs.Plex
|
||||||
if (!recentlyAddedSearch)
|
if (!recentlyAddedSearch)
|
||||||
{
|
{
|
||||||
Logger.LogInformation("Starting EP Cacher");
|
Logger.LogInformation("Starting EP Cacher");
|
||||||
|
|
||||||
await OmbiQuartz.TriggerJob(nameof(IPlexEpisodeSync), "Plex");
|
await OmbiQuartz.TriggerJob(nameof(IPlexEpisodeSync), "Plex");
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((processedContent?.HasProcessedContent ?? false) && recentlyAddedSearch)
|
if ((processedContent?.HasProcessedContent ?? false) && recentlyAddedSearch)
|
||||||
{
|
{
|
||||||
|
Logger.LogInformation("Kicking off Plex Availability Checker");
|
||||||
|
await OmbiQuartz.TriggerJob(nameof(IPlexAvailabilityChecker), "Plex");
|
||||||
Logger.LogInformation("Starting Metadata refresh");
|
Logger.LogInformation("Starting Metadata refresh");
|
||||||
// Just check what we send it
|
// Just check what we send it
|
||||||
await OmbiQuartz.TriggerJob(nameof(IRefreshMetadata), "System");
|
await OmbiQuartz.TriggerJob(nameof(IRefreshMetadata), "System");
|
||||||
|
|
|
@ -60,7 +60,7 @@ namespace Ombi.Schedule
|
||||||
|
|
||||||
private static async Task AddSystem(JobSettings s)
|
private static async Task AddSystem(JobSettings s)
|
||||||
{
|
{
|
||||||
await OmbiQuartz.Instance.AddJob<IRefreshMetadata>(nameof(IRefreshMetadata), "System", JobSettingsHelper.RefreshMetadata(s));
|
await OmbiQuartz.Instance.AddJob<IRefreshMetadata>(nameof(IRefreshMetadata), "System", null);
|
||||||
await OmbiQuartz.Instance.AddJob<IIssuesPurge>(nameof(IIssuesPurge), "System", JobSettingsHelper.IssuePurge(s));
|
await OmbiQuartz.Instance.AddJob<IIssuesPurge>(nameof(IIssuesPurge), "System", JobSettingsHelper.IssuePurge(s));
|
||||||
//OmbiQuartz.Instance.AddJob<IOmbiAutomaticUpdater>(nameof(IOmbiAutomaticUpdater), "System", JobSettingsHelper.Updater(s));
|
//OmbiQuartz.Instance.AddJob<IOmbiAutomaticUpdater>(nameof(IOmbiAutomaticUpdater), "System", JobSettingsHelper.Updater(s));
|
||||||
await OmbiQuartz.Instance.AddJob<INewsletterJob>(nameof(INewsletterJob), "System", JobSettingsHelper.Newsletter(s));
|
await OmbiQuartz.Instance.AddJob<INewsletterJob>(nameof(INewsletterJob), "System", JobSettingsHelper.Newsletter(s));
|
||||||
|
@ -82,7 +82,7 @@ namespace Ombi.Schedule
|
||||||
private static async Task AddPlex(JobSettings s)
|
private static async Task AddPlex(JobSettings s)
|
||||||
{
|
{
|
||||||
await OmbiQuartz.Instance.AddJob<IPlexContentSync>(nameof(IPlexContentSync), "Plex", JobSettingsHelper.PlexContent(s), new Dictionary<string, string> { { "recentlyAddedSearch", "false" } });
|
await OmbiQuartz.Instance.AddJob<IPlexContentSync>(nameof(IPlexContentSync), "Plex", JobSettingsHelper.PlexContent(s), new Dictionary<string, string> { { "recentlyAddedSearch", "false" } });
|
||||||
await OmbiQuartz.Instance.AddJob<IPlexContentSync>(nameof(IPlexContentSync) + "RecentlyAdded", "Plex", JobSettingsHelper.PlexRecentlyAdded(s), new Dictionary<string, string> { { "recentlyAddedSearch", "true" } });
|
await OmbiQuartz.Instance.AddJob<IPlexContentSync>(nameof(IPlexContentSync) + "RecentlyAdded", "Plex", JobSettingsHelper.PlexRecentlyAdded(s), new Dictionary<string, string> { { JobDataKeys.RecentlyAddedSearch, "true" } });
|
||||||
await OmbiQuartz.Instance.AddJob<IPlexUserImporter>(nameof(IPlexUserImporter), "Plex", JobSettingsHelper.UserImporter(s));
|
await OmbiQuartz.Instance.AddJob<IPlexUserImporter>(nameof(IPlexUserImporter), "Plex", JobSettingsHelper.UserImporter(s));
|
||||||
await OmbiQuartz.Instance.AddJob<IPlexEpisodeSync>(nameof(IPlexEpisodeSync), "Plex", null);
|
await OmbiQuartz.Instance.AddJob<IPlexEpisodeSync>(nameof(IPlexEpisodeSync), "Plex", null);
|
||||||
await OmbiQuartz.Instance.AddJob<IPlexAvailabilityChecker>(nameof(IPlexAvailabilityChecker), "Plex", null);
|
await OmbiQuartz.Instance.AddJob<IPlexAvailabilityChecker>(nameof(IPlexAvailabilityChecker), "Plex", null);
|
||||||
|
|
11
src/Ombi.Settings/Settings/Models/External/TheMovieDbSettings.cs
vendored
Normal file
11
src/Ombi.Settings/Settings/Models/External/TheMovieDbSettings.cs
vendored
Normal file
|
@ -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<int> ExcludedKeywordIds { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -53,6 +53,8 @@ namespace Ombi.Store.Entities
|
||||||
public DateTime AddedAt { get; set; }
|
public DateTime AddedAt { get; set; }
|
||||||
public string Quality { get; set; }
|
public string Quality { get; set; }
|
||||||
|
|
||||||
|
public int? RequestId { get; set; }
|
||||||
|
|
||||||
[NotMapped]
|
[NotMapped]
|
||||||
public bool HasImdb => !string.IsNullOrEmpty(ImdbId);
|
public bool HasImdb => !string.IsNullOrEmpty(ImdbId);
|
||||||
|
|
||||||
|
|
1216
src/Ombi.Store/Migrations/20191016203035_RequestIdOnPlexContent.Designer.cs
generated
Normal file
1216
src/Ombi.Store/Migrations/20191016203035_RequestIdOnPlexContent.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load diff
|
@ -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<int>(
|
||||||
|
name: "RequestId",
|
||||||
|
table: "PlexServerContent",
|
||||||
|
nullable: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "RequestId",
|
||||||
|
table: "PlexServerContent");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
314
src/Ombi.Store/Migrations/External/20191018225833_PlexContentId.Designer.cs
generated
vendored
Normal file
314
src/Ombi.Store/Migrations/External/20191018225833_PlexContentId.Designer.cs
generated
vendored
Normal file
|
@ -0,0 +1,314 @@
|
||||||
|
// <auto-generated />
|
||||||
|
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<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<int>("TheMovieDbId");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("CouchPotatoCache");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.EmbyContent", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<DateTime>("AddedAt");
|
||||||
|
|
||||||
|
b.Property<string>("EmbyId")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Property<string>("ImdbId");
|
||||||
|
|
||||||
|
b.Property<string>("ProviderId");
|
||||||
|
|
||||||
|
b.Property<string>("TheMovieDbId");
|
||||||
|
|
||||||
|
b.Property<string>("Title");
|
||||||
|
|
||||||
|
b.Property<string>("TvDbId");
|
||||||
|
|
||||||
|
b.Property<int>("Type");
|
||||||
|
|
||||||
|
b.Property<string>("Url");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("EmbyContent");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<DateTime>("AddedAt");
|
||||||
|
|
||||||
|
b.Property<string>("EmbyId");
|
||||||
|
|
||||||
|
b.Property<int>("EpisodeNumber");
|
||||||
|
|
||||||
|
b.Property<string>("ImdbId");
|
||||||
|
|
||||||
|
b.Property<string>("ParentId");
|
||||||
|
|
||||||
|
b.Property<string>("ProviderId");
|
||||||
|
|
||||||
|
b.Property<int>("SeasonNumber");
|
||||||
|
|
||||||
|
b.Property<string>("TheMovieDbId");
|
||||||
|
|
||||||
|
b.Property<string>("Title");
|
||||||
|
|
||||||
|
b.Property<string>("TvDbId");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("ParentId");
|
||||||
|
|
||||||
|
b.ToTable("EmbyEpisode");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.LidarrAlbumCache", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<DateTime>("AddedAt");
|
||||||
|
|
||||||
|
b.Property<int>("ArtistId");
|
||||||
|
|
||||||
|
b.Property<string>("ForeignAlbumId");
|
||||||
|
|
||||||
|
b.Property<bool>("Monitored");
|
||||||
|
|
||||||
|
b.Property<decimal>("PercentOfTracks");
|
||||||
|
|
||||||
|
b.Property<DateTime>("ReleaseDate");
|
||||||
|
|
||||||
|
b.Property<string>("Title");
|
||||||
|
|
||||||
|
b.Property<int>("TrackCount");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("LidarrAlbumCache");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.LidarrArtistCache", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<int>("ArtistId");
|
||||||
|
|
||||||
|
b.Property<string>("ArtistName");
|
||||||
|
|
||||||
|
b.Property<string>("ForeignArtistId");
|
||||||
|
|
||||||
|
b.Property<bool>("Monitored");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("LidarrArtistCache");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<int>("EpisodeNumber");
|
||||||
|
|
||||||
|
b.Property<int>("GrandparentKey");
|
||||||
|
|
||||||
|
b.Property<int>("Key");
|
||||||
|
|
||||||
|
b.Property<int>("ParentKey");
|
||||||
|
|
||||||
|
b.Property<int>("SeasonNumber");
|
||||||
|
|
||||||
|
b.Property<string>("Title");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("GrandparentKey");
|
||||||
|
|
||||||
|
b.ToTable("PlexEpisode");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<int>("ParentKey");
|
||||||
|
|
||||||
|
b.Property<int>("PlexContentId");
|
||||||
|
|
||||||
|
b.Property<int?>("PlexServerContentId");
|
||||||
|
|
||||||
|
b.Property<int>("SeasonKey");
|
||||||
|
|
||||||
|
b.Property<int>("SeasonNumber");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("PlexServerContentId");
|
||||||
|
|
||||||
|
b.ToTable("PlexSeasonsContent");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.PlexServerContent", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<DateTime>("AddedAt");
|
||||||
|
|
||||||
|
b.Property<string>("ImdbId");
|
||||||
|
|
||||||
|
b.Property<int>("Key");
|
||||||
|
|
||||||
|
b.Property<string>("Quality");
|
||||||
|
|
||||||
|
b.Property<string>("ReleaseYear");
|
||||||
|
|
||||||
|
b.Property<int?>("RequestId");
|
||||||
|
|
||||||
|
b.Property<string>("TheMovieDbId");
|
||||||
|
|
||||||
|
b.Property<string>("Title");
|
||||||
|
|
||||||
|
b.Property<string>("TvDbId");
|
||||||
|
|
||||||
|
b.Property<int>("Type");
|
||||||
|
|
||||||
|
b.Property<string>("Url");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("PlexServerContent");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.RadarrCache", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<bool>("HasFile");
|
||||||
|
|
||||||
|
b.Property<int>("TheMovieDbId");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("RadarrCache");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.SickRageCache", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<int>("TvDbId");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("SickRageCache");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.SickRageEpisodeCache", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<int>("EpisodeNumber");
|
||||||
|
|
||||||
|
b.Property<int>("SeasonNumber");
|
||||||
|
|
||||||
|
b.Property<int>("TvDbId");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("SickRageEpisodeCache");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.SonarrCache", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<int>("TvDbId");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("SonarrCache");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Ombi.Store.Entities.SonarrEpisodeCache", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<int>("EpisodeNumber");
|
||||||
|
|
||||||
|
b.Property<bool>("HasFile");
|
||||||
|
|
||||||
|
b.Property<int>("SeasonNumber");
|
||||||
|
|
||||||
|
b.Property<int>("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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
22
src/Ombi.Store/Migrations/External/20191018225833_PlexContentId.cs
vendored
Normal file
22
src/Ombi.Store/Migrations/External/20191018225833_PlexContentId.cs
vendored
Normal file
|
@ -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<int>(
|
||||||
|
name: "RequestId",
|
||||||
|
table: "PlexServerContent",
|
||||||
|
nullable: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "RequestId",
|
||||||
|
table: "PlexServerContent");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,7 +14,7 @@ namespace Ombi.Store.Migrations.External
|
||||||
{
|
{
|
||||||
#pragma warning disable 612, 618
|
#pragma warning disable 612, 618
|
||||||
modelBuilder
|
modelBuilder
|
||||||
.HasAnnotation("ProductVersion", "2.1.3-rtm-32065");
|
.HasAnnotation("ProductVersion", "2.2.2-servicing-10034");
|
||||||
|
|
||||||
modelBuilder.Entity("Ombi.Store.Entities.CouchPotatoCache", b =>
|
modelBuilder.Entity("Ombi.Store.Entities.CouchPotatoCache", b =>
|
||||||
{
|
{
|
||||||
|
@ -194,6 +194,8 @@ namespace Ombi.Store.Migrations.External
|
||||||
|
|
||||||
b.Property<string>("ReleaseYear");
|
b.Property<string>("ReleaseYear");
|
||||||
|
|
||||||
|
b.Property<int?>("RequestId");
|
||||||
|
|
||||||
b.Property<string>("TheMovieDbId");
|
b.Property<string>("TheMovieDbId");
|
||||||
|
|
||||||
b.Property<string>("Title");
|
b.Property<string>("Title");
|
||||||
|
|
|
@ -455,6 +455,8 @@ namespace Ombi.Store.Migrations
|
||||||
|
|
||||||
b.Property<string>("ReleaseYear");
|
b.Property<string>("ReleaseYear");
|
||||||
|
|
||||||
|
b.Property<int?>("RequestId");
|
||||||
|
|
||||||
b.Property<string>("TheMovieDbId");
|
b.Property<string>("TheMovieDbId");
|
||||||
|
|
||||||
b.Property<string>("Title");
|
b.Property<string>("Title");
|
||||||
|
|
|
@ -90,8 +90,8 @@ namespace Ombi.Store.Repository
|
||||||
.WaitAndRetryAsync(new[]
|
.WaitAndRetryAsync(new[]
|
||||||
{
|
{
|
||||||
TimeSpan.FromSeconds(1),
|
TimeSpan.FromSeconds(1),
|
||||||
TimeSpan.FromSeconds(2),
|
TimeSpan.FromSeconds(5),
|
||||||
TimeSpan.FromSeconds(3)
|
TimeSpan.FromSeconds(10)
|
||||||
});
|
});
|
||||||
|
|
||||||
var result = await policy.ExecuteAndCaptureAsync(async () =>
|
var result = await policy.ExecuteAndCaptureAsync(async () =>
|
||||||
|
|
|
@ -26,5 +26,7 @@ namespace Ombi.Api.TheMovieDb
|
||||||
Task<TheMovieDbContainer<DiscoverMovies>> DiscoverMovies(string langCode, int keywordId);
|
Task<TheMovieDbContainer<DiscoverMovies>> DiscoverMovies(string langCode, int keywordId);
|
||||||
Task<FullMovieInfo> GetFullMovieInfo(int movieId, CancellationToken cancellationToken, string langCode);
|
Task<FullMovieInfo> GetFullMovieInfo(int movieId, CancellationToken cancellationToken, string langCode);
|
||||||
Task<Collections> GetCollection(string langCode, int collectionId, CancellationToken cancellationToken);
|
Task<Collections> GetCollection(string langCode, int collectionId, CancellationToken cancellationToken);
|
||||||
|
Task<List<Keyword>> SearchKeyword(string searchTerm);
|
||||||
|
Task<Keyword> GetKeyword(int keywordId);
|
||||||
}
|
}
|
||||||
}
|
}
|
13
src/Ombi.TheMovieDbApi/Models/Keyword.cs
Normal file
13
src/Ombi.TheMovieDbApi/Models/Keyword.cs
Normal file
|
@ -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; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,31 +1,38 @@
|
||||||
using System.Collections.Generic;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using AutoMapper;
|
using AutoMapper;
|
||||||
|
using Nito.AsyncEx;
|
||||||
using Ombi.Api.TheMovieDb.Models;
|
using Ombi.Api.TheMovieDb.Models;
|
||||||
|
using Ombi.Core.Settings;
|
||||||
|
using Ombi.Core.Settings.Models.External;
|
||||||
using Ombi.TheMovieDbApi.Models;
|
using Ombi.TheMovieDbApi.Models;
|
||||||
|
|
||||||
namespace Ombi.Api.TheMovieDb
|
namespace Ombi.Api.TheMovieDb
|
||||||
{
|
{
|
||||||
public class TheMovieDbApi : IMovieDbApi
|
public class TheMovieDbApi : IMovieDbApi
|
||||||
{
|
{
|
||||||
public TheMovieDbApi(IMapper mapper, IApi api)
|
public TheMovieDbApi(IMapper mapper, IApi api, ISettingsService<TheMovieDbSettings> settingsService)
|
||||||
{
|
{
|
||||||
Api = api;
|
Api = api;
|
||||||
Mapper = mapper;
|
Mapper = mapper;
|
||||||
|
Settings = new AsyncLazy<TheMovieDbSettings>(() => settingsService.GetSettingsAsync());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private const string ApiToken = "b8eabaf5608b88d0298aa189dd90bf00";
|
||||||
|
private const string BaseUri ="http://api.themoviedb.org/3/";
|
||||||
private IMapper Mapper { get; }
|
private IMapper Mapper { get; }
|
||||||
private readonly string ApiToken = "b8eabaf5608b88d0298aa189dd90bf00";
|
|
||||||
private static readonly string BaseUri ="http://api.themoviedb.org/3/";
|
|
||||||
private IApi Api { get; }
|
private IApi Api { get; }
|
||||||
|
private AsyncLazy<TheMovieDbSettings> Settings { get; }
|
||||||
|
|
||||||
public async Task<MovieResponseDto> GetMovieInformation(int movieId)
|
public async Task<MovieResponseDto> GetMovieInformation(int movieId)
|
||||||
{
|
{
|
||||||
var request = new Request($"movie/{movieId}", BaseUri, HttpMethod.Get);
|
var request = new Request($"movie/{movieId}", BaseUri, HttpMethod.Get);
|
||||||
request.FullUri = request.FullUri.AddQueryParameter("api_key", ApiToken);
|
request.AddQueryString("api_key", ApiToken);
|
||||||
AddRetry(request);
|
AddRetry(request);
|
||||||
|
|
||||||
var result = await Api.Request<MovieResponse>(request);
|
var result = await Api.Request<MovieResponse>(request);
|
||||||
|
@ -69,7 +76,7 @@ namespace Ombi.Api.TheMovieDb
|
||||||
public async Task<FindResult> Find(string externalId, ExternalSource source)
|
public async Task<FindResult> Find(string externalId, ExternalSource source)
|
||||||
{
|
{
|
||||||
var request = new Request($"find/{externalId}", BaseUri, HttpMethod.Get);
|
var request = new Request($"find/{externalId}", BaseUri, HttpMethod.Get);
|
||||||
request.FullUri = request.FullUri.AddQueryParameter("api_key", ApiToken);
|
request.AddQueryString("api_key", ApiToken);
|
||||||
AddRetry(request);
|
AddRetry(request);
|
||||||
|
|
||||||
request.AddQueryString("external_source", source.ToString());
|
request.AddQueryString("external_source", source.ToString());
|
||||||
|
@ -80,9 +87,11 @@ namespace Ombi.Api.TheMovieDb
|
||||||
public async Task<TheMovieDbContainer<ActorResult>> SearchByActor(string searchTerm, string langCode)
|
public async Task<TheMovieDbContainer<ActorResult>> SearchByActor(string searchTerm, string langCode)
|
||||||
{
|
{
|
||||||
var request = new Request($"search/person", BaseUri, HttpMethod.Get);
|
var request = new Request($"search/person", BaseUri, HttpMethod.Get);
|
||||||
request.FullUri = request.FullUri.AddQueryParameter("api_key", ApiToken);
|
request.AddQueryString("api_key", ApiToken);
|
||||||
request.FullUri = request.FullUri.AddQueryParameter("query", searchTerm);
|
request.AddQueryString("query", searchTerm);
|
||||||
request.FullUri = request.FullUri.AddQueryParameter("language", langCode);
|
request.AddQueryString("language", langCode);
|
||||||
|
var settings = await Settings;
|
||||||
|
request.AddQueryString("include_adult", settings.ShowAdultMovies.ToString().ToLower());
|
||||||
|
|
||||||
var result = await Api.Request<TheMovieDbContainer<ActorResult>>(request);
|
var result = await Api.Request<TheMovieDbContainer<ActorResult>>(request);
|
||||||
return result;
|
return result;
|
||||||
|
@ -91,8 +100,8 @@ namespace Ombi.Api.TheMovieDb
|
||||||
public async Task<ActorCredits> GetActorMovieCredits(int actorId, string langCode)
|
public async Task<ActorCredits> GetActorMovieCredits(int actorId, string langCode)
|
||||||
{
|
{
|
||||||
var request = new Request($"person/{actorId}/movie_credits", BaseUri, HttpMethod.Get);
|
var request = new Request($"person/{actorId}/movie_credits", BaseUri, HttpMethod.Get);
|
||||||
request.FullUri = request.FullUri.AddQueryParameter("api_key", ApiToken);
|
request.AddQueryString("api_key", ApiToken);
|
||||||
request.FullUri = request.FullUri.AddQueryParameter("language", langCode);
|
request.AddQueryString("language", langCode);
|
||||||
|
|
||||||
var result = await Api.Request<ActorCredits>(request);
|
var result = await Api.Request<ActorCredits>(request);
|
||||||
return result;
|
return result;
|
||||||
|
@ -101,8 +110,8 @@ namespace Ombi.Api.TheMovieDb
|
||||||
public async Task<List<TvSearchResult>> SearchTv(string searchTerm)
|
public async Task<List<TvSearchResult>> SearchTv(string searchTerm)
|
||||||
{
|
{
|
||||||
var request = new Request($"search/tv", BaseUri, HttpMethod.Get);
|
var request = new Request($"search/tv", BaseUri, HttpMethod.Get);
|
||||||
request.FullUri = request.FullUri.AddQueryParameter("api_key", ApiToken);
|
request.AddQueryString("api_key", ApiToken);
|
||||||
request.FullUri = request.FullUri.AddQueryParameter("query", searchTerm);
|
request.AddQueryString("query", searchTerm);
|
||||||
AddRetry(request);
|
AddRetry(request);
|
||||||
|
|
||||||
var result = await Api.Request<TheMovieDbContainer<SearchResult>>(request);
|
var result = await Api.Request<TheMovieDbContainer<SearchResult>>(request);
|
||||||
|
@ -112,7 +121,7 @@ namespace Ombi.Api.TheMovieDb
|
||||||
public async Task<TvExternals> GetTvExternals(int theMovieDbId)
|
public async Task<TvExternals> GetTvExternals(int theMovieDbId)
|
||||||
{
|
{
|
||||||
var request = new Request($"/tv/{theMovieDbId}/external_ids", BaseUri, HttpMethod.Get);
|
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);
|
AddRetry(request);
|
||||||
|
|
||||||
return await Api.Request<TvExternals>(request);
|
return await Api.Request<TvExternals>(request);
|
||||||
|
@ -121,9 +130,8 @@ namespace Ombi.Api.TheMovieDb
|
||||||
public async Task<List<MovieSearchResult>> SimilarMovies(int movieId, string langCode)
|
public async Task<List<MovieSearchResult>> SimilarMovies(int movieId, string langCode)
|
||||||
{
|
{
|
||||||
var request = new Request($"movie/{movieId}/similar", BaseUri, HttpMethod.Get);
|
var request = new Request($"movie/{movieId}/similar", BaseUri, HttpMethod.Get);
|
||||||
request.FullUri = request.FullUri.AddQueryParameter("api_key", ApiToken);
|
request.AddQueryString("api_key", ApiToken);
|
||||||
|
request.AddQueryString("language", langCode);
|
||||||
request.FullUri = request.FullUri.AddQueryParameter("language", langCode);
|
|
||||||
AddRetry(request);
|
AddRetry(request);
|
||||||
|
|
||||||
var result = await Api.Request<TheMovieDbContainer<SearchResult>>(request);
|
var result = await Api.Request<TheMovieDbContainer<SearchResult>>(request);
|
||||||
|
@ -133,81 +141,128 @@ namespace Ombi.Api.TheMovieDb
|
||||||
public async Task<MovieResponseDto> GetMovieInformationWithExtraInfo(int movieId, string langCode = "en")
|
public async Task<MovieResponseDto> GetMovieInformationWithExtraInfo(int movieId, string langCode = "en")
|
||||||
{
|
{
|
||||||
var request = new Request($"movie/{movieId}", BaseUri, HttpMethod.Get);
|
var request = new Request($"movie/{movieId}", BaseUri, HttpMethod.Get);
|
||||||
request.FullUri = request.FullUri.AddQueryParameter("api_key", ApiToken);
|
request.AddQueryString("api_key", ApiToken);
|
||||||
request.FullUri = request.FullUri.AddQueryParameter("append_to_response", "videos,release_dates");
|
request.AddQueryString("append_to_response", "videos,release_dates");
|
||||||
request.FullUri = request.FullUri.AddQueryParameter("language", langCode);
|
request.AddQueryString("language", langCode);
|
||||||
AddRetry(request);
|
AddRetry(request);
|
||||||
var result = await Api.Request<MovieResponse>(request);
|
var result = await Api.Request<MovieResponse>(request);
|
||||||
return Mapper.Map<MovieResponseDto>(result);
|
return Mapper.Map<MovieResponseDto>(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<List<MovieSearchResult>> SearchMovie(string searchTerm, int? year, string langageCode)
|
public async Task<List<MovieSearchResult>> SearchMovie(string searchTerm, int? year, string langCode)
|
||||||
{
|
{
|
||||||
var request = new Request($"search/movie", BaseUri, HttpMethod.Get);
|
var request = new Request($"search/movie", BaseUri, HttpMethod.Get);
|
||||||
request.FullUri = request.FullUri.AddQueryParameter("api_key", ApiToken);
|
request.AddQueryString("api_key", ApiToken);
|
||||||
request.FullUri = request.FullUri.AddQueryParameter("query", searchTerm);
|
request.AddQueryString("query", searchTerm);
|
||||||
request.FullUri = request.FullUri.AddQueryParameter("language", langageCode);
|
request.AddQueryString("language", langCode);
|
||||||
if (year.HasValue && year.Value > 0)
|
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);
|
AddRetry(request);
|
||||||
|
|
||||||
var result = await Api.Request<TheMovieDbContainer<SearchResult>>(request);
|
var result = await Api.Request<TheMovieDbContainer<SearchResult>>(request);
|
||||||
return Mapper.Map<List<MovieSearchResult>>(result.results);
|
return Mapper.Map<List<MovieSearchResult>>(result.results);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<List<MovieSearchResult>> PopularMovies(string langageCode, int? page = null, CancellationToken cancellationToken = default(CancellationToken))
|
/// <remarks>
|
||||||
|
/// Maintains filter parity with <a href="https://developers.themoviedb.org/3/movies/get-popular-movies">/movie/popular</a>.
|
||||||
|
/// </remarks>
|
||||||
|
public async Task<List<MovieSearchResult>> PopularMovies(string langCode, int? page = null, CancellationToken cancellationToken = default(CancellationToken))
|
||||||
{
|
{
|
||||||
var request = new Request($"movie/popular", BaseUri, HttpMethod.Get);
|
var request = new Request($"discover/movie", BaseUri, HttpMethod.Get);
|
||||||
request.FullUri = request.FullUri.AddQueryParameter("api_key", ApiToken);
|
request.AddQueryString("api_key", ApiToken);
|
||||||
request.FullUri = request.FullUri.AddQueryParameter("language", langageCode);
|
request.AddQueryString("language", langCode);
|
||||||
|
request.AddQueryString("sort_by", "popularity.desc");
|
||||||
if (page != null)
|
if (page != null)
|
||||||
{
|
{
|
||||||
request.FullUri = request.FullUri.AddQueryParameter("page", page.ToString());
|
request.AddQueryString("page", page.ToString());
|
||||||
}
|
}
|
||||||
|
await AddDiscoverMovieSettings(request);
|
||||||
AddRetry(request);
|
AddRetry(request);
|
||||||
var result = await Api.Request<TheMovieDbContainer<SearchResult>>(request, cancellationToken);
|
var result = await Api.Request<TheMovieDbContainer<SearchResult>>(request, cancellationToken);
|
||||||
return Mapper.Map<List<MovieSearchResult>>(result.results);
|
return Mapper.Map<List<MovieSearchResult>>(result.results);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<List<MovieSearchResult>> TopRated(string langageCode, int? page = null)
|
/// <remarks>
|
||||||
|
/// Maintains filter parity with <a href="https://developers.themoviedb.org/3/movies/get-top-rated-movies">/movie/top_rated</a>.
|
||||||
|
/// </remarks>
|
||||||
|
public async Task<List<MovieSearchResult>> TopRated(string langCode, int? page = null)
|
||||||
{
|
{
|
||||||
var request = new Request($"movie/top_rated", BaseUri, HttpMethod.Get);
|
var request = new Request($"discover/movie", BaseUri, HttpMethod.Get);
|
||||||
request.FullUri = request.FullUri.AddQueryParameter("api_key", ApiToken);
|
request.AddQueryString("api_key", ApiToken);
|
||||||
request.FullUri = request.FullUri.AddQueryParameter("language", langageCode);
|
request.AddQueryString("language", langCode);
|
||||||
|
request.AddQueryString("sort_by", "vote_average.desc");
|
||||||
if (page != null)
|
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);
|
AddRetry(request);
|
||||||
var result = await Api.Request<TheMovieDbContainer<SearchResult>>(request);
|
var result = await Api.Request<TheMovieDbContainer<SearchResult>>(request);
|
||||||
return Mapper.Map<List<MovieSearchResult>>(result.results);
|
return Mapper.Map<List<MovieSearchResult>>(result.results);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<List<MovieSearchResult>> Upcoming(string langageCode, int? page = null)
|
/// <remarks>
|
||||||
|
/// Maintains filter parity with <a href="https://developers.themoviedb.org/3/movies/get-upcoming">/movie/upcoming</a>.
|
||||||
|
/// </remarks>
|
||||||
|
public async Task<List<MovieSearchResult>> Upcoming(string langCode, int? page = null)
|
||||||
{
|
{
|
||||||
var request = new Request($"movie/upcoming", BaseUri, HttpMethod.Get);
|
var request = new Request($"discover/movie", BaseUri, HttpMethod.Get);
|
||||||
request.FullUri = request.FullUri.AddQueryParameter("api_key", ApiToken);
|
request.AddQueryString("api_key", ApiToken);
|
||||||
request.FullUri = request.FullUri.AddQueryParameter("language", langageCode);
|
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)
|
if (page != null)
|
||||||
{
|
{
|
||||||
request.FullUri = request.FullUri.AddQueryParameter("page", page.ToString());
|
request.AddQueryString("page", page.ToString());
|
||||||
}
|
}
|
||||||
|
await AddDiscoverMovieSettings(request);
|
||||||
AddRetry(request);
|
AddRetry(request);
|
||||||
var result = await Api.Request<TheMovieDbContainer<SearchResult>>(request);
|
var result = await Api.Request<TheMovieDbContainer<SearchResult>>(request);
|
||||||
return Mapper.Map<List<MovieSearchResult>>(result.results);
|
return Mapper.Map<List<MovieSearchResult>>(result.results);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<List<MovieSearchResult>> NowPlaying(string langageCode, int? page = null)
|
/// <remarks>
|
||||||
|
/// Maintains filter parity with <a href="https://developers.themoviedb.org/3/movies/get-now-playing">/movie/now_playing</a>.
|
||||||
|
/// </remarks>
|
||||||
|
public async Task<List<MovieSearchResult>> NowPlaying(string langCode, int? page = null)
|
||||||
{
|
{
|
||||||
var request = new Request($"movie/now_playing", BaseUri, HttpMethod.Get);
|
var request = new Request($"discover/movie", BaseUri, HttpMethod.Get);
|
||||||
request.FullUri = request.FullUri.AddQueryParameter("api_key", ApiToken);
|
request.AddQueryString("api_key", ApiToken);
|
||||||
request.FullUri = request.FullUri.AddQueryParameter("language", langageCode);
|
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)
|
if (page != null)
|
||||||
{
|
{
|
||||||
request.FullUri = request.FullUri.AddQueryParameter("page", page.ToString());
|
request.AddQueryString("page", page.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await AddDiscoverMovieSettings(request);
|
||||||
AddRetry(request);
|
AddRetry(request);
|
||||||
var result = await Api.Request<TheMovieDbContainer<SearchResult>>(request);
|
var result = await Api.Request<TheMovieDbContainer<SearchResult>>(request);
|
||||||
return Mapper.Map<List<MovieSearchResult>>(result.results);
|
return Mapper.Map<List<MovieSearchResult>>(result.results);
|
||||||
|
@ -216,19 +271,51 @@ namespace Ombi.Api.TheMovieDb
|
||||||
public async Task<TvInfo> GetTVInfo(string themoviedbid)
|
public async Task<TvInfo> GetTVInfo(string themoviedbid)
|
||||||
{
|
{
|
||||||
var request = new Request($"/tv/{themoviedbid}", BaseUri, HttpMethod.Get);
|
var request = new Request($"/tv/{themoviedbid}", BaseUri, HttpMethod.Get);
|
||||||
request.FullUri = request.FullUri.AddQueryParameter("api_key", ApiToken);
|
request.AddQueryString("api_key", ApiToken);
|
||||||
request.FullUri = request.FullUri.AddQueryParameter("append_to_response", "external_ids");
|
request.AddQueryString("append_to_response", "external_ids");
|
||||||
AddRetry(request);
|
AddRetry(request);
|
||||||
|
|
||||||
return await Api.Request<TvInfo>(request);
|
return await Api.Request<TvInfo>(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<List<Keyword>> 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<TheMovieDbContainer<Keyword>>(request);
|
||||||
|
return result.results ?? new List<Keyword>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Keyword> GetKeyword(int keywordId)
|
||||||
|
{
|
||||||
|
var request = new Request($"keyword/{keywordId}", BaseUri, HttpMethod.Get);
|
||||||
|
request.AddQueryString("api_key", ApiToken);
|
||||||
|
AddRetry(request);
|
||||||
|
|
||||||
|
var keyword = await Api.Request<Keyword>(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<TheMovieDbContainer<MultiSearch>> MultiSearch(string searchTerm, string languageCode, CancellationToken cancellationToken)
|
public async Task<TheMovieDbContainer<MultiSearch>> MultiSearch(string searchTerm, string languageCode, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var request = new Request("search/multi", BaseUri, HttpMethod.Get);
|
var request = new Request("search/multi", BaseUri, HttpMethod.Get);
|
||||||
request.FullUri = request.FullUri.AddQueryParameter("api_key", ApiToken);
|
request.AddQueryString("api_key", ApiToken);
|
||||||
request.FullUri = request.FullUri.AddQueryParameter("language", languageCode);
|
request.AddQueryString("language", languageCode);
|
||||||
request.FullUri = request.FullUri.AddQueryParameter("query", searchTerm);
|
request.AddQueryString("query", searchTerm);
|
||||||
var result = await Api.Request<TheMovieDbContainer<MultiSearch>>(request, cancellationToken);
|
var result = await Api.Request<TheMovieDbContainer<MultiSearch>>(request, cancellationToken);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,6 +48,7 @@
|
||||||
"moment": "^2.23.0",
|
"moment": "^2.23.0",
|
||||||
"ng2-cookies": "^1.0.12",
|
"ng2-cookies": "^1.0.12",
|
||||||
"ngx-bootstrap": "^3.1.4",
|
"ngx-bootstrap": "^3.1.4",
|
||||||
|
"ngx-chips": "^2.1.0",
|
||||||
"ngx-clipboard": "^12.1.0",
|
"ngx-clipboard": "^12.1.0",
|
||||||
"ngx-infinite-scroll": "^7.1.0",
|
"ngx-infinite-scroll": "^7.1.0",
|
||||||
"ngx-moment": "^3.0.1",
|
"ngx-moment": "^3.0.1",
|
||||||
|
|
|
@ -127,6 +127,9 @@
|
||||||
<li [ngClass]="{'active': 'en' === translate.currentLang}">
|
<li [ngClass]="{'active': 'en' === translate.currentLang}">
|
||||||
<a (click)="translate.use('en')">English</a>
|
<a (click)="translate.use('en')">English</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li [ngClass]="{'active': 'es' === translate.currentLang}">
|
||||||
|
<a (click)="translate.use('es')">Español</a>
|
||||||
|
</li>
|
||||||
<li [ngClass]="{'active': 'fr' === translate.currentLang}">
|
<li [ngClass]="{'active': 'fr' === translate.currentLang}">
|
||||||
<a (click)="translate.use('fr')">Français</a>
|
<a (click)="translate.use('fr')">Français</a>
|
||||||
</li>
|
</li>
|
||||||
|
@ -139,9 +142,6 @@
|
||||||
<li [ngClass]="{'active': 'it' === translate.currentLang}">
|
<li [ngClass]="{'active': 'it' === translate.currentLang}">
|
||||||
<a (click)="translate.use('it')">Italiano</a>
|
<a (click)="translate.use('it')">Italiano</a>
|
||||||
</li>
|
</li>
|
||||||
<li [ngClass]="{'active': 'es' === translate.currentLang}">
|
|
||||||
<a (click)="translate.use('es')">Español</a>
|
|
||||||
</li>
|
|
||||||
<li [ngClass]="{'active': 'nl' === translate.currentLang}">
|
<li [ngClass]="{'active': 'nl' === translate.currentLang}">
|
||||||
<a (click)="translate.use('nl')">Nederlands</a>
|
<a (click)="translate.use('nl')">Nederlands</a>
|
||||||
</li>
|
</li>
|
||||||
|
@ -157,6 +157,9 @@
|
||||||
<li [ngClass]="{'active': 'sv' === translate.currentLang}">
|
<li [ngClass]="{'active': 'sv' === translate.currentLang}">
|
||||||
<a (click)="translate.use('sv')">Svenska</a>
|
<a (click)="translate.use('sv')">Svenska</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li [ngClass]="{'active': 'sk' === translate.currentLang}">
|
||||||
|
<a (click)="translate.use('sk')">Slovak</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
|
@ -59,7 +59,7 @@ export class AppComponent implements OnInit {
|
||||||
// __webpack_public_path__ = window['base-href']
|
// __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");
|
const selectedLang = this.storage.get("Language");
|
||||||
|
|
||||||
|
@ -70,7 +70,7 @@ export class AppComponent implements OnInit {
|
||||||
} else {
|
} else {
|
||||||
// See if we can match the supported langs with the current browser lang
|
// See if we can match the supported langs with the current browser lang
|
||||||
const browserLang: string = translate.getBrowserLang();
|
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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
4
src/Ombi/ClientApp/src/app/interfaces/IMovieDb.ts
Normal file
4
src/Ombi/ClientApp/src/app/interfaces/IMovieDb.ts
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
export interface IMovieDbKeyword {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
}
|
|
@ -246,3 +246,8 @@ export interface IVoteSettings extends ISettings {
|
||||||
musicVoteMax: number;
|
musicVoteMax: number;
|
||||||
tvShowVoteMax: number;
|
tvShowVoteMax: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ITheMovieDbSettings extends ISettings {
|
||||||
|
showAdultMovies: boolean;
|
||||||
|
excludedKeywordIds: number[];
|
||||||
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ export * from "./ICouchPotato";
|
||||||
export * from "./IImages";
|
export * from "./IImages";
|
||||||
export * from "./IMediaServerStatus";
|
export * from "./IMediaServerStatus";
|
||||||
export * from "./INotificationSettings";
|
export * from "./INotificationSettings";
|
||||||
|
export * from "./IMovieDb";
|
||||||
export * from "./IPlex";
|
export * from "./IPlex";
|
||||||
export * from "./IRadarr";
|
export * from "./IRadarr";
|
||||||
export * from "./IRequestEngineResult";
|
export * from "./IRequestEngineResult";
|
||||||
|
|
|
@ -24,14 +24,6 @@ import { MatSnackBar } from "@angular/material";
|
||||||
})
|
})
|
||||||
export class LoginComponent implements OnDestroy, OnInit {
|
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 form: FormGroup;
|
||||||
public customizationSettings: ICustomizationSettings;
|
public customizationSettings: ICustomizationSettings;
|
||||||
public authenticationSettings: IAuthenticationSettings;
|
public authenticationSettings: IAuthenticationSettings;
|
||||||
|
|
|
@ -1,30 +1,41 @@
|
||||||
<!-- Movie tab -->
|
<!-- Movie tab -->
|
||||||
<div role="tabpanel" class="tab-pane active" id="MoviesTab">
|
<div role="tabpanel" class="tab-pane active" id="MoviesTab">
|
||||||
|
|
||||||
<div class="input-group search-bar-background">
|
<ng-template #FilterRef>
|
||||||
<input id="search" type="text" placeholder="{{ 'Search.SearchBarPlaceholder' | translate}}" class="form-control form-control-custom form-control-search form-control-withbuttons"
|
<div class="btn-group" role="group">
|
||||||
(keyup)="search($event)">
|
<a href="#" class="btn btn-sm btn-primary-outline dropdown-toggle" data-toggle="dropdown" aria-expanded="false">
|
||||||
<div class="input-group-addon right-radius">
|
{{ 'Search.Suggestions' | translate }}
|
||||||
<div class="btn-group" role="group">
|
<i class="fa fa-chevron-down"></i>
|
||||||
<a href="#" class="btn btn-sm btn-primary-outline dropdown-toggle" data-toggle="dropdown" aria-expanded="false">
|
</a>
|
||||||
{{ 'Search.Suggestions' | translate }}
|
<ul class="dropdown-menu">
|
||||||
<i class="fa fa-chevron-down"></i>
|
<li><a (click)="popularMovies()" [translate]="'Search.Movies.PopularMovies'"></a></li>
|
||||||
</a>
|
<li><a (click)="upcomingMovies()" [translate]="'Search.Movies.UpcomingMovies'"></a></li>
|
||||||
<ul class="dropdown-menu">
|
<li><a (click)="topRatedMovies()" [translate]="'Search.Movies.TopRatedMovies'"></a></li>
|
||||||
<li><a (click)="popularMovies()" [translate]="'Search.Movies.PopularMovies'"></a></li>
|
<li><a (click)="nowPlayingMovies()" [translate]="'Search.Movies.NowPlayingMovies'"></a></li>
|
||||||
<li><a (click)="upcomingMovies()" [translate]="'Search.Movies.UpcomingMovies'"></a></li>
|
</ul>
|
||||||
<li><a (click)="topRatedMovies()" [translate]="'Search.Movies.TopRatedMovies'"></a></li>
|
<button class="btn btn-sm btn-primary-outline" (click)="refineOpen()">
|
||||||
<li><a (click)="nowPlayingMovies()" [translate]="'Search.Movies.NowPlayingMovies'"></a></li>
|
{{ 'Search.Refine' | translate }}
|
||||||
</ul>
|
<i class="fa" [ngClass]="{'fa-chevron-down': !refineSearchEnabled, 'fa-chevron-up': refineSearchEnabled}"></i>
|
||||||
<button class="btn btn-sm btn-primary-outline" (click)="refineOpen()">
|
</button>
|
||||||
{{ 'Search.Refine' | translate }}
|
</div>
|
||||||
<i class="fa" [ngClass]="{'fa-chevron-down': !refineSearchEnabled, 'fa-chevron-up': refineSearchEnabled}"></i>
|
</ng-template>
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
<div class="input-group search-bar-background">
|
||||||
|
<input id="search" type="text" placeholder="{{ 'Search.SearchBarPlaceholder' | translate}}"
|
||||||
|
class="form-control form-control-custom form-control-search form-control-withbuttons"
|
||||||
|
(keyup)="search($event)">
|
||||||
|
<div class="input-group-addon right-radius">
|
||||||
|
<div class="search-button-container-inline">
|
||||||
|
<ng-template [ngTemplateOutlet]="FilterRef"></ng-template>
|
||||||
|
</div>
|
||||||
<i class="fa fa-search"></i>
|
<i class="fa fa-search"></i>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="row search-button-container">
|
||||||
|
<ng-template [ngTemplateOutlet]="FilterRef"></ng-template>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Refine search options -->
|
<!-- Refine search options -->
|
||||||
<div class="row top-spacing form-group vcenter" *ngIf="refineSearchEnabled">
|
<div class="row top-spacing form-group vcenter" *ngIf="refineSearchEnabled">
|
||||||
<div class="col-md-1">
|
<div class="col-md-1">
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
padding-top: 5%
|
padding-top: 5%
|
||||||
}
|
}
|
||||||
.form-control-search {
|
.form-control-search {
|
||||||
width: 77%;
|
padding-right: 165px;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,32 +1,44 @@
|
||||||
<!-- Movie tab -->
|
<!-- Movie tab -->
|
||||||
<div role="tabpanel" class="tab-pane" id="TvShowTab">
|
<div role="tabpanel" class="tab-pane" id="TvShowTab">
|
||||||
|
<ng-template #FilterRef>
|
||||||
|
<div class="btn-group" role="group">
|
||||||
|
<a href="#" class="btn btn-sm btn-primary-outline dropdown-toggle" data-toggle="dropdown" aria-expanded="false">
|
||||||
|
{{ 'Search.Suggestions' | translate }}
|
||||||
|
<i class="fa fa-chevron-down"></i>
|
||||||
|
</a>
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
<li>
|
||||||
|
<a (click)="popularShows()">{{ 'Search.TvShows.Popular' | translate }} </a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a (click)="trendingShows()">{{ 'Search.TvShows.Trending' | translate }}</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a (click)="mostWatchedShows()">{{ 'Search.TvShows.MostWatched' | translate }}</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a (click)="anticipatedShows()">{{ 'Search.TvShows.MostAnticipated' | translate }}</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<input id="search" type="text" placeholder="{{ 'Search.SearchBarPlaceholder' | translate }}" class="form-control form-control-custom form-control-search form-control-withbuttons" (keyup)="search($event)">
|
<input id="search" type="text" placeholder="{{ 'Search.SearchBarPlaceholder' | translate }}"
|
||||||
|
class="form-control form-control-custom form-control-search form-control-withbuttons"
|
||||||
|
(keyup)="search($event)">
|
||||||
<div class="input-group-addon right-radius">
|
<div class="input-group-addon right-radius">
|
||||||
<div class="btn-group">
|
<div class="search-button-container-inline">
|
||||||
<a href="#" class="btn btn-sm btn-primary-outline dropdown-toggle" data-toggle="dropdown" aria-expanded="false">
|
<ng-template [ngTemplateOutlet]="FilterRef"></ng-template>
|
||||||
{{ 'Search.Suggestions' | translate }}
|
|
||||||
<i class="fa fa-chevron-down"></i>
|
|
||||||
</a>
|
|
||||||
<ul class="dropdown-menu">
|
|
||||||
<li>
|
|
||||||
<a (click)="popularShows()">{{ 'Search.TvShows.Popular' | translate }} </a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a (click)="trendingShows()">{{ 'Search.TvShows.Trending' | translate }}</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a (click)="mostWatchedShows()">{{ 'Search.TvShows.MostWatched' | translate }}</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a (click)="anticipatedShows()">{{ 'Search.TvShows.MostAnticipated' | translate }}</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
</div>
|
||||||
<i id="tvSearchButton" class="fa fa-search"></i>
|
<i id="tvSearchButton" class="fa fa-search"></i>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="row search-button-container">
|
||||||
|
<ng-template [ngTemplateOutlet]="FilterRef"></ng-template>
|
||||||
|
</div>
|
||||||
|
|
||||||
<remaining-requests [tv]="true" [quotaRefreshEvents]="tvRequested.asObservable()" #remainingTvShows></remaining-requests>
|
<remaining-requests [tv]="true" [quotaRefreshEvents]="tvRequested.asObservable()" #remainingTvShows></remaining-requests>
|
||||||
|
|
||||||
<!-- Movie content -->
|
<!-- Movie content -->
|
||||||
|
@ -159,6 +171,7 @@
|
||||||
<br/>
|
<br/>
|
||||||
<br/>
|
<br/>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -7,3 +7,4 @@ export * from "./tester.service";
|
||||||
export * from "./plexoauth.service";
|
export * from "./plexoauth.service";
|
||||||
export * from "./plextv.service";
|
export * from "./plextv.service";
|
||||||
export * from "./lidarr.service";
|
export * from "./lidarr.service";
|
||||||
|
export * from "./themoviedb.service";
|
||||||
|
|
|
@ -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<IMovieDbKeyword[]> {
|
||||||
|
const params = new HttpParams().set("searchTerm", searchTerm);
|
||||||
|
return this.http.get<IMovieDbKeyword[]>(`${this.url}/Keywords`, {headers: this.headers, params});
|
||||||
|
}
|
||||||
|
|
||||||
|
public getKeyword(keywordId: number): Observable<IMovieDbKeyword> {
|
||||||
|
return this.http.get<IMovieDbKeyword>(`${this.url}/Keywords/${keywordId}`, { headers: this.headers })
|
||||||
|
.pipe(catchError((error: HttpErrorResponse) => error.status === 404 ? empty() : throwError(error)));
|
||||||
|
}
|
||||||
|
}
|
|
@ -32,6 +32,7 @@ import {
|
||||||
ISlackNotificationSettings,
|
ISlackNotificationSettings,
|
||||||
ISonarrSettings,
|
ISonarrSettings,
|
||||||
ITelegramNotifcationSettings,
|
ITelegramNotifcationSettings,
|
||||||
|
ITheMovieDbSettings,
|
||||||
IUpdateSettings,
|
IUpdateSettings,
|
||||||
IUserManagementSettings,
|
IUserManagementSettings,
|
||||||
IVoteSettings,
|
IVoteSettings,
|
||||||
|
@ -301,6 +302,14 @@ export class SettingsService extends ServiceHelpers {
|
||||||
return this.http.post<boolean>(`${this.url}/vote`, JSON.stringify(settings), {headers: this.headers});
|
return this.http.post<boolean>(`${this.url}/vote`, JSON.stringify(settings), {headers: this.headers});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getTheMovieDbSettings(): Observable<ITheMovieDbSettings> {
|
||||||
|
return this.http.get<ITheMovieDbSettings>(`${this.url}/themoviedb`, {headers: this.headers});
|
||||||
|
}
|
||||||
|
|
||||||
|
public saveTheMovieDbSettings(settings: ITheMovieDbSettings) {
|
||||||
|
return this.http.post<boolean>(`${this.url}/themoviedb`, JSON.stringify(settings), {headers: this.headers});
|
||||||
|
}
|
||||||
|
|
||||||
public getNewsletterSettings(): Observable<INewsletterNotificationSettings> {
|
public getNewsletterSettings(): Observable<INewsletterNotificationSettings> {
|
||||||
return this.http.get<INewsletterNotificationSettings>(`${this.url}/notifications/newsletter`, {headers: this.headers});
|
return this.http.get<INewsletterNotificationSettings>(`${this.url}/notifications/newsletter`, {headers: this.headers});
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,13 +3,14 @@ import { NgModule } from "@angular/core";
|
||||||
import { FormsModule, ReactiveFormsModule } from "@angular/forms";
|
import { FormsModule, ReactiveFormsModule } from "@angular/forms";
|
||||||
import { RouterModule, Routes } from "@angular/router";
|
import { RouterModule, Routes } from "@angular/router";
|
||||||
import { NgbAccordionModule, NgbModule } from "@ng-bootstrap/ng-bootstrap";
|
import { NgbAccordionModule, NgbModule } from "@ng-bootstrap/ng-bootstrap";
|
||||||
|
import { TagInputModule } from "ngx-chips";
|
||||||
import { ClipboardModule } from "ngx-clipboard";
|
import { ClipboardModule } from "ngx-clipboard";
|
||||||
|
|
||||||
import { AuthGuard } from "../auth/auth.guard";
|
import { AuthGuard } from "../auth/auth.guard";
|
||||||
import { AuthService } from "../auth/auth.service";
|
import { AuthService } from "../auth/auth.service";
|
||||||
import {
|
import {
|
||||||
CouchPotatoService, EmbyService, IssuesService, JobService, LidarrService, MobileService, NotificationMessageService, PlexService, RadarrService,
|
CouchPotatoService, EmbyService, IssuesService, JobService, LidarrService, MobileService, NotificationMessageService, PlexService, RadarrService,
|
||||||
RequestRetryService, SonarrService, TesterService, ValidationService, SystemService, FileDownloadService,
|
RequestRetryService, SonarrService, TesterService, ValidationService, SystemService, FileDownloadService, TheMovieDbService
|
||||||
} from "../services";
|
} from "../services";
|
||||||
|
|
||||||
import { PipeModule } from "../pipes/pipe.module";
|
import { PipeModule } from "../pipes/pipe.module";
|
||||||
|
@ -41,6 +42,7 @@ import { PlexComponent } from "./plex/plex.component";
|
||||||
import { RadarrComponent } from "./radarr/radarr.component";
|
import { RadarrComponent } from "./radarr/radarr.component";
|
||||||
import { SickRageComponent } from "./sickrage/sickrage.component";
|
import { SickRageComponent } from "./sickrage/sickrage.component";
|
||||||
import { SonarrComponent } from "./sonarr/sonarr.component";
|
import { SonarrComponent } from "./sonarr/sonarr.component";
|
||||||
|
import { TheMovieDbComponent } from "./themoviedb/themoviedb.component";
|
||||||
import { UpdateComponent } from "./update/update.component";
|
import { UpdateComponent } from "./update/update.component";
|
||||||
import { UserManagementComponent } from "./usermanagement/usermanagement.component";
|
import { UserManagementComponent } from "./usermanagement/usermanagement.component";
|
||||||
import { VoteComponent } from "./vote/vote.component";
|
import { VoteComponent } from "./vote/vote.component";
|
||||||
|
@ -84,6 +86,7 @@ const routes: Routes = [
|
||||||
{ path: "Newsletter", component: NewsletterComponent, canActivate: [AuthGuard] },
|
{ path: "Newsletter", component: NewsletterComponent, canActivate: [AuthGuard] },
|
||||||
{ path: "Lidarr", component: LidarrComponent, canActivate: [AuthGuard] },
|
{ path: "Lidarr", component: LidarrComponent, canActivate: [AuthGuard] },
|
||||||
{ path: "Vote", component: VoteComponent, canActivate: [AuthGuard] },
|
{ path: "Vote", component: VoteComponent, canActivate: [AuthGuard] },
|
||||||
|
{ path: "TheMovieDb", component: TheMovieDbComponent, canActivate: [AuthGuard] },
|
||||||
{ path: "FailedRequests", component: FailedRequestsComponent, canActivate: [AuthGuard] },
|
{ path: "FailedRequests", component: FailedRequestsComponent, canActivate: [AuthGuard] },
|
||||||
{ path: "Logs", component: LogsComponent, canActivate: [AuthGuard] },
|
{ path: "Logs", component: LogsComponent, canActivate: [AuthGuard] },
|
||||||
];
|
];
|
||||||
|
@ -102,6 +105,7 @@ const routes: Routes = [
|
||||||
NgbAccordionModule,
|
NgbAccordionModule,
|
||||||
AutoCompleteModule,
|
AutoCompleteModule,
|
||||||
CalendarModule,
|
CalendarModule,
|
||||||
|
TagInputModule,
|
||||||
ClipboardModule,
|
ClipboardModule,
|
||||||
PipeModule,
|
PipeModule,
|
||||||
RadioButtonModule,
|
RadioButtonModule,
|
||||||
|
@ -142,6 +146,7 @@ const routes: Routes = [
|
||||||
NewsletterComponent,
|
NewsletterComponent,
|
||||||
LidarrComponent,
|
LidarrComponent,
|
||||||
VoteComponent,
|
VoteComponent,
|
||||||
|
TheMovieDbComponent,
|
||||||
FailedRequestsComponent,
|
FailedRequestsComponent,
|
||||||
LogsComponent,
|
LogsComponent,
|
||||||
],
|
],
|
||||||
|
@ -166,7 +171,8 @@ const routes: Routes = [
|
||||||
RequestRetryService,
|
RequestRetryService,
|
||||||
HubService,
|
HubService,
|
||||||
SystemService,
|
SystemService,
|
||||||
FileDownloadService
|
FileDownloadService,
|
||||||
|
TheMovieDbService,
|
||||||
],
|
],
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
<button mat-menu-item [routerLink]="['/Settings/UserManagement']">User Management</button>
|
<button mat-menu-item [routerLink]="['/Settings/UserManagement']">User Management</button>
|
||||||
<button mat-menu-item [routerLink]="['/Settings/Authentication']">Authentication</button>
|
<button mat-menu-item [routerLink]="['/Settings/Authentication']">Authentication</button>
|
||||||
<button mat-menu-item [routerLink]="['/Settings/Vote']">Vote</button>
|
<button mat-menu-item [routerLink]="['/Settings/Vote']">Vote</button>
|
||||||
|
<button mat-menu-item [routerLink]="['/Settings/TheMovieDb']">The Movie Database</button>
|
||||||
</mat-menu>
|
</mat-menu>
|
||||||
|
|
||||||
<button mat-button [matMenuTriggerFor]="mediaservermenu"><i class="fa fa-server" aria-hidden="true"></i> Media Server</button>
|
<button mat-button [matMenuTriggerFor]="mediaservermenu"><i class="fa fa-server" aria-hidden="true"></i> Media Server</button>
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
<settings-menu></settings-menu>
|
||||||
|
|
||||||
|
<fieldset *ngIf="settings">
|
||||||
|
<legend>The Movie Database</legend>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="checkbox">
|
||||||
|
<input type="checkbox" id="showAdultMovies" name="showAdultMovies" [(ngModel)]="settings.showAdultMovies">
|
||||||
|
<label for="showAdultMovies" tooltipPosition="top" pTooltip="Include adult movies (pornography) in results">Show Adult Movies</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="control-label" pTooltip="Prevent movies with certain keywords from being suggested. May require a restart to take effect.">
|
||||||
|
Excluded Keyword IDs for Movie Suggestions
|
||||||
|
</label>
|
||||||
|
<tag-input #input
|
||||||
|
[(ngModel)]="excludedKeywords"
|
||||||
|
[identifyBy]="'id'" [displayBy]="'name'"
|
||||||
|
[placeholder]="'Search by keyword'"
|
||||||
|
[secondaryPlaceholder]="'Search by keyword'"
|
||||||
|
[theme]="'dark'"
|
||||||
|
[onTextChangeDebounce]="500"
|
||||||
|
[onAdding]="onAddingKeyword"
|
||||||
|
(onSelect)="onKeywordSelect($event)">
|
||||||
|
<ng-template item-template let-item="item" let-index="index">
|
||||||
|
<span class="fa fa-cloud-download" *ngIf="item.initial"></span>
|
||||||
|
<span>{{item.id}}</span>
|
||||||
|
<span *ngIf="!item.initial"> ({{item.name}})</span>
|
||||||
|
<delete-icon aria-label="Remove tag" role="button"
|
||||||
|
(click)="input.removeItem(item, index)">
|
||||||
|
</delete-icon>
|
||||||
|
</ng-template>
|
||||||
|
<tag-input-dropdown [autocompleteObservable]="autocompleteKeyword"
|
||||||
|
[identifyBy]="'id'" [displayBy]="'name'"
|
||||||
|
[limitItemsTo]="6"
|
||||||
|
[minimumTextLength]="1"
|
||||||
|
[showDropdownIfEmpty]="false"
|
||||||
|
[keepOpen]="false">
|
||||||
|
</tag-input-dropdown>
|
||||||
|
</tag-input>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<div>
|
||||||
|
<button (click)="save()" type="submit" id="save" class="btn btn-primary-outline">Submit</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
|
@ -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");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -3917,10 +3917,25 @@ ng2-cookies@^1.0.12:
|
||||||
version "1.0.12"
|
version "1.0.12"
|
||||||
resolved "https://registry.yarnpkg.com/ng2-cookies/-/ng2-cookies-1.0.12.tgz#3f3e613e0137b0649b705c678074b4bd08149ccc"
|
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:
|
ngx-bootstrap@^3.1.4:
|
||||||
version "3.1.4"
|
version "3.1.4"
|
||||||
resolved "https://registry.yarnpkg.com/ngx-bootstrap/-/ngx-bootstrap-3.1.4.tgz#5105c0227da3b51a1972d04efa1504a79474fd57"
|
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:
|
ngx-clipboard@^12.1.0:
|
||||||
version "12.1.0"
|
version "12.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/ngx-clipboard/-/ngx-clipboard-12.1.0.tgz#41d10c9d031d5d6e854f8c21c85460c96685b10b"
|
resolved "https://registry.yarnpkg.com/ngx-clipboard/-/ngx-clipboard-12.1.0.tgz#41d10c9d031d5d6e854f8c21c85460c96685b10b"
|
||||||
|
|
38
src/Ombi/Controllers/External/TheMovieDbController.cs
vendored
Normal file
38
src/Ombi/Controllers/External/TheMovieDbController.cs
vendored
Normal file
|
@ -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; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Searches for keywords matching the specified term.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="searchTerm">The search term.</param>
|
||||||
|
[HttpGet("Keywords")]
|
||||||
|
public async Task<IEnumerable<Keyword>> GetKeywords([FromQuery]string searchTerm) =>
|
||||||
|
await TmdbApi.SearchKeyword(searchTerm);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the keyword matching the specified ID.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="keywordId">The keyword ID.</param>
|
||||||
|
[HttpGet("Keywords/{keywordId}")]
|
||||||
|
public async Task<IActionResult> GetKeywords(int keywordId)
|
||||||
|
{
|
||||||
|
var keyword = await TmdbApi.GetKeyword(keywordId);
|
||||||
|
return keyword == null ? NotFound() : (IActionResult)Ok(keyword);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -683,6 +683,25 @@ namespace Ombi.Controllers.V1
|
||||||
return vote.Enabled;
|
return vote.Enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Save The Movie DB settings.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="settings">The settings.</param>
|
||||||
|
[HttpPost("themoviedb")]
|
||||||
|
public async Task<bool> TheMovieDbSettings([FromBody]TheMovieDbSettings settings)
|
||||||
|
{
|
||||||
|
return await Save(settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get The Movie DB settings.
|
||||||
|
/// </summary>
|
||||||
|
[HttpGet("themoviedb")]
|
||||||
|
public async Task<TheMovieDbSettings> TheMovieDbSettings()
|
||||||
|
{
|
||||||
|
return await Get<TheMovieDbSettings>();
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Saves the email notification settings.
|
/// Saves the email notification settings.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -30,6 +30,7 @@ using Ombi.Store.Context;
|
||||||
using Ombi.Store.Entities;
|
using Ombi.Store.Entities;
|
||||||
using Ombi.Store.Repository;
|
using Ombi.Store.Repository;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
|
using SQLitePCL;
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using ILogger = Serilog.ILogger;
|
using ILogger = Serilog.ILogger;
|
||||||
|
@ -101,8 +102,7 @@ namespace Ombi
|
||||||
x.UseActivator(new IoCJobActivator(services.BuildServiceProvider()));
|
x.UseActivator(new IoCJobActivator(services.BuildServiceProvider()));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
SQLitePCL.raw.sqlite3_config(raw.SQLITE_CONFIG_MULTITHREAD);
|
||||||
SQLitePCL.raw.sqlite3_config(2);
|
|
||||||
|
|
||||||
services.AddCors(o => o.AddPolicy("MyPolicy", builder =>
|
services.AddCors(o => o.AddPolicy("MyPolicy", builder =>
|
||||||
{
|
{
|
||||||
|
|
186
src/Ombi/wwwroot/translations/sk.json
Normal file
186
src/Ombi/wwwroot/translations/sk.json
Normal file
|
@ -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"
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue