mirror of
https://github.com/Ombi-app/Ombi.git
synced 2025-07-30 03:28:28 -07:00
started on the episode panel !wip
This commit is contained in:
parent
6ace4d844e
commit
bbf4d72de1
11 changed files with 215 additions and 5 deletions
|
@ -130,6 +130,7 @@ namespace Ombi.Core.Engine.V2
|
||||||
item.Requested = oldModel.Requested;
|
item.Requested = oldModel.Requested;
|
||||||
item.Available = oldModel.Available;
|
item.Available = oldModel.Available;
|
||||||
item.Approved = oldModel.Approved;
|
item.Approved = oldModel.Approved;
|
||||||
|
item.SeasonRequests = oldModel.SeasonRequests;
|
||||||
|
|
||||||
return await GetExtraInfo(showInfoTask, item);
|
return await GetExtraInfo(showInfoTask, item);
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,14 @@ namespace Ombi.Core.Rule.Rules.Search
|
||||||
{
|
{
|
||||||
public static void CheckForUnairedEpisodes(SearchTvShowViewModel search)
|
public static void CheckForUnairedEpisodes(SearchTvShowViewModel search)
|
||||||
{
|
{
|
||||||
|
foreach (var season in search.SeasonRequests)
|
||||||
|
{
|
||||||
|
// If we have all the episodes for this season, then this season is available
|
||||||
|
if (season.Episodes.All(x => x.Available))
|
||||||
|
{
|
||||||
|
season.SeasonAvailable = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (search.SeasonRequests.All(x => x.Episodes.All(e => e.Available)))
|
if (search.SeasonRequests.All(x => x.Episodes.All(e => e.Available)))
|
||||||
{
|
{
|
||||||
search.FullyAvailable = true;
|
search.FullyAvailable = true;
|
||||||
|
|
|
@ -16,6 +16,7 @@ namespace Ombi.Store.Repository.Requests
|
||||||
public int ChildRequestId { get; set; }
|
public int ChildRequestId { get; set; }
|
||||||
[ForeignKey(nameof(ChildRequestId))]
|
[ForeignKey(nameof(ChildRequestId))]
|
||||||
public ChildRequests ChildRequest { get; set; }
|
public ChildRequests ChildRequest { get; set; }
|
||||||
|
[NotMapped] public bool SeasonAvailable { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class EpisodeRequests : Entity
|
public class EpisodeRequests : Entity
|
||||||
|
|
|
@ -107,6 +107,15 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-md-12" *ngIf="tv">
|
<div class="col-md-12" *ngIf="tv">
|
||||||
|
|
||||||
|
<div *ngIf="!tv.fullyAvailable" class="dropdown">
|
||||||
|
<button mat-raised-button class="btn-orange btn-spacing" type="button" (click)="request()">
|
||||||
|
<i class="fa fa-plus"></i>
|
||||||
|
{{ 'Common.Request' | translate }}
|
||||||
|
<span class="caret"></span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<button mat-raised-button class="btn-green btn-spacing" *ngIf="tv.available"> {{
|
<button mat-raised-button class="btn-green btn-spacing" *ngIf="tv.available"> {{
|
||||||
'Common.Available' | translate }}</button>
|
'Common.Available' | translate }}</button>
|
||||||
<button mat-raised-button class="btn-orange btn-spacing" *ngIf="tv.partlyAvailable"> {{
|
<button mat-raised-button class="btn-orange btn-spacing" *ngIf="tv.partlyAvailable"> {{
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
import { Component, Inject, OnInit } from "@angular/core";
|
import { Component, Inject, OnInit } from "@angular/core";
|
||||||
import { MatDialogRef, MAT_DIALOG_DATA } from "@angular/material";
|
import { MatDialogRef, MAT_DIALOG_DATA, MatDialog } from "@angular/material";
|
||||||
import { IDiscoverCardResult } from "../interfaces";
|
import { IDiscoverCardResult } from "../interfaces";
|
||||||
import { SearchV2Service, RequestService, MessageService } from "../../services";
|
import { SearchV2Service, RequestService, MessageService } from "../../services";
|
||||||
import { RequestType } from "../../interfaces";
|
import { RequestType } from "../../interfaces";
|
||||||
import { ISearchMovieResultV2 } from "../../interfaces/ISearchMovieResultV2";
|
import { ISearchMovieResultV2 } from "../../interfaces/ISearchMovieResultV2";
|
||||||
import { ISearchTvResultV2 } from "../../interfaces/ISearchTvResultV2";
|
import { ISearchTvResultV2 } from "../../interfaces/ISearchTvResultV2";
|
||||||
import { RouterLink, Router } from "@angular/router";
|
import { RouterLink, Router } from "@angular/router";
|
||||||
|
import { EpisodeRequestComponent } from "../../shared/episode-request/episode-request.component";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: "discover-card-details",
|
selector: "discover-card-details",
|
||||||
|
@ -23,7 +24,7 @@ export class DiscoverCardDetailsComponent implements OnInit {
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public dialogRef: MatDialogRef<DiscoverCardDetailsComponent>,
|
public dialogRef: MatDialogRef<DiscoverCardDetailsComponent>,
|
||||||
@Inject(MAT_DIALOG_DATA) public data: IDiscoverCardResult, private searchService: SearchV2Service,
|
@Inject(MAT_DIALOG_DATA) public data: IDiscoverCardResult, private searchService: SearchV2Service, private dialog: MatDialog,
|
||||||
private requestService: RequestService, public messageService: MessageService, private router: Router) { }
|
private requestService: RequestService, public messageService: MessageService, private router: Router) { }
|
||||||
|
|
||||||
public async ngOnInit() {
|
public async ngOnInit() {
|
||||||
|
@ -74,6 +75,9 @@ export class DiscoverCardDetailsComponent implements OnInit {
|
||||||
} else {
|
} else {
|
||||||
this.messageService.send(result.errorMessage, "Ok");
|
this.messageService.send(result.errorMessage, "Ok");
|
||||||
}
|
}
|
||||||
|
} else if (this.data.type === RequestType.tvShow) {
|
||||||
|
|
||||||
|
this.dialog.open(EpisodeRequestComponent, { width: "700px", data: this.tv })
|
||||||
}
|
}
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ import { AuthGuard } from "../auth/auth.guard";
|
||||||
import { PipeModule } from "../pipes/pipe.module";
|
import { PipeModule } from "../pipes/pipe.module";
|
||||||
import { DiscoverCardDetailsComponent } from "./card/discover-card-details.component";
|
import { DiscoverCardDetailsComponent } from "./card/discover-card-details.component";
|
||||||
import { MatDialog } from "@angular/material";
|
import { MatDialog } from "@angular/material";
|
||||||
|
import { EpisodeRequestComponent } from "../shared/episode-request/episode-request.component";
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{ path: "", component: DiscoverComponent, canActivate: [AuthGuard] },
|
{ path: "", component: DiscoverComponent, canActivate: [AuthGuard] },
|
||||||
|
@ -24,9 +25,11 @@ const routes: Routes = [
|
||||||
DiscoverComponent,
|
DiscoverComponent,
|
||||||
DiscoverCardComponent,
|
DiscoverCardComponent,
|
||||||
DiscoverCardDetailsComponent,
|
DiscoverCardDetailsComponent,
|
||||||
|
EpisodeRequestComponent,
|
||||||
],
|
],
|
||||||
entryComponents: [
|
entryComponents: [
|
||||||
DiscoverCardDetailsComponent
|
DiscoverCardDetailsComponent,
|
||||||
|
EpisodeRequestComponent
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
RouterModule,
|
RouterModule,
|
||||||
|
|
|
@ -139,6 +139,7 @@ export interface INewSeasonRequests {
|
||||||
id: number;
|
id: number;
|
||||||
seasonNumber: number;
|
seasonNumber: number;
|
||||||
episodes: IEpisodesRequests[];
|
episodes: IEpisodesRequests[];
|
||||||
|
seasonAvailable: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IEpisodesRequests {
|
export interface IEpisodesRequests {
|
||||||
|
|
|
@ -10,6 +10,7 @@ import { MovieDetailsComponent } from "./movie/movie-details.component";
|
||||||
import { TvDetailsComponent } from "./tv/tv-details.component";
|
import { TvDetailsComponent } from "./tv/tv-details.component";
|
||||||
import { PipeModule } from "../pipes/pipe.module";
|
import { PipeModule } from "../pipes/pipe.module";
|
||||||
import { YoutubeTrailerComponent } from "./youtube-trailer.component";
|
import { YoutubeTrailerComponent } from "./youtube-trailer.component";
|
||||||
|
import { EpisodeRequestComponent } from "../shared/episode-request/episode-request.component";
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{ path: "movie/:movieDbId", component: MovieDetailsComponent },
|
{ path: "movie/:movieDbId", component: MovieDetailsComponent },
|
||||||
|
@ -25,7 +26,8 @@ const routes: Routes = [
|
||||||
declarations: [
|
declarations: [
|
||||||
MovieDetailsComponent,
|
MovieDetailsComponent,
|
||||||
YoutubeTrailerComponent,
|
YoutubeTrailerComponent,
|
||||||
TvDetailsComponent
|
TvDetailsComponent,
|
||||||
|
EpisodeRequestComponent,
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
RouterModule,
|
RouterModule,
|
||||||
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
<div class="spinner-container">
|
||||||
|
<mat-spinner *ngIf="loading" [color]="'accent'"></mat-spinner>
|
||||||
|
</div>
|
||||||
|
<div *ngIf="!loading" mat-dialog-content class="background">
|
||||||
|
<div class="row">
|
||||||
|
|
||||||
|
<div class="col-12" *ngFor="let season of series.seasonRequests">
|
||||||
|
<mat-expansion-panel>
|
||||||
|
<mat-expansion-panel-header>
|
||||||
|
<mat-panel-title>
|
||||||
|
<mat-checkbox *ngIf="!season.seasonAvailable" (click)="$event.stopPropagation();" (change)="seasonChanged($event, season)"> Season {{season.seasonNumber}}</mat-checkbox>
|
||||||
|
<span *ngIf="season.seasonAvailable">Season {{season.seasonNumber}}</span>
|
||||||
|
</mat-panel-title>
|
||||||
|
<mat-panel-description>
|
||||||
|
Description
|
||||||
|
</mat-panel-description>
|
||||||
|
</mat-expansion-panel-header>
|
||||||
|
|
||||||
|
<div class="row" *ngFor="let ep of season.episodes">
|
||||||
|
<div class="col-1">
|
||||||
|
<div *ngIf="!ep.available && !ep.requested && !ep.approved">
|
||||||
|
<mat-checkbox *ngIf="!ep.selected" [ngModel]="ep.selected" (click)="addRequest(ep)"></mat-checkbox>
|
||||||
|
<mat-checkbox *ngIf="ep.selected" [ngModel]="ep.selected" (click)="removeRequest(ep)"></mat-checkbox>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-1">
|
||||||
|
{{ep.episodeNumber}}
|
||||||
|
</div>
|
||||||
|
<div class="col-3">
|
||||||
|
{{ep.title}}
|
||||||
|
</div>
|
||||||
|
<div class="col-2" *ngIf="ep.airDateDisplay != 'Unknown'">
|
||||||
|
{{ep.airDate | amLocal | amDateFormat: 'L' }}
|
||||||
|
</div>
|
||||||
|
<div class="col-2" *ngIf="ep.airDateDisplay == 'Unknown'">
|
||||||
|
{{ep.airDateDisplay }}
|
||||||
|
</div>
|
||||||
|
<div class="col-3">
|
||||||
|
<ng-template [ngIf]="ep.available"><span class="label label-success"
|
||||||
|
id="availableLabel">Available</span></ng-template>
|
||||||
|
<ng-template [ngIf]="ep.approved && !ep.available "><span class="label label-info"
|
||||||
|
id="processingRequestLabel">Processing Request</span></ng-template>
|
||||||
|
<ng-template [ngIf]="ep.selected"><span class="label label-info"
|
||||||
|
id="selectedLabel">Selected</span></ng-template>
|
||||||
|
<ng-template [ngIf]="ep.requested && !ep.approved && !ep.available && !ep.selected"><span
|
||||||
|
class="label label-warning" id="pendingApprovalLabel">Pending Approval</span>
|
||||||
|
</ng-template>
|
||||||
|
<ng-template [ngIf]="!ep.requested && !ep.available && !ep.approved && !ep.selected"><span
|
||||||
|
class="label label-danger" id="notRequetsedLabel">Not Requested</span></ng-template>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</mat-expansion-panel>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div mat-dialog-actions>
|
||||||
|
<div class="action-buttons-right">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<button mat-raised-button class="btn-spacing btn-orange">{{
|
||||||
|
'Common.Request' | translate }}</button>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -0,0 +1,91 @@
|
||||||
|
import { Component, OnInit, Inject } from "@angular/core";
|
||||||
|
import { MatDialogRef, MAT_DIALOG_DATA, MatCheckboxChange } from "@angular/material";
|
||||||
|
import { ISearchTvResultV2 } from "../../interfaces/ISearchTvResultV2";
|
||||||
|
import { RequestService, NotificationService } from "../../services";
|
||||||
|
import { ITvRequestViewModel, ISeasonsViewModel, IEpisodesRequests, INewSeasonRequests } from "../../interfaces";
|
||||||
|
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: "episode-request",
|
||||||
|
templateUrl: "episode-request.component.html",
|
||||||
|
})
|
||||||
|
export class EpisodeRequestComponent implements OnInit {
|
||||||
|
|
||||||
|
public loading: boolean;
|
||||||
|
|
||||||
|
constructor(public dialogRef: MatDialogRef<EpisodeRequestComponent>, @Inject(MAT_DIALOG_DATA) public series: ISearchTvResultV2,
|
||||||
|
private requestService: RequestService, private notificationService: NotificationService) { }
|
||||||
|
|
||||||
|
public ngOnInit() {
|
||||||
|
this.loading = true;
|
||||||
|
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public submitRequests() {
|
||||||
|
// Make sure something has been selected
|
||||||
|
const selected = this.series.seasonRequests.some((season) => {
|
||||||
|
return season.episodes.some((ep) => {
|
||||||
|
return ep.selected;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!selected) {
|
||||||
|
this.notificationService.error("You need to select some episodes!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.series.requested = true;
|
||||||
|
|
||||||
|
const viewModel = <ITvRequestViewModel>{ firstSeason: this.series.firstSeason, latestSeason: this.series.latestSeason, requestAll: this.series.requestAll, tvDbId: this.series.id };
|
||||||
|
viewModel.seasons = [];
|
||||||
|
this.series.seasonRequests.forEach((season) => {
|
||||||
|
const seasonsViewModel = <ISeasonsViewModel>{ seasonNumber: season.seasonNumber, episodes: [] };
|
||||||
|
season.episodes.forEach(ep => {
|
||||||
|
ep.requested = true;
|
||||||
|
if (!this.series.latestSeason || !this.series.requestAll || !this.series.firstSeason) {
|
||||||
|
if (ep.selected) {
|
||||||
|
seasonsViewModel.episodes.push({ episodeNumber: ep.episodeNumber });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
viewModel.seasons.push(seasonsViewModel);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.requestService.requestTv(viewModel)
|
||||||
|
.subscribe(x => {
|
||||||
|
if (x.result) {
|
||||||
|
this.notificationService.success(
|
||||||
|
`Request for ${this.series.title} has been added successfully`);
|
||||||
|
|
||||||
|
this.series.seasonRequests.forEach((season) => {
|
||||||
|
season.episodes.forEach((ep) => {
|
||||||
|
ep.selected = false;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
} else {
|
||||||
|
this.notificationService.warning("Request Added", x.errorMessage ? x.errorMessage : x.message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public addRequest(episode: IEpisodesRequests) {
|
||||||
|
episode.selected = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public removeRequest(episode: IEpisodesRequests) {
|
||||||
|
episode.selected = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public seasonChanged(checkbox: MatCheckboxChange, season: INewSeasonRequests) {
|
||||||
|
season.episodes.forEach((ep) => {
|
||||||
|
if (checkbox.checked) {
|
||||||
|
this.addRequest(ep)
|
||||||
|
} else {
|
||||||
|
this.removeRequest(ep);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,4 +17,21 @@
|
||||||
|
|
||||||
.bottom-page-gap {
|
.bottom-page-gap {
|
||||||
height: 50px;
|
height: 50px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Scrollbar */
|
||||||
|
::-webkit-scrollbar {
|
||||||
|
width: 7px;
|
||||||
|
background: rgba(0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
::-webkit-scrollbar-track {
|
||||||
|
background: rgba(0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
::-webkit-scrollbar-thumb {
|
||||||
|
border-radius: 3px;
|
||||||
|
background: #41927b;
|
||||||
|
}
|
||||||
|
.sidenav ::-webkit-scrollbar-track {
|
||||||
|
display: none;
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue