feat(radarr): 4K Requests and Radarr 4K support

* feat: updated radarr settings API to support 4k

* feat: refactored the radarr setting page to support the new model

* feat: Added 4k radarr to the settings page

* feat: Added the new Movie 4k Request

* feat: Got some of the backend rules done

* feat: Made a load of progress

* Removed the csproj ref

* feat: fixed the radarr ui

* feat: fixed up all the movie requests page

* feat: Hide the 4K buttons when the user does not have the 4k permission

* fix: fixed the templateref issue

* test: fixed up all the tests

* feat: Added migrations for media sever quality. Emby and Radarr Sync jobs now pull the quality

* feat: Done the media sync jobs

* feat: plex availability checker

* feat: Updated the jellyfin availability checker to check for 4k

* feat: updated emby availbility checker to check for 4k

* feat: almost got it all working now

* feat: Added 4k approve to the request list options

* feat: Added 4k to the requests list and bulk approve

* feat: Added the features service

* feat: added feature update to the frontend

* feat: got the features page working

* feat: Applied the feature service on the backend

* feat: added the feature flag on the UI

* feat: added 4k to the card
This commit is contained in:
Jamie 2022-02-14 22:08:09 +00:00 committed by GitHub
parent 09f648515e
commit ba88848866
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
153 changed files with 12438 additions and 784 deletions

View file

@ -22,6 +22,7 @@ using Ombi.Store.Entities.Requests;
using Ombi.Store.Repository;
using Ombi.Core.Models;
using System.Threading;
using Ombi.Core.Services;
namespace Ombi.Core.Engine
{
@ -30,7 +31,8 @@ namespace Ombi.Core.Engine
public MovieRequestEngine(IMovieDbApi movieApi, IRequestServiceMain requestService, IPrincipal user,
INotificationHelper helper, IRuleEvaluator r, IMovieSender sender, ILogger<MovieRequestEngine> log,
OmbiUserManager manager, IRepository<RequestLog> rl, ICacheService cache,
ISettingsService<OmbiSettings> ombiSettings, IRepository<RequestSubscription> sub, IMediaCacheService mediaCacheService)
ISettingsService<OmbiSettings> ombiSettings, IRepository<RequestSubscription> sub, IMediaCacheService mediaCacheService,
IFeatureService featureService)
: base(user, requestService, r, manager, cache, ombiSettings, sub)
{
MovieApi = movieApi;
@ -39,6 +41,7 @@ namespace Ombi.Core.Engine
Logger = log;
_requestLog = rl;
_mediaCacheService = mediaCacheService;
_featureService = featureService;
}
private IMovieDbApi MovieApi { get; }
@ -47,6 +50,7 @@ namespace Ombi.Core.Engine
private ILogger<MovieRequestEngine> Logger { get; }
private readonly IRepository<RequestLog> _requestLog;
private readonly IMediaCacheService _mediaCacheService;
private readonly IFeatureService _featureService;
/// <summary>
/// Requests the movie.
@ -72,7 +76,8 @@ namespace Ombi.Core.Engine
var userDetails = await GetUser();
var canRequestOnBehalf = model.RequestOnBehalf.HasValue();
var isAdmin = await UserManager.IsInRoleAsync(userDetails, OmbiRoles.PowerUser) || await UserManager.IsInRoleAsync(userDetails, OmbiRoles.Admin);
var isAdmin = await UserManager.IsInRoleAsync(userDetails, OmbiRoles.PowerUser)
|| await UserManager.IsInRoleAsync(userDetails, OmbiRoles.Admin);
if (canRequestOnBehalf && !isAdmin)
{
return new RequestEngineResult
@ -93,27 +98,53 @@ namespace Ombi.Core.Engine
};
}
var requestModel = new MovieRequests
var is4kFeatureEnabled = await _featureService.FeatureEnabled(FeatureNames.Movie4KRequests);
var is4kRequest = is4kFeatureEnabled && model.Is4kRequest;
MovieRequests requestModel;
bool isExisting = false;
// Do we already have a request? 4k or non 4k
var existingRequest = await MovieRepository.GetRequestAsync(movieInfo.Id);
if (existingRequest != null && is4kFeatureEnabled)
{
TheMovieDbId = movieInfo.Id,
RequestType = RequestType.Movie,
Overview = movieInfo.Overview,
ImdbId = movieInfo.ImdbId,
PosterPath = PosterPathHelper.FixPosterPath(movieInfo.PosterPath),
Title = movieInfo.Title,
ReleaseDate = !string.IsNullOrEmpty(movieInfo.ReleaseDate)
? DateTime.Parse(movieInfo.ReleaseDate)
: DateTime.MinValue,
Status = movieInfo.Status,
RequestedDate = DateTime.UtcNow,
Approved = false,
RequestedUserId = canRequestOnBehalf ? model.RequestOnBehalf : userDetails.Id,
Background = movieInfo.BackdropPath,
LangCode = model.LanguageCode,
RequestedByAlias = model.RequestedByAlias,
RootPathOverride = model.RootFolderOverride.GetValueOrDefault(),
QualityOverride = model.QualityPathOverride.GetValueOrDefault()
};
if (model.Is4kRequest)
{
existingRequest.Is4kRequest = true;
existingRequest.RequestedDate4k = DateTime.Now;
}
else
{
existingRequest.RequestedDate = DateTime.Now;
}
isExisting = true;
requestModel = existingRequest;
}
else
{
requestModel = new MovieRequests
{
TheMovieDbId = movieInfo.Id,
RequestType = RequestType.Movie,
Overview = movieInfo.Overview,
ImdbId = movieInfo.ImdbId,
PosterPath = PosterPathHelper.FixPosterPath(movieInfo.PosterPath),
Title = movieInfo.Title,
ReleaseDate = !string.IsNullOrEmpty(movieInfo.ReleaseDate)
? DateTime.Parse(movieInfo.ReleaseDate)
: DateTime.MinValue,
Status = movieInfo.Status,
RequestedDate = model.Is4kRequest ? DateTime.MinValue : DateTime.Now,
Approved = false,
RequestedUserId = canRequestOnBehalf ? model.RequestOnBehalf : userDetails.Id,
Background = movieInfo.BackdropPath,
LangCode = model.LanguageCode,
RequestedByAlias = model.RequestedByAlias,
RootPathOverride = model.RootFolderOverride.GetValueOrDefault(),
QualityOverride = model.QualityPathOverride.GetValueOrDefault(),
RequestedDate4k = model.Is4kRequest ? DateTime.Now : DateTime.MinValue,
Is4kRequest = model.Is4kRequest
};
}
var usDates = movieInfo.ReleaseDates?.Results?.FirstOrDefault(x => x.IsoCode == "US");
requestModel.DigitalReleaseDate = usDates?.ReleaseDate
@ -132,10 +163,10 @@ namespace Ombi.Core.Engine
if (requestModel.Approved) // The rules have auto approved this
{
var requestEngineResult = await AddMovieRequest(requestModel, fullMovieName, model.RequestOnBehalf);
var requestEngineResult = await AddMovieRequest(requestModel, fullMovieName, model.RequestOnBehalf, isExisting, is4kRequest);
if (requestEngineResult.Result)
{
var result = await ApproveMovie(requestModel);
var result = await ApproveMovie(requestModel, model.Is4kRequest);
if (result.IsError)
{
Logger.LogWarning("Tried auto sending movie but failed. Message: {0}", result.Message);
@ -153,7 +184,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, model.RequestOnBehalf);
return await AddMovieRequest(requestModel, fullMovieName, model.RequestOnBehalf, isExisting, is4kRequest);
}
@ -508,13 +539,13 @@ namespace Ombi.Core.Engine
return results;
}
public async Task<RequestEngineResult> ApproveMovieById(int requestId)
public async Task<RequestEngineResult> ApproveMovieById(int requestId, bool is4K)
{
var request = await MovieRepository.Find(requestId);
return await ApproveMovie(request);
return await ApproveMovie(request, is4K);
}
public async Task<RequestEngineResult> DenyMovieById(int modelId, string denyReason)
public async Task<RequestEngineResult> DenyMovieById(int modelId, string denyReason, bool is4K)
{
var request = await MovieRepository.Find(modelId);
if (request == null)
@ -525,8 +556,16 @@ namespace Ombi.Core.Engine
};
}
request.Denied = true;
request.DeniedReason = denyReason;
if (is4K)
{
request.Denied4K = true;
request.DeniedReason4K = denyReason;
}
else
{
request.Denied = true;
request.DeniedReason = denyReason;
}
await MovieRepository.Update(request);
await _mediaCacheService.Purge();
@ -540,7 +579,7 @@ namespace Ombi.Core.Engine
};
}
public async Task<RequestEngineResult> ApproveMovie(MovieRequests request)
public async Task<RequestEngineResult> ApproveMovie(MovieRequests request, bool is4K)
{
if (request == null)
{
@ -550,9 +589,18 @@ namespace Ombi.Core.Engine
};
}
request.MarkedAsApproved = DateTime.Now;
request.Approved = true;
request.Denied = false;
if (is4K)
{
request.MarkedAsApproved4K = DateTime.Now;
request.Approved4K = true;
request.Denied4K = false;
}
else
{
request.MarkedAsApproved = DateTime.Now;
request.Approved = true;
request.Denied = false;
}
await MovieRepository.Update(request);
var canNotify = await RunSpecificRule(request, SpecificRules.CanSendNotification, string.Empty);
@ -562,7 +610,7 @@ namespace Ombi.Core.Engine
}
await _mediaCacheService.Purge();
return await ProcessSendingMovie(request);
return await ProcessSendingMovie(request, is4K);
}
public async Task<RequestEngineResult> RequestCollection(int collectionId, CancellationToken cancellationToken)
@ -590,11 +638,11 @@ namespace Ombi.Core.Engine
return new RequestEngineResult { Result = true, Message = $"The collection {collections.name} has been successfully added!", RequestId = results.FirstOrDefault().RequestId };
}
private async Task<RequestEngineResult> ProcessSendingMovie(MovieRequests request)
private async Task<RequestEngineResult> ProcessSendingMovie(MovieRequests request, bool is4K)
{
if (request.Approved)
{
var result = await Sender.Send(request);
var result = await Sender.Send(request, is4K);
if (result.Success && result.Sent)
{
return new RequestEngineResult
@ -662,7 +710,7 @@ namespace Ombi.Core.Engine
var result = await CheckCanManageRequest(request);
if (result.IsError)
return result;
await MovieRepository.Delete(request);
await _mediaCacheService.Purge();
return new RequestEngineResult
@ -683,7 +731,7 @@ namespace Ombi.Core.Engine
return await MovieRepository.GetAll().AnyAsync(x => x.RequestedUserId == userId);
}
public async Task<RequestEngineResult> ReProcessRequest(int requestId, CancellationToken cancellationToken)
public async Task<RequestEngineResult> ReProcessRequest(int requestId, bool is4K, CancellationToken cancellationToken)
{
var request = await MovieRepository.Find(requestId);
if (request == null)
@ -695,10 +743,10 @@ namespace Ombi.Core.Engine
};
}
return await ProcessSendingMovie(request);
return await ProcessSendingMovie(request, is4K);
}
public async Task<RequestEngineResult> MarkUnavailable(int modelId)
public async Task<RequestEngineResult> MarkUnavailable(int modelId, bool is4K)
{
var request = await MovieRepository.Find(modelId);
if (request == null)
@ -709,7 +757,14 @@ namespace Ombi.Core.Engine
};
}
request.Available = false;
if (is4K)
{
request.Available4K = false;
}
else
{
request.Available = false;
}
await MovieRepository.Update(request);
await _mediaCacheService.Purge();
@ -720,7 +775,7 @@ namespace Ombi.Core.Engine
};
}
public async Task<RequestEngineResult> MarkAvailable(int modelId)
public async Task<RequestEngineResult> MarkAvailable(int modelId, bool is4K)
{
var request = await MovieRepository.Find(modelId);
if (request == null)
@ -730,9 +785,16 @@ namespace Ombi.Core.Engine
ErrorMessage = "Request does not exist"
};
}
request.Available = true;
request.MarkedAsAvailable = DateTime.Now;
if (!is4K)
{
request.Available = true;
request.MarkedAsAvailable = DateTime.Now;
}
else
{
request.Available4K = true;
request.MarkedAsAvailable4K = DateTime.Now;
}
await NotificationHelper.Notify(request, NotificationType.RequestAvailable);
await MovieRepository.Update(request);
await _mediaCacheService.Purge();
@ -744,9 +806,20 @@ namespace Ombi.Core.Engine
};
}
private async Task<RequestEngineResult> AddMovieRequest(MovieRequests model, string movieName, string requestOnBehalf)
private async Task<RequestEngineResult> AddMovieRequest(MovieRequests model, string movieName, string requestOnBehalf, bool isExisting, bool is4k)
{
await MovieRepository.Add(model);
if (is4k)
{
model.Has4KRequest = true;
}
if (!isExisting)
{
await MovieRepository.Add(model);
}
else
{
await MovieRepository.Update(model);
}
var result = await RunSpecificRule(model, SpecificRules.CanSendNotification, requestOnBehalf);
if (result.Success)