mirror of
https://github.com/Ombi-app/Ombi.git
synced 2025-07-11 15:56:05 -07:00
Fixed logo on reset password pages
fixed the run importer button on the user management settings Added root and qulaity profile selection for movies #1517 Added the Sonarr Cacher #1513 Refactored what we do to tv searches to use the rules engine Cache a few more things to speed some searches up
This commit is contained in:
parent
aad5c2a4bc
commit
9ae5ad0ecd
43 changed files with 1313 additions and 281 deletions
|
@ -10,9 +10,7 @@ using System.Linq;
|
|||
using System.Security.Principal;
|
||||
using System.Threading.Tasks;
|
||||
using Ombi.Core.Rule.Interfaces;
|
||||
using StackExchange.Profiling;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using Ombi.Api.Trakt;
|
||||
using Ombi.Core.Authentication;
|
||||
using Ombi.Helpers;
|
||||
|
||||
|
@ -55,22 +53,14 @@ namespace Ombi.Core.Engine
|
|||
/// <returns></returns>
|
||||
public async Task<IEnumerable<SearchMovieViewModel>> Search(string search)
|
||||
{
|
||||
using (MiniProfiler.Current.Step("Starting Movie Search Engine"))
|
||||
using (MiniProfiler.Current.Step("Searching Movie"))
|
||||
var result = await MovieApi.SearchMovie(search);
|
||||
|
||||
if (result != null)
|
||||
{
|
||||
var result = await MovieApi.SearchMovie(search);
|
||||
|
||||
using (MiniProfiler.Current.Step("Fin API, Transforming"))
|
||||
{
|
||||
if (result != null)
|
||||
{
|
||||
Logger.LogDebug("Search Result: {result}", result);
|
||||
return await TransformMovieResultsToResponse(result.Take(10)); // Take 10 to stop us overloading the API
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
Logger.LogDebug("Search Result: {result}", result);
|
||||
return await TransformMovieResultsToResponse(result.Take(10)); // Take 10 to stop us overloading the API
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -174,7 +164,7 @@ namespace Ombi.Core.Engine
|
|||
// So set the ImdbId to viewMovie.Id and then set it back afterwards
|
||||
var oldId = viewMovie.Id;
|
||||
viewMovie.CustomId = viewMovie.ImdbId ?? string.Empty;
|
||||
|
||||
|
||||
await RunSearchRules(viewMovie);
|
||||
|
||||
viewMovie.Id = oldId;
|
||||
|
|
|
@ -15,11 +15,7 @@ using System.Linq;
|
|||
using System.Security.Principal;
|
||||
using System.Threading.Tasks;
|
||||
using Ombi.Core.Rule.Interfaces;
|
||||
using Ombi.Store.Entities.Requests;
|
||||
using Ombi.Store.Repository.Requests;
|
||||
using Ombi.Store.Entities;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using Ombi.Core.Authentication;
|
||||
using Ombi.Helpers;
|
||||
|
@ -117,11 +113,7 @@ namespace Ombi.Core.Engine
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
var existingRequests = await GetTvRequests();
|
||||
var plexSettings = await PlexSettings.GetSettingsAsync();
|
||||
var embySettings = await EmbySettings.GetSettingsAsync();
|
||||
return await ProcessResult(mapped, existingRequests, plexSettings, embySettings);
|
||||
return await ProcessResult(mapped);
|
||||
}
|
||||
|
||||
public async Task<TreeNode<SearchTvShowViewModel>> GetShowInformationTreeNode(int tvdbid)
|
||||
|
@ -189,127 +181,21 @@ namespace Ombi.Core.Engine
|
|||
};
|
||||
}
|
||||
|
||||
|
||||
private async Task<IEnumerable<SearchTvShowViewModel>> ProcessResults<T>(IEnumerable<T> items)
|
||||
{
|
||||
var existingRequests = await GetTvRequests();
|
||||
|
||||
var plexSettings = await PlexSettings.GetSettingsAsync();
|
||||
var embySettings = await EmbySettings.GetSettingsAsync();
|
||||
|
||||
var retVal = new List<SearchTvShowViewModel>();
|
||||
foreach (var tvMazeSearch in items)
|
||||
{
|
||||
var viewT = Mapper.Map<SearchTvShowViewModel>(tvMazeSearch);
|
||||
retVal.Add(await ProcessResult(viewT, existingRequests, plexSettings, embySettings));
|
||||
retVal.Add(await ProcessResult(viewT));
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
private async Task<SearchTvShowViewModel> ProcessResult(SearchTvShowViewModel item, Dictionary<int, TvRequests> existingRequests, PlexSettings plexSettings, EmbySettings embySettings)
|
||||
private async Task<SearchTvShowViewModel> ProcessResult(SearchTvShowViewModel item)
|
||||
{
|
||||
if (embySettings.Enable)
|
||||
{
|
||||
var content = await EmbyContentRepo.Get(item.Id.ToString());
|
||||
|
||||
if (content != null)
|
||||
{
|
||||
item.Available = true;
|
||||
}
|
||||
|
||||
// Let's go through the episodes now
|
||||
if (item.SeasonRequests.Any())
|
||||
{
|
||||
var allEpisodes = EmbyContentRepo.GetAllEpisodes().Include(x => x.Series);
|
||||
foreach (var season in item.SeasonRequests)
|
||||
{
|
||||
foreach (var episode in season.Episodes)
|
||||
{
|
||||
var epExists = await allEpisodes.FirstOrDefaultAsync(x =>
|
||||
x.EpisodeNumber == episode.EpisodeNumber && x.SeasonNumber == season.SeasonNumber && item.Id.ToString() == x.Series.ProviderId);
|
||||
if (epExists != null)
|
||||
{
|
||||
episode.Available = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (plexSettings.Enable)
|
||||
{
|
||||
var content = await PlexContentRepo.Get(item.Id.ToString());
|
||||
|
||||
if (content != null)
|
||||
{
|
||||
item.Available = true;
|
||||
item.PlexUrl = content.Url;
|
||||
}
|
||||
// Let's go through the episodes now
|
||||
if (item.SeasonRequests.Any())
|
||||
{
|
||||
var allEpisodes = PlexContentRepo.GetAllEpisodes();
|
||||
foreach (var season in item.SeasonRequests)
|
||||
{
|
||||
foreach (var episode in season.Episodes)
|
||||
{
|
||||
var epExists = await allEpisodes.FirstOrDefaultAsync(x =>
|
||||
x.EpisodeNumber == episode.EpisodeNumber && x.SeasonNumber == season.SeasonNumber && x.Series.ProviderId == item.Id.ToString());
|
||||
if (epExists != null)
|
||||
{
|
||||
episode.Available = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (item.SeasonRequests.Any() && item.SeasonRequests.All(x => x.Episodes.All(e => e.Approved)))
|
||||
{
|
||||
item.FullyAvailable = true;
|
||||
}
|
||||
|
||||
if (item.Id > 0)
|
||||
{
|
||||
var tvdbid = item.Id;
|
||||
if (existingRequests.ContainsKey(tvdbid))
|
||||
{
|
||||
var existingRequest = existingRequests[tvdbid];
|
||||
|
||||
item.Requested = true;
|
||||
item.Approved = existingRequest.ChildRequests.Any(x => x.Approved);
|
||||
|
||||
// Let's modify the seasonsrequested to reflect what we have requested...
|
||||
foreach (var season in item.SeasonRequests)
|
||||
{
|
||||
foreach (var existingRequestChildRequest in existingRequest.ChildRequests)
|
||||
{
|
||||
// Find the existing request season
|
||||
var existingSeason =
|
||||
existingRequestChildRequest.SeasonRequests.FirstOrDefault(x => x.SeasonNumber == season.SeasonNumber);
|
||||
if (existingSeason == null) continue;
|
||||
|
||||
foreach (var ep in existingSeason.Episodes)
|
||||
{
|
||||
// Find the episode from what we are searching
|
||||
var episodeSearching = season.Episodes.FirstOrDefault(x => x.EpisodeNumber == ep.EpisodeNumber);
|
||||
if (episodeSearching == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
episodeSearching.Requested = true;
|
||||
episodeSearching.Available = ep.Available;
|
||||
episodeSearching.Approved = ep.Season.ChildRequest.Approved;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// TODO CHECK SONARR/RADARR
|
||||
//if (sonarrCached.Select(x => x.TvdbId).Contains(tvdbid) || sickRageCache.Contains(tvdbid))
|
||||
// // compare to the sonarr/sickrage db
|
||||
//{
|
||||
// item.Requested = true;
|
||||
//}
|
||||
}
|
||||
item.CustomId = item.Id.ToString();
|
||||
await RunSearchRules(item);
|
||||
|
||||
return item;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Ombi.Store.Entities;
|
||||
|
||||
namespace Ombi.Core.Models.Search
|
||||
{
|
||||
|
@ -22,5 +23,8 @@ namespace Ombi.Core.Models.Search
|
|||
public string Trailer { get; set; }
|
||||
public string Homepage { get; set; }
|
||||
public string ImdbId { get; set; }
|
||||
public int RootPathOverride { get; set; }
|
||||
public int QualityOverride { get; set; }
|
||||
public override RequestType Type => RequestType.Movie;
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
using Ombi.Core.Models.Requests;
|
||||
using Ombi.Store.Repository.Requests;
|
||||
using System.Collections.Generic;
|
||||
using Ombi.Store.Entities;
|
||||
|
||||
namespace Ombi.Core.Models.Search
|
||||
{
|
||||
|
@ -54,5 +55,7 @@ namespace Ombi.Core.Models.Search
|
|||
/// This is where we have EVERY Episode for that series
|
||||
/// </summary>
|
||||
public bool FullyAvailable { get; set; }
|
||||
|
||||
public override RequestType Type => RequestType.TvShow;
|
||||
}
|
||||
}
|
|
@ -1,8 +1,9 @@
|
|||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using Ombi.Store.Entities;
|
||||
|
||||
namespace Ombi.Core.Models.Search
|
||||
{
|
||||
public class SearchViewModel
|
||||
public abstract class SearchViewModel
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public bool Approved { get; set; }
|
||||
|
@ -10,6 +11,7 @@ namespace Ombi.Core.Models.Search
|
|||
public bool Available { get; set; }
|
||||
public string PlexUrl { get; set; }
|
||||
public string Quality { get; set; }
|
||||
public abstract RequestType Type { get; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
using System.Threading.Tasks;
|
||||
using Ombi.Core.Models.Requests;
|
||||
|
||||
namespace Ombi.Core.Rule.Interfaces
|
||||
{
|
||||
public interface IRules<T> where T : new()
|
||||
public interface IRules<T>
|
||||
{
|
||||
Task<RuleResult> Execute(T obj);
|
||||
}
|
||||
|
|
|
@ -73,7 +73,7 @@ namespace Ombi.Core.Rule
|
|||
}
|
||||
|
||||
|
||||
private void GetTypes<T>(IServiceProvider provider, Assembly ass, string baseSearchType, List<IRules<T>> ruleList) where T : new()
|
||||
private void GetTypes<T>(IServiceProvider provider, Assembly ass, string baseSearchType, List<IRules<T>> ruleList)
|
||||
{
|
||||
foreach (var ti in ass.DefinedTypes)
|
||||
{
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
using System.Threading.Tasks;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Ombi.Core.Models.Search;
|
||||
using Ombi.Core.Rule.Interfaces;
|
||||
using Ombi.Store.Entities;
|
||||
using Ombi.Store.Repository;
|
||||
|
||||
namespace Ombi.Core.Rule.Rules.Search
|
||||
|
@ -20,6 +23,28 @@ namespace Ombi.Core.Rule.Rules.Search
|
|||
if (item != null)
|
||||
{
|
||||
obj.Available = true;
|
||||
|
||||
if (obj.Type == RequestType.TvShow)
|
||||
{
|
||||
var searchResult = (SearchTvShowViewModel)obj;
|
||||
// Let's go through the episodes now
|
||||
if (searchResult.SeasonRequests.Any())
|
||||
{
|
||||
var allEpisodes = EmbyContentRepository.GetAllEpisodes().Include(x => x.Series);
|
||||
foreach (var season in searchResult.SeasonRequests)
|
||||
{
|
||||
foreach (var episode in season.Episodes)
|
||||
{
|
||||
var epExists = await allEpisodes.FirstOrDefaultAsync(x =>
|
||||
x.EpisodeNumber == episode.EpisodeNumber && x.SeasonNumber == season.SeasonNumber && item.ProviderId.ToString() == x.Series.ProviderId);
|
||||
if (epExists != null)
|
||||
{
|
||||
episode.Available = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return Success();
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ using System.Threading.Tasks;
|
|||
using Microsoft.EntityFrameworkCore;
|
||||
using Ombi.Core.Models.Search;
|
||||
using Ombi.Core.Rule.Interfaces;
|
||||
using Ombi.Store.Entities;
|
||||
using Ombi.Store.Repository;
|
||||
using Ombi.Store.Repository.Requests;
|
||||
|
||||
|
@ -21,28 +22,75 @@ namespace Ombi.Core.Rule.Rules.Search
|
|||
|
||||
public Task<RuleResult> Execute(SearchViewModel obj)
|
||||
{
|
||||
var movieRequests = Movie.GetRequest(obj.Id);
|
||||
if (movieRequests != null) // Do we already have a request for this?
|
||||
if (obj.Type == RequestType.Movie)
|
||||
{
|
||||
var movieRequests = Movie.GetRequest(obj.Id);
|
||||
if (movieRequests != null) // Do we already have a request for this?
|
||||
{
|
||||
|
||||
obj.Requested = true;
|
||||
obj.Approved = movieRequests.Approved;
|
||||
obj.Available = movieRequests.Available;
|
||||
|
||||
return Task.FromResult(Success());
|
||||
}
|
||||
return Task.FromResult(Success());
|
||||
}
|
||||
else
|
||||
{
|
||||
//var tvRequests = Tv.GetRequest(obj.Id);
|
||||
//if (tvRequests != null) // Do we already have a request for this?
|
||||
//{
|
||||
|
||||
// obj.Requested = true;
|
||||
// obj.Approved = tvRequests.ChildRequests.Any(x => x.Approved);
|
||||
// obj.Available = tvRequests.ChildRequests.Any(x => x.Available);
|
||||
|
||||
// return Task.FromResult(Success());
|
||||
//}
|
||||
|
||||
var request = (SearchTvShowViewModel) obj;
|
||||
var tvRequests = Tv.GetRequest(obj.Id);
|
||||
if (tvRequests != null) // Do we already have a request for this?
|
||||
{
|
||||
|
||||
request.Requested = true;
|
||||
request.Approved = tvRequests.ChildRequests.Any(x => x.Approved);
|
||||
|
||||
// Let's modify the seasonsrequested to reflect what we have requested...
|
||||
foreach (var season in request.SeasonRequests)
|
||||
{
|
||||
foreach (var existingRequestChildRequest in tvRequests.ChildRequests)
|
||||
{
|
||||
// Find the existing request season
|
||||
var existingSeason =
|
||||
existingRequestChildRequest.SeasonRequests.FirstOrDefault(x => x.SeasonNumber == season.SeasonNumber);
|
||||
if (existingSeason == null) continue;
|
||||
|
||||
foreach (var ep in existingSeason.Episodes)
|
||||
{
|
||||
// Find the episode from what we are searching
|
||||
var episodeSearching = season.Episodes.FirstOrDefault(x => x.EpisodeNumber == ep.EpisodeNumber);
|
||||
if (episodeSearching == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
episodeSearching.Requested = true;
|
||||
episodeSearching.Available = ep.Available;
|
||||
episodeSearching.Approved = ep.Season.ChildRequest.Approved;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (request.SeasonRequests.Any() && request.SeasonRequests.All(x => x.Episodes.All(e => e.Approved)))
|
||||
{
|
||||
request.FullyAvailable = true;
|
||||
}
|
||||
|
||||
obj.Requested = true;
|
||||
obj.Approved = movieRequests.Approved;
|
||||
obj.Available = movieRequests.Available;
|
||||
|
||||
return Task.FromResult(Success());
|
||||
}
|
||||
|
||||
var tvRequests = Tv.GetRequest(obj.Id);
|
||||
if (tvRequests != null) // Do we already have a request for this?
|
||||
{
|
||||
|
||||
obj.Requested = true;
|
||||
obj.Approved = tvRequests.ChildRequests.Any(x => x.Approved);
|
||||
obj.Available = tvRequests.ChildRequests.Any(x => x.Available);
|
||||
|
||||
return Task.FromResult(Success());
|
||||
}
|
||||
return Task.FromResult(Success());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,6 +1,9 @@
|
|||
using System.Threading.Tasks;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Ombi.Core.Models.Search;
|
||||
using Ombi.Core.Rule.Interfaces;
|
||||
using Ombi.Store.Entities;
|
||||
using Ombi.Store.Repository;
|
||||
|
||||
namespace Ombi.Core.Rule.Rules.Search
|
||||
|
@ -22,6 +25,29 @@ namespace Ombi.Core.Rule.Rules.Search
|
|||
obj.Available = true;
|
||||
obj.PlexUrl = item.Url;
|
||||
obj.Quality = item.Quality;
|
||||
|
||||
if (obj.Type == RequestType.TvShow)
|
||||
{
|
||||
var search = (SearchTvShowViewModel)obj;
|
||||
// Let's go through the episodes now
|
||||
if (search.SeasonRequests.Any())
|
||||
{
|
||||
var allEpisodes = PlexContentRepository.GetAllEpisodes();
|
||||
foreach (var season in search.SeasonRequests)
|
||||
{
|
||||
foreach (var episode in season.Episodes)
|
||||
{
|
||||
var epExists = await allEpisodes.FirstOrDefaultAsync(x =>
|
||||
x.EpisodeNumber == episode.EpisodeNumber && x.SeasonNumber == season.SeasonNumber &&
|
||||
x.Series.ProviderId == item.ProviderId.ToString());
|
||||
if (epExists != null)
|
||||
{
|
||||
episode.Available = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return Success();
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ using Microsoft.EntityFrameworkCore;
|
|||
using Ombi.Core.Models.Search;
|
||||
using Ombi.Core.Rule.Interfaces;
|
||||
using Ombi.Store.Context;
|
||||
using Ombi.Store.Entities;
|
||||
|
||||
namespace Ombi.Core.Rule.Rules.Search
|
||||
{
|
||||
|
@ -18,13 +19,16 @@ namespace Ombi.Core.Rule.Rules.Search
|
|||
|
||||
public async Task<RuleResult> Execute(SearchViewModel obj)
|
||||
{
|
||||
// Check if it's in Radarr
|
||||
var result = await _ctx.RadarrCache.FirstOrDefaultAsync(x => x.TheMovieDbId == obj.Id);
|
||||
if (result != null)
|
||||
if (obj.Type == RequestType.Movie)
|
||||
{
|
||||
obj.Approved = true; // It's in radarr so it's approved... Maybe have a new property called "Processing" or something?
|
||||
// Check if it's in Radarr
|
||||
var result = await _ctx.RadarrCache.FirstOrDefaultAsync(x => x.TheMovieDbId == obj.Id);
|
||||
if (result != null)
|
||||
{
|
||||
obj.Approved =
|
||||
true; // It's in radarr so it's approved... Maybe have a new property called "Processing" or something?
|
||||
}
|
||||
}
|
||||
|
||||
return Success();
|
||||
}
|
||||
}
|
||||
|
|
34
src/Ombi.Core/Rule/Rules/Search/SonarrCacheRule.cs
Normal file
34
src/Ombi.Core/Rule/Rules/Search/SonarrCacheRule.cs
Normal file
|
@ -0,0 +1,34 @@
|
|||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Ombi.Core.Models.Search;
|
||||
using Ombi.Core.Rule.Interfaces;
|
||||
using Ombi.Store.Context;
|
||||
using Ombi.Store.Entities;
|
||||
|
||||
namespace Ombi.Core.Rule.Rules.Search
|
||||
{
|
||||
public class SonarrCacheRule : BaseSearchRule, IRules<SearchViewModel>
|
||||
{
|
||||
public SonarrCacheRule(IOmbiContext ctx)
|
||||
{
|
||||
_ctx = ctx;
|
||||
}
|
||||
|
||||
private readonly IOmbiContext _ctx;
|
||||
|
||||
public async Task<RuleResult> Execute(SearchViewModel obj)
|
||||
{
|
||||
if (obj.Type == RequestType.TvShow)
|
||||
{
|
||||
// Check if it's in Radarr
|
||||
var result = await _ctx.SonarrCache.FirstOrDefaultAsync(x => x.TvDbId == obj.Id);
|
||||
if (result != null)
|
||||
{
|
||||
obj.Approved =
|
||||
true; // It's in radarr so it's approved... Maybe have a new property called "Processing" or something?
|
||||
}
|
||||
}
|
||||
return Success();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,6 +5,6 @@ namespace Ombi.Core
|
|||
{
|
||||
public interface IMovieSender
|
||||
{
|
||||
Task<MovieSenderResult> Send(MovieRequests model, string qualityId = "");
|
||||
Task<MovieSenderResult> Send(MovieRequests model);
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
using Ombi.Core.Settings;
|
||||
using System.Linq;
|
||||
using Ombi.Core.Settings;
|
||||
using Ombi.Settings.Settings.Models.External;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
@ -21,7 +22,7 @@ namespace Ombi.Core
|
|||
private IRadarrApi RadarrApi { get; }
|
||||
private ILogger<MovieSender> Log { get; }
|
||||
|
||||
public async Task<MovieSenderResult> Send(MovieRequests model, string qualityId = "")
|
||||
public async Task<MovieSenderResult> Send(MovieRequests model)
|
||||
{
|
||||
//var cpSettings = await CouchPotatoSettings.GetSettingsAsync();
|
||||
//var watcherSettings = await WatcherSettings.GetSettingsAsync();
|
||||
|
@ -39,7 +40,7 @@ namespace Ombi.Core
|
|||
|
||||
if (radarrSettings.Enabled)
|
||||
{
|
||||
return await SendToRadarr(model, radarrSettings, qualityId);
|
||||
return await SendToRadarr(model, radarrSettings);
|
||||
}
|
||||
|
||||
return new MovieSenderResult
|
||||
|
@ -49,22 +50,16 @@ namespace Ombi.Core
|
|||
};
|
||||
}
|
||||
|
||||
private async Task<MovieSenderResult> SendToRadarr(MovieRequests model, RadarrSettings settings, string qualityId)
|
||||
private async Task<MovieSenderResult> SendToRadarr(MovieRequests model, RadarrSettings settings)
|
||||
{
|
||||
var qualityProfile = 0;
|
||||
if (!string.IsNullOrEmpty(qualityId)) // try to parse the passed in quality, otherwise use the settings default quality
|
||||
var qualityToUse = int.Parse(settings.DefaultQualityProfile);
|
||||
if (model.QualityOverride <= 0)
|
||||
{
|
||||
int.TryParse(qualityId, out qualityProfile);
|
||||
qualityToUse = model.QualityOverride;
|
||||
}
|
||||
|
||||
if (qualityProfile <= 0)
|
||||
{
|
||||
int.TryParse(settings.DefaultQualityProfile, out qualityProfile);
|
||||
}
|
||||
|
||||
//var rootFolderPath = model.RootFolderSelected <= 0 ? settings.FullRootPath : GetRootPath(model.RootFolderSelected, settings);
|
||||
var rootFolderPath = settings.DefaultRootPath; // TODO Allow changing in the UI
|
||||
var result = await RadarrApi.AddMovie(model.TheMovieDbId, model.Title, model.ReleaseDate.Year, qualityProfile, rootFolderPath, settings.ApiKey, settings.FullUri, !settings.AddOnly, settings.MinimumAvailability);
|
||||
var rootFolderPath = model.RootPathOverride <= 0 ? settings.DefaultRootPath : await RadarrRootPath(model.RootPathOverride, settings);
|
||||
var result = await RadarrApi.AddMovie(model.TheMovieDbId, model.Title, model.ReleaseDate.Year, qualityToUse, rootFolderPath, settings.ApiKey, settings.FullUri, !settings.AddOnly, settings.MinimumAvailability);
|
||||
|
||||
if (!string.IsNullOrEmpty(result.Error?.message))
|
||||
{
|
||||
|
@ -77,5 +72,12 @@ namespace Ombi.Core
|
|||
}
|
||||
return new MovieSenderResult { Success = true, MovieSent = false };
|
||||
}
|
||||
|
||||
private async Task<string> RadarrRootPath(int overrideId, RadarrSettings settings)
|
||||
{
|
||||
var paths = await RadarrApi.GetRootFolders(settings.ApiKey, settings.FullUri);
|
||||
var selectedPath = paths.FirstOrDefault(x => x.id == overrideId);
|
||||
return selectedPath.path;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -42,6 +42,7 @@ using Ombi.Core.Senders;
|
|||
using Ombi.Schedule.Jobs.Emby;
|
||||
using Ombi.Schedule.Jobs.Ombi;
|
||||
using Ombi.Schedule.Jobs.Plex;
|
||||
using Ombi.Schedule.Jobs.Sonarr;
|
||||
using Ombi.Schedule.Ombi;
|
||||
using Ombi.Store.Repository.Requests;
|
||||
using PlexContentCacher = Ombi.Schedule.Jobs.Plex.PlexContentCacher;
|
||||
|
@ -142,6 +143,7 @@ namespace Ombi.DependencyInjection
|
|||
services.AddTransient<IPlexAvailabilityChecker, PlexAvailabilityChecker>();
|
||||
services.AddTransient<IJobSetup, JobSetup>();
|
||||
services.AddTransient<IRadarrCacher, RadarrCacher>();
|
||||
services.AddTransient<ISonarrCacher, SonarrCacher>();
|
||||
services.AddTransient<IOmbiAutomaticUpdater, OmbiAutomaticUpdater>();
|
||||
services.AddTransient<IPlexUserImporter, PlexUserImporter>();
|
||||
services.AddTransient<IEmbyUserImporter, EmbyUserImporter>();
|
||||
|
|
|
@ -16,5 +16,7 @@ namespace Ombi.Helpers
|
|||
public const string TopRatedMovies = nameof(TopRatedMovies);
|
||||
public const string UpcomingMovies = nameof(UpcomingMovies);
|
||||
public const string NowPlayingMovies = nameof(NowPlayingMovies);
|
||||
public const string RadarrRootProfiles = nameof(RadarrRootProfiles);
|
||||
public const string RadarrQualityProfiles = nameof(RadarrQualityProfiles);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,10 +11,11 @@ namespace Ombi.Helpers
|
|||
|
||||
public static EventId Cacher => new EventId(2000);
|
||||
public static EventId RadarrCacher => new EventId(2001);
|
||||
public static EventId PlexEpisodeCacher => new EventId(2001);
|
||||
public static EventId EmbyContentCacher => new EventId(2002);
|
||||
public static EventId PlexUserImporter => new EventId(2003);
|
||||
public static EventId EmbyUserImporter => new EventId(2004);
|
||||
public static EventId PlexEpisodeCacher => new EventId(2002);
|
||||
public static EventId EmbyContentCacher => new EventId(2003);
|
||||
public static EventId PlexUserImporter => new EventId(2004);
|
||||
public static EventId EmbyUserImporter => new EventId(2005);
|
||||
public static EventId SonarrCacher => new EventId(2006);
|
||||
|
||||
public static EventId MovieSender => new EventId(3000);
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ using Ombi.Schedule.Jobs;
|
|||
using Ombi.Schedule.Jobs.Emby;
|
||||
using Ombi.Schedule.Jobs.Plex;
|
||||
using Ombi.Schedule.Jobs.Radarr;
|
||||
using Ombi.Schedule.Jobs.Sonarr;
|
||||
using Ombi.Schedule.Ombi;
|
||||
|
||||
namespace Ombi.Schedule
|
||||
|
@ -11,7 +12,7 @@ namespace Ombi.Schedule
|
|||
{
|
||||
public JobSetup(IPlexContentCacher plexContentCacher, IRadarrCacher radarrCacher,
|
||||
IOmbiAutomaticUpdater updater, IEmbyContentCacher embyCacher, IPlexUserImporter userImporter,
|
||||
IEmbyUserImporter embyUserImporter)
|
||||
IEmbyUserImporter embyUserImporter, ISonarrCacher cache)
|
||||
{
|
||||
PlexContentCacher = plexContentCacher;
|
||||
RadarrCacher = radarrCacher;
|
||||
|
@ -19,6 +20,7 @@ namespace Ombi.Schedule
|
|||
EmbyContentCacher = embyCacher;
|
||||
PlexUserImporter = userImporter;
|
||||
EmbyUserImporter = embyUserImporter;
|
||||
SonarrCacher = cache;
|
||||
}
|
||||
|
||||
private IPlexContentCacher PlexContentCacher { get; }
|
||||
|
@ -27,17 +29,17 @@ namespace Ombi.Schedule
|
|||
private IPlexUserImporter PlexUserImporter { get; }
|
||||
private IEmbyContentCacher EmbyContentCacher { get; }
|
||||
private IEmbyUserImporter EmbyUserImporter { get; }
|
||||
private ISonarrCacher SonarrCacher { get; }
|
||||
|
||||
public void Setup()
|
||||
{
|
||||
RecurringJob.AddOrUpdate(() => PlexContentCacher.CacheContent(), Cron.Hourly(20));
|
||||
RecurringJob.AddOrUpdate(() => EmbyContentCacher.Start(), Cron.Hourly(5));
|
||||
RecurringJob.AddOrUpdate(() => RadarrCacher.CacheContent(), Cron.Hourly(10));
|
||||
RecurringJob.AddOrUpdate(() => PlexUserImporter.Start(), Cron.Daily(1));
|
||||
RecurringJob.AddOrUpdate(() => RadarrCacher.CacheContent(), Cron.Hourly(15));
|
||||
RecurringJob.AddOrUpdate(() => PlexUserImporter.Start(), Cron.Daily(5));
|
||||
RecurringJob.AddOrUpdate(() => EmbyUserImporter.Start(), Cron.Daily);
|
||||
RecurringJob.AddOrUpdate(() => Updater.Update(null), Cron.Daily(3));
|
||||
|
||||
//BackgroundJob.Enqueue(() => PlexUserImporter.Start());
|
||||
RecurringJob.AddOrUpdate(() => Updater.Update(null), Cron.HourInterval(6));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
@ -28,8 +29,11 @@ namespace Ombi.Schedule.Jobs.Radarr
|
|||
private ILogger<RadarrCacher> Logger { get; }
|
||||
private readonly IOmbiContext _ctx;
|
||||
|
||||
private static readonly SemaphoreSlim SemaphoreSlim = new SemaphoreSlim(1, 1);
|
||||
|
||||
public async Task CacheContent()
|
||||
{
|
||||
await SemaphoreSlim.WaitAsync();
|
||||
try
|
||||
{
|
||||
var settings = RadarrSettings.GetSettings();
|
||||
|
@ -48,7 +52,7 @@ namespace Ombi.Schedule.Jobs.Radarr
|
|||
{
|
||||
if (m.tmdbId > 0)
|
||||
{
|
||||
movieIds.Add(new RadarrCache { TheMovieDbId = m.tmdbId });
|
||||
movieIds.Add(new RadarrCache {TheMovieDbId = m.tmdbId});
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -70,6 +74,10 @@ namespace Ombi.Schedule.Jobs.Radarr
|
|||
{
|
||||
Logger.LogInformation(LoggingEvents.RadarrCacher, "Radarr is not setup, cannot cache episodes");
|
||||
}
|
||||
finally
|
||||
{
|
||||
SemaphoreSlim.Release();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<RadarrCache>> GetCachedContent()
|
||||
|
|
9
src/Ombi.Schedule/Jobs/Sonarr/ISonarrCacher.cs
Normal file
9
src/Ombi.Schedule/Jobs/Sonarr/ISonarrCacher.cs
Normal file
|
@ -0,0 +1,9 @@
|
|||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ombi.Schedule.Jobs.Sonarr
|
||||
{
|
||||
public interface ISonarrCacher
|
||||
{
|
||||
Task Start();
|
||||
}
|
||||
}
|
116
src/Ombi.Schedule/Jobs/Sonarr/SonarrCacher.cs
Normal file
116
src/Ombi.Schedule/Jobs/Sonarr/SonarrCacher.cs
Normal file
|
@ -0,0 +1,116 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Ombi.Api.Sonarr;
|
||||
using Ombi.Core.Settings;
|
||||
using Ombi.Core.Settings.Models.External;
|
||||
using Ombi.Helpers;
|
||||
using Ombi.Store.Context;
|
||||
using Ombi.Store.Entities;
|
||||
|
||||
namespace Ombi.Schedule.Jobs.Sonarr
|
||||
{
|
||||
public class SonarrCacher : ISonarrCacher
|
||||
{
|
||||
public SonarrCacher(ISettingsService<SonarrSettings> s, ISonarrApi api, ILogger<SonarrCacher> l, IOmbiContext ctx)
|
||||
{
|
||||
_settings = s;
|
||||
_api = api;
|
||||
_log = l;
|
||||
}
|
||||
|
||||
private readonly ISettingsService<SonarrSettings> _settings;
|
||||
private readonly ISonarrApi _api;
|
||||
private readonly ILogger<SonarrCacher> _log;
|
||||
private readonly IOmbiContext _ctx;
|
||||
|
||||
private static readonly SemaphoreSlim SemaphoreSlim = new SemaphoreSlim(1, 1);
|
||||
public async Task Start()
|
||||
{
|
||||
await SemaphoreSlim.WaitAsync();
|
||||
try
|
||||
{
|
||||
var settings = await _settings.GetSettingsAsync();
|
||||
if (!settings.Enabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var series = await _api.GetSeries(settings.ApiKey, settings.FullUri);
|
||||
if (series != null)
|
||||
{
|
||||
var ids = series.Select(x => x.tvdbId);
|
||||
|
||||
await _ctx.Database.ExecuteSqlCommandAsync("DELETE FROM SonarrCache");
|
||||
var entites = ids.Select(id => new SonarrCache {TvDbId = id}).ToList();
|
||||
|
||||
await _ctx.SonarrCache.AddRangeAsync(entites);
|
||||
await _ctx.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_log.LogError(LoggingEvents.SonarrCacher, e, "Exception when trying to cache Sonarr");
|
||||
}
|
||||
finally
|
||||
{
|
||||
SemaphoreSlim.Release();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//public void Queued()
|
||||
//{
|
||||
// var settings = SonarrSettings.GetSettings();
|
||||
// if (settings.Enabled)
|
||||
// {
|
||||
// Job.SetRunning(true, JobNames.SonarrCacher);
|
||||
// try
|
||||
// {
|
||||
// var series = SonarrApi.GetSeries(settings.ApiKey, settings.FullUri);
|
||||
// if (series != null)
|
||||
// {
|
||||
// Cache.Set(CacheKeys.SonarrQueued, series, CacheKeys.TimeFrameMinutes.SchedulerCaching);
|
||||
// }
|
||||
// }
|
||||
// catch (System.Exception ex)
|
||||
// {
|
||||
// Log.Error(ex, "Failed caching queued items from Sonarr");
|
||||
// }
|
||||
// finally
|
||||
// {
|
||||
// Job.Record(JobNames.SonarrCacher);
|
||||
// Job.SetRunning(false, JobNames.SonarrCacher);
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
//// we do not want to set here...
|
||||
//public IEnumerable<SonarrCachedResult> QueuedIds()
|
||||
//{
|
||||
// var result = new List<SonarrCachedResult>();
|
||||
|
||||
// var series = Cache.Get<List<Series>>(CacheKeys.SonarrQueued);
|
||||
// if (series != null)
|
||||
// {
|
||||
// foreach (var s in series)
|
||||
// {
|
||||
// var cached = new SonarrCachedResult { TvdbId = s.tvdbId };
|
||||
// foreach (var season in s.seasons)
|
||||
// {
|
||||
// cached.Seasons.Add(new SonarrSeasons
|
||||
// {
|
||||
// SeasonNumber = season.seasonNumber,
|
||||
// Monitored = season.monitored
|
||||
// });
|
||||
// }
|
||||
|
||||
// result.Add(cached);
|
||||
// }
|
||||
// }
|
||||
// return result;
|
||||
//}
|
||||
}
|
||||
}
|
|
@ -23,6 +23,7 @@
|
|||
<ProjectReference Include="..\Ombi.Api.Plex\Ombi.Api.Plex.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Api.Radarr\Ombi.Api.Radarr.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Api.Service\Ombi.Api.Service.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Api.Sonarr\Ombi.Api.Sonarr.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Notifications\Ombi.Notifications.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Settings\Ombi.Settings.csproj" />
|
||||
</ItemGroup>
|
||||
|
|
|
@ -33,6 +33,7 @@ namespace Ombi.Store.Context
|
|||
DbSet<MovieIssues> MovieIssues { get; set; }
|
||||
DbSet<TvIssues> TvIssues { get; set; }
|
||||
DbSet<Tokens> Tokens { get; set; }
|
||||
DbSet<SonarrCache> SonarrCache { get; set; }
|
||||
EntityEntry Update(object entity);
|
||||
EntityEntry<TEntity> Update<TEntity>(TEntity entity) where TEntity : class;
|
||||
}
|
||||
|
|
|
@ -38,6 +38,7 @@ namespace Ombi.Store.Context
|
|||
|
||||
public DbSet<Audit> Audit { get; set; }
|
||||
public DbSet<Tokens> Tokens { get; set; }
|
||||
public DbSet<SonarrCache> SonarrCache { get; set; }
|
||||
|
||||
public DbSet<ApplicationConfiguration> ApplicationConfigurations { get; set; }
|
||||
|
||||
|
|
|
@ -11,5 +11,8 @@ namespace Ombi.Store.Entities.Requests
|
|||
public int? IssueId { get; set; }
|
||||
[ForeignKey(nameof(IssueId))]
|
||||
public List<MovieIssues> Issues { get; set; }
|
||||
|
||||
public int RootPathOverride { get; set; }
|
||||
public int QualityOverride { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
10
src/Ombi.Store/Entities/SonarrCache.cs
Normal file
10
src/Ombi.Store/Entities/SonarrCache.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Ombi.Store.Entities
|
||||
{
|
||||
[Table("SonarrCache")]
|
||||
public class SonarrCache : Entity
|
||||
{
|
||||
public int TvDbId { get; set; }
|
||||
}
|
||||
}
|
747
src/Ombi.Store/Migrations/20171002113357_SonarrCacher.Designer.cs
generated
Normal file
747
src/Ombi.Store/Migrations/20171002113357_SonarrCacher.Designer.cs
generated
Normal file
|
@ -0,0 +1,747 @@
|
|||
// <auto-generated />
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage;
|
||||
using Microsoft.EntityFrameworkCore.Storage.Internal;
|
||||
using Ombi.Helpers;
|
||||
using Ombi.Store.Context;
|
||||
using Ombi.Store.Entities;
|
||||
using System;
|
||||
|
||||
namespace Ombi.Store.Migrations
|
||||
{
|
||||
[DbContext(typeof(OmbiContext))]
|
||||
[Migration("20171002113357_SonarrCacher")]
|
||||
partial class SonarrCacher
|
||||
{
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "2.0.0-rtm-26452");
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken();
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.Property<string>("NormalizedName")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("NormalizedName")
|
||||
.IsUnique()
|
||||
.HasName("RoleNameIndex");
|
||||
|
||||
b.ToTable("AspNetRoles");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<string>("ClaimType");
|
||||
|
||||
b.Property<string>("ClaimValue");
|
||||
|
||||
b.Property<string>("RoleId")
|
||||
.IsRequired();
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("RoleId");
|
||||
|
||||
b.ToTable("AspNetRoleClaims");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<string>("ClaimType");
|
||||
|
||||
b.Property<string>("ClaimValue");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired();
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("AspNetUserClaims");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||
{
|
||||
b.Property<string>("LoginProvider");
|
||||
|
||||
b.Property<string>("ProviderKey");
|
||||
|
||||
b.Property<string>("ProviderDisplayName");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired();
|
||||
|
||||
b.HasKey("LoginProvider", "ProviderKey");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("AspNetUserLogins");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||
{
|
||||
b.Property<string>("UserId");
|
||||
|
||||
b.Property<string>("RoleId");
|
||||
|
||||
b.HasKey("UserId", "RoleId");
|
||||
|
||||
b.HasIndex("RoleId");
|
||||
|
||||
b.ToTable("AspNetUserRoles");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||
{
|
||||
b.Property<string>("UserId");
|
||||
|
||||
b.Property<string>("LoginProvider");
|
||||
|
||||
b.Property<string>("Name");
|
||||
|
||||
b.Property<string>("Value");
|
||||
|
||||
b.HasKey("UserId", "LoginProvider", "Name");
|
||||
|
||||
b.ToTable("AspNetUserTokens");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.ApplicationConfiguration", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<int>("Type");
|
||||
|
||||
b.Property<string>("Value");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("ApplicationConfiguration");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.Audit", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<int>("AuditArea");
|
||||
|
||||
b.Property<int>("AuditType");
|
||||
|
||||
b.Property<DateTime>("DateTime");
|
||||
|
||||
b.Property<string>("Description");
|
||||
|
||||
b.Property<string>("User");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Audit");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.EmbyContent", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<DateTime>("AddedAt");
|
||||
|
||||
b.Property<string>("EmbyId")
|
||||
.IsRequired();
|
||||
|
||||
b.Property<string>("ProviderId");
|
||||
|
||||
b.Property<string>("Title");
|
||||
|
||||
b.Property<int>("Type");
|
||||
|
||||
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>("ParentId");
|
||||
|
||||
b.Property<string>("ProviderId");
|
||||
|
||||
b.Property<int>("SeasonNumber");
|
||||
|
||||
b.Property<string>("Title");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ParentId");
|
||||
|
||||
b.ToTable("EmbyEpisode");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.GlobalSettings", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<string>("Content");
|
||||
|
||||
b.Property<string>("SettingsName");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("GlobalSettings");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.NotificationTemplates", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<int>("Agent");
|
||||
|
||||
b.Property<bool>("Enabled");
|
||||
|
||||
b.Property<string>("Message");
|
||||
|
||||
b.Property<int>("NotificationType");
|
||||
|
||||
b.Property<string>("Subject");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("NotificationTemplates");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.OmbiUser", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<int>("AccessFailedCount");
|
||||
|
||||
b.Property<string>("Alias");
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken();
|
||||
|
||||
b.Property<string>("Email")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.Property<bool>("EmailConfirmed");
|
||||
|
||||
b.Property<DateTime?>("LastLoggedIn");
|
||||
|
||||
b.Property<bool>("LockoutEnabled");
|
||||
|
||||
b.Property<DateTimeOffset?>("LockoutEnd");
|
||||
|
||||
b.Property<string>("NormalizedEmail")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.Property<string>("NormalizedUserName")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.Property<string>("PasswordHash");
|
||||
|
||||
b.Property<string>("PhoneNumber");
|
||||
|
||||
b.Property<bool>("PhoneNumberConfirmed");
|
||||
|
||||
b.Property<string>("ProviderUserId");
|
||||
|
||||
b.Property<string>("SecurityStamp");
|
||||
|
||||
b.Property<bool>("TwoFactorEnabled");
|
||||
|
||||
b.Property<string>("UserName")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.Property<int>("UserType");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("NormalizedEmail")
|
||||
.HasName("EmailIndex");
|
||||
|
||||
b.HasIndex("NormalizedUserName")
|
||||
.IsUnique()
|
||||
.HasName("UserNameIndex");
|
||||
|
||||
b.ToTable("AspNetUsers");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.PlexContent", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<DateTime>("AddedAt");
|
||||
|
||||
b.Property<int>("Key");
|
||||
|
||||
b.Property<string>("ProviderId");
|
||||
|
||||
b.Property<string>("Quality");
|
||||
|
||||
b.Property<string>("ReleaseYear");
|
||||
|
||||
b.Property<string>("Title");
|
||||
|
||||
b.Property<int>("Type");
|
||||
|
||||
b.Property<string>("Url");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("PlexContent");
|
||||
});
|
||||
|
||||
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>("SeasonKey");
|
||||
|
||||
b.Property<int>("SeasonNumber");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("PlexContentId");
|
||||
|
||||
b.ToTable("PlexSeasonsContent");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.RadarrCache", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<int>("TheMovieDbId");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("RadarrCache");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Approved");
|
||||
|
||||
b.Property<bool>("Available");
|
||||
|
||||
b.Property<bool?>("Denied");
|
||||
|
||||
b.Property<string>("DeniedReason");
|
||||
|
||||
b.Property<int?>("IssueId");
|
||||
|
||||
b.Property<int>("ParentRequestId");
|
||||
|
||||
b.Property<int>("RequestType");
|
||||
|
||||
b.Property<DateTime>("RequestedDate");
|
||||
|
||||
b.Property<string>("RequestedUserId");
|
||||
|
||||
b.Property<string>("Title");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ParentRequestId");
|
||||
|
||||
b.HasIndex("RequestedUserId");
|
||||
|
||||
b.ToTable("ChildRequests");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieIssues", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<string>("Description");
|
||||
|
||||
b.Property<int?>("IssueId");
|
||||
|
||||
b.Property<int>("MovieId");
|
||||
|
||||
b.Property<string>("Subect");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("IssueId");
|
||||
|
||||
b.HasIndex("MovieId");
|
||||
|
||||
b.ToTable("MovieIssues");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Approved");
|
||||
|
||||
b.Property<bool>("Available");
|
||||
|
||||
b.Property<bool?>("Denied");
|
||||
|
||||
b.Property<string>("DeniedReason");
|
||||
|
||||
b.Property<string>("ImdbId");
|
||||
|
||||
b.Property<int?>("IssueId");
|
||||
|
||||
b.Property<string>("Overview");
|
||||
|
||||
b.Property<string>("PosterPath");
|
||||
|
||||
b.Property<int>("QualityOverride");
|
||||
|
||||
b.Property<DateTime>("ReleaseDate");
|
||||
|
||||
b.Property<int>("RequestType");
|
||||
|
||||
b.Property<DateTime>("RequestedDate");
|
||||
|
||||
b.Property<string>("RequestedUserId");
|
||||
|
||||
b.Property<int>("RootPathOverride");
|
||||
|
||||
b.Property<string>("Status");
|
||||
|
||||
b.Property<int>("TheMovieDbId");
|
||||
|
||||
b.Property<string>("Title");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("RequestedUserId");
|
||||
|
||||
b.ToTable("MovieRequests");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.Requests.TvIssues", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<string>("Description");
|
||||
|
||||
b.Property<int?>("IssueId");
|
||||
|
||||
b.Property<string>("Subect");
|
||||
|
||||
b.Property<int>("TvId");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("IssueId");
|
||||
|
||||
b.HasIndex("TvId");
|
||||
|
||||
b.ToTable("TvIssues");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.Requests.TvRequests", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<string>("ImdbId");
|
||||
|
||||
b.Property<string>("Overview");
|
||||
|
||||
b.Property<string>("PosterPath");
|
||||
|
||||
b.Property<DateTime>("ReleaseDate");
|
||||
|
||||
b.Property<int?>("RootFolder");
|
||||
|
||||
b.Property<string>("Status");
|
||||
|
||||
b.Property<string>("Title");
|
||||
|
||||
b.Property<int>("TvDbId");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("TvRequests");
|
||||
});
|
||||
|
||||
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.Tokens", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<string>("Token");
|
||||
|
||||
b.Property<string>("UserId");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("Tokens");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<DateTime>("AirDate");
|
||||
|
||||
b.Property<bool>("Approved");
|
||||
|
||||
b.Property<bool>("Available");
|
||||
|
||||
b.Property<int>("EpisodeNumber");
|
||||
|
||||
b.Property<bool>("Requested");
|
||||
|
||||
b.Property<int>("SeasonId");
|
||||
|
||||
b.Property<string>("Title");
|
||||
|
||||
b.Property<string>("Url");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("SeasonId");
|
||||
|
||||
b.ToTable("EpisodeRequests");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<int>("ChildRequestId");
|
||||
|
||||
b.Property<int>("SeasonNumber");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ChildRequestId");
|
||||
|
||||
b.ToTable("SeasonRequests");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||
{
|
||||
b.HasOne("Ombi.Store.Entities.OmbiUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||
{
|
||||
b.HasOne("Ombi.Store.Entities.OmbiUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("Ombi.Store.Entities.OmbiUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||
{
|
||||
b.HasOne("Ombi.Store.Entities.OmbiUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b =>
|
||||
{
|
||||
b.HasOne("Ombi.Store.Entities.EmbyContent", "Series")
|
||||
.WithMany("Episodes")
|
||||
.HasForeignKey("ParentId")
|
||||
.HasPrincipalKey("EmbyId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b =>
|
||||
{
|
||||
b.HasOne("Ombi.Store.Entities.PlexContent", "Series")
|
||||
.WithMany("Episodes")
|
||||
.HasForeignKey("GrandparentKey")
|
||||
.HasPrincipalKey("Key")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b =>
|
||||
{
|
||||
b.HasOne("Ombi.Store.Entities.PlexContent")
|
||||
.WithMany("Seasons")
|
||||
.HasForeignKey("PlexContentId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b =>
|
||||
{
|
||||
b.HasOne("Ombi.Store.Entities.Requests.TvRequests", "ParentRequest")
|
||||
.WithMany("ChildRequests")
|
||||
.HasForeignKey("ParentRequestId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("RequestedUserId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieIssues", b =>
|
||||
{
|
||||
b.HasOne("Ombi.Store.Entities.Requests.MovieRequests")
|
||||
.WithMany("Issues")
|
||||
.HasForeignKey("IssueId");
|
||||
|
||||
b.HasOne("Ombi.Store.Entities.Requests.MovieRequests", "Movie")
|
||||
.WithMany()
|
||||
.HasForeignKey("MovieId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b =>
|
||||
{
|
||||
b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("RequestedUserId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.Requests.TvIssues", b =>
|
||||
{
|
||||
b.HasOne("Ombi.Store.Entities.Requests.ChildRequests")
|
||||
.WithMany("Issues")
|
||||
.HasForeignKey("IssueId");
|
||||
|
||||
b.HasOne("Ombi.Store.Entities.Requests.ChildRequests", "Child")
|
||||
.WithMany()
|
||||
.HasForeignKey("TvId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Entities.Tokens", b =>
|
||||
{
|
||||
b.HasOne("Ombi.Store.Entities.OmbiUser", "User")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b =>
|
||||
{
|
||||
b.HasOne("Ombi.Store.Repository.Requests.SeasonRequests", "Season")
|
||||
.WithMany("Episodes")
|
||||
.HasForeignKey("SeasonId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b =>
|
||||
{
|
||||
b.HasOne("Ombi.Store.Entities.Requests.ChildRequests", "ChildRequest")
|
||||
.WithMany("SeasonRequests")
|
||||
.HasForeignKey("ChildRequestId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
53
src/Ombi.Store/Migrations/20171002113357_SonarrCacher.cs
Normal file
53
src/Ombi.Store/Migrations/20171002113357_SonarrCacher.cs
Normal file
|
@ -0,0 +1,53 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ombi.Store.Migrations
|
||||
{
|
||||
public partial class SonarrCacher : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "QualityOverride",
|
||||
table: "MovieRequests",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: 0);
|
||||
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "RootPathOverride",
|
||||
table: "MovieRequests",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: 0);
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "SonarrCache",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
TvDbId = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_SonarrCache", x => x.Id);
|
||||
});
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "SonarrCache");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "QualityOverride",
|
||||
table: "MovieRequests");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "RootPathOverride",
|
||||
table: "MovieRequests");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -460,6 +460,8 @@ namespace Ombi.Store.Migrations
|
|||
|
||||
b.Property<string>("PosterPath");
|
||||
|
||||
b.Property<int>("QualityOverride");
|
||||
|
||||
b.Property<DateTime>("ReleaseDate");
|
||||
|
||||
b.Property<int>("RequestType");
|
||||
|
@ -468,6 +470,8 @@ namespace Ombi.Store.Migrations
|
|||
|
||||
b.Property<string>("RequestedUserId");
|
||||
|
||||
b.Property<int>("RootPathOverride");
|
||||
|
||||
b.Property<string>("Status");
|
||||
|
||||
b.Property<int>("TheMovieDbId");
|
||||
|
@ -529,6 +533,18 @@ namespace Ombi.Store.Migrations
|
|||
b.ToTable("TvRequests");
|
||||
});
|
||||
|
||||
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.Tokens", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
|
|
|
@ -45,8 +45,8 @@ namespace Ombi.TheMovieDbApi.Models
|
|||
public ProductionCompanies[] production_companies { get; set; }
|
||||
public ProductionCountries[] production_countries { get; set; }
|
||||
public string release_date { get; set; }
|
||||
public int revenue { get; set; }
|
||||
public int runtime { get; set; }
|
||||
public float revenue { get; set; }
|
||||
public float runtime { get; set; }
|
||||
public SpokenLanguages[] spoken_languages { get; set; }
|
||||
public string status { get; set; }
|
||||
public string tagline { get; set; }
|
||||
|
|
|
@ -15,8 +15,8 @@
|
|||
public float Popularity { get; set; }
|
||||
public string PosterPath { get; set; }
|
||||
public string ReleaseDate { get; set; }
|
||||
public int Revenue { get; set; }
|
||||
public int Runtime { get; set; }
|
||||
public float Revenue { get; set; }
|
||||
public float Runtime { get; set; }
|
||||
public string Status { get; set; }
|
||||
public string Tagline { get; set; }
|
||||
public string Title { get; set; }
|
||||
|
|
|
@ -79,6 +79,8 @@ export interface IRequestGrid<T> {
|
|||
|
||||
export interface IMovieRequests extends IFullBaseRequest {
|
||||
theMovieDbId: number;
|
||||
rootPathOverride: number;
|
||||
qualityOverride: number;
|
||||
}
|
||||
|
||||
export interface IFullBaseRequest extends IBaseRequest {
|
||||
|
|
|
@ -41,7 +41,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="col-md-3 col-md-push-4 vcenter">
|
||||
<button [routerLink]="['/login', 'true']" class="btn btn-lg btn-success-outline">Contine</button>
|
||||
<button [routerLink]="['/login', 'true']" class="btn btn-lg btn-success-outline">Continue</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -7,7 +7,7 @@ include the remember me checkbox
|
|||
<div class="card card-container">
|
||||
<!-- <img class="profile-img-card" src="//lh3.googleusercontent.com/-6V8xOA6M7BA/AAAAAAAAAAI/AAAAAAAAAAA/rzlHcD0KYwo/photo.jpg?sz=120" alt="" /> -->
|
||||
<div *ngIf="!customizationSettings.logo"><img id="profile-img" class="profile-img-card" src="/images/ms-icon-150x150.png" /></div>
|
||||
<div *ngIf="customizationSettings.logo"><img id="profile-img" class="profile-img-card" [src]="customizationSettings.logo" /></div>
|
||||
<div *ngIf="customizationSettings.logo"><img id="profile-img" class="center" [src]="customizationSettings.logo" /></div>
|
||||
<p id="profile-name" class="profile-name-card"></p>
|
||||
|
||||
<form class="form-signin" novalidate [formGroup]="form" (ngSubmit)="onSubmit(form)">
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<div class="card card-container">
|
||||
<!-- <img class="profile-img-card" src="//lh3.googleusercontent.com/-6V8xOA6M7BA/AAAAAAAAAAI/AAAAAAAAAAA/rzlHcD0KYwo/photo.jpg?sz=120" alt="" /> -->
|
||||
<div *ngIf="!customizationSettings.logo"><img id="profile-img" class="profile-img-card" src="/images/ms-icon-150x150.png" /></div>
|
||||
<div *ngIf="customizationSettings.logo"><img id="profile-img" class="profile-img-card" [src]="customizationSettings.logo" /></div>
|
||||
<div *ngIf="customizationSettings.logo"><img id="profile-img" class="center" [src]="customizationSettings.logo" /></div>
|
||||
<p id="profile-name" class="profile-name-card"></p>
|
||||
|
||||
|
||||
|
|
|
@ -78,45 +78,32 @@
|
|||
<div *ngIf="isAdmin">
|
||||
<div *ngIf="!request.approved">
|
||||
<form>
|
||||
<input name="requestId" type="text" value="{{request.requestId}}" hidden="hidden" />
|
||||
<div *ngIf="request.hasQualities" class="btn-group btn-split">
|
||||
<button type="button" (click)="approve(request)" class="btn btn-sm btn-success-outline approve"><i class="fa fa-plus"></i> Approve</button>
|
||||
<button type="button" class="btn btn-success-outline dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<span class="caret"></span>
|
||||
<span class="sr-only">Toggle Dropdown</span>
|
||||
</button>
|
||||
<!--<ul class="dropdown-menu">
|
||||
{{#each qualities}}
|
||||
<li><a href="#" class="approve-with-quality" id="{{id}}">{{name}}</a></li>
|
||||
{{/each}}
|
||||
</ul>-->
|
||||
</div>
|
||||
|
||||
|
||||
<button *ngIf="!request.hasQualities" (click)="approve(request)" style="text-align: right" class="btn btn-sm btn-success-outline approve" type="submit"><i class="fa fa-plus"></i> Approve</button>
|
||||
<button (click)="approve(request)" style="text-align: right" class="btn btn-sm btn-success-outline approve" type="submit"><i class="fa fa-plus"></i> Approve</button>
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
<!--<form method="POST" action="@formAction/requests/changeRootFolder{{#if_eq type "tv"}}tv{{else}}movie{{/if_eq}}" id="changeFolder{{requestId}}">
|
||||
<input name="requestId" type="text" value="{{requestId}}" hidden="hidden"/>
|
||||
{{#if_eq hasRootFolders true}}
|
||||
<div class="btn-group btn-split">
|
||||
<button type="button" class="btn btn-sm btn-success-outline" id="changeRootFolderBtn{{requestId}}" custom-button="{{requestId}}">@*<i class="fa fa-plus"></i>*@ Change Root Folder</button>
|
||||
<button type="button" class="btn btn-success-outline dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<span class="caret"></span>
|
||||
<span class="sr-only">@UI.Requests_ToggleDropdown</span>
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
{{#each rootFolders}}
|
||||
<li><a href="#" class="change-root-folder" id="{{id}}" requestId="{{requestId}}">{{path}}</a></li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
</div>
|
||||
{{/if_eq}}
|
||||
</form>-->
|
||||
|
||||
|
||||
<!--Radarr Root Folder-->
|
||||
<div *ngIf="radarrRootFolders" class="btn-group btn-split">
|
||||
<button type="button" class="btn btn-sm btn-success-outline"><i class="fa fa-plus"></i> Change Root Folder</button>
|
||||
<button type="button" class="btn btn-success-outline dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<span class="caret"></span>
|
||||
<span class="sr-only">Toggle Dropdown</span>
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
<li *ngFor="let folder of radarrRootFolders"><a href="#" (click)="selectRootFolder(request, folder)">{{folder.path}}</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!--Radarr Quality Profiles -->
|
||||
<div *ngIf="radarrProfiles" class="btn-group btn-split">
|
||||
<button type="button" class="btn btn-sm btn-success-outline"><i class="fa fa-plus"></i> Change Quality Profile</button>
|
||||
<button type="button" class="btn btn-success-outline dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<span class="caret"></span>
|
||||
<span class="sr-only">Toggle Dropdown</span>
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
<li *ngFor="let profile of radarrProfiles"><a href="#" (click)="selectQualityProfile(request, profile)">{{profile.name}}</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div *ngIf="!request.denied">
|
||||
<button type="button" (click)="deny(request)" class="btn btn-sm btn-danger-outline deny"><i class="fa fa-times"></i> Deny</button>
|
||||
|
|
|
@ -5,9 +5,9 @@ import "rxjs/add/operator/map";
|
|||
import { Subject } from "rxjs/Subject";
|
||||
|
||||
import { AuthService } from "../auth/auth.service";
|
||||
import { NotificationService, RequestService } from "../services";
|
||||
import { NotificationService, RadarrService, RequestService } from "../services";
|
||||
|
||||
import { IMovieRequests } from "../interfaces";
|
||||
import { IMovieRequests, IRadarrProfile, IRadarrRootFolder } from "../interfaces";
|
||||
|
||||
@Component({
|
||||
selector: "movie-requests",
|
||||
|
@ -21,12 +21,16 @@ export class MovieRequestsComponent implements OnInit {
|
|||
|
||||
public isAdmin: boolean;
|
||||
|
||||
public radarrProfiles: IRadarrProfile[];
|
||||
public radarrRootFolders: IRadarrRootFolder[];
|
||||
|
||||
private currentlyLoaded: number;
|
||||
private amountToLoad: number;
|
||||
|
||||
constructor(private requestService: RequestService,
|
||||
private auth: AuthService,
|
||||
private notificationService: NotificationService) {
|
||||
private notificationService: NotificationService,
|
||||
private radarrService: RadarrService) {
|
||||
this.searchChanged
|
||||
.debounceTime(600) // Wait Xms after the last event before emitting last event
|
||||
.distinctUntilChanged() // only emit if value is different from previous value
|
||||
|
@ -42,10 +46,13 @@ export class MovieRequestsComponent implements OnInit {
|
|||
}
|
||||
|
||||
public ngOnInit() {
|
||||
this.radarrService.getQualityProfilesFromSettings().subscribe(x => this.radarrProfiles = x);
|
||||
this.radarrService.getRootFoldersFromSettings().subscribe(x => this.radarrRootFolders = x);
|
||||
|
||||
this.amountToLoad = 5;
|
||||
this.currentlyLoaded = 5;
|
||||
this.loadInit();
|
||||
this.isAdmin = this.auth.hasRole("admin");
|
||||
this.isAdmin = this.auth.hasRole("admin") || this.auth.hasRole("poweruser");
|
||||
}
|
||||
|
||||
public loadMore() {
|
||||
|
@ -80,6 +87,14 @@ export class MovieRequestsComponent implements OnInit {
|
|||
this.updateRequest(request);
|
||||
}
|
||||
|
||||
public selectRootFolder(searchResult: IMovieRequests, rootFolderSelected: IRadarrRootFolder) {
|
||||
searchResult.rootPathOverride = rootFolderSelected.id;
|
||||
}
|
||||
|
||||
public selectQualityProfile(searchResult: IMovieRequests, profileSelected: IRadarrProfile) {
|
||||
searchResult.qualityOverride = profileSelected.id;
|
||||
}
|
||||
|
||||
private loadRequests(amountToLoad: number, currentlyLoaded: number) {
|
||||
this.requestService.getMovieRequests(amountToLoad, currentlyLoaded + 1)
|
||||
.subscribe(x => {
|
||||
|
|
|
@ -1,38 +1,32 @@
|
|||
import { Component, OnDestroy, OnInit } from "@angular/core";
|
||||
import { Component, OnInit } from "@angular/core";
|
||||
import "rxjs/add/operator/debounceTime";
|
||||
import "rxjs/add/operator/distinctUntilChanged";
|
||||
import "rxjs/add/operator/map";
|
||||
import "rxjs/add/operator/takeUntil";
|
||||
import { Subject } from "rxjs/Subject";
|
||||
|
||||
import { AuthService } from "../auth/auth.service";
|
||||
import { NotificationService } from "../services";
|
||||
import { RequestService } from "../services";
|
||||
import { SearchService } from "../services";
|
||||
import { NotificationService, RequestService, SearchService } from "../services";
|
||||
|
||||
import { IRequestEngineResult } from "../interfaces";
|
||||
import { ISearchMovieResult } from "../interfaces";
|
||||
import { IRequestEngineResult, ISearchMovieResult } from "../interfaces";
|
||||
|
||||
@Component({
|
||||
selector: "movie-search",
|
||||
templateUrl: "./moviesearch.component.html",
|
||||
})
|
||||
export class MovieSearchComponent implements OnInit, OnDestroy {
|
||||
export class MovieSearchComponent implements OnInit {
|
||||
|
||||
public searchText: string;
|
||||
public searchChanged: Subject<string> = new Subject<string>();
|
||||
public movieResults: ISearchMovieResult[];
|
||||
public result: IRequestEngineResult;
|
||||
public searchApplied = false;
|
||||
private subscriptions = new Subject<void>();
|
||||
|
||||
|
||||
constructor(private searchService: SearchService, private requestService: RequestService,
|
||||
private notificationService: NotificationService, private authService: AuthService) {
|
||||
|
||||
this.searchChanged
|
||||
.debounceTime(600) // Wait Xms afterthe last event before emitting last event
|
||||
.distinctUntilChanged() // only emit if value is different from previous value
|
||||
.takeUntil(this.subscriptions)
|
||||
.subscribe(x => {
|
||||
this.searchText = x as string;
|
||||
if (this.searchText === "") {
|
||||
|
@ -40,7 +34,6 @@ export class MovieSearchComponent implements OnInit, OnDestroy {
|
|||
return;
|
||||
}
|
||||
this.searchService.searchMovie(this.searchText)
|
||||
.takeUntil(this.subscriptions)
|
||||
.subscribe(x => {
|
||||
this.movieResults = x;
|
||||
this.searchApplied = true;
|
||||
|
@ -72,7 +65,6 @@ export class MovieSearchComponent implements OnInit, OnDestroy {
|
|||
}
|
||||
|
||||
this.requestService.requestMovie(searchResult)
|
||||
.takeUntil(this.subscriptions)
|
||||
.subscribe(x => {
|
||||
this.result = x;
|
||||
|
||||
|
@ -90,7 +82,6 @@ export class MovieSearchComponent implements OnInit, OnDestroy {
|
|||
public popularMovies() {
|
||||
this.clearResults();
|
||||
this.searchService.popularMovies()
|
||||
.takeUntil(this.subscriptions)
|
||||
.subscribe(x => {
|
||||
this.movieResults = x;
|
||||
this.getExtaInfo();
|
||||
|
@ -99,7 +90,6 @@ export class MovieSearchComponent implements OnInit, OnDestroy {
|
|||
public nowPlayingMovies() {
|
||||
this.clearResults();
|
||||
this.searchService.nowPlayingMovies()
|
||||
.takeUntil(this.subscriptions)
|
||||
.subscribe(x => {
|
||||
this.movieResults = x;
|
||||
this.getExtaInfo();
|
||||
|
@ -108,7 +98,6 @@ export class MovieSearchComponent implements OnInit, OnDestroy {
|
|||
public topRatedMovies() {
|
||||
this.clearResults();
|
||||
this.searchService.topRatedMovies()
|
||||
.takeUntil(this.subscriptions)
|
||||
.subscribe(x => {
|
||||
this.movieResults = x;
|
||||
this.getExtaInfo();
|
||||
|
@ -117,23 +106,16 @@ export class MovieSearchComponent implements OnInit, OnDestroy {
|
|||
public upcomingMovies() {
|
||||
this.clearResults();
|
||||
this.searchService.upcomignMovies()
|
||||
.takeUntil(this.subscriptions)
|
||||
.subscribe(x => {
|
||||
this.movieResults = x;
|
||||
this.getExtaInfo();
|
||||
});
|
||||
}
|
||||
|
||||
public ngOnDestroy() {
|
||||
this.subscriptions.next();
|
||||
this.subscriptions.complete();
|
||||
}
|
||||
|
||||
private getExtaInfo() {
|
||||
private getExtaInfo() {
|
||||
|
||||
this.movieResults.forEach((val, index) => {
|
||||
this.searchService.getMovieInformation(val.id)
|
||||
.takeUntil(this.subscriptions)
|
||||
.subscribe(m => this.updateItem(val, m));
|
||||
});
|
||||
}
|
||||
|
|
|
@ -19,4 +19,11 @@ export class RadarrService extends ServiceAuthHelpers {
|
|||
public getQualityProfiles(settings: IRadarrSettings): Observable<IRadarrProfile[]> {
|
||||
return this.http.post(`${this.url}/Profiles/`, JSON.stringify(settings), { headers: this.headers }).map(this.extractData);
|
||||
}
|
||||
|
||||
public getRootFoldersFromSettings(): Observable<IRadarrRootFolder[]> {
|
||||
return this.http.get(`${this.url}/RootFolders/`, { headers: this.headers }).map(this.extractData);
|
||||
}
|
||||
public getQualityProfilesFromSettings(): Observable<IRadarrProfile[]> {
|
||||
return this.http.get(`${this.url}/Profiles/`, { headers: this.headers }).map(this.extractData);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -102,7 +102,7 @@ export class UserManagementComponent implements OnInit {
|
|||
|
||||
public runImporter(): void {
|
||||
this.jobService.runPlexImporter().subscribe();
|
||||
this.jobService.runPlexImporter().subscribe();
|
||||
this.jobService.runEmbyImporter().subscribe();
|
||||
}
|
||||
|
||||
private filter(query: string, users: IUsersModel[]): IUsersModel[] {
|
||||
|
|
|
@ -1,28 +1,33 @@
|
|||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using Ombi.Api.Radarr;
|
||||
using Ombi.Api.Radarr.Models;
|
||||
using Ombi.Attributes;
|
||||
using Ombi.Core.Settings;
|
||||
using Ombi.Helpers;
|
||||
using Ombi.Settings.Settings.Models.External;
|
||||
|
||||
namespace Ombi.Controllers.External
|
||||
{
|
||||
[Admin]
|
||||
[PowerUser]
|
||||
[ApiV1]
|
||||
[Produces("application/json")]
|
||||
public class RadarrController : Controller
|
||||
{
|
||||
public RadarrController(IRadarrApi radarr, ISettingsService<RadarrSettings> settings)
|
||||
public RadarrController(IRadarrApi radarr, ISettingsService<RadarrSettings> settings,
|
||||
IMemoryCache mem)
|
||||
{
|
||||
RadarrApi = radarr;
|
||||
RadarrSettings = settings;
|
||||
Cache = mem;
|
||||
}
|
||||
|
||||
private IRadarrApi RadarrApi { get; }
|
||||
private ISettingsService<RadarrSettings> RadarrSettings { get; }
|
||||
|
||||
private IMemoryCache Cache { get; }
|
||||
/// <summary>
|
||||
/// Gets the Radarr profiles.
|
||||
/// </summary>
|
||||
|
@ -44,5 +49,37 @@ namespace Ombi.Controllers.External
|
|||
{
|
||||
return await RadarrApi.GetRootFolders(settings.ApiKey, settings.FullUri);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Radarr profiles using the saved settings
|
||||
/// <remarks>The data is cached for an hour</remarks>
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpGet("Profiles")]
|
||||
public async Task<IEnumerable<RadarrProfile>> GetProfiles()
|
||||
{
|
||||
return await Cache.GetOrCreate(CacheKeys.RadarrQualityProfiles, async entry =>
|
||||
{
|
||||
entry.AbsoluteExpiration = DateTimeOffset.Now.AddHours(1);
|
||||
var settings = await RadarrSettings.GetSettingsAsync();
|
||||
return await RadarrApi.GetProfiles(settings.ApiKey, settings.FullUri);
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Radar root folders using the saved settings.
|
||||
/// <remarks>The data is cached for an hour</remarks>
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpGet("RootFolders")]
|
||||
public async Task<IEnumerable<RadarrRootFolder>> GetRootFolders()
|
||||
{
|
||||
return await Cache.GetOrCreate(CacheKeys.RadarrRootProfiles, async entry =>
|
||||
{
|
||||
entry.AbsoluteExpiration = DateTimeOffset.Now.AddHours(1);
|
||||
var settings = await RadarrSettings.GetSettingsAsync();
|
||||
return await RadarrApi.GetRootFolders(settings.ApiKey, settings.FullUri);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -7,6 +7,7 @@ using AutoMapper;
|
|||
using Hangfire;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using Microsoft.Extensions.PlatformAbstractions;
|
||||
using Ombi.Api.Emby;
|
||||
using Ombi.Attributes;
|
||||
|
@ -52,7 +53,8 @@ namespace Ombi.Controllers
|
|||
IEmbyApi embyApi,
|
||||
IPlexContentCacher cacher,
|
||||
IEmbyContentCacher embyCacher,
|
||||
IRadarrCacher radarrCacher)
|
||||
IRadarrCacher radarrCacher,
|
||||
IMemoryCache memCache)
|
||||
{
|
||||
SettingsResolver = resolver;
|
||||
Mapper = mapper;
|
||||
|
@ -61,6 +63,7 @@ namespace Ombi.Controllers
|
|||
_plexContentCacher = cacher;
|
||||
_embyContentCacher = embyCacher;
|
||||
_radarrCacher = radarrCacher;
|
||||
_cache = memCache;
|
||||
}
|
||||
|
||||
private ISettingsResolver SettingsResolver { get; }
|
||||
|
@ -70,6 +73,7 @@ namespace Ombi.Controllers
|
|||
private readonly IPlexContentCacher _plexContentCacher;
|
||||
private readonly IEmbyContentCacher _embyContentCacher;
|
||||
private readonly IRadarrCacher _radarrCacher;
|
||||
private readonly IMemoryCache _cache;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Ombi settings.
|
||||
|
@ -290,6 +294,8 @@ namespace Ombi.Controllers
|
|||
var result = await Save(settings);
|
||||
if (result)
|
||||
{
|
||||
_cache.Remove(CacheKeys.RadarrRootProfiles);
|
||||
_cache.Remove(CacheKeys.RadarrQualityProfiles);
|
||||
BackgroundJob.Enqueue(() => _radarrCacher.CacheContent());
|
||||
}
|
||||
return result;
|
||||
|
|
|
@ -138,19 +138,6 @@ namespace Ombi
|
|||
});
|
||||
}
|
||||
|
||||
app.UseHangfireServer();
|
||||
app.UseHangfireDashboard("/hangfire", new DashboardOptions
|
||||
{
|
||||
Authorization = new[] { new HangfireAuthorizationFilter() }
|
||||
});
|
||||
|
||||
// Setup the scheduler
|
||||
var jobSetup = app.ApplicationServices.GetService<IJobSetup>();
|
||||
jobSetup.Setup();
|
||||
ctx.Seed();
|
||||
|
||||
var provider = new FileExtensionContentTypeProvider { Mappings = { [".map"] = "application/octet-stream" } };
|
||||
|
||||
var ombiService =
|
||||
app.ApplicationServices.GetService<ISettingsService<OmbiSettings>>();
|
||||
var settings = ombiService.GetSettings();
|
||||
|
@ -159,6 +146,20 @@ namespace Ombi
|
|||
app.UsePathBase(settings.BaseUrl);
|
||||
}
|
||||
|
||||
app.UseHangfireServer();
|
||||
app.UseHangfireDashboard(settings.BaseUrl.HasValue() ? $"{settings.BaseUrl}/hangfire" : "/hangfire",
|
||||
new DashboardOptions
|
||||
{
|
||||
Authorization = new[] {new HangfireAuthorizationFilter()}
|
||||
});
|
||||
|
||||
// Setup the scheduler
|
||||
var jobSetup = app.ApplicationServices.GetService<IJobSetup>();
|
||||
jobSetup.Setup();
|
||||
ctx.Seed();
|
||||
|
||||
var provider = new FileExtensionContentTypeProvider { Mappings = { [".map"] = "application/octet-stream" } };
|
||||
|
||||
app.UseStaticFiles(new StaticFileOptions()
|
||||
{
|
||||
ContentTypeProvider = provider,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue