mirror of
https://github.com/Ombi-app/Ombi.git
synced 2025-07-13 08:42:57 -07:00
Merge pull request #2081 from tidusjar/develop
Merge Develop to RecentlyAdded
This commit is contained in:
commit
0e94270f84
42 changed files with 700 additions and 80 deletions
|
@ -9,7 +9,7 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="2.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Options" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options" Version="2.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
using System.IO;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Threading.Tasks;
|
||||
|
@ -6,6 +9,7 @@ using System.Xml.Serialization;
|
|||
using Newtonsoft.Json;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Ombi.Helpers;
|
||||
using Polly;
|
||||
|
||||
namespace Ombi.Api
|
||||
{
|
||||
|
@ -36,6 +40,30 @@ namespace Ombi.Api
|
|||
if (!httpResponseMessage.IsSuccessStatusCode)
|
||||
{
|
||||
LogError(request, httpResponseMessage);
|
||||
if (request.Retry)
|
||||
{
|
||||
|
||||
var result = Policy
|
||||
.Handle<HttpRequestException>()
|
||||
.OrResult<HttpResponseMessage>(r => request.StatusCodeToRetry.Contains(r.StatusCode))
|
||||
.WaitAndRetryAsync(new[]
|
||||
{
|
||||
TimeSpan.FromSeconds(10),
|
||||
}, (exception, timeSpan, context) =>
|
||||
{
|
||||
|
||||
Logger.LogError(LoggingEvents.Api,
|
||||
$"Retrying RequestUri: {request.FullUri} Because we got Status Code: {exception?.Result?.StatusCode}");
|
||||
});
|
||||
|
||||
httpResponseMessage = await result.ExecuteAsync(async () =>
|
||||
{
|
||||
using (var req = await httpRequestMessage.Clone())
|
||||
{
|
||||
return await _client.SendAsync(req);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// do something with the response
|
||||
|
|
45
src/Ombi.Api/HttpRequestExtnesions.cs
Normal file
45
src/Ombi.Api/HttpRequestExtnesions.cs
Normal file
|
@ -0,0 +1,45 @@
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ombi.Api
|
||||
{
|
||||
public static class HttpRequestExtnesions
|
||||
{
|
||||
public static async Task<HttpRequestMessage> Clone(this HttpRequestMessage request)
|
||||
{
|
||||
var clone = new HttpRequestMessage(request.Method, request.RequestUri)
|
||||
{
|
||||
Content = await request.Content.Clone(),
|
||||
Version = request.Version
|
||||
};
|
||||
foreach (KeyValuePair<string, object> prop in request.Properties)
|
||||
{
|
||||
clone.Properties.Add(prop);
|
||||
}
|
||||
foreach (KeyValuePair<string, IEnumerable<string>> header in request.Headers)
|
||||
{
|
||||
clone.Headers.TryAddWithoutValidation(header.Key, header.Value);
|
||||
}
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
public static async Task<HttpContent> Clone(this HttpContent content)
|
||||
{
|
||||
if (content == null) return null;
|
||||
|
||||
var ms = new MemoryStream();
|
||||
await content.CopyToAsync(ms);
|
||||
ms.Position = 0;
|
||||
|
||||
var clone = new StreamContent(ms);
|
||||
foreach (KeyValuePair<string, IEnumerable<string>> header in content.Headers)
|
||||
{
|
||||
clone.Headers.Add(header.Key, header.Value);
|
||||
}
|
||||
return clone;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -9,8 +9,9 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="2.0.1" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
|
||||
<PackageReference Include="Polly" Version="5.8.0" />
|
||||
<PackageReference Include="System.Xml.XmlSerializer" Version="4.3.0" />
|
||||
</ItemGroup>
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
|
||||
|
@ -25,6 +26,9 @@ namespace Ombi.Api
|
|||
public string BaseUrl { get; }
|
||||
public HttpMethod HttpMethod { get; }
|
||||
|
||||
public bool Retry { get; set; }
|
||||
public List<HttpStatusCode> StatusCodeToRetry { get; set; } = new List<HttpStatusCode>();
|
||||
|
||||
public Action<string> OnBeforeDeserialization { get; set; }
|
||||
|
||||
private string FullUrl
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
<PackageReference Include="Nunit" Version="3.8.1" />
|
||||
<PackageReference Include="NUnit.ConsoleRunner" Version="3.7.0" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="3.8.0" />
|
||||
<packagereference Include="Microsoft.NET.Test.Sdk" Version="15.0.0"></packagereference>
|
||||
<packagereference Include="Microsoft.NET.Test.Sdk" Version="15.6.1"></packagereference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
@ -12,9 +12,9 @@
|
|||
<PackageReference Include="AutoMapper" Version="6.1.1" />
|
||||
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="3.0.1" />
|
||||
<PackageReference Include="Hangfire" Version="1.6.17" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Cryptography.KeyDerivation" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Design" Version="1.1.3" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Cryptography.KeyDerivation" Version="2.0.2" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.0.2" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Design" Version="1.1.5" />
|
||||
<PackageReference Include="MiniProfiler.AspNetCore" Version="4.0.0-alpha6-79" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
|
||||
<PackageReference Include="System.Diagnostics.Process" Version="4.3.0" />
|
||||
|
|
|
@ -173,6 +173,7 @@ namespace Ombi.DependencyInjection
|
|||
services.AddTransient<ICouchPotatoSync, CouchPotatoSync>();
|
||||
services.AddTransient<IProcessProvider, ProcessProvider>();
|
||||
services.AddTransient<ISickRageSync, SickRageSync>();
|
||||
services.AddTransient<IRefreshMetadata, RefreshMetadata>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,8 +9,8 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authorization" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="2.0.3" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authorization" Version="2.0.3" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="2.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
|
|
|
@ -10,8 +10,8 @@
|
|||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="EasyCrypto" Version="3.3.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="2.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="2.0.1" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
|
||||
<PackageReference Include="Nito.AsyncEx" Version="5.0.0-pre-05" />
|
||||
<PackageReference Include="System.Security.Claims" Version="4.3.0" />
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
<PackageReference Include="Nunit" Version="3.8.1" />
|
||||
<PackageReference Include="NUnit.ConsoleRunner" Version="3.7.0" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="3.8.0" />
|
||||
<packagereference Include="Microsoft.NET.Test.Sdk" Version="15.0.0"></packagereference>
|
||||
<packagereference Include="Microsoft.NET.Test.Sdk" Version="15.6.1"></packagereference>
|
||||
<PackageReference Include="Moq" Version="4.7.99" />
|
||||
</ItemGroup>
|
||||
|
||||
|
|
|
@ -6,12 +6,12 @@
|
|||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore" Version="2.0.2" />
|
||||
<PackageReference Include="Moq" Version="4.7.99" />
|
||||
<PackageReference Include="Nunit" Version="3.8.1" />
|
||||
<PackageReference Include="NUnit.ConsoleRunner" Version="3.7.0" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="3.8.0" />
|
||||
<packagereference Include="Microsoft.NET.Test.Sdk" Version="15.0.0"></packagereference>
|
||||
<packagereference Include="Microsoft.NET.Test.Sdk" Version="15.6.1"></packagereference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
@ -17,46 +17,49 @@ namespace Ombi.Schedule
|
|||
public JobSetup(IPlexContentSync plexContentSync, IRadarrSync radarrSync,
|
||||
IOmbiAutomaticUpdater updater, IEmbyContentSync embySync, IPlexUserImporter userImporter,
|
||||
IEmbyUserImporter embyUserImporter, ISonarrSync cache, ICouchPotatoSync cpCache,
|
||||
ISettingsService<JobSettings> jobsettings, ISickRageSync srSync)
|
||||
ISettingsService<JobSettings> jobsettings, ISickRageSync srSync, IRefreshMetadata refresh)
|
||||
{
|
||||
PlexContentSync = plexContentSync;
|
||||
RadarrSync = radarrSync;
|
||||
Updater = updater;
|
||||
EmbyContentSync = embySync;
|
||||
PlexUserImporter = userImporter;
|
||||
EmbyUserImporter = embyUserImporter;
|
||||
SonarrSync = cache;
|
||||
CpCache = cpCache;
|
||||
JobSettings = jobsettings;
|
||||
SrSync = srSync;
|
||||
_plexContentSync = plexContentSync;
|
||||
_radarrSync = radarrSync;
|
||||
_updater = updater;
|
||||
_embyContentSync = embySync;
|
||||
_plexUserImporter = userImporter;
|
||||
_embyUserImporter = embyUserImporter;
|
||||
_sonarrSync = cache;
|
||||
_cpCache = cpCache;
|
||||
_jobSettings = jobsettings;
|
||||
_srSync = srSync;
|
||||
_refreshMetadata = refresh;
|
||||
}
|
||||
|
||||
private IPlexContentSync PlexContentSync { get; }
|
||||
private IRadarrSync RadarrSync { get; }
|
||||
private IOmbiAutomaticUpdater Updater { get; }
|
||||
private IPlexUserImporter PlexUserImporter { get; }
|
||||
private IEmbyContentSync EmbyContentSync { get; }
|
||||
private IEmbyUserImporter EmbyUserImporter { get; }
|
||||
private ISonarrSync SonarrSync { get; }
|
||||
private ICouchPotatoSync CpCache { get; }
|
||||
private ISickRageSync SrSync { get; }
|
||||
private ISettingsService<JobSettings> JobSettings { get; set; }
|
||||
private readonly IPlexContentSync _plexContentSync;
|
||||
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;
|
||||
|
||||
public void Setup()
|
||||
{
|
||||
var s = JobSettings.GetSettings();
|
||||
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.CacheContent(), JobSettingsHelper.PlexContent(s));
|
||||
RecurringJob.AddOrUpdate(() => CpCache.Start(), JobSettingsHelper.CouchPotato(s));
|
||||
RecurringJob.AddOrUpdate(() => SrSync.Start(), JobSettingsHelper.SickRageSync(s));
|
||||
RecurringJob.AddOrUpdate(() => _embyContentSync.Start(), JobSettingsHelper.EmbyContent(s));
|
||||
RecurringJob.AddOrUpdate(() => _sonarrSync.Start(), JobSettingsHelper.Sonarr(s));
|
||||
RecurringJob.AddOrUpdate(() => _radarrSync.CacheContent(), JobSettingsHelper.Radarr(s));
|
||||
RecurringJob.AddOrUpdate(() => _plexContentSync.CacheContent(), JobSettingsHelper.PlexContent(s));
|
||||
RecurringJob.AddOrUpdate(() => _cpCache.Start(), JobSettingsHelper.CouchPotato(s));
|
||||
RecurringJob.AddOrUpdate(() => _srSync.Start(), JobSettingsHelper.SickRageSync(s));
|
||||
RecurringJob.AddOrUpdate(() => _refreshMetadata.Start(), JobSettingsHelper.RefreshMetadata(s));
|
||||
|
||||
RecurringJob.AddOrUpdate(() => Updater.Update(null), JobSettingsHelper.Updater(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(() => _embyUserImporter.Start(), JobSettingsHelper.UserImporter(s));
|
||||
RecurringJob.AddOrUpdate(() => _plexUserImporter.Start(), JobSettingsHelper.UserImporter(s));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
9
src/Ombi.Schedule/Jobs/Ombi/IRefreshMetadata.cs
Normal file
9
src/Ombi.Schedule/Jobs/Ombi/IRefreshMetadata.cs
Normal file
|
@ -0,0 +1,9 @@
|
|||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ombi.Schedule.Jobs.Ombi
|
||||
{
|
||||
public interface IRefreshMetadata : IBaseJob
|
||||
{
|
||||
Task Start();
|
||||
}
|
||||
}
|
249
src/Ombi.Schedule/Jobs/Ombi/RefreshMetadata.cs
Normal file
249
src/Ombi.Schedule/Jobs/Ombi/RefreshMetadata.cs
Normal file
|
@ -0,0 +1,249 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Ombi.Api.TheMovieDb;
|
||||
using Ombi.Api.TheMovieDb.Models;
|
||||
using Ombi.Api.TvMaze;
|
||||
using Ombi.Core.Settings;
|
||||
using Ombi.Core.Settings.Models.External;
|
||||
using Ombi.Helpers;
|
||||
using Ombi.Store.Entities;
|
||||
using Ombi.Store.Repository;
|
||||
using Ombi.Store.Repository.Requests;
|
||||
|
||||
namespace Ombi.Schedule.Jobs.Ombi
|
||||
{
|
||||
public class RefreshMetadata : IRefreshMetadata
|
||||
{
|
||||
public RefreshMetadata(IPlexContentRepository plexRepo, IEmbyContentRepository embyRepo,
|
||||
ILogger<RefreshMetadata> log, ITvMazeApi tvApi, ISettingsService<PlexSettings> plexSettings,
|
||||
IMovieDbApi movieApi)
|
||||
{
|
||||
_plexRepo = plexRepo;
|
||||
_embyRepo = embyRepo;
|
||||
_log = log;
|
||||
_movieApi = movieApi;
|
||||
_tvApi = tvApi;
|
||||
_plexSettings = plexSettings;
|
||||
}
|
||||
|
||||
private readonly IPlexContentRepository _plexRepo;
|
||||
private readonly IEmbyContentRepository _embyRepo;
|
||||
private readonly ILogger _log;
|
||||
private readonly IMovieDbApi _movieApi;
|
||||
private readonly ITvMazeApi _tvApi;
|
||||
private readonly ISettingsService<PlexSettings> _plexSettings;
|
||||
|
||||
public async Task Start()
|
||||
{
|
||||
_log.LogInformation("Starting the Metadata refresh");
|
||||
try
|
||||
{
|
||||
var settings = await _plexSettings.GetSettingsAsync();
|
||||
if (settings.Enable)
|
||||
{
|
||||
await StartPlex();
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_log.LogError(e, "Exception when refreshing the Plex Metadata");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task StartPlex()
|
||||
{
|
||||
await StartPlexMovies();
|
||||
|
||||
// Now Tv
|
||||
await StartPlexTv();
|
||||
}
|
||||
|
||||
private async Task StartPlexTv()
|
||||
{
|
||||
var allTv = _plexRepo.GetAll().Where(x =>
|
||||
x.Type == PlexMediaTypeEntity.Show && (!x.TheMovieDbId.HasValue() || !x.ImdbId.HasValue() || !x.TvDbId.HasValue()));
|
||||
var tvCount = 0;
|
||||
foreach (var show in allTv)
|
||||
{
|
||||
var hasImdb = show.ImdbId.HasValue();
|
||||
var hasTheMovieDb = show.TheMovieDbId.HasValue();
|
||||
var hasTvDbId = show.TvDbId.HasValue();
|
||||
|
||||
if (!hasTheMovieDb)
|
||||
{
|
||||
var id = await GetTheMovieDbId(hasTvDbId, hasImdb, show.TvDbId, show.ImdbId, show.Title);
|
||||
show.TheMovieDbId = id;
|
||||
}
|
||||
|
||||
if (!hasImdb)
|
||||
{
|
||||
var id = await GetImdbId(hasTheMovieDb, hasTvDbId, show.Title, show.TheMovieDbId, show.TvDbId);
|
||||
show.ImdbId = id;
|
||||
_plexRepo.UpdateWithoutSave(show);
|
||||
}
|
||||
|
||||
if (!hasTvDbId)
|
||||
{
|
||||
var id = await GetTvDbId(hasTheMovieDb, hasImdb, show.TheMovieDbId, show.ImdbId, show.Title);
|
||||
show.TvDbId = id;
|
||||
_plexRepo.UpdateWithoutSave(show);
|
||||
}
|
||||
tvCount++;
|
||||
if (tvCount >= 20)
|
||||
{
|
||||
await _plexRepo.SaveChangesAsync();
|
||||
tvCount = 0;
|
||||
}
|
||||
}
|
||||
await _plexRepo.SaveChangesAsync();
|
||||
}
|
||||
|
||||
private async Task StartPlexMovies()
|
||||
{
|
||||
var allMovies = _plexRepo.GetAll().Where(x =>
|
||||
x.Type == PlexMediaTypeEntity.Movie && (!x.TheMovieDbId.HasValue() || !x.ImdbId.HasValue()));
|
||||
int movieCount = 0;
|
||||
foreach (var movie in allMovies)
|
||||
{
|
||||
var hasImdb = movie.ImdbId.HasValue();
|
||||
var hasTheMovieDb = movie.TheMovieDbId.HasValue();
|
||||
// Movies don't really use TheTvDb
|
||||
|
||||
if (!hasImdb)
|
||||
{
|
||||
var imdbId = await GetImdbId(hasTheMovieDb, false, movie.Title, movie.TheMovieDbId, string.Empty);
|
||||
movie.ImdbId = imdbId;
|
||||
_plexRepo.UpdateWithoutSave(movie);
|
||||
}
|
||||
if (!hasTheMovieDb)
|
||||
{
|
||||
var id = await GetTheMovieDbId(false, hasImdb, string.Empty, movie.ImdbId, movie.Title);
|
||||
movie.TheMovieDbId = id;
|
||||
_plexRepo.UpdateWithoutSave(movie);
|
||||
}
|
||||
movieCount++;
|
||||
if (movieCount >= 20)
|
||||
{
|
||||
await _plexRepo.SaveChangesAsync();
|
||||
movieCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
await _plexRepo.SaveChangesAsync();
|
||||
}
|
||||
|
||||
private async Task<string> GetTheMovieDbId(bool hasTvDbId, bool hasImdb, string tvdbID, string imdbId, string title)
|
||||
{
|
||||
_log.LogInformation("The Media item {0} does not have a TheMovieDbId, searching for TheMovieDbId", title);
|
||||
FindResult result = null;
|
||||
var hasResult = false;
|
||||
if (hasTvDbId)
|
||||
{
|
||||
result = await _movieApi.Find(tvdbID, ExternalSource.tvdb_id);
|
||||
hasResult = result?.tv_results?.Length > 0;
|
||||
|
||||
_log.LogInformation("Setting Show {0} because we have TvDbId, result: {1}", title, hasResult);
|
||||
}
|
||||
if (hasImdb && !hasResult)
|
||||
{
|
||||
result = await _movieApi.Find(imdbId, ExternalSource.imdb_id);
|
||||
hasResult = result?.tv_results?.Length > 0;
|
||||
|
||||
_log.LogInformation("Setting Show {0} because we have ImdbId, result: {1}", title, hasResult);
|
||||
}
|
||||
if (hasResult)
|
||||
{
|
||||
return result.tv_results?[0]?.id.ToString() ?? string.Empty;
|
||||
}
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
private async Task<string> GetImdbId(bool hasTheMovieDb, bool hasTvDbId, string title, string theMovieDbId, string tvDbId)
|
||||
{
|
||||
_log.LogInformation("The media item {0} does not have a ImdbId, searching for ImdbId", title);
|
||||
// Looks like TV Maze does not provide the moviedb id, neither does the TV endpoint on TheMovieDb
|
||||
if (hasTheMovieDb)
|
||||
{
|
||||
_log.LogInformation("The show {0} has TheMovieDbId but not ImdbId, searching for ImdbId", title);
|
||||
if (int.TryParse(theMovieDbId, out var id))
|
||||
{
|
||||
var result = await _movieApi.GetTvExternals(id);
|
||||
|
||||
return result.imdb_id;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasTvDbId)
|
||||
{
|
||||
_log.LogInformation("The show {0} has tvdbid but not ImdbId, searching for ImdbId", title);
|
||||
if (int.TryParse(tvDbId, out var id))
|
||||
{
|
||||
var result = await _tvApi.ShowLookupByTheTvDbId(id);
|
||||
return result?.externals?.imdb;
|
||||
}
|
||||
}
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
|
||||
private async Task<string> GetTvDbId(bool hasTheMovieDb, bool hasImdb, string theMovieDbId, string imdbId, string title)
|
||||
{
|
||||
_log.LogInformation("The media item {0} does not have a TvDbId, searching for TvDbId", title);
|
||||
if (hasTheMovieDb)
|
||||
{
|
||||
_log.LogInformation("The show {0} has theMovieDBId but not ImdbId, searching for ImdbId", title);
|
||||
if (int.TryParse(theMovieDbId, out var id))
|
||||
{
|
||||
var result = await _movieApi.GetTvExternals(id);
|
||||
|
||||
return result.tvdb_id.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
if (hasImdb)
|
||||
{
|
||||
_log.LogInformation("The show {0} has ImdbId but not ImdbId, searching for ImdbId", title);
|
||||
var result = await _movieApi.Find(imdbId, ExternalSource.imdb_id);
|
||||
if (result?.tv_results?.Length > 0)
|
||||
{
|
||||
var movieId = result.tv_results?[0]?.id ?? 0;
|
||||
|
||||
var externalResult = await _movieApi.GetTvExternals(movieId);
|
||||
|
||||
return externalResult.imdb_id;
|
||||
}
|
||||
}
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
|
||||
private async Task StartEmby()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
private bool _disposed;
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (_disposed)
|
||||
return;
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
_plexRepo?.Dispose();
|
||||
_embyRepo?.Dispose();
|
||||
}
|
||||
_disposed = true;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -32,8 +32,10 @@
|
|||
<ProjectReference Include="..\Ombi.Api.Service\Ombi.Api.Service.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Api.SickRage\Ombi.Api.SickRage.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Api.Sonarr\Ombi.Api.Sonarr.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Api.TvMaze\Ombi.Api.TvMaze.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Notifications\Ombi.Notifications.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Settings\Ombi.Settings.csproj" />
|
||||
<ProjectReference Include="..\Ombi.TheMovieDbApi\Ombi.Api.TheMovieDb.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -79,9 +79,9 @@ namespace Ombi.Schedule.Processor
|
|||
Downloads = new List<Downloads>()
|
||||
};
|
||||
|
||||
var releaseTag = latestRelease.InnerText.Substring(0, 6);
|
||||
if (masterBranch)
|
||||
{
|
||||
var releaseTag = latestRelease.InnerText.Substring(0, 9);
|
||||
await GetGitubRelease(release, releaseTag);
|
||||
}
|
||||
else
|
||||
|
@ -148,7 +148,7 @@ namespace Ombi.Schedule.Processor
|
|||
var builds = await _api.Request<AppveyorBranchResult>(request);
|
||||
var jobId = builds.build.jobs.FirstOrDefault()?.jobId ?? string.Empty;
|
||||
|
||||
if (builds.build.finished == DateTime.MinValue)
|
||||
if (builds.build.finished == DateTime.MinValue || builds.build.status.Equals("failed"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -10,5 +10,6 @@
|
|||
public string AutomaticUpdater { get; set; }
|
||||
public string UserImporter { get; set; }
|
||||
public string SickRageSync { get; set; }
|
||||
public string RefreshMetadata { get; set; }
|
||||
}
|
||||
}
|
|
@ -39,6 +39,10 @@ namespace Ombi.Settings.Settings.Models
|
|||
{
|
||||
return Get(s.SickRageSync, Cron.Hourly(35));
|
||||
}
|
||||
public static string RefreshMetadata(JobSettings s)
|
||||
{
|
||||
return Get(s.RefreshMetadata, Cron.Daily(3));
|
||||
}
|
||||
|
||||
|
||||
private static string Get(string settings, string defaultCron)
|
||||
|
|
|
@ -10,10 +10,10 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="2.0.3" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="2.0.2" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.0.2" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.0.2" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
|
||||
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="1.1.9" />
|
||||
</ItemGroup>
|
||||
|
|
|
@ -22,5 +22,7 @@ namespace Ombi.Store.Repository
|
|||
Task DeleteEpisode(PlexEpisode content);
|
||||
void DeleteWithoutSave(PlexServerContent content);
|
||||
void DeleteWithoutSave(PlexEpisode content);
|
||||
Task UpdateRange(IEnumerable<PlexServerContent> existingContent);
|
||||
void UpdateWithoutSave(PlexServerContent existingContent);
|
||||
}
|
||||
}
|
|
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
|||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Query;
|
||||
using Ombi.Store.Entities;
|
||||
|
||||
|
@ -24,5 +25,6 @@ namespace Ombi.Store.Repository
|
|||
where TEntity : class;
|
||||
|
||||
Task ExecuteSql(string sql);
|
||||
DbSet<T> _db { get; }
|
||||
}
|
||||
}
|
|
@ -97,6 +97,16 @@ namespace Ombi.Store.Repository
|
|||
Db.PlexServerContent.Update(existingContent);
|
||||
await Db.SaveChangesAsync();
|
||||
}
|
||||
public void UpdateWithoutSave(PlexServerContent existingContent)
|
||||
{
|
||||
Db.PlexServerContent.Update(existingContent);
|
||||
}
|
||||
|
||||
public async Task UpdateRange(IEnumerable<PlexServerContent> existingContent)
|
||||
{
|
||||
Db.PlexServerContent.UpdateRange(existingContent);
|
||||
await Db.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public IQueryable<PlexEpisode> GetAllEpisodes()
|
||||
{
|
||||
|
|
|
@ -17,7 +17,7 @@ namespace Ombi.Store.Repository
|
|||
_ctx = ctx;
|
||||
_db = _ctx.Set<T>();
|
||||
}
|
||||
private readonly DbSet<T> _db;
|
||||
public DbSet<T> _db { get; }
|
||||
private readonly IOmbiContext _ctx;
|
||||
|
||||
public async Task<T> Find(object key)
|
||||
|
|
|
@ -7,13 +7,12 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="2.0.2" />
|
||||
<PackageReference Include="Moq" Version="4.7.99" />
|
||||
<PackageReference Include="Nunit" Version="3.8.1" />
|
||||
<PackageReference Include="NUnit.ConsoleRunner" Version="3.7.0" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="3.8.0" />
|
||||
<packagereference Include="Microsoft.NET.Test.Sdk" Version="15.0.0"></packagereference>
|
||||
<packagereference Include="Microsoft.NET.Test.Sdk" Version="15.6.1"></packagereference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
@ -15,5 +15,7 @@ namespace Ombi.Api.TheMovieDb
|
|||
Task<List<MovieSearchResult>> TopRated();
|
||||
Task<List<MovieSearchResult>> Upcoming();
|
||||
Task<List<MovieSearchResult>> SimilarMovies(int movieId);
|
||||
Task<FindResult> Find(string externalId, ExternalSource source);
|
||||
Task<TvExternals> GetTvExternals(int theMovieDbId);
|
||||
}
|
||||
}
|
52
src/Ombi.TheMovieDbApi/Models/FindResult.cs
Normal file
52
src/Ombi.TheMovieDbApi/Models/FindResult.cs
Normal file
|
@ -0,0 +1,52 @@
|
|||
namespace Ombi.Api.TheMovieDb.Models
|
||||
{
|
||||
public class FindResult
|
||||
{
|
||||
public Movie_Results[] movie_results { get; set; }
|
||||
public object[] person_results { get; set; }
|
||||
public TvResults[] tv_results { get; set; }
|
||||
public object[] tv_episode_results { get; set; }
|
||||
public object[] tv_season_results { get; set; }
|
||||
}
|
||||
|
||||
public class Movie_Results
|
||||
{
|
||||
public bool adult { get; set; }
|
||||
public string backdrop_path { get; set; }
|
||||
public int[] genre_ids { get; set; }
|
||||
public int id { get; set; }
|
||||
public string original_language { get; set; }
|
||||
public string original_title { get; set; }
|
||||
public string overview { get; set; }
|
||||
public string poster_path { get; set; }
|
||||
public string release_date { get; set; }
|
||||
public string title { get; set; }
|
||||
public bool video { get; set; }
|
||||
public float vote_average { get; set; }
|
||||
public int vote_count { get; set; }
|
||||
}
|
||||
|
||||
|
||||
public class TvResults
|
||||
{
|
||||
public string original_name { get; set; }
|
||||
public int id { get; set; }
|
||||
public string name { get; set; }
|
||||
public int vote_count { get; set; }
|
||||
public float vote_average { get; set; }
|
||||
public string first_air_date { get; set; }
|
||||
public string poster_path { get; set; }
|
||||
public int[] genre_ids { get; set; }
|
||||
public string original_language { get; set; }
|
||||
public string backdrop_path { get; set; }
|
||||
public string overview { get; set; }
|
||||
public string[] origin_country { get; set; }
|
||||
}
|
||||
|
||||
|
||||
public enum ExternalSource
|
||||
{
|
||||
imdb_id,
|
||||
tvdb_id
|
||||
}
|
||||
}
|
16
src/Ombi.TheMovieDbApi/Models/TvExternals.cs
Normal file
16
src/Ombi.TheMovieDbApi/Models/TvExternals.cs
Normal file
|
@ -0,0 +1,16 @@
|
|||
namespace Ombi.Api.TheMovieDb.Models
|
||||
{
|
||||
public class TvExternals
|
||||
{
|
||||
public string imdb_id { get; set; }
|
||||
public string freebase_mid { get; set; }
|
||||
public string freebase_id { get; set; }
|
||||
public int tvdb_id { get; set; }
|
||||
public int tvrage_id { get; set; }
|
||||
public string facebook_id { get; set; }
|
||||
public object instagram_id { get; set; }
|
||||
public object twitter_id { get; set; }
|
||||
public int id { get; set; }
|
||||
}
|
||||
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using AutoMapper;
|
||||
|
@ -25,15 +26,37 @@ namespace Ombi.Api.TheMovieDb
|
|||
{
|
||||
var request = new Request($"movie/{movieId}", BaseUri, HttpMethod.Get);
|
||||
request.FullUri = request.FullUri.AddQueryParameter("api_key", ApiToken);
|
||||
AddRetry(request);
|
||||
|
||||
var result = await Api.Request<MovieResponse>(request);
|
||||
return Mapper.Map<MovieResponseDto>(result);
|
||||
}
|
||||
|
||||
public async Task<FindResult> Find(string externalId, ExternalSource source)
|
||||
{
|
||||
var request = new Request($"find/{externalId}", BaseUri, HttpMethod.Get);
|
||||
request.FullUri = request.FullUri.AddQueryParameter("api_key", ApiToken);
|
||||
AddRetry(request);
|
||||
|
||||
request.AddQueryString("external_source", source.ToString());
|
||||
|
||||
return await Api.Request<FindResult>(request);
|
||||
}
|
||||
|
||||
public async Task<TvExternals> GetTvExternals(int theMovieDbId)
|
||||
{
|
||||
var request = new Request($"/tv/{theMovieDbId}/external_ids", BaseUri, HttpMethod.Get);
|
||||
request.FullUri = request.FullUri.AddQueryParameter("api_key", ApiToken);
|
||||
AddRetry(request);
|
||||
|
||||
return await Api.Request<TvExternals>(request);
|
||||
}
|
||||
|
||||
public async Task<List<MovieSearchResult>> SimilarMovies(int movieId)
|
||||
{
|
||||
var request = new Request($"movie/{movieId}/similar", BaseUri, HttpMethod.Get);
|
||||
request.FullUri = request.FullUri.AddQueryParameter("api_key", ApiToken);
|
||||
AddRetry(request);
|
||||
|
||||
var result = await Api.Request<TheMovieDbContainer<SearchResult>>(request);
|
||||
return Mapper.Map<List<MovieSearchResult>>(result.results);
|
||||
|
@ -44,6 +67,7 @@ namespace Ombi.Api.TheMovieDb
|
|||
var request = new Request($"movie/{movieId}", BaseUri, HttpMethod.Get);
|
||||
request.FullUri = request.FullUri.AddQueryParameter("api_key", ApiToken);
|
||||
request.FullUri = request.FullUri.AddQueryParameter("append_to_response", "videos,release_dates");
|
||||
AddRetry(request);
|
||||
var result = await Api.Request<MovieResponse>(request);
|
||||
return Mapper.Map<MovieResponseDto>(result);
|
||||
}
|
||||
|
@ -53,6 +77,7 @@ namespace Ombi.Api.TheMovieDb
|
|||
var request = new Request($"search/movie", BaseUri, HttpMethod.Get);
|
||||
request.FullUri = request.FullUri.AddQueryParameter("api_key", ApiToken);
|
||||
request.FullUri = request.FullUri.AddQueryParameter("query", searchTerm);
|
||||
AddRetry(request);
|
||||
|
||||
var result = await Api.Request<TheMovieDbContainer<SearchResult>>(request);
|
||||
return Mapper.Map<List<MovieSearchResult>>(result.results);
|
||||
|
@ -62,6 +87,7 @@ namespace Ombi.Api.TheMovieDb
|
|||
{
|
||||
var request = new Request($"movie/popular", BaseUri, HttpMethod.Get);
|
||||
request.FullUri = request.FullUri.AddQueryParameter("api_key", ApiToken);
|
||||
AddRetry(request);
|
||||
var result = await Api.Request<TheMovieDbContainer<SearchResult>>(request);
|
||||
return Mapper.Map<List<MovieSearchResult>>(result.results);
|
||||
}
|
||||
|
@ -70,6 +96,7 @@ namespace Ombi.Api.TheMovieDb
|
|||
{
|
||||
var request = new Request($"movie/top_rated", BaseUri, HttpMethod.Get);
|
||||
request.FullUri = request.FullUri.AddQueryParameter("api_key", ApiToken);
|
||||
AddRetry(request);
|
||||
var result = await Api.Request<TheMovieDbContainer<SearchResult>>(request);
|
||||
return Mapper.Map<List<MovieSearchResult>>(result.results);
|
||||
}
|
||||
|
@ -78,6 +105,7 @@ namespace Ombi.Api.TheMovieDb
|
|||
{
|
||||
var request = new Request($"movie/upcoming", BaseUri, HttpMethod.Get);
|
||||
request.FullUri = request.FullUri.AddQueryParameter("api_key", ApiToken);
|
||||
AddRetry(request);
|
||||
var result = await Api.Request<TheMovieDbContainer<SearchResult>>(request);
|
||||
return Mapper.Map<List<MovieSearchResult>>(result.results);
|
||||
}
|
||||
|
@ -86,9 +114,14 @@ namespace Ombi.Api.TheMovieDb
|
|||
{
|
||||
var request = new Request($"movie/now_playing", BaseUri, HttpMethod.Get);
|
||||
request.FullUri = request.FullUri.AddQueryParameter("api_key", ApiToken);
|
||||
AddRetry(request);
|
||||
var result = await Api.Request<TheMovieDbContainer<SearchResult>>(request);
|
||||
return Mapper.Map<List<MovieSearchResult>>(result.results);
|
||||
}
|
||||
|
||||
private static void AddRetry(Request request)
|
||||
{
|
||||
request.Retry = true;
|
||||
request.StatusCodeToRetry.Add((HttpStatusCode)429);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,14 +12,14 @@
|
|||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CommandLineParser" Version="2.1.1-beta" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.CommandLine" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration" Version="2.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="2.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.CommandLine" Version="2.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="2.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="2.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="2.0.1" />
|
||||
<PackageReference Include="Serilog" Version="2.6.0-dev-00892" />
|
||||
<PackageReference Include="Serilog.Extensions.Logging" Version="2.0.2" />
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="3.2.0" />
|
||||
|
|
|
@ -125,6 +125,7 @@ export interface IJobSettings {
|
|||
automaticUpdater: string;
|
||||
userImporter: string;
|
||||
sickRageSync: string;
|
||||
refreshMetadata: string;
|
||||
}
|
||||
|
||||
export interface IIssueSettings extends ISettings {
|
||||
|
@ -193,3 +194,18 @@ export interface IDogNzbSettings extends ISettings {
|
|||
export interface IIssueCategory extends ISettings {
|
||||
value: string;
|
||||
}
|
||||
|
||||
export interface ICronTestModel {
|
||||
success: boolean;
|
||||
message: string;
|
||||
schedule: Date[];
|
||||
}
|
||||
|
||||
export interface ICronViewModelBody {
|
||||
expression: string;
|
||||
}
|
||||
|
||||
export interface IJobSettingsViewModel {
|
||||
result: boolean;
|
||||
message: string;
|
||||
}
|
||||
|
|
|
@ -7,6 +7,8 @@ import {
|
|||
IAbout,
|
||||
IAuthenticationSettings,
|
||||
ICouchPotatoSettings,
|
||||
ICronTestModel,
|
||||
ICronViewModelBody,
|
||||
ICustomizationSettings,
|
||||
IDiscordNotifcationSettings,
|
||||
IDogNzbSettings,
|
||||
|
@ -14,6 +16,7 @@ import {
|
|||
IEmbySettings,
|
||||
IIssueSettings,
|
||||
IJobSettings,
|
||||
IJobSettingsViewModel,
|
||||
ILandingPageSettings,
|
||||
IMattermostNotifcationSettings,
|
||||
IMobileNotifcationSettings,
|
||||
|
@ -231,10 +234,15 @@ export class SettingsService extends ServiceHelpers {
|
|||
return this.http.get<IJobSettings>(`${this.url}/jobs`, {headers: this.headers});
|
||||
}
|
||||
|
||||
public saveJobSettings(settings: IJobSettings): Observable<boolean> {
|
||||
public saveJobSettings(settings: IJobSettings): Observable<IJobSettingsViewModel> {
|
||||
return this.http
|
||||
.post<boolean>(`${this.url}/jobs`, JSON.stringify(settings), {headers: this.headers});
|
||||
}
|
||||
.post<IJobSettingsViewModel>(`${this.url}/jobs`, JSON.stringify(settings), {headers: this.headers});
|
||||
}
|
||||
|
||||
public testCron(body: ICronViewModelBody): Observable<ICronTestModel> {
|
||||
return this.http
|
||||
.post<ICronTestModel>(`${this.url}/testcron`, JSON.stringify(body), {headers: this.headers});
|
||||
}
|
||||
|
||||
public getSickRageSettings(): Observable<ISickRageSettings> {
|
||||
return this.http.get<ISickRageSettings>(`${this.url}/sickrage`, {headers: this.headers});
|
||||
|
|
|
@ -12,29 +12,34 @@
|
|||
<label for="sonarrSync" class="control-label">Sonarr Sync</label>
|
||||
<input type="text" class="form-control form-control-custom" [ngClass]="{'form-error': form.get('sonarrSync').hasError('required')}" id="sonarrSync" name="sonarrSync" formControlName="sonarrSync">
|
||||
<small *ngIf="form.get('sonarrSync').hasError('required')" class="error-text">The Sonarr Sync is required</small>
|
||||
<button type="button" class="btn btn-sm btn-primary-outline" (click)="testCron(form.get('sonarrSync')?.value)">Test</button>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="sickRageSync" class="control-label">SickRage Sync</label>
|
||||
<input type="text" class="form-control form-control-custom" [ngClass]="{'form-error': form.get('sonarrSync').hasError('required')}" id="sickRageSync" name="sickRageSync" formControlName="sickRageSync">
|
||||
<small *ngIf="form.get('sickRageSync').hasError('required')" class="error-text">The SickRage Sync is required</small>
|
||||
<button type="button" class="btn btn-sm btn-primary-outline" (click)="testCron(form.get('sickRageSync')?.value)">Test</button>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="radarrSync" class="control-label">Radarr Sync</label>
|
||||
<input type="text" class="form-control form-control-custom" [ngClass]="{'form-error': form.get('radarrSync').hasError('required')}" id="radarrSync" name="radarrSync" formControlName="radarrSync">
|
||||
<small *ngIf="form.get('radarrSync').hasError('required')" class="error-text">The Radarr Sync is required</small>
|
||||
<button type="button" class="btn btn-sm btn-primary-outline" (click)="testCron(form.get('radarrSync')?.value)">Test</button>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="couchPotatoSync" class="control-label">CouchPotato Sync</label>
|
||||
<input type="text" class="form-control form-control-custom" [ngClass]="{'form-error': form.get('radarrSync').hasError('required')}" id="couchPotatoSync" name="couchPotatoSync" formControlName="couchPotatoSync">
|
||||
<small *ngIf="form.get('couchPotatoSync').hasError('required')" class="error-text">The CouchPotato Sync is required</small>
|
||||
<button type="button" class="btn btn-sm btn-primary-outline" (click)="testCron(form.get('couchPotatoSync')?.value)">Test</button>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="automaticUpdater" class="control-label">Automatic Update</label>
|
||||
<input type="text" class="form-control form-control-custom" [ngClass]="{'form-error': form.get('automaticUpdater').hasError('required')}" id="automaticUpdater" name="automaticUpdater" formControlName="automaticUpdater">
|
||||
<small *ngIf="form.get('automaticUpdater').hasError('required')" class="error-text">The Automatic Update is required</small>
|
||||
<button type="button" class="btn btn-sm btn-primary-outline" (click)="testCron(form.get('automaticUpdater')?.value)">Test</button>
|
||||
</div>
|
||||
|
||||
|
||||
|
@ -50,21 +55,37 @@
|
|||
<label for="plexContentSync" class="control-label">Plex Sync</label>
|
||||
<input type="text" class="form-control form-control-custom" [ngClass]="{'form-error': form.get('plexContentSync').hasError('required')}" id="plexContentSync" name="plexContentSync" formControlName="plexContentSync">
|
||||
<small *ngIf="form.get('plexContentSync').hasError('required')" class="error-text">The Plex Sync is required</small>
|
||||
<button type="button" class="btn btn-sm btn-primary-outline" (click)="testCron(form.get('plexContentSync')?.value)">Test</button>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="embyContentSync" class="control-label">Emby Sync</label>
|
||||
<input type="text" class="form-control form-control-custom" [ngClass]="{'form-error': form.get('embyContentSync').hasError('required')}" id="embyContentSync" name="embyContentSync" formControlName="embyContentSync">
|
||||
<small *ngIf="form.get('embyContentSync').hasError('required')" class="error-text">The Emby Sync is required</small>
|
||||
<button type="button" class="btn btn-sm btn-primary-outline" (click)="testCron(form.get('embyContentSync')?.value)">Test</button>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="userImporter" class="control-label">User Importer</label>
|
||||
<input type="text" class="form-control form-control-custom" [ngClass]="{'form-error': form.get('userImporter').hasError('required')}" id="userImporter" name="userImporter" formControlName="userImporter">
|
||||
<small *ngIf="form.get('userImporter').hasError('required')" class="error-text">The User Importer is required</small>
|
||||
<button type="button" class="btn btn-sm btn-primary-outline" (click)="testCron(form.get('userImporter')?.value)">Test</button>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="userImporter" class="control-label">Refresh Metadata</label>
|
||||
<input type="text" class="form-control form-control-custom" [ngClass]="{'form-error': form.get('refreshMetadata').hasError('required')}" id="refreshMetadata" name="refreshMetadata" formControlName="refreshMetadata">
|
||||
<small *ngIf="form.get('refreshMetadata').hasError('required')" class="error-text">The Refresh Metadata is required</small>
|
||||
<button type="button" class="btn btn-sm btn-primary-outline" (click)="testCron(form.get('refreshMetadata')?.value)">Test</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</form>
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p-dialog header="CRON Schedule" [(visible)]="displayTest">
|
||||
<ul *ngIf="testModel">
|
||||
<li *ngFor="let item of testModel.schedule">{{item | date:'short'}}</li>
|
||||
</ul>
|
||||
</p-dialog>
|
|
@ -1,7 +1,10 @@
|
|||
import { Component, OnInit } from "@angular/core";
|
||||
|
||||
import { FormBuilder, FormGroup, Validators } from "@angular/forms";
|
||||
import { NotificationService, SettingsService } from "../../services";
|
||||
|
||||
import { ICronTestModel } from "./../../interfaces";
|
||||
|
||||
@Component({
|
||||
templateUrl: "./jobs.component.html",
|
||||
})
|
||||
|
@ -10,6 +13,8 @@ export class JobsComponent implements OnInit {
|
|||
public form: FormGroup;
|
||||
|
||||
public profilesRunning: boolean;
|
||||
public testModel: ICronTestModel;
|
||||
public displayTest: boolean;
|
||||
|
||||
constructor(private readonly settingsService: SettingsService,
|
||||
private readonly fb: FormBuilder,
|
||||
|
@ -26,7 +31,19 @@ export class JobsComponent implements OnInit {
|
|||
sonarrSync: [x.radarrSync, Validators.required],
|
||||
radarrSync: [x.sonarrSync, Validators.required],
|
||||
sickRageSync: [x.sickRageSync, Validators.required],
|
||||
});
|
||||
refreshMetadata: [x.refreshMetadata, Validators.required],
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public testCron(expression: string) {
|
||||
this.settingsService.testCron({ expression }).subscribe(x => {
|
||||
if(x.success) {
|
||||
this.testModel = x;
|
||||
this.displayTest = true;
|
||||
} else {
|
||||
this.notificationService.error(x.message);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -37,10 +54,10 @@ export class JobsComponent implements OnInit {
|
|||
}
|
||||
const settings = form.value;
|
||||
this.settingsService.saveJobSettings(settings).subscribe(x => {
|
||||
if (x) {
|
||||
if (x.result) {
|
||||
this.notificationService.success("Successfully saved the job settings");
|
||||
} else {
|
||||
this.notificationService.success("There was an error when saving the job settings");
|
||||
this.notificationService.error("There was an error when saving the job settings. " + x.message);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@ import { WikiComponent } from "./wiki.component";
|
|||
|
||||
import { SettingsMenuComponent } from "./settingsmenu.component";
|
||||
|
||||
import { AutoCompleteModule, CalendarModule, InputSwitchModule, InputTextModule, MenuModule, RadioButtonModule, TooltipModule } from "primeng/primeng";
|
||||
import { AutoCompleteModule, CalendarModule, DialogModule, InputSwitchModule, InputTextModule, MenuModule, RadioButtonModule, TooltipModule } from "primeng/primeng";
|
||||
|
||||
const routes: Routes = [
|
||||
{ path: "Ombi", component: OmbiComponent, canActivate: [AuthGuard] },
|
||||
|
@ -88,6 +88,7 @@ const routes: Routes = [
|
|||
ClipboardModule,
|
||||
PipeModule,
|
||||
RadioButtonModule,
|
||||
DialogModule,
|
||||
],
|
||||
declarations: [
|
||||
SettingsMenuComponent,
|
||||
|
@ -139,4 +140,4 @@ const routes: Routes = [
|
|||
],
|
||||
|
||||
})
|
||||
export class SettingsModule { }
|
||||
export class SettingsModule { }
|
|
@ -5,15 +5,18 @@ using System.Linq;
|
|||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using AutoMapper;
|
||||
using Hangfire;
|
||||
using Hangfire.RecurringJobExtensions;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using Microsoft.Extensions.PlatformAbstractions;
|
||||
using NCrontab;
|
||||
using Ombi.Api.Emby;
|
||||
using Ombi.Attributes;
|
||||
using Ombi.Core.Models.UI;
|
||||
|
@ -465,7 +468,8 @@ namespace Ombi.Controllers
|
|||
j.PlexContentSync = j.PlexContentSync.HasValue() ? j.PlexContentSync : JobSettingsHelper.PlexContent(j);
|
||||
j.UserImporter = j.UserImporter.HasValue() ? j.UserImporter : JobSettingsHelper.UserImporter(j);
|
||||
j.SickRageSync = j.SickRageSync.HasValue() ? j.SickRageSync : JobSettingsHelper.SickRageSync(j);
|
||||
|
||||
j.RefreshMetadata = j.RefreshMetadata.HasValue() ? j.RefreshMetadata : JobSettingsHelper.RefreshMetadata(j);
|
||||
|
||||
return j;
|
||||
}
|
||||
|
||||
|
@ -475,9 +479,71 @@ namespace Ombi.Controllers
|
|||
/// <param name="settings">The settings.</param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("jobs")]
|
||||
public async Task<bool> JobSettings([FromBody]JobSettings settings)
|
||||
public async Task<JobSettingsViewModel> JobSettings([FromBody]JobSettings settings)
|
||||
{
|
||||
return await Save(settings);
|
||||
// Verify that we have correct CRON's
|
||||
foreach (var propertyInfo in settings.GetType()
|
||||
.GetProperties(BindingFlags.Public | BindingFlags.Instance))
|
||||
{
|
||||
if (propertyInfo.Name.Equals("Id", StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
var expression = (string)propertyInfo.GetValue(settings, null);
|
||||
|
||||
try
|
||||
{
|
||||
var r = CrontabSchedule.TryParse(expression);
|
||||
if (r == null)
|
||||
{
|
||||
return new JobSettingsViewModel
|
||||
{
|
||||
Message = $"{propertyInfo.Name} does not have a valid CRON Expression"
|
||||
};
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return new JobSettingsViewModel
|
||||
{
|
||||
Message = $"{propertyInfo.Name} does not have a valid CRON Expression"
|
||||
};
|
||||
}
|
||||
}
|
||||
var result = await Save(settings);
|
||||
|
||||
return new JobSettingsViewModel
|
||||
{
|
||||
Result = result
|
||||
};
|
||||
}
|
||||
|
||||
[HttpPost("testcron")]
|
||||
public CronTestModel TestCron([FromBody] CronViewModelBody body)
|
||||
{
|
||||
var model = new CronTestModel();
|
||||
try
|
||||
{
|
||||
var time = DateTime.UtcNow;
|
||||
var result = CrontabSchedule.TryParse(body.Expression);
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
var next = result.GetNextOccurrence(time);
|
||||
model.Schedule.Add(next);
|
||||
time = next;
|
||||
}
|
||||
model.Success = true;
|
||||
return model;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return new CronTestModel
|
||||
{
|
||||
Message = $"CRON Expression {body.Expression} is not valid"
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
12
src/Ombi/Models/CronTestModel.cs
Normal file
12
src/Ombi/Models/CronTestModel.cs
Normal file
|
@ -0,0 +1,12 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ombi.Models
|
||||
{
|
||||
public class CronTestModel
|
||||
{
|
||||
public bool Success { get; set; }
|
||||
public string Message { get; set; }
|
||||
public List<DateTime> Schedule { get; set; } = new List<DateTime>();
|
||||
}
|
||||
}
|
7
src/Ombi/Models/CronViewModelBody.cs
Normal file
7
src/Ombi/Models/CronViewModelBody.cs
Normal file
|
@ -0,0 +1,7 @@
|
|||
namespace Ombi.Models
|
||||
{
|
||||
public class CronViewModelBody
|
||||
{
|
||||
public string Expression { get; set; }
|
||||
}
|
||||
}
|
8
src/Ombi/Models/JobSettingsViewModel.cs
Normal file
8
src/Ombi/Models/JobSettingsViewModel.cs
Normal file
|
@ -0,0 +1,8 @@
|
|||
namespace Ombi.Models
|
||||
{
|
||||
public class JobSettingsViewModel
|
||||
{
|
||||
public bool Result { get; set; }
|
||||
public string Message { get; set; }
|
||||
}
|
||||
}
|
|
@ -65,11 +65,12 @@
|
|||
<PackageReference Include="Hangfire.RecurringJobExtensions" Version="1.1.6" />
|
||||
<PackageReference Include="Hangfire.SQLite" Version="1.4.2" />
|
||||
|
||||
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.CommandLine" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Web.BrowserLink" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.6" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.CommandLine" Version="2.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="2.0.1" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Web.BrowserLink" Version="2.0.2" />
|
||||
<PackageReference Include="MiniProfiler.AspNetCore.Mvc" Version="4.0.0-alpha6-79" />
|
||||
<PackageReference Include="ncrontab" Version="3.3.0" />
|
||||
<PackageReference Include="Serilog" Version="2.6.0-dev-00892" />
|
||||
<PackageReference Include="Serilog.Extensions.Logging" Version="2.0.2" />
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="3.2.0" />
|
||||
|
|
Binary file not shown.
Loading…
Add table
Add a link
Reference in a new issue