diff --git a/src/Ombi.Core/Engine/Interfaces/IRequestEngine.cs b/src/Ombi.Core/Engine/Interfaces/IRequestEngine.cs index c8b7746f0..f4eeb6fc3 100644 --- a/src/Ombi.Core/Engine/Interfaces/IRequestEngine.cs +++ b/src/Ombi.Core/Engine/Interfaces/IRequestEngine.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Threading; using System.Threading.Tasks; using Ombi.Core.Models; using Ombi.Core.Models.Requests; @@ -24,5 +25,6 @@ namespace Ombi.Core.Engine.Interfaces Task UnSubscribeRequest(int requestId, RequestType type); Task SubscribeToRequest(int requestId, RequestType type); Task GetRemainingRequests(OmbiUser user = null); + Task ReProcessRequest(int requestId, CancellationToken cancellationToken); } } \ No newline at end of file diff --git a/src/Ombi.Core/Engine/MovieRequestEngine.cs b/src/Ombi.Core/Engine/MovieRequestEngine.cs index 00a97f766..8d54f0fc3 100644 --- a/src/Ombi.Core/Engine/MovieRequestEngine.cs +++ b/src/Ombi.Core/Engine/MovieRequestEngine.cs @@ -21,6 +21,7 @@ using Ombi.Settings.Settings.Models; using Ombi.Store.Entities.Requests; using Ombi.Store.Repository; using Ombi.Core.Models; +using System.Threading; namespace Ombi.Core.Engine { @@ -555,6 +556,11 @@ namespace Ombi.Core.Engine await NotificationHelper.Notify(request, NotificationType.RequestApproved); } + return await ProcessSendingMovie(request); + } + + private async Task ProcessSendingMovie(MovieRequests request) + { if (request.Approved) { var result = await Sender.Send(request); @@ -634,6 +640,21 @@ namespace Ombi.Core.Engine return await MovieRepository.GetAll().AnyAsync(x => x.RequestedUserId == userId); } + public async Task ReProcessRequest(int requestId, CancellationToken cancellationToken) + { + var request = await MovieRepository.Find(requestId); + if (request == null) + { + return new RequestEngineResult + { + Result = false, + ErrorMessage = "Request does not exist" + }; + } + + return await ProcessSendingMovie(request); + } + public async Task MarkUnavailable(int modelId) { var request = await MovieRepository.Find(modelId); diff --git a/src/Ombi.Core/Engine/TvRequestEngine.cs b/src/Ombi.Core/Engine/TvRequestEngine.cs index e332dde75..5eae0912c 100644 --- a/src/Ombi.Core/Engine/TvRequestEngine.cs +++ b/src/Ombi.Core/Engine/TvRequestEngine.cs @@ -25,6 +25,7 @@ using Ombi.Settings.Settings.Models; using Ombi.Store.Entities.Requests; using Ombi.Store.Repository; using Ombi.Core.Models; +using System.Threading; namespace Ombi.Core.Engine { @@ -896,6 +897,22 @@ namespace Ombi.Core.Engine } + public async Task ReProcessRequest(int requestId, CancellationToken cancellationToken) + { + var request = await TvRepository.GetChild().FirstOrDefaultAsync(x => x.Id == requestId, cancellationToken); + if (request == null) + { + return new RequestEngineResult + { + Result = false, + ErrorMessage = "Request does not exist" + }; + } + + return await ProcessSendingShow(request); + } + + private async Task AfterRequest(ChildRequests model, string requestOnBehalf) { var sendRuleResult = await RunSpecificRule(model, SpecificRules.CanSendNotification); @@ -913,6 +930,11 @@ namespace Ombi.Core.Engine EpisodeCount = model.SeasonRequests.Select(m => m.Episodes.Count).Sum(), }); + return await ProcessSendingShow(model); + } + + private async Task ProcessSendingShow(ChildRequests model) + { if (model.Approved) { // Autosend diff --git a/src/Ombi.Store/Repository/BaseRepository.cs b/src/Ombi.Store/Repository/BaseRepository.cs index 2908309ce..9d7d91447 100644 --- a/src/Ombi.Store/Repository/BaseRepository.cs +++ b/src/Ombi.Store/Repository/BaseRepository.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; +using System.Threading; using System.Threading.Tasks; using Microsoft.Data.Sqlite; using Microsoft.EntityFrameworkCore; @@ -28,6 +29,11 @@ namespace Ombi.Store.Repository return await _db.FindAsync(key); } + public async Task Find(object key, CancellationToken cancellationToken) + { + return await _db.FindAsync(new[] { key }, cancellationToken: cancellationToken); + } + public IQueryable GetAll() { return _db.AsQueryable(); diff --git a/src/Ombi.Store/Repository/IRepository.cs b/src/Ombi.Store/Repository/IRepository.cs index fd7dcc86d..b93b07d45 100644 --- a/src/Ombi.Store/Repository/IRepository.cs +++ b/src/Ombi.Store/Repository/IRepository.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; +using System.Threading; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Query; @@ -12,6 +13,7 @@ namespace Ombi.Store.Repository public interface IRepository where T : Entity { Task Find(object key); + Task Find(object key, CancellationToken cancellationToken); IQueryable GetAll(); Task FirstOrDefaultAsync(Expression> predicate); Task AddRange(IEnumerable content, bool save = true); diff --git a/src/Ombi/ClientApp/src/app/media-details/components/movie/movie-details.component.html b/src/Ombi/ClientApp/src/app/media-details/components/movie/movie-details.component.html index 928645f47..c0434980d 100644 --- a/src/Ombi/ClientApp/src/app/media-details/components/movie/movie-details.component.html +++ b/src/Ombi/ClientApp/src/app/media-details/components/movie/movie-details.component.html @@ -24,6 +24,7 @@ [type]="requestType" (openTrailer)="openDialog()" (onAdvancedOptions)="openAdvancedOptions()" + (onReProcessRequest)="reProcessRequest()" > diff --git a/src/Ombi/ClientApp/src/app/media-details/components/movie/movie-details.component.ts b/src/Ombi/ClientApp/src/app/media-details/components/movie/movie-details.component.ts index 3911d64ce..01c8a5783 100644 --- a/src/Ombi/ClientApp/src/app/media-details/components/movie/movie-details.component.ts +++ b/src/Ombi/ClientApp/src/app/media-details/components/movie/movie-details.component.ts @@ -190,6 +190,16 @@ export class MovieDetailsComponent { }); } + public reProcessRequest() { + this.requestService2.reprocessRequest(this.movieRequest.id, RequestType.movie).subscribe(result => { + if (result.result) { + this.messageService.send(result.message ? result.message : "Successfully Re-processed the request", "Ok"); + } else { + this.messageService.send(result.errorMessage, "Ok"); + } + }); + } + private loadBanner() { this.imageService.getMovieBanner(this.theMovidDbId.toString()).subscribe(x => { if (!this.movie.backdropPath) { diff --git a/src/Ombi/ClientApp/src/app/media-details/components/shared/social-icons/social-icons.component.html b/src/Ombi/ClientApp/src/app/media-details/components/shared/social-icons/social-icons.component.html index 60cad7bb0..f05792aa9 100644 --- a/src/Ombi/ClientApp/src/app/media-details/components/shared/social-icons/social-icons.component.html +++ b/src/Ombi/ClientApp/src/app/media-details/components/shared/social-icons/social-icons.component.html @@ -35,5 +35,9 @@ {{ 'MediaDetails.RadarrConfiguration' | translate}} {{ 'MediaDetails.SonarrConfiguration' | translate}} + diff --git a/src/Ombi/ClientApp/src/app/media-details/components/shared/social-icons/social-icons.component.ts b/src/Ombi/ClientApp/src/app/media-details/components/shared/social-icons/social-icons.component.ts index 3a27fa723..bc504347b 100644 --- a/src/Ombi/ClientApp/src/app/media-details/components/shared/social-icons/social-icons.component.ts +++ b/src/Ombi/ClientApp/src/app/media-details/components/shared/social-icons/social-icons.component.ts @@ -26,6 +26,7 @@ export class SocialIconsComponent { @Output() openTrailer: EventEmitter = new EventEmitter(); @Output() onAdvancedOptions: EventEmitter = new EventEmitter(); + @Output() onReProcessRequest: EventEmitter = new EventEmitter(); public RequestType = RequestType; @@ -37,4 +38,8 @@ export class SocialIconsComponent { public openAdvancedOptions() { this.onAdvancedOptions.emit(); } + + public reProcessRequest() { + this.onReProcessRequest.emit(); + } } diff --git a/src/Ombi/ClientApp/src/app/media-details/components/tv/panels/tv-requests/tv-requests-panel.component.html b/src/Ombi/ClientApp/src/app/media-details/components/tv/panels/tv-requests/tv-requests-panel.component.html index 5db522024..8bca28fb5 100644 --- a/src/Ombi/ClientApp/src/app/media-details/components/tv/panels/tv-requests/tv-requests-panel.component.html +++ b/src/Ombi/ClientApp/src/app/media-details/components/tv/panels/tv-requests/tv-requests-panel.component.html @@ -70,6 +70,7 @@ + diff --git a/src/Ombi/ClientApp/src/app/media-details/components/tv/panels/tv-requests/tv-requests-panel.component.ts b/src/Ombi/ClientApp/src/app/media-details/components/tv/panels/tv-requests/tv-requests-panel.component.ts index c7b0711b6..e51414902 100644 --- a/src/Ombi/ClientApp/src/app/media-details/components/tv/panels/tv-requests/tv-requests-panel.component.ts +++ b/src/Ombi/ClientApp/src/app/media-details/components/tv/panels/tv-requests/tv-requests-panel.component.ts @@ -4,6 +4,7 @@ import { RequestService } from "../../../../../services/request.service"; import { MessageService } from "../../../../../services"; import { MatDialog } from "@angular/material/dialog"; import { DenyDialogComponent } from "../../../shared/deny-dialog/deny-dialog.component"; +import { RequestServiceV2 } from "../../../../../services/requestV2.service"; @Component({ templateUrl: "./tv-requests-panel.component.html", @@ -16,7 +17,9 @@ export class TvRequestsPanelComponent { public displayedColumns: string[] = ['number', 'title', 'airDate', 'status']; - constructor(private requestService: RequestService, private messageService: MessageService, + constructor(private requestService: RequestService, + private requestService2: RequestServiceV2, + private messageService: MessageService, public dialog: MatDialog) { } @@ -83,9 +86,9 @@ export class TvRequestsPanelComponent { width: '250px', data: {requestId: request.id, requestType: RequestType.tvShow} }); - + dialogRef.afterClosed().subscribe(result => { - request.denied = true; + request.denied = true; request.seasonRequests.forEach((season) => { season.episodes.forEach((ep) => { ep.approved = false; @@ -93,4 +96,14 @@ export class TvRequestsPanelComponent { }); }); } + + public reProcessRequest(request: IChildRequests) { + this.requestService2.reprocessRequest(request.id, RequestType.tvShow).subscribe(result => { + if (result.result) { + this.messageService.send(result.message ? result.message : "Successfully Re-processed the request", "Ok"); + } else { + this.messageService.send(result.errorMessage, "Ok"); + } + }); + } } diff --git a/src/Ombi/ClientApp/src/app/services/request.service.ts b/src/Ombi/ClientApp/src/app/services/request.service.ts index 19a211f93..3fe60e5f3 100644 --- a/src/Ombi/ClientApp/src/app/services/request.service.ts +++ b/src/Ombi/ClientApp/src/app/services/request.service.ts @@ -187,5 +187,4 @@ export class RequestService extends ServiceHelpers { public removeAlbumRequest(request: number): any { return this.http.delete(`${this.url}music/${request}`, {headers: this.headers}); } - } diff --git a/src/Ombi/ClientApp/src/app/services/requestV2.service.ts b/src/Ombi/ClientApp/src/app/services/requestV2.service.ts index c6e155096..a7ebd6d0c 100644 --- a/src/Ombi/ClientApp/src/app/services/requestV2.service.ts +++ b/src/Ombi/ClientApp/src/app/services/requestV2.service.ts @@ -4,7 +4,7 @@ import { Injectable, Inject } from "@angular/core"; import { HttpClient } from "@angular/common/http"; import { Observable } from "rxjs"; import { ServiceHelpers } from "./service.helpers"; -import { IRequestsViewModel, IMovieRequests, IChildRequests, IMovieAdvancedOptions as IMediaAdvancedOptions, IRequestEngineResult, IAlbumRequest, ITvRequestViewModelV2 } from "../interfaces"; +import { IRequestsViewModel, IMovieRequests, IChildRequests, IMovieAdvancedOptions as IMediaAdvancedOptions, IRequestEngineResult, IAlbumRequest, ITvRequestViewModelV2, RequestType } from "../interfaces"; @Injectable() @@ -92,4 +92,8 @@ export class RequestServiceV2 extends ServiceHelpers { public requestTv(tv: ITvRequestViewModelV2): Observable { return this.http.post(`${this.url}TV/`, JSON.stringify(tv), {headers: this.headers}); } + + public reprocessRequest(requestId: number, type: RequestType): Observable { + return this.http.post(`${this.url}reprocess/${type}/${requestId}`, undefined, { headers: this.headers }); + } } diff --git a/src/Ombi/Controllers/V2/RequestsController.cs b/src/Ombi/Controllers/V2/RequestsController.cs index 0bbbd4128..67d206e0b 100644 --- a/src/Ombi/Controllers/V2/RequestsController.cs +++ b/src/Ombi/Controllers/V2/RequestsController.cs @@ -11,6 +11,7 @@ using System; using Ombi.Store.Entities; using System.Linq; using Microsoft.Extensions.Logging; +using Ombi.Attributes; namespace Ombi.Controllers.V2 { @@ -134,12 +135,14 @@ namespace Ombi.Controllers.V2 return await _tvRequestEngine.GetUnavailableRequests(count, position, sort, sortOrder); } + [PowerUser] [HttpPost("movie/advancedoptions")] public async Task UpdateAdvancedOptions([FromBody] MediaAdvancedOptions options) { return await _movieRequestEngine.UpdateAdvancedOptions(options); } + [PowerUser] [HttpPost("tv/advancedoptions")] public async Task UpdateTvAdvancedOptions([FromBody] MediaAdvancedOptions options) { @@ -198,6 +201,21 @@ namespace Ombi.Controllers.V2 return result; } + [PowerUser] + [HttpPost("reprocess/{type}/{requestId}")] + public async Task ReProcessRequest(RequestType type, int requestId) + { + switch (type) + { + case RequestType.TvShow: + return Ok(await _tvRequestEngine.ReProcessRequest(requestId, HttpContext.RequestAborted)); + case RequestType.Movie: + return Ok(await _movieRequestEngine.ReProcessRequest(requestId, HttpContext.RequestAborted)); + } + + return BadRequest(); + } + private string GetApiAlias() { // Make sure this only applies when using the API KEY diff --git a/src/Ombi/wwwroot/translations/en.json b/src/Ombi/wwwroot/translations/en.json index eab1b18d0..18e864bdd 100644 --- a/src/Ombi/wwwroot/translations/en.json +++ b/src/Ombi/wwwroot/translations/en.json @@ -288,7 +288,8 @@ "RadarrConfiguration": "Radarr Configuration", "RequestOnBehalf": "Request on behalf of", "PleaseSelectUser": "Please select a user", - "StreamingOn": "Streaming On" + "StreamingOn": "Streaming On", + "ReProcessRequest": "Re-Process Request" }, "Discovery": { "PopularTab": "Popular",