Got TV working nicely

pull/4112/head
tidusjar 4 years ago
parent 62cceb803d
commit 5e6edc2ad8

@ -67,7 +67,7 @@ namespace Ombi.Core.Engine
$"{movieInfo.Title}{(!string.IsNullOrEmpty(movieInfo.ReleaseDate) ? $" ({DateTime.Parse(movieInfo.ReleaseDate).Year})" : string.Empty)}";
var userDetails = await GetUser();
var canRequestOnBehalf = false;
var canRequestOnBehalf = model.RequestOnBehalf.HasValue();
var isAdmin = await UserManager.IsInRoleAsync(userDetails, OmbiRoles.PowerUser) || await UserManager.IsInRoleAsync(userDetails, OmbiRoles.Admin);
if (model.RequestOnBehalf.HasValue() && !isAdmin)

@ -140,7 +140,7 @@ namespace Ombi.Core.Engine
ErrorMessage = "This has already been requested"
};
}
return await AddExistingRequest(tvBuilder.ChildRequest, existingRequest, tv.RequestOnBehalf);
return await AddExistingRequest(tvBuilder.ChildRequest, existingRequest, tv.RequestOnBehalf, tv.RootFolderOverride.GetValueOrDefault(), tv.QualityPathOverride.GetValueOrDefault());
}
// This is a new request
@ -151,21 +151,27 @@ namespace Ombi.Core.Engine
public async Task<RequestEngineResult> RequestTvShow(TvRequestViewModelV2 tv)
{
var user = await GetUser();
var canRequestOnBehalf = false;
var canRequestOnBehalf = tv.RequestOnBehalf.HasValue();
if (tv.RequestOnBehalf.HasValue())
var isAdmin = await UserManager.IsInRoleAsync(user, OmbiRoles.PowerUser) || await UserManager.IsInRoleAsync(user, OmbiRoles.Admin);
if (tv.RequestOnBehalf.HasValue() && !isAdmin)
{
canRequestOnBehalf = await UserManager.IsInRoleAsync(user, OmbiRoles.PowerUser) || await UserManager.IsInRoleAsync(user, OmbiRoles.Admin);
return new RequestEngineResult
{
Result = false,
Message = "You do not have the correct permissions to request on behalf of users!",
ErrorMessage = $"You do not have the correct permissions to request on behalf of users!"
};
}
if (!canRequestOnBehalf)
if ((tv.RootFolderOverride.HasValue || tv.QualityPathOverride.HasValue) && !isAdmin)
{
return new RequestEngineResult
{
return new RequestEngineResult
{
Result = false,
Message = "You do not have the correct permissions to request on behalf of users!",
ErrorMessage = $"You do not have the correct permissions to request on behalf of users!"
};
}
Result = false,
Message = "You do not have the correct permissions!",
ErrorMessage = $"You do not have the correct permissions!"
};
}
var tvBuilder = new TvShowRequestBuilderV2(MovieDbApi);
@ -240,11 +246,11 @@ namespace Ombi.Core.Engine
ErrorMessage = "This has already been requested"
};
}
return await AddExistingRequest(tvBuilder.ChildRequest, existingRequest, tv.RequestOnBehalf);
return await AddExistingRequest(tvBuilder.ChildRequest, existingRequest, tv.RequestOnBehalf, tv.RootFolderOverride.GetValueOrDefault(), tv.QualityPathOverride.GetValueOrDefault());
}
// This is a new request
var newRequest = tvBuilder.CreateNewRequest(tv);
var newRequest = tvBuilder.CreateNewRequest(tv, tv.RootFolderOverride.GetValueOrDefault(), tv.QualityPathOverride.GetValueOrDefault());
return await AddRequest(newRequest.NewRequest, tv.RequestOnBehalf);
}
@ -852,10 +858,18 @@ namespace Ombi.Core.Engine
}
}
private async Task<RequestEngineResult> AddExistingRequest(ChildRequests newRequest, TvRequests existingRequest, string requestOnBehalf)
private async Task<RequestEngineResult> AddExistingRequest(ChildRequests newRequest, TvRequests existingRequest, string requestOnBehalf, int rootFolder, int qualityProfile)
{
// Add the child
existingRequest.ChildRequests.Add(newRequest);
if (qualityProfile > 0)
{
existingRequest.QualityOverride = qualityProfile;
}
if (rootFolder > 0)
{
existingRequest.RootFolder = rootFolder;
}
await TvRepository.Update(existingRequest);

@ -217,7 +217,7 @@ namespace Ombi.Core.Helpers
}
public TvShowRequestBuilderV2 CreateNewRequest(TvRequestViewModelV2 tv)
public TvShowRequestBuilderV2 CreateNewRequest(TvRequestViewModelV2 tv, int rootPathOverride, int qualityOverride)
{
int.TryParse(TheMovieDbRecord.ExternalIds?.TvDbId, out var tvdbId);
NewRequest = new TvRequests
@ -232,7 +232,9 @@ namespace Ombi.Core.Helpers
TvDbId = tvdbId,
ChildRequests = new List<ChildRequests>(),
TotalSeasons = tv.Seasons.Count(),
Background = BackdropPath
Background = BackdropPath,
RootFolder = rootPathOverride,
QualityOverride = qualityOverride
};
NewRequest.ChildRequests.Add(ChildRequest);

@ -208,6 +208,10 @@ namespace Ombi.Core.Senders
{
qualityToUse = model.ParentRequest.QualityOverride.Value;
}
if (model.ParentRequest.RootFolder.HasValue)
{
rootFolderPath = await GetSonarrRootPath(model.ParentRequest.RootFolder.Value, s);
}
// Are we using v3 sonarr?
var sonarrV3 = s.V3;

@ -5,7 +5,7 @@
</div>
<div *ngIf="discoverResults" class="row full-height">
<div class="col-xl-2 col-lg-3 col-md-3 col-6 col-sm-4 small-padding" *ngFor="let result of discoverResults">
<discover-card [result]="result"></discover-card>
<discover-card [isAdmin]="isAdmin" [result]="result"></discover-card>
</div>
</div>
</div>

@ -1,25 +1,29 @@
import { Component, AfterViewInit } from "@angular/core";
import { Component } from "@angular/core";
import { ActivatedRoute } from "@angular/router";
import { SearchV2Service } from "../../../services";
import { IActorCredits } from "../../../interfaces/ISearchTvResultV2";
import { IDiscoverCardResult } from "../../interfaces";
import { RequestType } from "../../../interfaces";
import { AuthService } from "../../../auth/auth.service";
@Component({
templateUrl: "./discover-actor.component.html",
styleUrls: ["./discover-actor.component.scss"],
})
export class DiscoverActorComponent implements AfterViewInit {
export class DiscoverActorComponent {
public actorId: number;
public actorCredits: IActorCredits;
public loadingFlag: boolean;
public isAdmin: boolean;
public discoverResults: IDiscoverCardResult[] = [];
constructor(private searchService: SearchV2Service,
private route: ActivatedRoute) {
private route: ActivatedRoute,
private auth: AuthService) {
this.route.params.subscribe((params: any) => {
this.actorId = params.actorId;
this.isAdmin = this.auth.isAdmin();
this.loading();
this.searchService.getMoviesByActor(this.actorId).subscribe(res => {
this.actorCredits = res;
@ -28,18 +32,6 @@ export class DiscoverActorComponent implements AfterViewInit {
});
}
public async ngAfterViewInit() {
// this.discoverResults.forEach((result) => {
// this.searchService.getFullMovieDetails(result.id).subscribe(x => {
// result.available = x.available;
// result.approved = x.approved;
// result.rating = x.voteAverage;
// result.requested = x.requested;
// result.url = x.homepage;
// });
// });
}
private createModel() {
this.finishLoading();
this.discoverResults = [];

@ -118,12 +118,17 @@ export class DiscoverCardComponent implements OnInit {
this.loading = true;
switch (this.result.type) {
case RequestType.tvShow:
const dia = this.dialog.open(EpisodeRequestComponent, { width: "700px", data: { series: this.tvSearchResult }, panelClass: 'modal-panel' });
const dia = this.dialog.open(EpisodeRequestComponent, { width: "700px", data: { series: this.tvSearchResult, isAdmin: this.isAdmin }, panelClass: 'modal-panel' });
dia.afterClosed().subscribe(x => this.loading = false);
return;
case RequestType.movie:
if (this.isAdmin) {
this.dialog.open(AdminRequestDialogComponent, { width: "700px", data: { type: RequestType.movie, id: this.result.id }, panelClass: 'modal-panel' });
const dialog = this.dialog.open(AdminRequestDialogComponent, { width: "700px", data: { type: RequestType.movie, id: this.result.id }, panelClass: 'modal-panel' });
dialog.afterClosed().subscribe((result) => {
if (result) {
this.result.requested = true;
}
});
} else {
this.requestService.requestMovie({ theMovieDbId: +this.result.id, languageCode: null, requestOnBehalf: null, qualityPathOverride: null, rootFolderOverride: null }).subscribe(x => {
if (x.result) {

@ -15,7 +15,7 @@
</div>
<div *ngIf="discoverResults" class="row full-height">
<div class="col-xl-2 col-lg-3 col-md-3 col-6 col-sm-4 small-padding" *ngFor="let result of discoverResults">
<discover-card [result]="result"></discover-card>
<discover-card [isAdmin]="isAdmins" [result]="result"></discover-card>
</div>
</div>
</div>

@ -4,6 +4,7 @@ import { SearchV2Service, RequestService, MessageService } from "../../../servic
import { IMovieCollectionsViewModel } from "../../../interfaces/ISearchTvResultV2";
import { IDiscoverCardResult } from "../../interfaces";
import { RequestType } from "../../../interfaces";
import { AuthService } from "../../../auth/auth.service";
@Component({
templateUrl: "./discover-collections.component.html",
@ -14,13 +15,15 @@ export class DiscoverCollectionsComponent implements OnInit {
public collectionId: number;
public collection: IMovieCollectionsViewModel;
public loadingFlag: boolean;
public isAdmin: boolean;
public discoverResults: IDiscoverCardResult[] = [];
constructor(private searchService: SearchV2Service,
private route: ActivatedRoute,
private requestService: RequestService,
private messageService: MessageService) {
private messageService: MessageService,
private auth: AuthService) {
this.route.params.subscribe((params: any) => {
this.collectionId = params.collectionId;
});
@ -28,6 +31,7 @@ export class DiscoverCollectionsComponent implements OnInit {
public async ngOnInit() {
this.loadingFlag = true;
this.isAdmin = this.auth.isAdmin();
this.collection = await this.searchService.getMovieCollections(this.collectionId);
this.createModel();
}

@ -88,16 +88,28 @@ export class MovieDetailsComponent {
if (this.isAdmin) {
const dialog = this.dialog.open(AdminRequestDialogComponent, { width: "700px", data: { type: RequestType.movie, id: this.movie.id }, panelClass: 'modal-panel' });
dialog.afterClosed().subscribe(async (result) => {
if (result) {
this.movie.requested = true;
this.movie.requestId = result.requestId;
this.movieRequest = await this.requestService.getMovieRequest(this.movie.requestId);
}
});
if (result) {
const requestResult = await this.requestService.requestMovie({ theMovieDbId: this.theMovidDbId,
languageCode: null,
qualityPathOverride: result.radarrPathId,
requestOnBehalf: result.username?.id,
rootFolderOverride: result.radarrFolderId, }).toPromise();
if (requestResult.result) {
this.movie.requested = true;
this.movie.requestId = result.requestId;
this.movieRequest = await this.requestService.getMovieRequest(this.movie.requestId);
this.messageService.send(requestResult.message, "Ok");
} else {
this.messageService.send(requestResult.errorMessage, "Ok");
}
}
});
} else {
const result = await this.requestService.requestMovie({ theMovieDbId: this.theMovidDbId, languageCode: null, requestOnBehalf: userId, qualityPathOverride: undefined, rootFolderOverride: undefined }).toPromise();
if (result.result) {
this.movie.requested = true;
this.movie.requestId = result.requestId;
this.movieRequest = await this.requestService.getMovieRequest(this.movie.requestId);
this.messageService.send(result.message, "Ok");
} else {
this.messageService.send(result.errorMessage, "Ok");

@ -45,7 +45,7 @@
<div *ngIf="request">
<span class="label">{{'Requests.RequestedBy' | translate }}:</span>
{{request.requestedUser.userAlias}}
<span id="requestedByInfo">{{request.requestedUser.userAlias}}</span>
</div>
<div *ngIf="request">

@ -1,5 +1,5 @@
import { Component, Input } from "@angular/core";
import { IChildRequests, IEpisodesRequests, INewSeasonRequests, ISeasonsViewModel, ITvRequestViewModelV2, RequestType } from "../../../../../interfaces";
import { IChildRequests, IEpisodesRequests, INewSeasonRequests, IRequestEngineResult, ISeasonsViewModel, ITvRequestViewModelV2, RequestType } from "../../../../../interfaces";
import { RequestService } from "../../../../../services/request.service";
import { MessageService } from "../../../../../services";
import { DenyDialogComponent } from "../../../shared/deny-dialog/deny-dialog.component";
@ -7,6 +7,7 @@ import { ISearchTvResultV2 } from "../../../../../interfaces/ISearchTvResultV2";
import { MatDialog } from "@angular/material/dialog";
import { SelectionModel } from "@angular/cdk/collections";
import { RequestServiceV2 } from "../../../../../services/requestV2.service";
import { AdminRequestDialogComponent } from "../../../../../shared/admin-request-dialog/admin-request-dialog.component";
@Component({
templateUrl: "./tv-request-grid.component.html",
@ -59,38 +60,21 @@ export class TvRequestGridComponent {
viewModel.seasons.push(seasonsViewModel);
});
const requestResult = await this.requestServiceV2.requestTv(viewModel).toPromise();
if (requestResult.result) {
this.notificationService.send(
`Request for ${this.tv.title} has been added successfully`);
debugger;
this.selection.clear();
if (this.tv.firstSeason) {
this.tv.seasonRequests[0].episodes.forEach(ep => {
ep.requested = true;
ep.requestStatus = "Common.PendingApproval";
});
}
if (this.tv.requestAll) {
this.tv.seasonRequests.forEach(season => {
season.episodes.forEach(ep => {
ep.requested = true;
ep.requestStatus = "Common.PendingApproval";
});
});
}
if (this.tv.latestSeason) {
this.tv.seasonRequests[this.tv.seasonRequests.length - 1].episodes.forEach(ep => {
ep.requested = true;
ep.requestStatus = "Common.PendingApproval";
});
}
if (this.isAdmin) {
const dialog = this.dialog.open(AdminRequestDialogComponent, { width: "700px", data: { type: RequestType.tvShow, id: this.tv.id }, panelClass: 'modal-panel' });
dialog.afterClosed().subscribe(async (result) => {
if (result) {
viewModel.requestOnBehalf = result.username?.id;
viewModel.qualityPathOverride = result?.sonarrPathId;
viewModel.rootFolderOverride = result?.sonarrFolderId;
const requestResult = await this.requestServiceV2.requestTv(viewModel).toPromise();
this.postRequest(requestResult);
}
});
} else {
this.notificationService.send(requestResult.errorMessage ? requestResult.errorMessage : requestResult.message);
const requestResult = await this.requestServiceV2.requestTv(viewModel).toPromise();
this.postRequest(requestResult);
}
}
@ -236,4 +220,37 @@ export class TvRequestGridComponent {
}
return "";
}
private postRequest(requestResult: IRequestEngineResult) {
if (requestResult.result) {
this.notificationService.send(
`Request for ${this.tv.title} has been added successfully`);
this.selection.clear();
if (this.tv.firstSeason) {
this.tv.seasonRequests[0].episodes.forEach(ep => {
ep.requested = true;
ep.requestStatus = "Common.PendingApproval";
});
}
if (this.tv.requestAll) {
this.tv.seasonRequests.forEach(season => {
season.episodes.forEach(ep => {
ep.requested = true;
ep.requestStatus = "Common.PendingApproval";
});
});
}
if (this.tv.latestSeason) {
this.tv.seasonRequests[this.tv.seasonRequests.length - 1].episodes.forEach(ep => {
ep.requested = true;
ep.requestStatus = "Common.PendingApproval";
});
}
} else {
this.notificationService.send(requestResult.errorMessage ? requestResult.errorMessage : requestResult.message);
}
}
}

@ -76,7 +76,7 @@ export class TvDetailsComponent implements OnInit {
}
public async request(userId: string) {
this.dialog.open(EpisodeRequestComponent, { width: "800px", data: <EpisodeRequestData> { series: this.tv, requestOnBehalf: userId }, panelClass: 'modal-panel' })
this.dialog.open(EpisodeRequestComponent, { width: "800px", data: <EpisodeRequestData> { series: this.tv, requestOnBehalf: userId, isAdmin: this.isAdmin }, panelClass: 'modal-panel' })
}
public async issue() {

@ -3,7 +3,8 @@
<form [formGroup]="form" *ngIf="form">
<h1 id="advancedOptionsTitle">{{'MediaDetails.AdvancedOptions' | translate }}</h1>
<div class="alert alert-info" role="alert">
{{'MediaDetails.AutoApproveOptions' | translate }}
<span *ngIf="data.type === RequestType.movie">{{'MediaDetails.AutoApproveOptions' | translate }}</span>
<span *ngIf="data.type === RequestType.tvShow">{{'MediaDetails.AutoApproveOptionsTv' | translate }}</span>
</div>
<div style="max-width: 0; max-height: 0; overflow: hidden;">
@ -31,7 +32,7 @@
<hr />
<!-- Sonarr -->
<div *ngIf="data.type === RequestType.tvShow">
<div *ngIf="data.type === RequestType.tvShow && sonarrEnabled">
<div>
<h3>Sonarr Overrides</h3>
<mat-form-field appearance="outline" floatLabel=auto>

@ -21,12 +21,9 @@ export class AdminRequestDialogComponent implements OnInit {
constructor(
public dialogRef: MatDialogRef<AdminRequestDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: IAdminRequestDialogData,
private requestServiceV2: RequestServiceV2,
private notificationService: MessageService,
private identityService: IdentityService,
private sonarrService: SonarrService,
private radarrService: RadarrService,
private requestService: RequestService,
private fb: FormBuilder
) {}
@ -38,7 +35,7 @@ export class AdminRequestDialogComponent implements OnInit {
public userId: string;
public radarrEnabled: boolean;
public sonarrEnabled: boolean;
public sonarrProfiles: ISonarrProfile[];
public sonarrRootFolders: ISonarrRootFolder[];
@ -63,12 +60,15 @@ export class AdminRequestDialogComponent implements OnInit {
);
if (this.data.type === RequestType.tvShow) {
this.sonarrEnabled = await this.sonarrService.isEnabled();
if (this.sonarrEnabled) {
this.sonarrService.getQualityProfilesWithoutSettings().subscribe(c => {
this.sonarrProfiles = c;
});
this.sonarrService.getRootFoldersWithoutSettings().subscribe(c => {
this.sonarrRootFolders = c;
});
}
}
if (this.data.type === RequestType.movie) {
this.radarrEnabled = await this.radarrService.isRadarrEnabled();
@ -102,29 +102,10 @@ export class AdminRequestDialogComponent implements OnInit {
public async submitRequest() {
const model = this.form.value;
if (this.data.type === RequestType.movie) {
this.requestService.requestMovie({
qualityPathOverride: model.radarrPathId,
requestOnBehalf: model.username?.id,
rootFolderOverride: model.radarrFolderId,
theMovieDbId: this.data.id,
languageCode: null
}).subscribe((x) => {
if (x.result) {
this.notificationService.send(x.message, "Ok");
model.radarrQualityOverrideTitle = this.radarrProfiles?.filter(x => x.id == model.radarrPathId)[0]?.name;
model.radarrRootFolderTitle = this.radarrRootFolders?.filter(x => x.id == model.radarrFolderId)[0]?.path;
model.requestId = x.requestId;
this.dialogRef.close(model);
} else {
this.notificationService.send(x.errorMessage, "Ok");
}
})
}
model.radarrQualityOverrideTitle = this.radarrProfiles?.filter(x => x.id == model.radarrPathId)[0]?.name;
model.radarrRootFolderTitle = this.radarrRootFolders?.filter(x => x.id == model.radarrFolderId)[0]?.path;
model.sonarrRootFolderTitle = this.sonarrRootFolders?.filter(x => x.id == model.sonarrFolderId)[0]?.path;
model.sonarrQualityOverrideTitle = this.sonarrProfiles?.filter(x => x.id == model.sonarrPathId)[0]?.name;
this.dialogRef.close(model);
}
}

@ -1,13 +1,15 @@
import { Component, Inject } from "@angular/core";
import { MatCheckboxChange } from "@angular/material/checkbox";
import { MatDialogRef, MAT_DIALOG_DATA } from "@angular/material/dialog";
import { MatDialog, MatDialogRef, MAT_DIALOG_DATA } from "@angular/material/dialog";
import { ISearchTvResultV2 } from "../../interfaces/ISearchTvResultV2";
import { MessageService } from "../../services";
import { ISeasonsViewModel, IEpisodesRequests, INewSeasonRequests, ITvRequestViewModelV2 } from "../../interfaces";
import { ISeasonsViewModel, IEpisodesRequests, INewSeasonRequests, ITvRequestViewModelV2, IRequestEngineResult, RequestType } from "../../interfaces";
import { RequestServiceV2 } from "../../services/requestV2.service";
import { AdminRequestDialogComponent } from "../admin-request-dialog/admin-request-dialog.component";
export interface EpisodeRequestData {
series: ISearchTvResultV2;
isAdmin: boolean;
requestOnBehalf: string | undefined;
}
@Component({
@ -21,7 +23,7 @@ export class EpisodeRequestComponent {
}
constructor(public dialogRef: MatDialogRef<EpisodeRequestComponent>, @Inject(MAT_DIALOG_DATA) public data: EpisodeRequestData,
private requestService: RequestServiceV2, private notificationService: MessageService) { }
private requestService: RequestServiceV2, private notificationService: MessageService, private dialog: MatDialog) { }
public async submitRequests() {
@ -57,21 +59,23 @@ export class EpisodeRequestComponent {
viewModel.seasons.push(seasonsViewModel);
});
const requestResult = await this.requestService.requestTv(viewModel).toPromise();
if (requestResult.result) {
this.notificationService.send(
`Request for ${this.data.series.title} has been added successfully`);
this.data.series.seasonRequests.forEach((season) => {
season.episodes.forEach((ep) => {
ep.selected = false;
});
if (this.data.isAdmin) {
const dialog = this.dialog.open(AdminRequestDialogComponent, { width: "700px", data: { type: RequestType.tvShow, id: this.data.series.id }, panelClass: 'modal-panel' });
dialog.afterClosed().subscribe(async (result) => {
if (result) {
viewModel.requestOnBehalf = result.username?.id;
viewModel.qualityPathOverride = result?.sonarrPathId;
viewModel.rootFolderOverride = result?.sonarrFolderId;
const requestResult = await this.requestService.requestTv(viewModel).toPromise();
this.postRequest(requestResult);
}
});
} else {
this.notificationService.send(requestResult.errorMessage ? requestResult.errorMessage : requestResult.message);
const requestResult = await this.requestService.requestTv(viewModel).toPromise();
this.postRequest(requestResult);
}
this.dialogRef.close();
}
@ -114,4 +118,20 @@ export class EpisodeRequestComponent {
this.data.series.latestSeason = true;
await this.submitRequests();
}
private postRequest(requestResult: IRequestEngineResult) {
if (requestResult.result) {
this.notificationService.send(
`Request for ${this.data.series.title} has been added successfully`);
this.data.series.seasonRequests.forEach((season) => {
season.episodes.forEach((ep) => {
ep.selected = false;
});
});
} else {
this.notificationService.send(requestResult.errorMessage ? requestResult.errorMessage : requestResult.message);
}
}
}

@ -253,6 +253,7 @@
"NotEnoughInfo": "Unfortunately there is not enough information about this show yet!",
"AdvancedOptions":"Advanced Options",
"AutoApproveOptions":"You can configure the request here, once requested it will be send to your DVR application and will be auto approved!",
"AutoApproveOptionsTv":"You can configure the request here, once requested it will be send to your DVR application and will be auto approved! If the request is already in Sonarr, we will not change the root folder or quality profile if you set it!",
"QualityProfilesSelect":"Select A Quality Profile",
"RootFolderSelect":"Select A Root Folder",
"Status":"Status",

Loading…
Cancel
Save