feat: Made a load of progress

This commit is contained in:
tidusjar 2022-02-10 23:10:43 +00:00
commit 645ea3b996
38 changed files with 3165 additions and 145 deletions

View file

@ -83,7 +83,7 @@ namespace Ombi.Core.Tests.Engine
Assert.That(result.Result, Is.True); Assert.That(result.Result, Is.True);
VoteRepository.Verify(x => x.Add(It.Is<Votes>(c => c.UserId == "abc" && c.VoteType == type)), Times.Once); VoteRepository.Verify(x => x.Add(It.Is<Votes>(c => c.UserId == "abc" && c.VoteType == type)), Times.Once);
VoteRepository.Verify(x => x.Delete(It.IsAny<Votes>()), Times.Never); VoteRepository.Verify(x => x.Delete(It.IsAny<Votes>()), Times.Never);
MovieRequestEngine.Verify(x => x.ApproveMovieById(1), Times.Never); MovieRequestEngine.Verify(x => x.ApproveMovieById(1, false), Times.Never);
} }
public static IEnumerable<TestCaseData> VoteData public static IEnumerable<TestCaseData> VoteData
{ {
@ -129,7 +129,7 @@ namespace Ombi.Core.Tests.Engine
Assert.That(result.Result, Is.False); Assert.That(result.Result, Is.False);
VoteRepository.Verify(x => x.Delete(It.IsAny<Votes>()), Times.Never); VoteRepository.Verify(x => x.Delete(It.IsAny<Votes>()), Times.Never);
MovieRequestEngine.Verify(x => x.ApproveMovieById(1), Times.Never); MovieRequestEngine.Verify(x => x.ApproveMovieById(1, false), Times.Never);
} }
public static IEnumerable<TestCaseData> AttemptedTwiceData public static IEnumerable<TestCaseData> AttemptedTwiceData
{ {
@ -175,7 +175,7 @@ namespace Ombi.Core.Tests.Engine
Assert.That(result.Result, Is.True); Assert.That(result.Result, Is.True);
VoteRepository.Verify(x => x.Delete(It.IsAny<Votes>()), Times.Once); VoteRepository.Verify(x => x.Delete(It.IsAny<Votes>()), Times.Once);
VoteRepository.Verify(x => x.Add(It.Is<Votes>(v => v.VoteType == type)), Times.Once); VoteRepository.Verify(x => x.Add(It.Is<Votes>(v => v.VoteType == type)), Times.Once);
MovieRequestEngine.Verify(x => x.ApproveMovieById(1), Times.Never); MovieRequestEngine.Verify(x => x.ApproveMovieById(1, false), Times.Never);
} }
public static IEnumerable<TestCaseData> VoteConvertData public static IEnumerable<TestCaseData> VoteConvertData
{ {

View file

@ -18,9 +18,9 @@ namespace Ombi.Core.Engine.Interfaces
Task RemoveAllMovieRequests(); Task RemoveAllMovieRequests();
Task<MovieRequests> GetRequest(int requestId); Task<MovieRequests> GetRequest(int requestId);
Task<MovieRequests> UpdateMovieRequest(MovieRequests request); Task<MovieRequests> UpdateMovieRequest(MovieRequests request);
Task<RequestEngineResult> ApproveMovie(MovieRequests request); Task<RequestEngineResult> ApproveMovie(MovieRequests request, bool is4K);
Task<RequestEngineResult> ApproveMovieById(int requestId); Task<RequestEngineResult> ApproveMovieById(int requestId, bool is4K);
Task<RequestEngineResult> DenyMovieById(int modelId, string denyReason); Task<RequestEngineResult> DenyMovieById(int modelId, string denyReason, bool is4K);
Task<RequestsViewModel<MovieRequests>> GetRequests(int count, int position, string sortProperty, string sortOrder); Task<RequestsViewModel<MovieRequests>> GetRequests(int count, int position, string sortProperty, string sortOrder);
Task<RequestsViewModel<MovieRequests>> GetUnavailableRequests(int count, int position, string sortProperty, Task<RequestsViewModel<MovieRequests>> GetUnavailableRequests(int count, int position, string sortProperty,

View file

@ -19,11 +19,11 @@ namespace Ombi.Core.Engine.Interfaces
Task<IEnumerable<T>> GetRequests(); Task<IEnumerable<T>> GetRequests();
Task<bool> UserHasRequest(string userId); Task<bool> UserHasRequest(string userId);
Task<RequestEngineResult> MarkUnavailable(int modelId); Task<RequestEngineResult> MarkUnavailable(int modelId, bool is4K);
Task<RequestEngineResult> MarkAvailable(int modelId); Task<RequestEngineResult> MarkAvailable(int modelId, bool is4K);
Task<int> GetTotal(); Task<int> GetTotal();
Task UnSubscribeRequest(int requestId, RequestType type); Task UnSubscribeRequest(int requestId, RequestType type);
Task SubscribeToRequest(int requestId, RequestType type); Task SubscribeToRequest(int requestId, RequestType type);
Task<RequestEngineResult> ReProcessRequest(int requestId, CancellationToken cancellationToken); Task<RequestEngineResult> ReProcessRequest(int requestId, bool is4K, CancellationToken cancellationToken);
} }
} }

View file

@ -72,7 +72,7 @@ namespace Ombi.Core.Engine
var userDetails = await GetUser(); var userDetails = await GetUser();
var canRequestOnBehalf = model.RequestOnBehalf.HasValue(); var canRequestOnBehalf = model.RequestOnBehalf.HasValue();
var isAdmin = await UserManager.IsInRoleAsync(userDetails, OmbiRoles.PowerUser) var isAdmin = await UserManager.IsInRoleAsync(userDetails, OmbiRoles.PowerUser)
|| await UserManager.IsInRoleAsync(userDetails, OmbiRoles.Admin); || await UserManager.IsInRoleAsync(userDetails, OmbiRoles.Admin);
if (canRequestOnBehalf && !isAdmin) if (canRequestOnBehalf && !isAdmin)
{ {
@ -94,28 +94,49 @@ namespace Ombi.Core.Engine
}; };
} }
var requestModel = new MovieRequests 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)
{ {
TheMovieDbId = movieInfo.Id, if (model.Is4kRequest)
RequestType = RequestType.Movie, {
Overview = movieInfo.Overview, existingRequest.Has4KRequest = model.Is4kRequest;
ImdbId = movieInfo.ImdbId, existingRequest.RequestedDate4k = DateTime.Now;
PosterPath = PosterPathHelper.FixPosterPath(movieInfo.PosterPath), }
Title = movieInfo.Title, else
ReleaseDate = !string.IsNullOrEmpty(movieInfo.ReleaseDate) {
? DateTime.Parse(movieInfo.ReleaseDate) existingRequest.RequestedDate = DateTime.Now;
: DateTime.MinValue, }
Status = movieInfo.Status, isExisting = true;
RequestedDate = DateTime.UtcNow, requestModel = existingRequest;
Approved = false, }
RequestedUserId = canRequestOnBehalf ? model.RequestOnBehalf : userDetails.Id, else
Background = movieInfo.BackdropPath, {
LangCode = model.LanguageCode, requestModel = new MovieRequests
RequestedByAlias = model.RequestedByAlias, {
RootPathOverride = model.RootFolderOverride.GetValueOrDefault(), TheMovieDbId = movieInfo.Id,
QualityOverride = model.QualityPathOverride.GetValueOrDefault(), RequestType = RequestType.Movie,
Has4KRequest = model.Is4kRequest 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(),
Has4KRequest = model.Is4kRequest
};
}
var usDates = movieInfo.ReleaseDates?.Results?.FirstOrDefault(x => x.IsoCode == "US"); var usDates = movieInfo.ReleaseDates?.Results?.FirstOrDefault(x => x.IsoCode == "US");
requestModel.DigitalReleaseDate = usDates?.ReleaseDate requestModel.DigitalReleaseDate = usDates?.ReleaseDate
@ -134,10 +155,10 @@ namespace Ombi.Core.Engine
if (requestModel.Approved) // The rules have auto approved this 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);
if (requestEngineResult.Result) if (requestEngineResult.Result)
{ {
var result = await ApproveMovie(requestModel); var result = await ApproveMovie(requestModel, model.Is4kRequest);
if (result.IsError) if (result.IsError)
{ {
Logger.LogWarning("Tried auto sending movie but failed. Message: {0}", result.Message); Logger.LogWarning("Tried auto sending movie but failed. Message: {0}", result.Message);
@ -155,7 +176,7 @@ namespace Ombi.Core.Engine
// If there are no providers then it's successful but movie has not been sent // 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);
} }
@ -510,13 +531,13 @@ namespace Ombi.Core.Engine
return results; return results;
} }
public async Task<RequestEngineResult> ApproveMovieById(int requestId) public async Task<RequestEngineResult> ApproveMovieById(int requestId, bool is4K)
{ {
var request = await MovieRepository.Find(requestId); 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); var request = await MovieRepository.Find(modelId);
if (request == null) if (request == null)
@ -527,8 +548,16 @@ namespace Ombi.Core.Engine
}; };
} }
request.Denied = true; if (is4K)
request.DeniedReason = denyReason; {
request.Denied4K = true;
request.DeniedReason4K = denyReason;
}
else
{
request.Denied = true;
request.DeniedReason = denyReason;
}
await MovieRepository.Update(request); await MovieRepository.Update(request);
await _mediaCacheService.Purge(); await _mediaCacheService.Purge();
@ -542,7 +571,7 @@ namespace Ombi.Core.Engine
}; };
} }
public async Task<RequestEngineResult> ApproveMovie(MovieRequests request) public async Task<RequestEngineResult> ApproveMovie(MovieRequests request, bool is4K)
{ {
if (request == null) if (request == null)
{ {
@ -564,7 +593,7 @@ namespace Ombi.Core.Engine
} }
await _mediaCacheService.Purge(); await _mediaCacheService.Purge();
return await ProcessSendingMovie(request); return await ProcessSendingMovie(request, is4K);
} }
public async Task<RequestEngineResult> RequestCollection(int collectionId, CancellationToken cancellationToken) public async Task<RequestEngineResult> RequestCollection(int collectionId, CancellationToken cancellationToken)
@ -592,11 +621,11 @@ namespace Ombi.Core.Engine
return new RequestEngineResult { Result = true, Message = $"The collection {collections.name} has been successfully added!", RequestId = results.FirstOrDefault().RequestId }; 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) if (request.Approved)
{ {
var result = await Sender.Send(request); var result = await Sender.Send(request, is4K);
if (result.Success && result.Sent) if (result.Success && result.Sent)
{ {
return new RequestEngineResult return new RequestEngineResult
@ -664,7 +693,7 @@ namespace Ombi.Core.Engine
var result = await CheckCanManageRequest(request); var result = await CheckCanManageRequest(request);
if (result.IsError) if (result.IsError)
return result; return result;
await MovieRepository.Delete(request); await MovieRepository.Delete(request);
await _mediaCacheService.Purge(); await _mediaCacheService.Purge();
return new RequestEngineResult return new RequestEngineResult
@ -685,7 +714,7 @@ namespace Ombi.Core.Engine
return await MovieRepository.GetAll().AnyAsync(x => x.RequestedUserId == userId); 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); var request = await MovieRepository.Find(requestId);
if (request == null) if (request == null)
@ -697,10 +726,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); var request = await MovieRepository.Find(modelId);
if (request == null) if (request == null)
@ -711,7 +740,14 @@ namespace Ombi.Core.Engine
}; };
} }
request.Available = false; if (is4K)
{
request.Available4K = false;
}
else
{
request.Available = false;
}
await MovieRepository.Update(request); await MovieRepository.Update(request);
await _mediaCacheService.Purge(); await _mediaCacheService.Purge();
@ -722,7 +758,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); var request = await MovieRepository.Find(modelId);
if (request == null) if (request == null)
@ -732,9 +768,16 @@ namespace Ombi.Core.Engine
ErrorMessage = "Request does not exist" ErrorMessage = "Request does not exist"
}; };
} }
if (!is4K)
request.Available = true; {
request.MarkedAsAvailable = DateTime.Now; request.Available = true;
request.MarkedAsAvailable = DateTime.Now;
}
else
{
request.Available4K = true;
request.MarkedAsAvailable4K = DateTime.Now;
}
await NotificationHelper.Notify(request, NotificationType.RequestAvailable); await NotificationHelper.Notify(request, NotificationType.RequestAvailable);
await MovieRepository.Update(request); await MovieRepository.Update(request);
await _mediaCacheService.Purge(); await _mediaCacheService.Purge();
@ -746,9 +789,16 @@ 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)
{ {
await MovieRepository.Add(model); if (!isExisting)
{
await MovieRepository.Add(model);
}
else
{
await MovieRepository.Update(model);
}
var result = await RunSpecificRule(model, SpecificRules.CanSendNotification, requestOnBehalf); var result = await RunSpecificRule(model, SpecificRules.CanSendNotification, requestOnBehalf);
if (result.Success) if (result.Success)

View file

@ -793,7 +793,7 @@ namespace Ombi.Core.Engine
return await TvRepository.GetChild().AnyAsync(x => x.RequestedUserId == userId); return await TvRepository.GetChild().AnyAsync(x => x.RequestedUserId == userId);
} }
public async Task<RequestEngineResult> MarkUnavailable(int modelId) public async Task<RequestEngineResult> MarkUnavailable(int modelId, bool is4K)
{ {
var request = await TvRepository.GetChild().FirstOrDefaultAsync(x => x.Id == modelId); var request = await TvRepository.GetChild().FirstOrDefaultAsync(x => x.Id == modelId);
if (request == null) if (request == null)
@ -821,7 +821,7 @@ namespace Ombi.Core.Engine
}; };
} }
public async Task<RequestEngineResult> MarkAvailable(int modelId) public async Task<RequestEngineResult> MarkAvailable(int modelId, bool is4K)
{ {
ChildRequests request = await TvRepository.GetChild().FirstOrDefaultAsync(x => x.Id == modelId); ChildRequests request = await TvRepository.GetChild().FirstOrDefaultAsync(x => x.Id == modelId);
if (request == null) if (request == null)
@ -918,7 +918,7 @@ namespace Ombi.Core.Engine
return await AfterRequest(model.ChildRequests.FirstOrDefault(), requestOnBehalf); return await AfterRequest(model.ChildRequests.FirstOrDefault(), requestOnBehalf);
} }
public async Task<RequestEngineResult> ReProcessRequest(int requestId, CancellationToken cancellationToken) public async Task<RequestEngineResult> ReProcessRequest(int requestId, bool is4K, CancellationToken cancellationToken)
{ {
var request = await TvRepository.GetChild().FirstOrDefaultAsync(x => x.Id == requestId, cancellationToken); var request = await TvRepository.GetChild().FirstOrDefaultAsync(x => x.Id == requestId, cancellationToken);
if (request == null) if (request == null)

View file

@ -406,6 +406,13 @@ namespace Ombi.Core.Engine.V2
mapped.Subscribed = viewMovie.Subscribed; mapped.Subscribed = viewMovie.Subscribed;
mapped.ShowSubscribe = viewMovie.ShowSubscribe; mapped.ShowSubscribe = viewMovie.ShowSubscribe;
mapped.DigitalReleaseDate = viewMovie.DigitalReleaseDate; mapped.DigitalReleaseDate = viewMovie.DigitalReleaseDate;
mapped.RequestedDate4k = viewMovie.RequestedDate4k;
mapped.Approved4K = viewMovie.Approved4K;
mapped.Available4K = viewMovie.Available4K;
mapped.Denied4K = viewMovie.Denied4K;
mapped.DeniedReason4K = viewMovie.DeniedReason4K;
mapped.Has4KRequest = viewMovie.Has4KRequest;
return mapped; return mapped;
} }

View file

@ -193,7 +193,7 @@ namespace Ombi.Core.Engine
case RequestType.Movie: case RequestType.Movie:
if (totalVotes >= voteSettings.MovieVoteMax) if (totalVotes >= voteSettings.MovieVoteMax)
{ {
result = await _movieRequestEngine.ApproveMovieById(requestId); result = await _movieRequestEngine.ApproveMovieById(requestId, false);
} }
break; break;
case RequestType.Album: case RequestType.Album:

View file

@ -28,5 +28,14 @@ namespace Ombi.Core.Models.Search
public override RequestType Type => RequestType.Movie; public override RequestType Type => RequestType.Movie;
public ReleaseDatesDto ReleaseDates { get; set; } public ReleaseDatesDto ReleaseDates { get; set; }
public DateTime? DigitalReleaseDate { get; set; } public DateTime? DigitalReleaseDate { get; set; }
public bool Has4KRequest { get; set; }
public bool Approved4K { get; set; }
public DateTime MarkedAsApproved4K { get; set; }
public DateTime RequestedDate4k { get; set; }
public bool Available4K { get; set; }
public DateTime? MarkedAsAvailable4K { get; set; }
public bool? Denied4K { get; set; }
public DateTime MarkedAsDenied4K { get; set; }
public string DeniedReason4K { get; set; }
} }
} }

View file

@ -41,6 +41,15 @@ namespace Ombi.Core.Models.Search.V2
public Recommendations Recommendations { get; set; } public Recommendations Recommendations { get; set; }
public ExternalIds ExternalIds { get; set; } public ExternalIds ExternalIds { get; set; }
public Keywords Keywords { get; set; } public Keywords Keywords { get; set; }
public bool Has4KRequest { get; set; }
public bool Approved4K { get; set; }
public DateTime MarkedAsApproved4K { get; set; }
public DateTime RequestedDate4k { get; set; }
public bool Available4K { get; set; }
public DateTime? MarkedAsAvailable4K { get; set; }
public bool? Denied4K { get; set; }
public DateTime MarkedAsDenied4K { get; set; }
public string DeniedReason4K { get; set; }
} }
public class Keywords public class Keywords
{ {

View file

@ -28,12 +28,37 @@ namespace Ombi.Core.Rule.Rules.Request
var user = await _manager.Users.FirstOrDefaultAsync(x => x.NormalizedUserName == username); var user = await _manager.Users.FirstOrDefaultAsync(x => x.NormalizedUserName == username);
if (await _manager.IsInRoleAsync(user, OmbiRoles.Admin) || user.IsSystemUser) if (await _manager.IsInRoleAsync(user, OmbiRoles.Admin) || user.IsSystemUser)
{ {
obj.Approved = true; if (obj.RequestType == RequestType.Movie)
{
var movie = (MovieRequests)obj;
if (movie.Has4KRequest)
{
movie.Approved4K = true;
}
if (movie.RequestedDate != DateTime.MinValue)
{
obj.Approved = true;
}
}
else
{
obj.Approved = true;
}
return Success(); return Success();
} }
if (obj.RequestType == RequestType.Movie && await _manager.IsInRoleAsync(user, OmbiRoles.AutoApproveMovie)) if (obj.RequestType == RequestType.Movie && await _manager.IsInRoleAsync(user, OmbiRoles.AutoApproveMovie))
obj.Approved = true; {
var movie = (MovieRequests)obj;
if (movie.Has4KRequest)
{
movie.Approved4K = true;
}
if (movie.RequestedDate != DateTime.MinValue)
{
obj.Approved = true;
}
}
if (obj.RequestType == RequestType.TvShow && await _manager.IsInRoleAsync(user, OmbiRoles.AutoApproveTv)) if (obj.RequestType == RequestType.TvShow && await _manager.IsInRoleAsync(user, OmbiRoles.AutoApproveTv))
obj.Approved = true; obj.Approved = true;
if (obj.RequestType == RequestType.Album && await _manager.IsInRoleAsync(user, OmbiRoles.AutoApproveMusic)) if (obj.RequestType == RequestType.Album && await _manager.IsInRoleAsync(user, OmbiRoles.AutoApproveMusic))

View file

@ -28,15 +28,26 @@ namespace Ombi.Core.Rule.Rules.Search
{ {
if (obj.Type == RequestType.Movie) if (obj.Type == RequestType.Movie)
{ {
var movie = (SearchMovieViewModel)obj;
var movieRequests = await Movie.GetRequestAsync(obj.Id); var movieRequests = await Movie.GetRequestAsync(obj.Id);
if (movieRequests != null) // Do we already have a request for this? if (movieRequests != null) // Do we already have a request for this?
{ {
obj.Requested = true; // If the RequestDate is a min value, that means there's only a 4k request
obj.RequestId = movieRequests.Id; movie.Requested = movieRequests.RequestedDate != DateTime.MinValue;
obj.Approved = movieRequests.Approved; movie.RequestId = movieRequests.Id;
obj.Denied = movieRequests.Denied ?? false; movie.Approved = movieRequests.Approved;
obj.DeniedReason = movieRequests.DeniedReason; movie.Denied = movieRequests.Denied ?? false;
obj.Available = movieRequests.Available; movie.DeniedReason = movieRequests.DeniedReason;
movie.Available = movieRequests.Available;
movie.Has4KRequest = movieRequests.Has4KRequest;
movie.RequestedDate4k = movieRequests.RequestedDate4k;
movie.Approved4K = movieRequests.Approved4K;
movie.Available4K = movieRequests.Available4K;
movie.Denied4K = movieRequests.Denied4K;
movie.DeniedReason4K = movieRequests.DeniedReason4K;
movie.MarkedAsApproved4K = movieRequests.MarkedAsApproved4K;
movie.MarkedAsAvailable4K = movieRequests.MarkedAsAvailable4K;
movie.MarkedAsDenied4K = movieRequests.MarkedAsDenied4K;
return Success(); return Success();
} }

View file

@ -6,6 +6,6 @@ namespace Ombi.Core
{ {
public interface IMovieSender public interface IMovieSender
{ {
Task<SenderResult> Send(MovieRequests model); Task<SenderResult> Send(MovieRequests model, bool is4K);
} }
} }

View file

@ -20,13 +20,12 @@ namespace Ombi.Core.Senders
{ {
public class MovieSender : IMovieSender public class MovieSender : IMovieSender
{ {
public MovieSender(ISettingsService<RadarrSettings> radarrSettings, IRadarrApi api, ILogger<MovieSender> log, public MovieSender(ISettingsService<RadarrSettings> radarrSettings, ISettingsService<Radarr4KSettings> radarr4kSettings, ILogger<MovieSender> log,
ISettingsService<DogNzbSettings> dogSettings, IDogNzbApi dogApi, ISettingsService<CouchPotatoSettings> cpSettings, ISettingsService<DogNzbSettings> dogSettings, IDogNzbApi dogApi, ISettingsService<CouchPotatoSettings> cpSettings,
ICouchPotatoApi cpApi, IRepository<UserQualityProfiles> userProfiles, IRepository<RequestQueue> requestQueue, INotificationHelper notify, ICouchPotatoApi cpApi, IRepository<UserQualityProfiles> userProfiles, IRepository<RequestQueue> requestQueue, INotificationHelper notify,
IRadarrV3Api radarrV3Api) IRadarrV3Api radarrV3Api)
{ {
_radarrSettings = radarrSettings; _radarrSettings = radarrSettings;
_radarrV2Api = api;
_log = log; _log = log;
_dogNzbSettings = dogSettings; _dogNzbSettings = dogSettings;
_dogNzbApi = dogApi; _dogNzbApi = dogApi;
@ -36,10 +35,11 @@ namespace Ombi.Core.Senders
_requestQueuRepository = requestQueue; _requestQueuRepository = requestQueue;
_notificationHelper = notify; _notificationHelper = notify;
_radarrV3Api = radarrV3Api; _radarrV3Api = radarrV3Api;
_radarr4KSettings = radarr4kSettings;
} }
private readonly ISettingsService<RadarrSettings> _radarrSettings; private readonly ISettingsService<RadarrSettings> _radarrSettings;
private readonly IRadarrApi _radarrV2Api; private readonly ISettingsService<Radarr4KSettings> _radarr4KSettings;
private readonly ILogger<MovieSender> _log; private readonly ILogger<MovieSender> _log;
private readonly IDogNzbApi _dogNzbApi; private readonly IDogNzbApi _dogNzbApi;
private readonly ISettingsService<DogNzbSettings> _dogNzbSettings; private readonly ISettingsService<DogNzbSettings> _dogNzbSettings;
@ -50,16 +50,24 @@ namespace Ombi.Core.Senders
private readonly INotificationHelper _notificationHelper; private readonly INotificationHelper _notificationHelper;
private readonly IRadarrV3Api _radarrV3Api; private readonly IRadarrV3Api _radarrV3Api;
public async Task<SenderResult> Send(MovieRequests model) public async Task<SenderResult> Send(MovieRequests model, bool is4K)
{ {
try try
{ {
var cpSettings = await _couchPotatoSettings.GetSettingsAsync(); var cpSettings = await _couchPotatoSettings.GetSettingsAsync();
//var watcherSettings = await WatcherSettings.GetSettingsAsync();
var radarrSettings = await _radarrSettings.GetSettingsAsync(); RadarrSettings radarrSettings;
if (is4K)
{
radarrSettings = await _radarr4KSettings.GetSettingsAsync();
}
else
{
radarrSettings = await _radarrSettings.GetSettingsAsync();
}
if (radarrSettings.Enabled) if (radarrSettings.Enabled)
{ {
return await SendToRadarr(model, radarrSettings); return await SendToRadarr(model, is4K, radarrSettings);
} }
var dogSettings = await _dogNzbSettings.GetSettingsAsync(); var dogSettings = await _dogNzbSettings.GetSettingsAsync();
@ -123,7 +131,7 @@ namespace Ombi.Core.Senders
return await _dogNzbApi.AddMovie(settings.ApiKey, id); return await _dogNzbApi.AddMovie(settings.ApiKey, id);
} }
private async Task<SenderResult> SendToRadarr(MovieRequests model, RadarrSettings settings) private async Task<SenderResult> SendToRadarr(MovieRequests model, bool is4K, RadarrSettings settings)
{ {
var qualityToUse = int.Parse(settings.DefaultQualityProfile); var qualityToUse = int.Parse(settings.DefaultQualityProfile);

View file

@ -49,7 +49,9 @@ namespace Ombi.Schedule.Jobs.Ombi
await _requestQueue.SaveChangesAsync(); await _requestQueue.SaveChangesAsync();
continue; continue;
} }
var result = await _movieSender.Send(movieRequest);
// TODO probably need to add something to the request queue to better idenitfy if it's a 4k request
var result = await _movieSender.Send(movieRequest, movieRequest.Approved4K);
if (result.Success) if (result.Success)
{ {
request.Completed = DateTime.UtcNow; request.Completed = DateTime.UtcNow;

View file

@ -2,6 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
using Newtonsoft.Json; using Newtonsoft.Json;
using System;
namespace Ombi.Store.Entities.Requests namespace Ombi.Store.Entities.Requests
{ {
@ -27,6 +28,16 @@ namespace Ombi.Store.Entities.Requests
public bool Has4KRequest { get; set; } public bool Has4KRequest { get; set; }
public bool Approved4K { get; set; }
public DateTime MarkedAsApproved4K { get; set; }
public DateTime RequestedDate4k { get; set; }
public bool Available4K { get; set; }
public DateTime? MarkedAsAvailable4K { get; set; }
public bool? Denied4K { get; set; }
public DateTime MarkedAsDenied4K { get; set; }
public string DeniedReason4K { get; set; }
/// <summary> /// <summary>
/// Only Use for setting the Language Code, Use the LanguageCode property for reading /// Only Use for setting the Language Code, Use the LanguageCode property for reading
/// </summary> /// </summary>

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,102 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Ombi.Store.Migrations.OmbiMySql
{
public partial class _4kMovieProperties : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<bool>(
name: "Approved4K",
table: "MovieRequests",
type: "tinyint(1)",
nullable: false,
defaultValue: false);
migrationBuilder.AddColumn<bool>(
name: "Available4K",
table: "MovieRequests",
type: "tinyint(1)",
nullable: false,
defaultValue: false);
migrationBuilder.AddColumn<bool>(
name: "Denied4K",
table: "MovieRequests",
type: "tinyint(1)",
nullable: true);
migrationBuilder.AddColumn<string>(
name: "DeniedReason4K",
table: "MovieRequests",
type: "longtext",
nullable: true)
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.AddColumn<DateTime>(
name: "MarkedAsApproved4K",
table: "MovieRequests",
type: "datetime(6)",
nullable: false,
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
migrationBuilder.AddColumn<DateTime>(
name: "MarkedAsAvailable4K",
table: "MovieRequests",
type: "datetime(6)",
nullable: true);
migrationBuilder.AddColumn<DateTime>(
name: "MarkedAsDenied4K",
table: "MovieRequests",
type: "datetime(6)",
nullable: false,
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
migrationBuilder.AddColumn<DateTime>(
name: "RequestedDate4k",
table: "MovieRequests",
type: "datetime(6)",
nullable: false,
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "Approved4K",
table: "MovieRequests");
migrationBuilder.DropColumn(
name: "Available4K",
table: "MovieRequests");
migrationBuilder.DropColumn(
name: "Denied4K",
table: "MovieRequests");
migrationBuilder.DropColumn(
name: "DeniedReason4K",
table: "MovieRequests");
migrationBuilder.DropColumn(
name: "MarkedAsApproved4K",
table: "MovieRequests");
migrationBuilder.DropColumn(
name: "MarkedAsAvailable4K",
table: "MovieRequests");
migrationBuilder.DropColumn(
name: "MarkedAsDenied4K",
table: "MovieRequests");
migrationBuilder.DropColumn(
name: "RequestedDate4k",
table: "MovieRequests");
}
}
}

View file

@ -641,27 +641,39 @@ namespace Ombi.Store.Migrations.OmbiMySql
b.Property<bool>("Approved") b.Property<bool>("Approved")
.HasColumnType("tinyint(1)"); .HasColumnType("tinyint(1)");
b.Property<bool>("Approved4K")
.HasColumnType("tinyint(1)");
b.Property<bool>("Available") b.Property<bool>("Available")
.HasColumnType("tinyint(1)"); .HasColumnType("tinyint(1)");
b.Property<bool>("Available4K")
.HasColumnType("tinyint(1)");
b.Property<string>("Background") b.Property<string>("Background")
.HasColumnType("longtext"); .HasColumnType("longtext");
b.Property<bool?>("Denied") b.Property<bool?>("Denied")
.HasColumnType("tinyint(1)"); .HasColumnType("tinyint(1)");
b.Property<bool?>("Denied4K")
.HasColumnType("tinyint(1)");
b.Property<string>("DeniedReason") b.Property<string>("DeniedReason")
.HasColumnType("longtext"); .HasColumnType("longtext");
b.Property<string>("DeniedReason4K")
.HasColumnType("longtext");
b.Property<DateTime?>("DigitalReleaseDate") b.Property<DateTime?>("DigitalReleaseDate")
.HasColumnType("datetime(6)"); .HasColumnType("datetime(6)");
b.Property<string>("ImdbId")
.HasColumnType("longtext");
b.Property<bool>("Has4KRequest") b.Property<bool>("Has4KRequest")
.HasColumnType("tinyint(1)"); .HasColumnType("tinyint(1)");
b.Property<string>("ImdbId")
.HasColumnType("longtext");
b.Property<int?>("IssueId") b.Property<int?>("IssueId")
.HasColumnType("int"); .HasColumnType("int");
@ -671,12 +683,21 @@ namespace Ombi.Store.Migrations.OmbiMySql
b.Property<DateTime>("MarkedAsApproved") b.Property<DateTime>("MarkedAsApproved")
.HasColumnType("datetime(6)"); .HasColumnType("datetime(6)");
b.Property<DateTime>("MarkedAsApproved4K")
.HasColumnType("datetime(6)");
b.Property<DateTime?>("MarkedAsAvailable") b.Property<DateTime?>("MarkedAsAvailable")
.HasColumnType("datetime(6)"); .HasColumnType("datetime(6)");
b.Property<DateTime?>("MarkedAsAvailable4K")
.HasColumnType("datetime(6)");
b.Property<DateTime>("MarkedAsDenied") b.Property<DateTime>("MarkedAsDenied")
.HasColumnType("datetime(6)"); .HasColumnType("datetime(6)");
b.Property<DateTime>("MarkedAsDenied4K")
.HasColumnType("datetime(6)");
b.Property<string>("Overview") b.Property<string>("Overview")
.HasColumnType("longtext"); .HasColumnType("longtext");
@ -698,6 +719,9 @@ namespace Ombi.Store.Migrations.OmbiMySql
b.Property<DateTime>("RequestedDate") b.Property<DateTime>("RequestedDate")
.HasColumnType("datetime(6)"); .HasColumnType("datetime(6)");
b.Property<DateTime>("RequestedDate4k")
.HasColumnType("datetime(6)");
b.Property<string>("RequestedUserId") b.Property<string>("RequestedUserId")
.HasColumnType("varchar(255)"); .HasColumnType("varchar(255)");

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,101 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Ombi.Store.Migrations.OmbiSqlite
{
public partial class _4kMovieProperties : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<bool>(
name: "Approved4K",
table: "MovieRequests",
type: "INTEGER",
nullable: false,
defaultValue: false);
migrationBuilder.AddColumn<bool>(
name: "Available4K",
table: "MovieRequests",
type: "INTEGER",
nullable: false,
defaultValue: false);
migrationBuilder.AddColumn<bool>(
name: "Denied4K",
table: "MovieRequests",
type: "INTEGER",
nullable: true);
migrationBuilder.AddColumn<string>(
name: "DeniedReason4K",
table: "MovieRequests",
type: "TEXT",
nullable: true);
migrationBuilder.AddColumn<DateTime>(
name: "MarkedAsApproved4K",
table: "MovieRequests",
type: "TEXT",
nullable: false,
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
migrationBuilder.AddColumn<DateTime>(
name: "MarkedAsAvailable4K",
table: "MovieRequests",
type: "TEXT",
nullable: true);
migrationBuilder.AddColumn<DateTime>(
name: "MarkedAsDenied4K",
table: "MovieRequests",
type: "TEXT",
nullable: false,
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
migrationBuilder.AddColumn<DateTime>(
name: "RequestedDate4k",
table: "MovieRequests",
type: "TEXT",
nullable: false,
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "Approved4K",
table: "MovieRequests");
migrationBuilder.DropColumn(
name: "Available4K",
table: "MovieRequests");
migrationBuilder.DropColumn(
name: "Denied4K",
table: "MovieRequests");
migrationBuilder.DropColumn(
name: "DeniedReason4K",
table: "MovieRequests");
migrationBuilder.DropColumn(
name: "MarkedAsApproved4K",
table: "MovieRequests");
migrationBuilder.DropColumn(
name: "MarkedAsAvailable4K",
table: "MovieRequests");
migrationBuilder.DropColumn(
name: "MarkedAsDenied4K",
table: "MovieRequests");
migrationBuilder.DropColumn(
name: "RequestedDate4k",
table: "MovieRequests");
}
}
}

View file

@ -639,27 +639,39 @@ namespace Ombi.Store.Migrations.OmbiSqlite
b.Property<bool>("Approved") b.Property<bool>("Approved")
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
b.Property<bool>("Approved4K")
.HasColumnType("INTEGER");
b.Property<bool>("Available") b.Property<bool>("Available")
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
b.Property<bool>("Available4K")
.HasColumnType("INTEGER");
b.Property<string>("Background") b.Property<string>("Background")
.HasColumnType("TEXT"); .HasColumnType("TEXT");
b.Property<bool?>("Denied") b.Property<bool?>("Denied")
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
b.Property<bool?>("Denied4K")
.HasColumnType("INTEGER");
b.Property<string>("DeniedReason") b.Property<string>("DeniedReason")
.HasColumnType("TEXT"); .HasColumnType("TEXT");
b.Property<string>("DeniedReason4K")
.HasColumnType("TEXT");
b.Property<DateTime?>("DigitalReleaseDate") b.Property<DateTime?>("DigitalReleaseDate")
.HasColumnType("TEXT"); .HasColumnType("TEXT");
b.Property<string>("ImdbId")
.HasColumnType("TEXT");
b.Property<bool>("Has4KRequest") b.Property<bool>("Has4KRequest")
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
b.Property<string>("ImdbId")
.HasColumnType("TEXT");
b.Property<int?>("IssueId") b.Property<int?>("IssueId")
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
@ -669,12 +681,21 @@ namespace Ombi.Store.Migrations.OmbiSqlite
b.Property<DateTime>("MarkedAsApproved") b.Property<DateTime>("MarkedAsApproved")
.HasColumnType("TEXT"); .HasColumnType("TEXT");
b.Property<DateTime>("MarkedAsApproved4K")
.HasColumnType("TEXT");
b.Property<DateTime?>("MarkedAsAvailable") b.Property<DateTime?>("MarkedAsAvailable")
.HasColumnType("TEXT"); .HasColumnType("TEXT");
b.Property<DateTime?>("MarkedAsAvailable4K")
.HasColumnType("TEXT");
b.Property<DateTime>("MarkedAsDenied") b.Property<DateTime>("MarkedAsDenied")
.HasColumnType("TEXT"); .HasColumnType("TEXT");
b.Property<DateTime>("MarkedAsDenied4K")
.HasColumnType("TEXT");
b.Property<string>("Overview") b.Property<string>("Overview")
.HasColumnType("TEXT"); .HasColumnType("TEXT");
@ -696,6 +717,9 @@ namespace Ombi.Store.Migrations.OmbiSqlite
b.Property<DateTime>("RequestedDate") b.Property<DateTime>("RequestedDate")
.HasColumnType("TEXT"); .HasColumnType("TEXT");
b.Property<DateTime>("RequestedDate4k")
.HasColumnType("TEXT");
b.Property<string>("RequestedUserId") b.Property<string>("RequestedUserId")
.HasColumnType("TEXT"); .HasColumnType("TEXT");

View file

@ -17,6 +17,10 @@ export interface IMovieRequests extends IFullBaseRequest {
showSubscribe: boolean; showSubscribe: boolean;
requestStatus: string; requestStatus: string;
has4KRequest: boolean; has4KRequest: boolean;
approved4K: boolean;
available4K: boolean;
denied4K: boolean;
deniedReason4K: string;
// For the UI // For the UI
rootPathOverrideTitle: string; rootPathOverrideTitle: string;
@ -54,6 +58,7 @@ export interface IRequestsViewModel<T> {
export interface IMovieUpdateModel { export interface IMovieUpdateModel {
id: number; id: number;
is4K: boolean;
} }
export interface IDenyMovieModel extends IMovieUpdateModel { export interface IDenyMovieModel extends IMovieUpdateModel {

View file

@ -42,6 +42,11 @@
externalIds: IExternalIds; externalIds: IExternalIds;
keywords: IKeywords; keywords: IKeywords;
belongsToCollection: ICollectionsModel; belongsToCollection: ICollectionsModel;
has4KRequest: boolean;
approved4K: boolean;
available4K: boolean;
denied4K: boolean;
deniedReason4K: string;
// for the UI // for the UI
requestProcessing: boolean; requestProcessing: boolean;

View file

@ -22,9 +22,11 @@
[isAdmin]="isAdmin" [isAdmin]="isAdmin"
[canShowAdvanced]="showAdvanced && movieRequest" [canShowAdvanced]="showAdvanced && movieRequest"
[type]="requestType" [type]="requestType"
[has4KRequest]="movie.has4KRequest"
(openTrailer)="openDialog()" (openTrailer)="openDialog()"
(onAdvancedOptions)="openAdvancedOptions()" (onAdvancedOptions)="openAdvancedOptions()"
(onReProcessRequest)="reProcessRequest()" (onReProcessRequest)="reProcessRequest(false)"
(onReProcess4KRequest)="reProcessRequest(true)"
> >
</social-icons> </social-icons>
@ -54,6 +56,7 @@
<i class="far fa-play-circle fa-2x"></i> <i class="far fa-play-circle fa-2x"></i>
</a> </a>
</span> </span>
<!-- Regular Movie Status -->
<button mat-raised-button class="btn-green btn-spacing" id="availableBtn" *ngIf="movie.available && !movie.plexUrl && !movie.embyUrl && !movie.jellyfinUrl"> {{ <button mat-raised-button class="btn-green btn-spacing" id="availableBtn" *ngIf="movie.available && !movie.plexUrl && !movie.embyUrl && !movie.jellyfinUrl"> {{
'Common.Available' | translate }}</button> 'Common.Available' | translate }}</button>
<span *ngIf="!movie.available"> <span *ngIf="!movie.available">
@ -65,13 +68,34 @@
</button> </button>
</ng-template> </ng-template>
<ng-template #notRequestedBtn> <ng-template #notRequestedBtn>
<button *ngIf="!movie.requested" id="requestBtn" mat-raised-button class="btn-spacing" color="primary" (click)="request()"> <button *ngIf="!movie.requested" id="requestBtn" mat-raised-button class="btn-spacing" color="primary" (click)="request(false)">
<i *ngIf="movie.requestProcessing" class="fas fa-circle-notch fa-spin fa-fw"></i> <i *ngIf="movie.requestProcessing" class="fas fa-circle-notch fa-spin fa-fw"></i>
<i *ngIf="!movie.requestProcessing && !movie.processed" class="fas fa-plus"></i> <i *ngIf="!movie.requestProcessing && !movie.processed" class="fas fa-plus"></i>
<i *ngIf="movie.processed && !movie.requestProcessing" class="fas fa-check"></i> <i *ngIf="movie.processed && !movie.requestProcessing" class="fas fa-check"></i>
{{'Common.Request' | translate }} {{'Common.Request' | translate }}
</button> </button>
</ng-template> </ng-template>
</span>
<!-- 4k Status -->
<button mat-raised-button class="btn-green btn-spacing" id="availableBtn" *ngIf="movie.available4K && !movie.plexUrl && !movie.embyUrl && !movie.jellyfinUrl"> {{
'Common.Available' | translate }}</button>
<span *ngIf="!movie.available4K">
<span *ngIf="movie.has4KRequest || movie.approved4K; then requestedBtn4K else notRequestedBtn4K"></span>
<ng-template #requestedBtn4K>
<button id="requestedBtn4K" mat-raised-button *ngIf="!hasRequest || hasRequest && movieRequest && !movieRequest.denied4K" class="btn-spacing" color="warn" [disabled]>
<i class="fas fa-check"></i>
{{ 'Common.Requested4K' | translate }}
</button>
</ng-template>
<ng-template #notRequestedBtn4K>
<button *ngIf="!movie.has4KRequest" id="requestBtn4k" mat-raised-button class="btn-spacing" color="primary" (click)="request(true)">
<i *ngIf="movie.requestProcessing" class="fas fa-circle-notch fa-spin fa-fw"></i>
<i *ngIf="!movie.requestProcessing && !movie.processed" class="fas fa-plus"></i>
<i *ngIf="movie.processed && !movie.requestProcessing" class="fas fa-check"></i>
{{'Common.Request4K' | translate }}
</button>
</ng-template>
<span *ngIf="!isAdmin && movie.showSubscribe" > <span *ngIf="!isAdmin && movie.showSubscribe" >
<button *ngIf="!movie.subscribed" (click)="notify()" id="notifyBtn" mat-raised-button class="btn-spacing" > <i class="fas fa-bell"></i> <button *ngIf="!movie.subscribed" (click)="notify()" id="notifyBtn" mat-raised-button class="btn-spacing" > <i class="fas fa-bell"></i>
{{ 'Requests.Notify' | translate }}</button> {{ 'Requests.Notify' | translate }}</button>
@ -79,26 +103,45 @@
{{ 'Requests.RemoveNotification' | translate }}</button> {{ 'Requests.RemoveNotification' | translate }}</button>
</span> </span>
</span> </span>
<span *ngIf="isAdmin && hasRequest"> <span *ngIf="isAdmin && hasRequest">
<button id="approveBtn" *ngIf="!movie.approved " (click)="approve()" mat-raised-button class="btn-spacing" color="accent"> <button id="approveBtn" *ngIf="!movie.approved && movie.requested" (click)="approve(false)" mat-raised-button class="btn-spacing" color="accent">
<i class="fas fa-plus"></i> {{ 'Common.Approve' | translate }} <i class="fas fa-plus"></i> {{ 'Common.Approve' | translate }}
</button> </button>
<button id="markAvailableBtn" *ngIf="!movie.available" (click)="markAvailable()" mat-raised-button class="btn-spacing" <button id="approve4kBtn" *ngIf="!movie.approved4K && movie.has4KRequest " (click)="approve(true)" mat-raised-button class="btn-spacing" color="accent">
color="accent"> <i class="fas fa-plus"></i> {{ 'Common.Approve4K' | translate }}
<i class="fas fa-plus"></i> {{ 'Requests.MarkAvailable' | translate }} </button>
</button> <button id="markAvailableBtn" *ngIf="!movie.available" (click)="markAvailable(false)" mat-raised-button class="btn-spacing"
color="accent">
<i class="fas fa-plus"></i> {{ 'Requests.MarkAvailable' | translate }}
</button>
<button id="markAvailable4kBtn" *ngIf="!movie.available4K" (click)="markAvailable(true)" mat-raised-button class="btn-spacing"
color="accent">
<i class="fas fa-plus"></i> {{ 'Requests.MarkAvailable4K' | translate }}
</button>
<button id="markUnavailableBtn" *ngIf="movie.available" (click)="markUnavailable()" mat-raised-button class="btn-spacing" <button id="markUnavailableBtn" *ngIf="movie.available" (click)="markUnavailable(false)" mat-raised-button class="btn-spacing"
color="accent"> color="accent">
<i class="fas fa-minus"></i> {{ 'Requests.MarkUnavailable' | translate }} <i class="fas fa-minus"></i> {{ 'Requests.MarkUnavailable' | translate }}
</button> </button>
<button id="markUnavailable4kBtn" *ngIf="movie.available4K" (click)="markUnavailable(true)" mat-raised-button class="btn-spacing"
color="accent">
<i class="fas fa-minus"></i> {{ 'Requests.MarkUnavailable4K' | translate }}
</button>
<button id="denyBtn" *ngIf="movieRequest && !movieRequest.denied" mat-raised-button class="btn-spacing" color="warn" (click)="deny()"> <button id="denyBtn" *ngIf="!movieRequest.denied" mat-raised-button class="btn-spacing" color="warn" (click)="deny(false)">
<i class="fas fa-times"></i> {{'Requests.Deny' | translate }} <i class="fas fa-times"></i> {{'Requests.Deny' | translate }}
</button> </button>
<button id="deniedButton" *ngIf="movieRequest && movieRequest.denied" [matTooltip]="movieRequest.deniedReason" mat-raised-button class="btn-spacing" color="warn"> <button id="denyBtn4K" *ngIf="!movieRequest.denied4K" mat-raised-button class="btn-spacing" color="warn" (click)="deny(true)">
<i class="fas fa-times"></i> {{'MediaDetails.Denied' | translate }} <i class="fas fa-times"></i> {{'Requests.Deny4K' | translate }}
</button> </button>
<button id="deniedButton" *ngIf="movieRequest && movieRequest.denied" [matTooltip]="movieRequest.deniedReason" mat-raised-button class="btn-spacing" color="warn">
<i class="fas fa-times"></i> {{'MediaDetails.Denied' | translate }}
</button>
<button id="deniedButton4k" *ngIf="movieRequest.denied4K" [matTooltip]="movieRequest.deniedReason4K" mat-raised-button class="btn-spacing" color="warn">
<i class="fas fa-times"></i> {{'MediaDetails.Denied4K' | translate }}
</button>
</span> </span>
<button id="reportIssueBtn" mat-raised-button class="btn-spacing" color="danger" (click)="issue()" *ngIf="issuesEnabled"> <button id="reportIssueBtn" mat-raised-button class="btn-spacing" color="danger" (click)="issue()" *ngIf="issuesEnabled">

View file

@ -13,7 +13,7 @@ import { TranslateService } from "@ngx-translate/core";
import { MovieAdvancedOptionsComponent } from "./panels/movie-advanced-options/movie-advanced-options.component"; import { MovieAdvancedOptionsComponent } from "./panels/movie-advanced-options/movie-advanced-options.component";
import { RequestServiceV2 } from "../../../services/requestV2.service"; import { RequestServiceV2 } from "../../../services/requestV2.service";
import { RequestBehalfComponent } from "../shared/request-behalf/request-behalf.component"; import { RequestBehalfComponent } from "../shared/request-behalf/request-behalf.component";
import { forkJoin } from "rxjs"; import { firstValueFrom, forkJoin } from "rxjs";
import { AdminRequestDialogComponent } from "../../../shared/admin-request-dialog/admin-request-dialog.component"; import { AdminRequestDialogComponent } from "../../../shared/admin-request-dialog/admin-request-dialog.component";
@Component({ @Component({
@ -86,18 +86,23 @@ export class MovieDetailsComponent {
} }
} }
public async request(userId?: string) { public async request(is4K: boolean, userId?: string) {
if (this.isAdmin) { if (this.isAdmin) {
const dialog = this.dialog.open(AdminRequestDialogComponent, { width: "700px", data: { type: RequestType.movie, id: this.movie.id }, panelClass: 'modal-panel' }); const dialog = this.dialog.open(AdminRequestDialogComponent, { width: "700px", data: { type: RequestType.movie, id: this.movie.id }, panelClass: 'modal-panel' });
dialog.afterClosed().subscribe(async (result) => { dialog.afterClosed().subscribe(async (result) => {
if (result) { if (result) {
const requestResult = await this.requestService.requestMovie({ theMovieDbId: this.theMovidDbId, const requestResult = await firstValueFrom(this.requestService.requestMovie({ theMovieDbId: this.theMovidDbId,
languageCode: this.translate.currentLang, languageCode: this.translate.currentLang,
qualityPathOverride: result.radarrPathId, qualityPathOverride: result.radarrPathId,
requestOnBehalf: result.username?.id, requestOnBehalf: result.username?.id,
rootFolderOverride: result.radarrFolderId, }).toPromise(); rootFolderOverride: result.radarrFolderId,
is4KRequest: is4K }));
if (requestResult.result) { if (requestResult.result) {
this.movie.requested = true; if (is4K) {
this.movie.has4KRequest = true;
} else {
this.movie.requested = true;
}
this.movie.requestId = requestResult.requestId; this.movie.requestId = requestResult.requestId;
this.messageService.send(this.translate.instant("Requests.RequestAddedSuccessfully", { title: this.movie.title }), "Ok"); this.messageService.send(this.translate.instant("Requests.RequestAddedSuccessfully", { title: this.movie.title }), "Ok");
this.movieRequest = await this.requestService.getMovieRequest(this.movie.requestId); this.movieRequest = await this.requestService.getMovieRequest(this.movie.requestId);
@ -107,7 +112,7 @@ export class MovieDetailsComponent {
} }
}); });
} else { } else {
const result = await this.requestService.requestMovie({ theMovieDbId: this.theMovidDbId, languageCode: this.translate.currentLang, requestOnBehalf: userId, qualityPathOverride: undefined, rootFolderOverride: undefined }).toPromise(); const result = await firstValueFrom(this.requestService.requestMovie({ theMovieDbId: this.theMovidDbId, languageCode: this.translate.currentLang, requestOnBehalf: userId, qualityPathOverride: undefined, rootFolderOverride: undefined, is4KRequest: is4K }));
if (result.result) { if (result.result) {
this.movie.requested = true; this.movie.requested = true;
this.movie.requestId = result.requestId; this.movie.requestId = result.requestId;
@ -149,21 +154,28 @@ export class MovieDetailsComponent {
}); });
} }
public async approve() { public async approve(is4K: boolean) {
this.movie.approved = true; const result = await firstValueFrom(this.requestService.approveMovie({ id: this.movieRequest.id, is4K }));
const result = await this.requestService.approveMovie({ id: this.movieRequest.id }).toPromise();
if (result.result) { if (result.result) {
if (is4K) {
this.movie.approved4K = true;
} else {
this.movie.approved = true;
}
this.messageService.send(this.translate.instant("Requests.SuccessfullyApproved"), "Ok"); this.messageService.send(this.translate.instant("Requests.SuccessfullyApproved"), "Ok");
} else { } else {
this.movie.approved = false;
this.messageService.sendRequestEngineResultError(result); this.messageService.sendRequestEngineResultError(result);
} }
} }
public async markAvailable() { public async markAvailable(is4K: boolean) {
const result = await this.requestService.markMovieAvailable({ id: this.movieRequest.id }).toPromise(); const result = await firstValueFrom(this.requestService.markMovieAvailable({ id: this.movieRequest.id, is4K }))
if (result.result) { if (result.result) {
this.movie.available = true; if (is4K) {
this.movie.available4K = true;
} else {
this.movie.available = true;
}
this.messageService.send(this.translate.instant("Requests.NowAvailable"), "Ok"); this.messageService.send(this.translate.instant("Requests.NowAvailable"), "Ok");
} else { } else {
this.messageService.sendRequestEngineResultError(result); this.messageService.sendRequestEngineResultError(result);
@ -171,10 +183,14 @@ export class MovieDetailsComponent {
} }
public async markUnavailable() { public async markUnavailable(is4K: boolean) {
const result = await this.requestService.markMovieUnavailable({ id: this.movieRequest.id }).toPromise(); const result = await firstValueFrom(this.requestService.markMovieUnavailable({ id: this.movieRequest.id, is4K }));
if (result.result) { if (result.result) {
this.movie.available = false; if (is4K) {
this.movie.available4K = false;
} else {
this.movie.available = false;
}
this.messageService.send(this.translate.instant("Requests.NowUnavailable"), "Ok"); this.messageService.send(this.translate.instant("Requests.NowUnavailable"), "Ok");
} else { } else {
this.messageService.sendRequestEngineResultError(result); this.messageService.sendRequestEngineResultError(result);
@ -203,8 +219,8 @@ export class MovieDetailsComponent {
}); });
} }
public reProcessRequest() { public reProcessRequest(is4K: boolean) {
this.requestService2.reprocessRequest(this.movieRequest.id, RequestType.movie).subscribe(result => { this.requestService2.reprocessRequest(this.movieRequest.id, RequestType.movie, is4K).subscribe(result => {
if (result.result) { if (result.result) {
this.messageService.send(result.message ? result.message : this.translate.instant("Requests.SuccessfullyReprocessed"), "Ok"); this.messageService.send(result.message ? result.message : this.translate.instant("Requests.SuccessfullyReprocessed"), "Ok");
} else { } else {

View file

@ -4,6 +4,7 @@ import { MatDialogRef, MAT_DIALOG_DATA } from "@angular/material/dialog";
import { RequestService, MessageService } from "../../../../services"; import { RequestService, MessageService } from "../../../../services";
import { TranslateService } from "@ngx-translate/core"; import { TranslateService } from "@ngx-translate/core";
import { RequestType, IRequestEngineResult } from "../../../../interfaces"; import { RequestType, IRequestEngineResult } from "../../../../interfaces";
import { firstValueFrom } from "rxjs";
@Component({ @Component({
selector: "deny-dialog", selector: "deny-dialog",
@ -22,13 +23,13 @@ export class DenyDialogComponent {
public async deny() { public async deny() {
let result: IRequestEngineResult; let result: IRequestEngineResult;
if(this.data.requestType == RequestType.movie) { if(this.data.requestType == RequestType.movie) {
result = await this.requestService.denyMovie({id: this.data.requestId, reason: this.denyReason }).toPromise(); result = await firstValueFrom(this.requestService.denyMovie({id: this.data.requestId, reason: this.denyReason, is4K: this.data.is4K }));
} }
if(this.data.requestType == RequestType.tvShow) { if(this.data.requestType == RequestType.tvShow) {
result = await this.requestService.denyChild({id: this.data.requestId, reason: this.denyReason }).toPromise(); result = await firstValueFrom(this.requestService.denyChild({id: this.data.requestId, reason: this.denyReason }));
} }
if(this.data.requestType == RequestType.album) { if(this.data.requestType == RequestType.album) {
result = await this.requestService.denyAlbum({id: this.data.requestId, reason: this.denyReason }).toPromise(); result = await firstValueFrom(this.requestService.denyAlbum({id: this.data.requestId, reason: this.denyReason }));
} }
if (result.result) { if (result.result) {

View file

@ -4,6 +4,7 @@ export interface IDenyDialogData {
requestType: RequestType; requestType: RequestType;
requestId: number; requestId: number;
denied: boolean; denied: boolean;
is4K: boolean;
} }
export interface IIssueDialogData { export interface IIssueDialogData {

View file

@ -35,9 +35,13 @@
<span *ngIf="type === RequestType.movie"> {{ 'MediaDetails.RadarrConfiguration' | translate}}</span> <span *ngIf="type === RequestType.movie"> {{ 'MediaDetails.RadarrConfiguration' | translate}}</span>
<span *ngIf="type === RequestType.tvShow"> {{ 'MediaDetails.SonarrConfiguration' | translate}}</span> <span *ngIf="type === RequestType.tvShow"> {{ 'MediaDetails.SonarrConfiguration' | translate}}</span>
</button> </button>
<button *ngIf="type === RequestType.movie" mat-menu-item (click)="reProcessRequest()"> <button *ngIf="type === RequestType.movie" mat-menu-item (click)="reProcessRequest(false)">
<i class="fas fa-sync icon-spacing"></i> <i class="fas fa-sync icon-spacing"></i>
<span> {{ 'MediaDetails.ReProcessRequest' | translate}}</span> <span> {{ 'MediaDetails.ReProcessRequest' | translate}}</span>
</button> </button>
<button *ngIf="type === RequestType.movie && has4KRequest" mat-menu-item (click)="reProcessRequest(true)">
<i class="fas fa-sync icon-spacing"></i>
<span> {{ 'MediaDetails.ReProcessRequest4K' | translate}}</span>
</button>
</mat-menu> </mat-menu>
</div> </div>

View file

@ -23,10 +23,12 @@ export class SocialIconsComponent {
@Input() isAdmin: boolean; @Input() isAdmin: boolean;
@Input() canShowAdvanced: boolean; @Input() canShowAdvanced: boolean;
@Input() has4KRequest: boolean;
@Output() openTrailer: EventEmitter<any> = new EventEmitter(); @Output() openTrailer: EventEmitter<any> = new EventEmitter();
@Output() onAdvancedOptions: EventEmitter<any> = new EventEmitter(); @Output() onAdvancedOptions: EventEmitter<any> = new EventEmitter();
@Output() onReProcessRequest: EventEmitter<any> = new EventEmitter(); @Output() onReProcessRequest: EventEmitter<any> = new EventEmitter();
@Output() onReProcess4KRequest: EventEmitter<any> = new EventEmitter();
public RequestType = RequestType; public RequestType = RequestType;
@ -39,7 +41,11 @@ export class SocialIconsComponent {
this.onAdvancedOptions.emit(); this.onAdvancedOptions.emit();
} }
public reProcessRequest() { public reProcessRequest(is4K: boolean) {
this.onReProcessRequest.emit(); if (is4K) {
this.onReProcess4KRequest.emit();
} else {
this.onReProcessRequest.emit();
}
} }
} }

View file

@ -100,7 +100,7 @@ export class TvRequestsPanelComponent {
} }
public reProcessRequest(request: IChildRequests) { public reProcessRequest(request: IChildRequests) {
this.requestService2.reprocessRequest(request.id, RequestType.tvShow).subscribe(result => { this.requestService2.reprocessRequest(request.id, RequestType.tvShow, false).subscribe(result => {
if (result.result) { if (result.result) {
this.messageService.send(result.message ? result.message : "Successfully Re-processed the request", "Ok"); this.messageService.send(result.message ? result.message : "Successfully Re-processed the request", "Ok");
} else { } else {

View file

@ -182,7 +182,7 @@ export class MoviesGridComponent implements OnInit, AfterViewInit {
} }
let tasks = new Array<Observable<IRequestEngineResult>>(); let tasks = new Array<Observable<IRequestEngineResult>>();
this.selection.selected.forEach((selected) => { this.selection.selected.forEach((selected) => {
tasks.push(this.requestServiceV1.approveMovie({ id: selected.id })); tasks.push(this.requestServiceV1.approveMovie({ id: selected.id, is4K: false }));
}); });
this.isLoadingResults = true; this.isLoadingResults = true;

View file

@ -4,7 +4,7 @@ import { MessageService, RequestService } from '../../../services';
import { IRequestEngineResult, RequestType } from '../../../interfaces'; import { IRequestEngineResult, RequestType } from '../../../interfaces';
import { UpdateType } from '../../models/UpdateType'; import { UpdateType } from '../../models/UpdateType';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { Observable } from 'rxjs'; import { firstValueFrom, Observable } from 'rxjs';
@Component({ @Component({
selector: 'request-options', selector: 'request-options',
@ -43,14 +43,15 @@ export class RequestOptionsComponent {
} }
public async approve() { public async approve() {
// TODO 4K
if (this.data.type === RequestType.movie) { if (this.data.type === RequestType.movie) {
await this.requestService.approveMovie({id: this.data.id}).toPromise(); await firstValueFrom(this.requestService.approveMovie({id: this.data.id, is4K: false}));
} }
if (this.data.type === RequestType.tvShow) { if (this.data.type === RequestType.tvShow) {
await this.requestService.approveChild({id: this.data.id}).toPromise(); await firstValueFrom(this.requestService.approveChild({id: this.data.id}));
} }
if (this.data.type === RequestType.album) { if (this.data.type === RequestType.album) {
await this.requestService.approveAlbum({id: this.data.id}).toPromise(); await firstValueFrom(this.requestService.approveAlbum({id: this.data.id}));
} }
this.bottomSheetRef.dismiss({type: UpdateType.Approve}); this.bottomSheetRef.dismiss({type: UpdateType.Approve});
@ -59,10 +60,11 @@ export class RequestOptionsComponent {
public async changeAvailability() { public async changeAvailability() {
if (this.data.type === RequestType.movie) { if (this.data.type === RequestType.movie) {
await this.requestService.markMovieAvailable({id: this.data.id}).toPromise(); // TODO 4K
await firstValueFrom(this.requestService.markMovieAvailable({id: this.data.id, is4K: false}))
} }
if (this.data.type === RequestType.album) { if (this.data.type === RequestType.album) {
await this.requestService.markAlbumAvailable({id: this.data.id}).toPromise(); await firstValueFrom(this.requestService.markAlbumAvailable({id: this.data.id}));
} }
this.bottomSheetRef.dismiss({type: UpdateType.Availability}); this.bottomSheetRef.dismiss({type: UpdateType.Availability});

View file

@ -93,8 +93,8 @@ export class RequestServiceV2 extends ServiceHelpers {
return this.http.post<IRequestEngineResult>(`${this.url}TV/`, JSON.stringify(tv), {headers: this.headers}); return this.http.post<IRequestEngineResult>(`${this.url}TV/`, JSON.stringify(tv), {headers: this.headers});
} }
public reprocessRequest(requestId: number, type: RequestType): Observable<IRequestEngineResult> { public reprocessRequest(requestId: number, type: RequestType, is4K: boolean): Observable<IRequestEngineResult> {
return this.http.post<IRequestEngineResult>(`${this.url}reprocess/${type}/${requestId}`, undefined, { headers: this.headers }); return this.http.post<IRequestEngineResult>(`${this.url}reprocess/${type}/${requestId}/${is4K}`, undefined, { headers: this.headers });
} }
public requestMovieCollection(collectionId: number): Observable<IRequestEngineResult> { public requestMovieCollection(collectionId: number): Observable<IRequestEngineResult> {

View file

@ -165,7 +165,7 @@ namespace Ombi.Controllers.V1
[PowerUser] [PowerUser]
public async Task<RequestEngineResult> ApproveMovie([FromBody] MovieUpdateModel model) public async Task<RequestEngineResult> ApproveMovie([FromBody] MovieUpdateModel model)
{ {
return await MovieRequestEngine.ApproveMovieById(model.Id); return await MovieRequestEngine.ApproveMovieById(model.Id, model.Is4K);
} }
/// <summary> /// <summary>
@ -177,7 +177,7 @@ namespace Ombi.Controllers.V1
[PowerUser] [PowerUser]
public async Task<RequestEngineResult> MarkMovieAvailable([FromBody] MovieUpdateModel model) public async Task<RequestEngineResult> MarkMovieAvailable([FromBody] MovieUpdateModel model)
{ {
return await MovieRequestEngine.MarkAvailable(model.Id); return await MovieRequestEngine.MarkAvailable(model.Id, model.Is4K);
} }
/// <summary> /// <summary>
@ -189,7 +189,7 @@ namespace Ombi.Controllers.V1
[PowerUser] [PowerUser]
public async Task<RequestEngineResult> MarkMovieUnAvailable([FromBody] MovieUpdateModel model) public async Task<RequestEngineResult> MarkMovieUnAvailable([FromBody] MovieUpdateModel model)
{ {
return await MovieRequestEngine.MarkUnavailable(model.Id); return await MovieRequestEngine.MarkUnavailable(model.Id, model.Is4K);
} }
/// <summary> /// <summary>
@ -201,7 +201,7 @@ namespace Ombi.Controllers.V1
[PowerUser] [PowerUser]
public async Task<RequestEngineResult> DenyMovie([FromBody] DenyMovieModel model) public async Task<RequestEngineResult> DenyMovie([FromBody] DenyMovieModel model)
{ {
return await MovieRequestEngine.DenyMovieById(model.Id, model.Reason); return await MovieRequestEngine.DenyMovieById(model.Id, model.Reason, model.Is4K);
} }
/// <summary> /// <summary>
@ -403,7 +403,7 @@ namespace Ombi.Controllers.V1
[PowerUser] [PowerUser]
public async Task<RequestEngineResult> MarkTvAvailable([FromBody] TvUpdateModel model) public async Task<RequestEngineResult> MarkTvAvailable([FromBody] TvUpdateModel model)
{ {
return await TvRequestEngine.MarkAvailable(model.Id); return await TvRequestEngine.MarkAvailable(model.Id, false);
} }
/// <summary> /// <summary>
@ -415,7 +415,7 @@ namespace Ombi.Controllers.V1
[PowerUser] [PowerUser]
public async Task<RequestEngineResult> MarkTvUnAvailable([FromBody] TvUpdateModel model) public async Task<RequestEngineResult> MarkTvUnAvailable([FromBody] TvUpdateModel model)
{ {
return await TvRequestEngine.MarkUnavailable(model.Id); return await TvRequestEngine.MarkUnavailable(model.Id, false);
} }
/// <summary> /// <summary>

View file

@ -203,15 +203,15 @@ namespace Ombi.Controllers.V2
} }
[PowerUser] [PowerUser]
[HttpPost("reprocess/{type}/{requestId}")] [HttpPost("reprocess/{type}/{requestId}/{is4K}")]
public async Task<IActionResult> ReProcessRequest(RequestType type, int requestId) public async Task<IActionResult> ReProcessRequest(RequestType type, int requestId, bool? is4K)
{ {
switch (type) switch (type)
{ {
case RequestType.TvShow: case RequestType.TvShow:
return Ok(await _tvRequestEngine.ReProcessRequest(requestId, HttpContext.RequestAborted)); return Ok(await _tvRequestEngine.ReProcessRequest(requestId, false, HttpContext.RequestAborted));
case RequestType.Movie: case RequestType.Movie:
return Ok(await _movieRequestEngine.ReProcessRequest(requestId, HttpContext.RequestAborted)); return Ok(await _movieRequestEngine.ReProcessRequest(requestId, is4K ?? false, HttpContext.RequestAborted));
} }
return BadRequest(); return BadRequest();

View file

@ -29,5 +29,6 @@ namespace Ombi.Core.Models.Requests
public class MovieUpdateModel public class MovieUpdateModel
{ {
public int Id { get; set; } public int Id { get; set; }
public bool Is4K { get; set; }
} }
} }

View file

@ -95,6 +95,7 @@
<ProjectReference Include="..\Ombi.Core\Ombi.Core.csproj" /> <ProjectReference Include="..\Ombi.Core\Ombi.Core.csproj" />
<ProjectReference Include="..\Ombi.DependencyInjection\Ombi.DependencyInjection.csproj" /> <ProjectReference Include="..\Ombi.DependencyInjection\Ombi.DependencyInjection.csproj" />
<ProjectReference Include="..\Ombi.HealthChecks\Ombi.HealthChecks.csproj" /> <ProjectReference Include="..\Ombi.HealthChecks\Ombi.HealthChecks.csproj" />
<ProjectReference Include="..\Ombi.Helpers\Ombi.Helpers.csproj" />
<ProjectReference Include="..\Ombi.Hubs\Ombi.Hubs.csproj" /> <ProjectReference Include="..\Ombi.Hubs\Ombi.Hubs.csproj" />
<ProjectReference Include="..\Ombi.Mapping\Ombi.Mapping.csproj" /> <ProjectReference Include="..\Ombi.Mapping\Ombi.Mapping.csproj" />
<ProjectReference Include="..\Ombi.Schedule\Ombi.Schedule.csproj" /> <ProjectReference Include="..\Ombi.Schedule\Ombi.Schedule.csproj" />

View file

@ -15,6 +15,7 @@
"ContinueButton": "Continue", "ContinueButton": "Continue",
"Available": "Available", "Available": "Available",
"Approved": "Approved", "Approved": "Approved",
"Approve4K": "Approve 4K",
"Pending": "Pending", "Pending": "Pending",
"PartiallyAvailable": "Partially Available", "PartiallyAvailable": "Partially Available",
"Monitored": "Monitored", "Monitored": "Monitored",
@ -24,8 +25,10 @@
"RequestDenied": "Request Denied", "RequestDenied": "Request Denied",
"NotRequested": "Not Requested", "NotRequested": "Not Requested",
"Requested": "Requested", "Requested": "Requested",
"Requested4K": "Requested 4K",
"Search":"Search", "Search":"Search",
"Request": "Request", "Request": "Request",
"Request4K": "Request 4K",
"Denied": "Denied", "Denied": "Denied",
"Approve": "Approve", "Approve": "Approve",
"PartlyAvailable": "Partly Available", "PartlyAvailable": "Partly Available",
@ -161,9 +164,12 @@
"ChangeRootFolder": "Root Folder", "ChangeRootFolder": "Root Folder",
"ChangeQualityProfile": "Quality Profile", "ChangeQualityProfile": "Quality Profile",
"MarkUnavailable": "Mark Unavailable", "MarkUnavailable": "Mark Unavailable",
"MarkUnavailable4K": "Mark Unavailable 4K",
"MarkAvailable": "Mark Available", "MarkAvailable": "Mark Available",
"MarkAvailable4K": "Mark Available 4K",
"Remove": "Remove", "Remove": "Remove",
"Deny": "Deny", "Deny": "Deny",
"Deny4K": "Deny 4K",
"DenyReason": "Deny Reason", "DenyReason": "Deny Reason",
"DeniedReason": "Denied Reason", "DeniedReason": "Denied Reason",
"Season": "Season", "Season": "Season",
@ -295,6 +301,7 @@
}, },
"MediaDetails": { "MediaDetails": {
"Denied": "Denied", "Denied": "Denied",
"Denied4K": "Denied 4K",
"Trailers": "Trailers", "Trailers": "Trailers",
"RecommendationsTitle": "Recommendations", "RecommendationsTitle": "Recommendations",
"SimilarTitle": "Similar", "SimilarTitle": "Similar",
@ -363,6 +370,7 @@
"RequestDate": "Request Date:", "RequestDate": "Request Date:",
"DeniedReason": "Denied Reason:", "DeniedReason": "Denied Reason:",
"ReProcessRequest": "Re-Process Request", "ReProcessRequest": "Re-Process Request",
"ReProcessRequest4K": "Re-Process 4K Request",
"Music": { "Music": {
"Type": "Type:", "Type": "Type:",
"Country": "Country:", "Country": "Country:",