mirror of
https://github.com/Ombi-app/Ombi.git
synced 2025-07-30 03:28:28 -07:00
merge
This commit is contained in:
commit
eedd67b205
269 changed files with 10168 additions and 1028 deletions
|
@ -33,6 +33,7 @@ using Microsoft.EntityFrameworkCore;
|
|||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Ombi.Api.Emby;
|
||||
using Ombi.Api.Jellyfin;
|
||||
using Ombi.Api.Plex;
|
||||
using Ombi.Api.Plex.Models;
|
||||
using Ombi.Core.Settings;
|
||||
|
@ -49,18 +50,24 @@ namespace Ombi.Core.Authentication
|
|||
IPasswordHasher<OmbiUser> passwordHasher, IEnumerable<IUserValidator<OmbiUser>> userValidators,
|
||||
IEnumerable<IPasswordValidator<OmbiUser>> passwordValidators, ILookupNormalizer keyNormalizer,
|
||||
IdentityErrorDescriber errors, IServiceProvider services, ILogger<UserManager<OmbiUser>> logger, IPlexApi plexApi,
|
||||
IEmbyApiFactory embyApi, ISettingsService<EmbySettings> embySettings, ISettingsService<AuthenticationSettings> auth)
|
||||
IEmbyApiFactory embyApi, ISettingsService<EmbySettings> embySettings,
|
||||
IJellyfinApiFactory jellyfinApi, ISettingsService<JellyfinSettings> jellyfinSettings,
|
||||
ISettingsService<AuthenticationSettings> auth)
|
||||
: base(store, optionsAccessor, passwordHasher, userValidators, passwordValidators, keyNormalizer, errors, services, logger)
|
||||
{
|
||||
_plexApi = plexApi;
|
||||
_embyApi = embyApi;
|
||||
_jellyfinApi = jellyfinApi;
|
||||
_embySettings = embySettings;
|
||||
_jellyfinSettings = jellyfinSettings;
|
||||
_authSettings = auth;
|
||||
}
|
||||
|
||||
private readonly IPlexApi _plexApi;
|
||||
private readonly IEmbyApiFactory _embyApi;
|
||||
private readonly IJellyfinApiFactory _jellyfinApi;
|
||||
private readonly ISettingsService<EmbySettings> _embySettings;
|
||||
private readonly ISettingsService<JellyfinSettings> _jellyfinSettings;
|
||||
private readonly ISettingsService<AuthenticationSettings> _authSettings;
|
||||
|
||||
public override async Task<bool> CheckPasswordAsync(OmbiUser user, string password)
|
||||
|
@ -83,6 +90,10 @@ namespace Ombi.Core.Authentication
|
|||
{
|
||||
return await CheckEmbyPasswordAsync(user, password);
|
||||
}
|
||||
if (user.UserType == UserType.JellyfinUser)
|
||||
{
|
||||
return await CheckJellyfinPasswordAsync(user, password);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -185,5 +196,36 @@ namespace Ombi.Core.Authentication
|
|||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sign the user into Jellyfin
|
||||
/// <remarks>We do not check if the user is in the owners "friends" since they must have a local user account to get this far.
|
||||
/// We also have to try and authenticate them with every server, the first server that work we just say it was a success</remarks>
|
||||
/// </summary>
|
||||
/// <param name="user"></param>
|
||||
/// <param name="password"></param>
|
||||
/// <returns></returns>
|
||||
private async Task<bool> CheckJellyfinPasswordAsync(OmbiUser user, string password)
|
||||
{
|
||||
var jellyfinSettings = await _jellyfinSettings.GetSettingsAsync();
|
||||
var client = _jellyfinApi.CreateClient(jellyfinSettings);
|
||||
|
||||
foreach (var server in jellyfinSettings.Servers)
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = await client.LogIn(user.UserName, password, server.ApiKey, server.FullUri);
|
||||
if (result != null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.LogError(e, "Jellyfin Login Failed");
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,8 @@ using Ombi.Core.Settings;
|
|||
using Ombi.Settings.Settings.Models;
|
||||
using Ombi.Store.Entities;
|
||||
using Ombi.Store.Repository;
|
||||
using Ombi.Api.TheMovieDb.Models;
|
||||
using Ombi.Core.Helpers;
|
||||
|
||||
namespace Ombi.Core.Engine
|
||||
{
|
||||
|
@ -179,6 +181,12 @@ namespace Ombi.Core.Engine
|
|||
return user.Language;
|
||||
}
|
||||
|
||||
protected async Task<List<StreamData>> GetUserWatchProvider(WatchProviders providers)
|
||||
{
|
||||
var user = await GetUser();
|
||||
return WatchProviderParser.GetUserWatchProviders(providers, user);
|
||||
}
|
||||
|
||||
private OmbiSettings ombiSettings;
|
||||
protected async Task<OmbiSettings> GetOmbiSettings()
|
||||
{
|
||||
|
|
|
@ -26,5 +26,6 @@ namespace Ombi.Core.Engine.Interfaces
|
|||
int ResultLimit { get; set; }
|
||||
|
||||
Task<MovieFullInfoViewModel> GetMovieInfoByImdbId(string imdbId, CancellationToken requestAborted);
|
||||
Task<IEnumerable<StreamingData>> GetStreamInformation(int movieDbId, CancellationToken cancellationToken);
|
||||
}
|
||||
}
|
|
@ -9,5 +9,6 @@ namespace Ombi.Core.Engine.Interfaces
|
|||
Task<ArtistInformation> GetArtistInformation(string artistId);
|
||||
Task<ArtistInformation> GetArtistInformationByRequestId(int requestId);
|
||||
Task<AlbumArt> GetReleaseGroupArt(string musicBrainzId, CancellationToken token);
|
||||
Task<ReleaseGroup> GetAlbum(string albumId);
|
||||
}
|
||||
}
|
|
@ -1,4 +1,6 @@
|
|||
using System.Threading.Tasks;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Ombi.Core.Models.Search.V2;
|
||||
|
||||
namespace Ombi.Core
|
||||
|
@ -7,5 +9,6 @@ namespace Ombi.Core
|
|||
{
|
||||
Task<SearchFullInfoTvShowViewModel> GetShowInformation(int tvdbid);
|
||||
Task<SearchFullInfoTvShowViewModel> GetShowByRequest(int requestId);
|
||||
Task<IEnumerable<StreamingData>> GetStreamInformation(int tvDbId, int tvMazeId, CancellationToken cancellationToken);
|
||||
}
|
||||
}
|
|
@ -67,6 +67,22 @@ namespace Ombi.Core.Engine
|
|||
$"{movieInfo.Title}{(!string.IsNullOrEmpty(movieInfo.ReleaseDate) ? $" ({DateTime.Parse(movieInfo.ReleaseDate).Year})" : string.Empty)}";
|
||||
|
||||
var userDetails = await GetUser();
|
||||
var canRequestOnBehalf = false;
|
||||
|
||||
if (model.RequestOnBehalf.HasValue())
|
||||
{
|
||||
canRequestOnBehalf = await UserManager.IsInRoleAsync(userDetails, OmbiRoles.PowerUser) || await UserManager.IsInRoleAsync(userDetails, OmbiRoles.Admin);
|
||||
|
||||
if (!canRequestOnBehalf)
|
||||
{
|
||||
return new RequestEngineResult
|
||||
{
|
||||
Result = false,
|
||||
Message = "You do not have the correct permissions to request on behalf of users!",
|
||||
ErrorMessage = $"You do not have the correct permissions to request on behalf of users!"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
var requestModel = new MovieRequests
|
||||
{
|
||||
|
@ -82,7 +98,7 @@ namespace Ombi.Core.Engine
|
|||
Status = movieInfo.Status,
|
||||
RequestedDate = DateTime.UtcNow,
|
||||
Approved = false,
|
||||
RequestedUserId = userDetails.Id,
|
||||
RequestedUserId = canRequestOnBehalf ? model.RequestOnBehalf : userDetails.Id,
|
||||
Background = movieInfo.BackdropPath,
|
||||
LangCode = model.LanguageCode,
|
||||
RequestedByAlias = model.RequestedByAlias
|
||||
|
@ -103,7 +119,7 @@ namespace Ombi.Core.Engine
|
|||
|
||||
if (requestModel.Approved) // The rules have auto approved this
|
||||
{
|
||||
var requestEngineResult = await AddMovieRequest(requestModel, fullMovieName);
|
||||
var requestEngineResult = await AddMovieRequest(requestModel, fullMovieName, model.RequestOnBehalf);
|
||||
if (requestEngineResult.Result)
|
||||
{
|
||||
var result = await ApproveMovie(requestModel);
|
||||
|
@ -124,7 +140,7 @@ namespace Ombi.Core.Engine
|
|||
// If there are no providers then it's successful but movie has not been sent
|
||||
}
|
||||
|
||||
return await AddMovieRequest(requestModel, fullMovieName);
|
||||
return await AddMovieRequest(requestModel, fullMovieName, model.RequestOnBehalf);
|
||||
}
|
||||
|
||||
|
||||
|
@ -270,7 +286,7 @@ namespace Ombi.Core.Engine
|
|||
allRequests = allRequests.Where(x => x.Available);
|
||||
break;
|
||||
case RequestStatus.Denied:
|
||||
allRequests = allRequests.Where(x => x.Denied.HasValue && x.Denied.Value && !x.Available);
|
||||
allRequests = allRequests.Where(x => x.Denied.HasValue && x.Denied.Value && !x.Available);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
@ -429,7 +445,7 @@ namespace Ombi.Core.Engine
|
|||
public async Task<MovieRequests> GetRequest(int requestId)
|
||||
{
|
||||
var request = await MovieRepository.GetWithUser().Where(x => x.Id == requestId).FirstOrDefaultAsync();
|
||||
await CheckForSubscription(new HideResult(), new List<MovieRequests>{request });
|
||||
await CheckForSubscription(new HideResult(), new List<MovieRequests> { request });
|
||||
|
||||
return request;
|
||||
}
|
||||
|
@ -654,19 +670,19 @@ namespace Ombi.Core.Engine
|
|||
};
|
||||
}
|
||||
|
||||
private async Task<RequestEngineResult> AddMovieRequest(MovieRequests model, string movieName)
|
||||
private async Task<RequestEngineResult> AddMovieRequest(MovieRequests model, string movieName, string requestOnBehalf)
|
||||
{
|
||||
await MovieRepository.Add(model);
|
||||
|
||||
var result = await RunSpecificRule(model, SpecificRules.CanSendNotification);
|
||||
if (result.Success)
|
||||
{
|
||||
{
|
||||
await NotificationHelper.NewRequest(model);
|
||||
}
|
||||
|
||||
await _requestLog.Add(new RequestLog
|
||||
{
|
||||
UserId = (await GetUser()).Id,
|
||||
UserId = requestOnBehalf.HasValue() ? requestOnBehalf : (await GetUser()).Id,
|
||||
RequestDate = DateTime.UtcNow,
|
||||
RequestId = model.Id,
|
||||
RequestType = RequestType.Movie,
|
||||
|
|
|
@ -13,15 +13,17 @@ namespace Ombi.Core.Engine
|
|||
{
|
||||
public class RecentlyAddedEngine : IRecentlyAddedEngine
|
||||
{
|
||||
public RecentlyAddedEngine(IPlexContentRepository plex, IEmbyContentRepository emby, IRepository<RecentlyAddedLog> recentlyAdded)
|
||||
public RecentlyAddedEngine(IPlexContentRepository plex, IEmbyContentRepository emby, IJellyfinContentRepository jellyfin, IRepository<RecentlyAddedLog> recentlyAdded)
|
||||
{
|
||||
_plex = plex;
|
||||
_emby = emby;
|
||||
_jellyfin = jellyfin;
|
||||
_recentlyAddedLog = recentlyAdded;
|
||||
}
|
||||
|
||||
private readonly IPlexContentRepository _plex;
|
||||
private readonly IEmbyContentRepository _emby;
|
||||
private readonly IJellyfinContentRepository _jellyfin;
|
||||
private readonly IRepository<RecentlyAddedLog> _recentlyAddedLog;
|
||||
|
||||
|
||||
|
@ -30,23 +32,26 @@ namespace Ombi.Core.Engine
|
|||
{
|
||||
var plexMovies = _plex.GetAll().Where(x => x.Type == PlexMediaTypeEntity.Movie && x.AddedAt > from && x.AddedAt < to);
|
||||
var embyMovies = _emby.GetAll().Where(x => x.Type == EmbyMediaType.Movie && x.AddedAt > from && x.AddedAt < to);
|
||||
var jellyfinMovies = _jellyfin.GetAll().Where(x => x.Type == JellyfinMediaType.Movie && x.AddedAt > from && x.AddedAt < to);
|
||||
|
||||
return GetRecentlyAddedMovies(plexMovies, embyMovies).Take(30);
|
||||
return GetRecentlyAddedMovies(plexMovies, embyMovies, jellyfinMovies).Take(30);
|
||||
}
|
||||
|
||||
public IEnumerable<RecentlyAddedMovieModel> GetRecentlyAddedMovies()
|
||||
{
|
||||
var plexMovies = _plex.GetAll().Where(x => x.Type == PlexMediaTypeEntity.Movie);
|
||||
var embyMovies = _emby.GetAll().Where(x => x.Type == EmbyMediaType.Movie);
|
||||
return GetRecentlyAddedMovies(plexMovies, embyMovies);
|
||||
var jellyfinMovies = _jellyfin.GetAll().Where(x => x.Type == JellyfinMediaType.Movie);
|
||||
return GetRecentlyAddedMovies(plexMovies, embyMovies, jellyfinMovies);
|
||||
}
|
||||
|
||||
public IEnumerable<RecentlyAddedTvModel> GetRecentlyAddedTv(DateTime from, DateTime to, bool groupBySeason)
|
||||
{
|
||||
var plexTv = _plex.GetAll().Include(x => x.Seasons).Include(x => x.Episodes).Where(x => x.Type == PlexMediaTypeEntity.Show && x.AddedAt > from && x.AddedAt < to);
|
||||
var embyTv = _emby.GetAll().Include(x => x.Episodes).Where(x => x.Type == EmbyMediaType.Series && x.AddedAt > from && x.AddedAt < to);
|
||||
var jellyfinTv = _jellyfin.GetAll().Include(x => x.Episodes).Where(x => x.Type == JellyfinMediaType.Series && x.AddedAt > from && x.AddedAt < to);
|
||||
|
||||
return GetRecentlyAddedTv(plexTv, embyTv, groupBySeason).Take(30);
|
||||
return GetRecentlyAddedTv(plexTv, embyTv, jellyfinTv, groupBySeason).Take(30);
|
||||
}
|
||||
|
||||
|
||||
|
@ -54,14 +59,16 @@ namespace Ombi.Core.Engine
|
|||
{
|
||||
var plexTv = _plex.GetAll().Include(x => x.Seasons).Include(x => x.Episodes).Where(x => x.Type == PlexMediaTypeEntity.Show);
|
||||
var embyTv = _emby.GetAll().Include(x => x.Episodes).Where(x => x.Type == EmbyMediaType.Series);
|
||||
var jellyfinTv = _jellyfin.GetAll().Include(x => x.Episodes).Where(x => x.Type == JellyfinMediaType.Series);
|
||||
|
||||
return GetRecentlyAddedTv(plexTv, embyTv, groupBySeason);
|
||||
return GetRecentlyAddedTv(plexTv, embyTv, jellyfinTv, groupBySeason);
|
||||
}
|
||||
|
||||
public async Task<bool> UpdateRecentlyAddedDatabase()
|
||||
{
|
||||
var plexContent = _plex.GetAll().Include(x => x.Episodes);
|
||||
var embyContent = _emby.GetAll().Include(x => x.Episodes);
|
||||
var jellyfinContent = _jellyfin.GetAll().Include(x => x.Episodes);
|
||||
var recentlyAddedLog = new HashSet<RecentlyAddedLog>();
|
||||
foreach (var p in plexContent)
|
||||
{
|
||||
|
@ -138,17 +145,56 @@ namespace Ombi.Core.Engine
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var e in jellyfinContent)
|
||||
{
|
||||
if (e.TheMovieDbId.IsNullOrEmpty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (e.Type == JellyfinMediaType.Movie)
|
||||
{
|
||||
recentlyAddedLog.Add(new RecentlyAddedLog
|
||||
{
|
||||
AddedAt = DateTime.Now,
|
||||
Type = RecentlyAddedType.Jellyfin,
|
||||
ContentId = int.Parse(e.TheMovieDbId),
|
||||
ContentType = ContentType.Parent
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
// Add the episodes
|
||||
foreach (var ep in e.Episodes)
|
||||
{
|
||||
if (ep.Series.TvDbId.IsNullOrEmpty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
recentlyAddedLog.Add(new RecentlyAddedLog
|
||||
{
|
||||
AddedAt = DateTime.Now,
|
||||
Type = RecentlyAddedType.Jellyfin,
|
||||
ContentId = int.Parse(ep.Series.TvDbId),
|
||||
ContentType = ContentType.Episode,
|
||||
EpisodeNumber = ep.EpisodeNumber,
|
||||
SeasonNumber = ep.SeasonNumber
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
await _recentlyAddedLog.AddRange(recentlyAddedLog);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private IEnumerable<RecentlyAddedTvModel> GetRecentlyAddedTv(IQueryable<PlexServerContent> plexTv, IQueryable<EmbyContent> embyTv,
|
||||
private IEnumerable<RecentlyAddedTvModel> GetRecentlyAddedTv(IQueryable<PlexServerContent> plexTv, IQueryable<EmbyContent> embyTv, IQueryable<JellyfinContent> jellyfinTv,
|
||||
bool groupBySeason)
|
||||
{
|
||||
var model = new HashSet<RecentlyAddedTvModel>();
|
||||
TransformPlexShows(plexTv, model);
|
||||
TransformEmbyShows(embyTv, model);
|
||||
TransformJellyfinShows(jellyfinTv, model);
|
||||
|
||||
if (groupBySeason)
|
||||
{
|
||||
|
@ -158,11 +204,12 @@ namespace Ombi.Core.Engine
|
|||
return model;
|
||||
}
|
||||
|
||||
private IEnumerable<RecentlyAddedMovieModel> GetRecentlyAddedMovies(IQueryable<PlexServerContent> plexMovies, IQueryable<EmbyContent> embyMovies)
|
||||
private IEnumerable<RecentlyAddedMovieModel> GetRecentlyAddedMovies(IQueryable<PlexServerContent> plexMovies, IQueryable<EmbyContent> embyMovies, IQueryable<JellyfinContent> jellyfinMovies)
|
||||
{
|
||||
var model = new HashSet<RecentlyAddedMovieModel>();
|
||||
TransformPlexMovies(plexMovies, model);
|
||||
TransformEmbyMovies(embyMovies, model);
|
||||
TransformJellyfinMovies(jellyfinMovies, model);
|
||||
|
||||
return model;
|
||||
}
|
||||
|
@ -183,6 +230,22 @@ namespace Ombi.Core.Engine
|
|||
}
|
||||
}
|
||||
|
||||
private static void TransformJellyfinMovies(IQueryable<JellyfinContent> jellyfinMovies, HashSet<RecentlyAddedMovieModel> model)
|
||||
{
|
||||
foreach (var jellyfin in jellyfinMovies)
|
||||
{
|
||||
model.Add(new RecentlyAddedMovieModel
|
||||
{
|
||||
Id = jellyfin.Id,
|
||||
ImdbId = jellyfin.ImdbId,
|
||||
TheMovieDbId = jellyfin.TheMovieDbId,
|
||||
TvDbId = jellyfin.TvDbId,
|
||||
AddedAt = jellyfin.AddedAt,
|
||||
Title = jellyfin.Title,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static void TransformPlexMovies(IQueryable<PlexServerContent> plexMovies, HashSet<RecentlyAddedMovieModel> model)
|
||||
{
|
||||
foreach (var plex in plexMovies)
|
||||
|
@ -246,5 +309,26 @@ namespace Ombi.Core.Engine
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void TransformJellyfinShows(IQueryable<JellyfinContent> jellyfinShows, HashSet<RecentlyAddedTvModel> model)
|
||||
{
|
||||
foreach (var jellyfin in jellyfinShows)
|
||||
{
|
||||
foreach (var episode in jellyfin.Episodes)
|
||||
{
|
||||
model.Add(new RecentlyAddedTvModel
|
||||
{
|
||||
Id = jellyfin.Id,
|
||||
ImdbId = jellyfin.ImdbId,
|
||||
TvDbId = jellyfin.TvDbId,
|
||||
TheMovieDbId = jellyfin.TheMovieDbId,
|
||||
AddedAt = jellyfin.AddedAt,
|
||||
Title = jellyfin.Title,
|
||||
EpisodeNumber = episode.EpisodeNumber,
|
||||
SeasonNumber = episode.SeasonNumber
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,12 +51,28 @@ namespace Ombi.Core.Engine
|
|||
public async Task<RequestEngineResult> RequestTvShow(TvRequestViewModel tv)
|
||||
{
|
||||
var user = await GetUser();
|
||||
var canRequestOnBehalf = false;
|
||||
|
||||
if (tv.RequestOnBehalf.HasValue())
|
||||
{
|
||||
canRequestOnBehalf = await UserManager.IsInRoleAsync(user, OmbiRoles.PowerUser) || await UserManager.IsInRoleAsync(user, OmbiRoles.Admin);
|
||||
|
||||
if (!canRequestOnBehalf)
|
||||
{
|
||||
return new RequestEngineResult
|
||||
{
|
||||
Result = false,
|
||||
Message = "You do not have the correct permissions to request on behalf of users!",
|
||||
ErrorMessage = $"You do not have the correct permissions to request on behalf of users!"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
var tvBuilder = new TvShowRequestBuilder(TvApi, MovieDbApi);
|
||||
(await tvBuilder
|
||||
.GetShowInfo(tv.TvDbId))
|
||||
.CreateTvList(tv)
|
||||
.CreateChild(tv, user.Id);
|
||||
.CreateChild(tv, canRequestOnBehalf ? tv.RequestOnBehalf : user.Id);
|
||||
|
||||
await tvBuilder.BuildEpisodes(tv);
|
||||
|
||||
|
@ -124,12 +140,12 @@ namespace Ombi.Core.Engine
|
|||
ErrorMessage = "This has already been requested"
|
||||
};
|
||||
}
|
||||
return await AddExistingRequest(tvBuilder.ChildRequest, existingRequest);
|
||||
return await AddExistingRequest(tvBuilder.ChildRequest, existingRequest, tv.RequestOnBehalf);
|
||||
}
|
||||
|
||||
// This is a new request
|
||||
var newRequest = tvBuilder.CreateNewRequest(tv);
|
||||
return await AddRequest(newRequest.NewRequest);
|
||||
return await AddRequest(newRequest.NewRequest, tv.RequestOnBehalf);
|
||||
}
|
||||
|
||||
public async Task<RequestsViewModel<TvRequests>> GetRequests(int count, int position, OrderFilterModel type)
|
||||
|
@ -736,21 +752,21 @@ namespace Ombi.Core.Engine
|
|||
}
|
||||
}
|
||||
|
||||
private async Task<RequestEngineResult> AddExistingRequest(ChildRequests newRequest, TvRequests existingRequest)
|
||||
private async Task<RequestEngineResult> AddExistingRequest(ChildRequests newRequest, TvRequests existingRequest, string requestOnBehalf)
|
||||
{
|
||||
// Add the child
|
||||
existingRequest.ChildRequests.Add(newRequest);
|
||||
|
||||
await TvRepository.Update(existingRequest);
|
||||
|
||||
return await AfterRequest(newRequest);
|
||||
return await AfterRequest(newRequest, requestOnBehalf);
|
||||
}
|
||||
|
||||
private async Task<RequestEngineResult> AddRequest(TvRequests model)
|
||||
private async Task<RequestEngineResult> AddRequest(TvRequests model, string requestOnBehalf)
|
||||
{
|
||||
await TvRepository.Add(model);
|
||||
// This is a new request so we should only have 1 child
|
||||
return await AfterRequest(model.ChildRequests.FirstOrDefault());
|
||||
return await AfterRequest(model.ChildRequests.FirstOrDefault(), requestOnBehalf);
|
||||
}
|
||||
|
||||
private static List<ChildRequests> SortEpisodes(List<ChildRequests> items)
|
||||
|
@ -766,7 +782,7 @@ namespace Ombi.Core.Engine
|
|||
}
|
||||
|
||||
|
||||
private async Task<RequestEngineResult> AfterRequest(ChildRequests model)
|
||||
private async Task<RequestEngineResult> AfterRequest(ChildRequests model, string requestOnBehalf)
|
||||
{
|
||||
var sendRuleResult = await RunSpecificRule(model, SpecificRules.CanSendNotification);
|
||||
if (sendRuleResult.Success)
|
||||
|
@ -776,7 +792,7 @@ namespace Ombi.Core.Engine
|
|||
|
||||
await _requestLog.Add(new RequestLog
|
||||
{
|
||||
UserId = (await GetUser()).Id,
|
||||
UserId = requestOnBehalf.HasValue() ? requestOnBehalf : (await GetUser()).Id,
|
||||
RequestDate = DateTime.UtcNow,
|
||||
RequestId = model.Id,
|
||||
RequestType = RequestType.TvShow,
|
||||
|
|
|
@ -249,6 +249,26 @@ namespace Ombi.Core.Engine.V2
|
|||
return result;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<StreamingData>> GetStreamInformation(int movieDbId, CancellationToken cancellationToken)
|
||||
{
|
||||
var providers = await MovieApi.GetMovieWatchProviders(movieDbId, cancellationToken);
|
||||
var results = await GetUserWatchProvider(providers);
|
||||
|
||||
var data = new List<StreamingData>();
|
||||
|
||||
foreach (var result in results)
|
||||
{
|
||||
data.Add(new StreamingData
|
||||
{
|
||||
Logo = result.logo_path,
|
||||
Order = result.display_priority,
|
||||
StreamingProvider = result.provider_name
|
||||
});
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
protected async Task<List<SearchMovieViewModel>> TransformMovieResultsToResponse(
|
||||
IEnumerable<MovieSearchResult> movies)
|
||||
{
|
||||
|
@ -287,6 +307,7 @@ namespace Ombi.Core.Engine.V2
|
|||
mapped.Requested = viewMovie.Requested;
|
||||
mapped.PlexUrl = viewMovie.PlexUrl;
|
||||
mapped.EmbyUrl = viewMovie.EmbyUrl;
|
||||
mapped.JellyfinUrl = viewMovie.JellyfinUrl;
|
||||
mapped.Subscribed = viewMovie.Subscribed;
|
||||
mapped.ShowSubscribe = viewMovie.ShowSubscribe;
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ using System.Linq;
|
|||
using System.Security.Principal;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
using Ombi.Api.Lidarr;
|
||||
using Ombi.Api.Lidarr.Models;
|
||||
using Ombi.Api.MusicBrainz;
|
||||
|
@ -41,6 +42,21 @@ namespace Ombi.Core.Engine.V2
|
|||
_lidarrApi = lidarrApi;
|
||||
}
|
||||
|
||||
public async Task<ReleaseGroup> GetAlbum(string albumId)
|
||||
{
|
||||
var g = await _musicBrainzApi.GetAlbumInformation(albumId);
|
||||
var release = new ReleaseGroup
|
||||
{
|
||||
ReleaseType = g.ReleaseGroup.PrimaryType,
|
||||
Id = g.Id,
|
||||
Title = g.Title,
|
||||
ReleaseDate = g.ReleaseGroup.FirstReleaseDate,
|
||||
};
|
||||
|
||||
await RunSearchRules(release);
|
||||
return release;
|
||||
}
|
||||
|
||||
public async Task<ArtistInformation> GetArtistInformation(string artistId)
|
||||
{
|
||||
var artist = await _musicBrainzApi.GetArtistInformation(artistId);
|
||||
|
@ -84,12 +100,19 @@ namespace Ombi.Core.Engine.V2
|
|||
|
||||
if (lidarrArtistTask != null)
|
||||
{
|
||||
var artistResult = await lidarrArtistTask;
|
||||
info.Banner = artistResult.images?.FirstOrDefault(x => x.coverType.Equals("banner", StringComparison.InvariantCultureIgnoreCase))?.url.ToHttpsUrl();
|
||||
info.Logo = artistResult.images?.FirstOrDefault(x => x.coverType.Equals("logo", StringComparison.InvariantCultureIgnoreCase))?.url.ToHttpsUrl();
|
||||
info.Poster = artistResult.images?.FirstOrDefault(x => x.coverType.Equals("poster", StringComparison.InvariantCultureIgnoreCase))?.url.ToHttpsUrl();
|
||||
info.FanArt = artistResult.images?.FirstOrDefault(x => x.coverType.Equals("fanart", StringComparison.InvariantCultureIgnoreCase))?.url.ToHttpsUrl();
|
||||
info.Overview = artistResult.overview;
|
||||
try
|
||||
{
|
||||
var artistResult = await lidarrArtistTask;
|
||||
info.Banner = artistResult.images?.FirstOrDefault(x => x.coverType.Equals("banner", StringComparison.InvariantCultureIgnoreCase))?.url.ToHttpsUrl();
|
||||
info.Logo = artistResult.images?.FirstOrDefault(x => x.coverType.Equals("logo", StringComparison.InvariantCultureIgnoreCase))?.url.ToHttpsUrl();
|
||||
info.Poster = artistResult.images?.FirstOrDefault(x => x.coverType.Equals("poster", StringComparison.InvariantCultureIgnoreCase))?.url.ToHttpsUrl();
|
||||
info.FanArt = artistResult.images?.FirstOrDefault(x => x.coverType.Equals("fanart", StringComparison.InvariantCultureIgnoreCase))?.url.ToHttpsUrl();
|
||||
info.Overview = artistResult.overview;
|
||||
}
|
||||
catch (JsonSerializationException)
|
||||
{
|
||||
// swallow, Lidarr probably doesn't have this artist
|
||||
}
|
||||
}
|
||||
|
||||
return info;
|
||||
|
@ -118,7 +141,7 @@ namespace Ombi.Core.Engine.V2
|
|||
|
||||
return new AlbumArt();
|
||||
}
|
||||
|
||||
|
||||
public async Task<ArtistInformation> GetArtistInformationByRequestId(int requestId)
|
||||
{
|
||||
var request = await RequestService.MusicRequestRepository.Find(requestId);
|
||||
|
|
|
@ -19,6 +19,8 @@ using Ombi.Core.Settings;
|
|||
using Ombi.Store.Repository;
|
||||
using TraktSharp.Entities;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.Threading;
|
||||
using Ombi.Api.TheMovieDb;
|
||||
|
||||
namespace Ombi.Core.Engine.V2
|
||||
{
|
||||
|
@ -27,15 +29,17 @@ namespace Ombi.Core.Engine.V2
|
|||
private readonly ITvMazeApi _tvMaze;
|
||||
private readonly IMapper _mapper;
|
||||
private readonly ITraktApi _traktApi;
|
||||
private readonly IMovieDbApi _movieApi;
|
||||
|
||||
public TvSearchEngineV2(IPrincipal identity, IRequestServiceMain service, ITvMazeApi tvMaze, IMapper mapper,
|
||||
ITraktApi trakt, IRuleEvaluator r, OmbiUserManager um, ICacheService memCache, ISettingsService<OmbiSettings> s,
|
||||
IRepository<RequestSubscription> sub)
|
||||
IRepository<RequestSubscription> sub, IMovieDbApi movieApi)
|
||||
: base(identity, service, r, um, memCache, s, sub)
|
||||
{
|
||||
_tvMaze = tvMaze;
|
||||
_mapper = mapper;
|
||||
_traktApi = trakt;
|
||||
_movieApi = movieApi;
|
||||
}
|
||||
|
||||
|
||||
|
@ -106,6 +110,39 @@ namespace Ombi.Core.Engine.V2
|
|||
return await ProcessResult(mapped, traktInfoTask);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<StreamingData>> GetStreamInformation(int tvDbId, int tvMazeId, CancellationToken cancellationToken)
|
||||
{
|
||||
var tvdbshow = await Cache.GetOrAdd(nameof(GetShowInformation) + tvMazeId,
|
||||
async () => await _tvMaze.ShowLookupByTheTvDbId(tvMazeId), DateTime.Now.AddHours(12));
|
||||
if (tvdbshow == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/// this is a best effort guess since TV maze do not provide the TheMovieDbId
|
||||
var movieDbResults = await _movieApi.SearchTv(tvdbshow.name, tvdbshow.premiered.Substring(0, 4));
|
||||
var potential = movieDbResults.FirstOrDefault();
|
||||
tvDbId = potential.Id;
|
||||
// end guess
|
||||
|
||||
var providers = await _movieApi.GetTvWatchProviders(tvDbId, cancellationToken);
|
||||
var results = await GetUserWatchProvider(providers);
|
||||
|
||||
var data = new List<StreamingData>();
|
||||
|
||||
foreach (var result in results)
|
||||
{
|
||||
data.Add(new StreamingData
|
||||
{
|
||||
Logo = result.logo_path,
|
||||
Order = result.display_priority,
|
||||
StreamingProvider = result.provider_name
|
||||
});
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
private IEnumerable<SearchTvShowViewModel> ProcessResults<T>(IEnumerable<T> items)
|
||||
{
|
||||
var retVal = new List<SearchTvShowViewModel>();
|
||||
|
@ -141,7 +178,7 @@ namespace Ombi.Core.Engine.V2
|
|||
{
|
||||
item.Images.Medium = item.Images.Medium.ToHttpsUrl();
|
||||
}
|
||||
|
||||
|
||||
if (item.Cast?.Any() ?? false)
|
||||
{
|
||||
foreach (var cast in item.Cast)
|
||||
|
|
35
src/Ombi.Core/Helpers/WatchProviderParser.cs
Normal file
35
src/Ombi.Core/Helpers/WatchProviderParser.cs
Normal file
|
@ -0,0 +1,35 @@
|
|||
using Ombi.Api.TheMovieDb.Models;
|
||||
using Ombi.Store.Entities;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Ombi.Core.Helpers
|
||||
{
|
||||
public static class WatchProviderParser
|
||||
{
|
||||
public static List<StreamData> GetUserWatchProviders(WatchProviders providers, OmbiUser user)
|
||||
{
|
||||
var data = new List<StreamData>();
|
||||
|
||||
if (providers?.Results == null)
|
||||
{
|
||||
return data;
|
||||
}
|
||||
|
||||
var resultsProp = providers.Results.GetType().GetProperties();
|
||||
var matchingStreamingCountry = resultsProp.FirstOrDefault(x => x.Name.Equals(user.StreamingCountry, StringComparison.InvariantCultureIgnoreCase));
|
||||
if (matchingStreamingCountry == null)
|
||||
{
|
||||
return data;
|
||||
}
|
||||
|
||||
var result = (WatchProviderData)matchingStreamingCountry.GetValue(providers.Results);
|
||||
if (result == null || result.StreamInformation == null)
|
||||
{
|
||||
return data;
|
||||
}
|
||||
return result.StreamInformation;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -18,6 +18,7 @@ namespace Ombi.Core.Models
|
|||
public enum RecentlyAddedType
|
||||
{
|
||||
Plex,
|
||||
Emby
|
||||
Emby,
|
||||
Jellyfin
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@ namespace Ombi.Core.Models.Requests
|
|||
{
|
||||
public int TheMovieDbId { get; set; }
|
||||
public string LanguageCode { get; set; } = "en";
|
||||
public string RequestOnBehalf { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This is only set from a HTTP Header
|
||||
|
|
|
@ -12,6 +12,8 @@ namespace Ombi.Core.Models.Requests
|
|||
public List<SeasonsViewModel> Seasons { get; set; } = new List<SeasonsViewModel>();
|
||||
[JsonIgnore]
|
||||
public string RequestedByAlias { get; set; }
|
||||
|
||||
public string RequestOnBehalf { get; set; }
|
||||
}
|
||||
|
||||
public class SeasonsViewModel
|
||||
|
|
|
@ -14,11 +14,12 @@ namespace Ombi.Core.Models.Search
|
|||
public bool Available { get; set; }
|
||||
public string PlexUrl { get; set; }
|
||||
public string EmbyUrl { get; set; }
|
||||
public string JellyfinUrl { get; set; }
|
||||
public string Quality { get; set; }
|
||||
public abstract RequestType Type { get; }
|
||||
|
||||
/// <summary>
|
||||
/// This is used for the PlexAvailabilityCheck/EmbyAvailabilityRule rule
|
||||
/// This is used for the PlexAvailabilityCheck/EmbyAvailabilityRule/JellyfinAvailabilityRule rule
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The custom identifier.
|
||||
|
@ -35,4 +36,4 @@ namespace Ombi.Core.Models.Search
|
|||
[NotMapped]
|
||||
public bool ShowSubscribe { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
9
src/Ombi.Core/Models/Search/V2/StreamingData.cs
Normal file
9
src/Ombi.Core/Models/Search/V2/StreamingData.cs
Normal file
|
@ -0,0 +1,9 @@
|
|||
namespace Ombi.Core.Models.Search.V2
|
||||
{
|
||||
public class StreamingData
|
||||
{
|
||||
public int Order { get; set; }
|
||||
public string StreamingProvider { get; set; }
|
||||
public string Logo { get; set; }
|
||||
}
|
||||
}
|
|
@ -18,6 +18,7 @@ namespace Ombi.Core.Models.UI
|
|||
public UserType UserType { get; set; }
|
||||
public int MovieRequestLimit { get; set; }
|
||||
public int EpisodeRequestLimit { get; set; }
|
||||
public string StreamingCountry { get; set; }
|
||||
public RequestQuotaCountModel EpisodeRequestQuota { get; set; }
|
||||
public RequestQuotaCountModel MovieRequestQuota { get; set; }
|
||||
public RequestQuotaCountModel MusicRequestQuota { get; set; }
|
||||
|
@ -30,4 +31,10 @@ namespace Ombi.Core.Models.UI
|
|||
public string Value { get; set; }
|
||||
public bool Enabled { get; set; }
|
||||
}
|
||||
|
||||
public class UserViewModelDropdown
|
||||
{
|
||||
public string Id { get; set; }
|
||||
public string Username { get; set; }
|
||||
}
|
||||
}
|
|
@ -19,6 +19,7 @@ namespace Ombi.Core.Models
|
|||
{
|
||||
LocalUser = 1,
|
||||
PlexUser = 2,
|
||||
EmbyUser = 3
|
||||
EmbyUser = 3,
|
||||
JellyfinUser = 5
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
<ProjectReference Include="..\Ombi.Api.CouchPotato\Ombi.Api.CouchPotato.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Api.DogNzb\Ombi.Api.DogNzb.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Api.Emby\Ombi.Api.Emby.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Api.Jellyfin\Ombi.Api.Jellyfin.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Api.FanartTv\Ombi.Api.FanartTv.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Api.Lidarr\Ombi.Api.Lidarr.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Api.MusicBrainz\Ombi.Api.MusicBrainz.csproj" />
|
||||
|
@ -40,4 +41,4 @@
|
|||
<ProjectReference Include="..\Ombi.TheMovieDbApi\Ombi.Api.TheMovieDb.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
</Project>
|
||||
|
|
|
@ -108,10 +108,40 @@ namespace Ombi.Core.Rule.Rules.Search
|
|||
x.Series.TvDbId == item.TvDbId);
|
||||
}
|
||||
|
||||
if (epExists != null)
|
||||
{
|
||||
episode.Available = true;
|
||||
}
|
||||
}
|
||||
public static async Task SingleEpisodeCheck(bool useImdb, IQueryable<JellyfinEpisode> allEpisodes, EpisodeRequests episode,
|
||||
SeasonRequests season, JellyfinContent item, bool useTheMovieDb, bool useTvDb)
|
||||
{
|
||||
JellyfinEpisode epExists = null;
|
||||
if (useImdb)
|
||||
{
|
||||
epExists = await allEpisodes.FirstOrDefaultAsync(x =>
|
||||
x.EpisodeNumber == episode.EpisodeNumber && x.SeasonNumber == season.SeasonNumber &&
|
||||
x.Series.ImdbId == item.ImdbId);
|
||||
}
|
||||
|
||||
if (useTheMovieDb)
|
||||
{
|
||||
epExists = await allEpisodes.FirstOrDefaultAsync(x =>
|
||||
x.EpisodeNumber == episode.EpisodeNumber && x.SeasonNumber == season.SeasonNumber &&
|
||||
x.Series.TheMovieDbId == item.TheMovieDbId);
|
||||
}
|
||||
|
||||
if (useTvDb)
|
||||
{
|
||||
epExists = await allEpisodes.FirstOrDefaultAsync(x =>
|
||||
x.EpisodeNumber == episode.EpisodeNumber && x.SeasonNumber == season.SeasonNumber &&
|
||||
x.Series.TvDbId == item.TvDbId);
|
||||
}
|
||||
|
||||
if (epExists != null)
|
||||
{
|
||||
episode.Available = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -70,11 +70,11 @@ namespace Ombi.Core.Rule.Rules.Search
|
|||
var server = s.Servers.FirstOrDefault(x => x.ServerHostname != null);
|
||||
if ((server?.ServerHostname ?? string.Empty).HasValue())
|
||||
{
|
||||
obj.EmbyUrl = EmbyHelper.GetEmbyMediaUrl(item.EmbyId, server?.ServerId, server?.ServerHostname, s.IsJellyfin);
|
||||
obj.EmbyUrl = EmbyHelper.GetEmbyMediaUrl(item.EmbyId, server?.ServerId, server?.ServerHostname);
|
||||
}
|
||||
else
|
||||
{
|
||||
obj.EmbyUrl = EmbyHelper.GetEmbyMediaUrl(item.EmbyId, server?.ServerId, null, s.IsJellyfin);
|
||||
obj.EmbyUrl = EmbyHelper.GetEmbyMediaUrl(item.EmbyId, server?.ServerId, null);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -100,4 +100,4 @@ namespace Ombi.Core.Rule.Rules.Search
|
|||
return Success();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
104
src/Ombi.Core/Rule/Rules/Search/JellyfinAvailabilityRule.cs
Normal file
104
src/Ombi.Core/Rule/Rules/Search/JellyfinAvailabilityRule.cs
Normal file
|
@ -0,0 +1,104 @@
|
|||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Ombi.Core.Models.Search;
|
||||
using Ombi.Core.Rule.Interfaces;
|
||||
using Ombi.Core.Settings;
|
||||
using Ombi.Core.Settings.Models.External;
|
||||
using Ombi.Helpers;
|
||||
using Ombi.Store.Entities;
|
||||
using Ombi.Store.Repository;
|
||||
|
||||
namespace Ombi.Core.Rule.Rules.Search
|
||||
{
|
||||
public class JellyfinAvailabilityRule : BaseSearchRule, IRules<SearchViewModel>
|
||||
{
|
||||
public JellyfinAvailabilityRule(IJellyfinContentRepository repo, ISettingsService<JellyfinSettings> s)
|
||||
{
|
||||
JellyfinContentRepository = repo;
|
||||
JellyfinSettings = s;
|
||||
}
|
||||
|
||||
private IJellyfinContentRepository JellyfinContentRepository { get; }
|
||||
private ISettingsService<JellyfinSettings> JellyfinSettings { get; }
|
||||
|
||||
public async Task<RuleResult> Execute(SearchViewModel obj)
|
||||
{
|
||||
JellyfinContent item = null;
|
||||
var useImdb = false;
|
||||
var useTheMovieDb = false;
|
||||
var useTvDb = false;
|
||||
|
||||
if (obj.ImdbId.HasValue())
|
||||
{
|
||||
item = await JellyfinContentRepository.GetByImdbId(obj.ImdbId);
|
||||
if (item != null)
|
||||
{
|
||||
useImdb = true;
|
||||
}
|
||||
}
|
||||
if (item == null)
|
||||
{
|
||||
if (obj.TheMovieDbId.HasValue())
|
||||
{
|
||||
item = await JellyfinContentRepository.GetByTheMovieDbId(obj.TheMovieDbId);
|
||||
if (item != null)
|
||||
{
|
||||
useTheMovieDb = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (item == null)
|
||||
{
|
||||
if (obj.TheTvDbId.HasValue())
|
||||
{
|
||||
item = await JellyfinContentRepository.GetByTvDbId(obj.TheTvDbId);
|
||||
if (item != null)
|
||||
{
|
||||
useTvDb = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (item != null)
|
||||
{
|
||||
obj.Available = true;
|
||||
var s = await JellyfinSettings.GetSettingsAsync();
|
||||
if (s.Enable)
|
||||
{
|
||||
var server = s.Servers.FirstOrDefault(x => x.ServerHostname != null);
|
||||
if ((server?.ServerHostname ?? string.Empty).HasValue())
|
||||
{
|
||||
obj.JellyfinUrl = JellyfinHelper.GetJellyfinMediaUrl(item.JellyfinId, server?.ServerId, server?.ServerHostname);
|
||||
}
|
||||
else
|
||||
{
|
||||
var firstServer = s.Servers?.FirstOrDefault();
|
||||
obj.JellyfinUrl = JellyfinHelper.GetJellyfinMediaUrl(item.JellyfinId, firstServer.ServerId, firstServer.FullUri);
|
||||
}
|
||||
}
|
||||
|
||||
if (obj.Type == RequestType.TvShow)
|
||||
{
|
||||
var search = (SearchTvShowViewModel)obj;
|
||||
// Let's go through the episodes now
|
||||
if (search.SeasonRequests.Any())
|
||||
{
|
||||
var allEpisodes = JellyfinContentRepository.GetAllEpisodes().Include(x => x.Series);
|
||||
foreach (var season in search.SeasonRequests)
|
||||
{
|
||||
foreach (var episode in season.Episodes)
|
||||
{
|
||||
await AvailabilityRuleHelper.SingleEpisodeCheck(useImdb, allEpisodes, episode, season, item, useTheMovieDb, useTvDb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AvailabilityRuleHelper.CheckForUnairedEpisodes(search);
|
||||
}
|
||||
}
|
||||
return Success();
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue