tidusjar 9 months ago
parent 5a0c15dfbc
commit 55ffa68d9d

@ -1,43 +1,55 @@
<div class="small-middle-container">
<div class="section">
@defer {
<h2 id="genreHeading" data-toggle="collapse" href="#genreCollapse" role="button">{{ 'Discovery.Genres' | translate }}</h2>
<genre-button-select class="collapse show" id="genreCollapse"></genre-button-select>
}
</div>
@defer {
<div class="section">
<h2>{{ 'Discovery.RecentlyRequestedTab' | translate }}</h2>
<div>
<ombi-recently-list [id]="'recentlyRequested'"></ombi-recently-list>
</div>
</div>
}
<div class="section" [hidden]="!showSeasonal">
<h2>{{ 'Discovery.SeasonalTab' | translate }}</h2>
<div>
<carousel-list [id]="'seasonal'" [isAdmin]="isAdmin" [discoverType]="DiscoverType.Seasonal" (movieCount)="setSeasonalMovieCount($event)"></carousel-list>
<carousel-list
[id]="'seasonal'"
[isAdmin]="isAdmin"
[discoverType]="DiscoverType.Seasonal"
(movieCount)="setSeasonalMovieCount($event)"
></carousel-list>
</div>
</div>
<div class="section">
<h2>{{ 'Discovery.PopularTab' | translate }}</h2>
<div>
@defer {
<carousel-list [id]="'popular'" [isAdmin]="isAdmin" [discoverType]="DiscoverType.Popular"></carousel-list>
}
</div>
</div>
<div class="section">
<h2>{{ 'Discovery.TrendingTab' | translate }}</h2>
<div>
@defer {
<carousel-list [id]="'trending'" [isAdmin]="isAdmin" [discoverType]="DiscoverType.Trending"></carousel-list>
}
</div>
</div>
<div class="section">
<h2>{{ 'Discovery.UpcomingTab' | translate }}</h2>
<div>
@defer {
<carousel-list [id]="'upcoming'" [isAdmin]="isAdmin" [discoverType]="DiscoverType.Upcoming"></carousel-list>
}
</div>
</div>
</div>

@ -3,67 +3,112 @@
</div>
<div *ngIf="movie" class="main-content-container">
<top-banner [background]="movie.background" [available]="movie.available" [title]="movie.title"
[releaseDate]="movie.releaseDate" [tagline]="movie.tagline"></top-banner>
<top-banner
[background]="movie.background"
[available]="movie.available"
[title]="movie.title"
[releaseDate]="movie.releaseDate"
[tagline]="movie.tagline"
></top-banner>
<div class="social-icons-container">
<social-icons [homepage]="movie.homepage" [theMoviedbId]="movie.id"
[hasTrailer]="movie.videos?.results?.length > 0" [imdbId]="movie.imdbId"
[twitter]="movie.externalIds?.twitterId" [facebook]="movie.externalIds?.facebookId"
[instagram]="movie.externalIds?.instagramId" [available]="movie.available" [isAdmin]="isAdmin"
[canShowAdvanced]="showAdvanced && movieRequest" [type]="requestType" [has4KRequest]="movie.has4KRequest"
(openTrailer)="openDialog()" (onAdvancedOptions)="openAdvancedOptions()"
(onReProcessRequest)="reProcessRequest(false)" (onReProcess4KRequest)="reProcessRequest(true)">
<social-icons
[homepage]="movie.homepage"
[theMoviedbId]="movie.id"
[hasTrailer]="movie.videos?.results?.length > 0"
[imdbId]="movie.imdbId"
[twitter]="movie.externalIds?.twitterId"
[facebook]="movie.externalIds?.facebookId"
[instagram]="movie.externalIds?.instagramId"
[available]="movie.available"
[isAdmin]="isAdmin"
[canShowAdvanced]="showAdvanced && movieRequest"
[type]="requestType"
[has4KRequest]="movie.has4KRequest"
(openTrailer)="openDialog()"
(onAdvancedOptions)="openAdvancedOptions()"
(onReProcessRequest)="reProcessRequest(false)"
(onReProcess4KRequest)="reProcessRequest(true)"
>
</social-icons>
</div>
<section id="info-wrapper">
<div class="small-middle-container">
<div class="row justify-content-center justify-content-sm-start header-container">
<div class="details-poster-container">
<media-poster [posterPath]=movie.posterPath></media-poster>
<media-poster [posterPath]="movie.posterPath"></media-poster>
</div>
<!--Next to poster-->
<div class="details-button-container">
<div class="col-12 media-row">
<span *ngIf="movie.available || movie.available4K">
<a id="viewOnPlexButton" *ngIf="movie.plexUrl" href="{{movie.plexUrl}}" mat-raised-button
target="_blank" class="btn-spacing viewon-btn plex">
<a
id="viewOnPlexButton"
*ngIf="movie.plexUrl"
href="{{ movie.plexUrl }}"
mat-raised-button
target="_blank"
class="btn-spacing viewon-btn plex"
>
{{ 'Search.ViewOnPlex' | translate }}
<i class="far fa-play-circle fa-2x"></i>
</a>
<a id="viewOnEmbyButton" *ngIf="movie.embyUrl" href="{{movie.embyUrl}}" mat-raised-button
target="_blank" class="btn-spacing viewon-btn emby">
<a
id="viewOnEmbyButton"
*ngIf="movie.embyUrl"
href="{{ movie.embyUrl }}"
mat-raised-button
target="_blank"
class="btn-spacing viewon-btn emby"
>
{{ 'Search.ViewOnEmby' | translate }}
<i class="far fa-play-circle fa-2x"></i>
</a>
<a id="viewOnJellyfinButton" *ngIf="movie.jellyfinUrl" href="{{movie.jellyfinUrl}}"
mat-raised-button target="_blank" class="btn-spacing viewon-btn jellyfin">
<a
id="viewOnJellyfinButton"
*ngIf="movie.jellyfinUrl"
href="{{ movie.jellyfinUrl }}"
mat-raised-button
target="_blank"
class="btn-spacing viewon-btn jellyfin"
>
{{ 'Search.ViewOnJellyfin' | translate }}
<i class="far fa-play-circle fa-2x"></i>
</a>
</span>
<!-- Regular Movie Status -->
<button mat-raised-button class="btn-green btn-spacing" id="availableBtn"
*ngIf="movie.available && !movie.plexUrl && !movie.embyUrl && !movie.jellyfinUrl"> {{
'Common.Available' | translate }}</button>
<button
mat-raised-button
class="btn-green btn-spacing"
id="availableBtn"
*ngIf="movie.available && !movie.plexUrl && !movie.embyUrl && !movie.jellyfinUrl"
>
{{ 'Common.Available' | translate }}
</button>
<span *ngIf="!movie.available">
<span
*ngIf="movie.requested || movie.approved; then requestedBtn else notRequestedBtn"></span>
<span *ngIf="movie.requested || movie.approved; then requestedBtn; else notRequestedBtn"></span>
<ng-template #requestedBtn>
<button id="requestedBtn" mat-raised-button
*ngIf="!hasRequest || hasRequest && movieRequest && !movieRequest.denied"
class="btn-spacing" color="warn" [disabled]>
<button
id="requestedBtn"
mat-raised-button
*ngIf="!hasRequest || (hasRequest && movieRequest && !movieRequest.denied)"
class="btn-spacing"
color="warn"
[disabled]
>
<i class="fas fa-check"></i>
{{ 'Common.Requested' | translate }}
</button>
</ng-template>
<ng-template #notRequestedBtn>
<button *ngIf="!movie.requested" id="requestBtn" mat-raised-button class="btn-spacing"
color="primary" (click)="request(false)">
<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 && !movie.processed" class="fas fa-plus"></i>
<i *ngIf="movie.processed && !movie.requestProcessing" class="fas fa-check"></i>
@ -75,31 +120,37 @@
<!-- 4k Status -->
<span *ngIf="is4KEnabled">
<span *permission="roleName4k">
<button mat-raised-button class="btn-green btn-spacing" id="availableBtn4k"
*ngIf="movie.available4K"> {{
'Common.Available4K' | translate }}
<button mat-raised-button class="btn-green btn-spacing" id="availableBtn4k" *ngIf="movie.available4K">
{{ 'Common.Available4K' | translate }}
</button>
<span *ngIf="!movie.available4K">
<span
*ngIf="movie.has4KRequest || movie.approved4K; then requestedBtn4K else notRequestedBtn4K"></span>
<span *ngIf="movie.has4KRequest || movie.approved4K; then requestedBtn4K; else notRequestedBtn4K"></span>
<ng-template #requestedBtn4K>
<button id="requestedBtn4K" mat-raised-button
*ngIf="movieRequest && !movieRequest.denied4K" class="btn-spacing"
color="warn" [disabled]>
<button
id="requestedBtn4K"
mat-raised-button
*ngIf="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>
<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>
@ -108,67 +159,115 @@
</span>
<span *ngIf="movieRequest?.showSubscribe">
<button *ngIf="!movieRequest?.subscribed" (click)="notify()" id="notifyBtn"
mat-raised-button class="btn-spacing"> <i class="fas fa-bell"></i>
{{ 'Requests.Notify' | translate }}</button>
<button *ngIf="movieRequest?.subscribed" (click)="unNotify()" id="unnotifyBtn"
mat-raised-button class="btn-spacing"> <i class="fas fa-bell-slash"></i>
{{ 'Requests.RemoveNotification' | translate }}</button>
<button *ngIf="!movieRequest?.subscribed" (click)="notify()" id="notifyBtn" mat-raised-button class="btn-spacing">
<i class="fas fa-bell"></i> {{ 'Requests.Notify' | translate }}
</button>
<button *ngIf="movieRequest?.subscribed" (click)="unNotify()" id="unnotifyBtn" mat-raised-button class="btn-spacing">
<i class="fas fa-bell-slash"></i> {{ 'Requests.RemoveNotification' | translate }}
</button>
</span>
<span *ngIf="isAdmin && hasRequest">
<button id="approveBtn" *ngIf="!movie.approved && movie.requested" (click)="approve(false)"
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 }}
</button>
<button id="markAvailableBtn" *ngIf="!movie.available && movie.requested"
(click)="markAvailable(false)" mat-raised-button class="btn-spacing" color="accent">
<button
id="markAvailableBtn"
*ngIf="!movie.available && movie.requested"
(click)="markAvailable(false)"
mat-raised-button
class="btn-spacing"
color="accent"
>
<i class="fas fa-plus"></i> {{ 'Requests.MarkAvailable' | translate }}
</button>
<button id="markUnavailableBtn" *ngIf="movie.available && movie.requested"
(click)="markUnavailable(false)" mat-raised-button class="btn-spacing" color="accent">
<button
id="markUnavailableBtn"
*ngIf="movie.available && movie.requested"
(click)="markUnavailable(false)"
mat-raised-button
class="btn-spacing"
color="accent"
>
<i class="fas fa-minus"></i> {{ 'Requests.MarkUnavailable' | translate }}
</button>
<!-- 4k -->
<span *ngIf="is4KEnabled">
<span *permission="roleName4k">
<button id="approve4kBtn" *ngIf="!movie.approved4K && movie.has4KRequest"
(click)="approve(true)" mat-raised-button class="btn-spacing" color="accent">
<button
id="approve4kBtn"
*ngIf="!movie.approved4K && movie.has4KRequest"
(click)="approve(true)"
mat-raised-button
class="btn-spacing"
color="accent"
>
<i class="fas fa-plus"></i> {{ 'Common.Approve4K' | translate }}
</button>
<button id="markAvailable4kBtn" *ngIf="!movie.available4K && movie.has4KRequest"
(click)="markAvailable(true)" mat-raised-button class="btn-spacing"
color="accent">
<button
id="markAvailable4kBtn"
*ngIf="!movie.available4K && movie.has4KRequest"
(click)="markAvailable(true)"
mat-raised-button
class="btn-spacing"
color="accent"
>
<i class="fas fa-plus"></i> {{ 'Requests.MarkAvailable4K' | translate }}
</button>
<button id="markUnavailable4kBtn" *ngIf="movie.available4K"
(click)="markUnavailable(true)" mat-raised-button class="btn-spacing"
color="accent">
<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>
</span>
</span>
<button id="denyBtn" *ngIf="!movieRequest.denied && movie.requested" mat-raised-button
class="btn-spacing" color="warn" (click)="deny(false)">
<button
id="denyBtn"
*ngIf="!movieRequest.denied && movie.requested"
mat-raised-button
class="btn-spacing"
color="warn"
(click)="deny(false)"
>
<i class="fas fa-times"></i> {{ 'Requests.Deny' | translate }}
</button>
<button id="deniedButton" *ngIf="movieRequest && movieRequest.denied"
[matTooltip]="movieRequest.deniedReason" mat-raised-button class="btn-spacing"
color="warn">
<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>
</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">
<i class="fas fa-exclamation"></i> {{ 'Requests.ReportIssue' | translate }}
</button>
<button id="viewCollectionBtn" *ngIf="movie.belongsToCollection"
[routerLink]="'/discover/collection/' + movie.belongsToCollection.id" mat-raised-button
class="btn-spacing">
<button
id="viewCollectionBtn"
*ngIf="movie.belongsToCollection"
[routerLink]="'/discover/collection/' + movie.belongsToCollection.id"
mat-raised-button
class="btn-spacing"
>
<i class="fas fa-list"></i> {{ 'MediaDetails.ViewCollection' | translate }}
</button>
</div>
@ -179,12 +278,13 @@
<div class="col-12 col-md-2">
<mat-card class="mat-elevation-z8">
<mat-card-content>
<movie-information-panel [movie]="movie" [request]="movieRequest"
[advancedOptions]="showAdvanced"></movie-information-panel>
<movie-information-panel
[movie]="movie"
[request]="movieRequest"
[advancedOptions]="showAdvanced"
></movie-information-panel>
</mat-card-content>
</mat-card>
</div>
<div class="col-12 col-md-10">
@ -198,6 +298,7 @@
</div>
</div>
@defer{
<div class="row">
<div class="col-12">
<cast-carousel [cast]="movie.credits.cast"></cast-carousel>
@ -209,6 +310,7 @@
<crew-carousel [crew]="movie.credits.crew"></crew-carousel>
</div>
</div>
}
<!-- <div class="row card-spacer" *ngIf="movie.videos?.results?.length > 0">
@ -216,26 +318,29 @@
<iframe width="100%" height="315px" [src]="'https://www.youtube.com/embed/' + video.key | safe" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
</div> -->
@defer {
<div class="row" *ngIf="movie.videos?.results?.length > 0">
<div class="col-12">
<mat-card class="mat-elevation-z8">
<mat-card-header>{{ 'MediaDetails.Trailers' | translate }}</mat-card-header>
<mat-card-content>
<p-carousel class="no-indicator" [numVisible]="2" [numScroll]="10" [page]="0"
[value]="movie.videos?.results">
<p-carousel class="no-indicator" [numVisible]="2" [numScroll]="10" [page]="0" [value]="movie.videos?.results">
<ng-template let-result pTemplate="item">
<iframe width="98%" height="315px"
<iframe
width="98%"
height="315px"
[src]="'https://www.youtube.com/embed/' + result.key | safe"
frameborder="0"
allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture"
allowfullscreen></iframe>
allowfullscreen
></iframe>
</ng-template>
</p-carousel>
</mat-card-content>
</mat-card>
</div>
</div>
}
<div class="row">
<div class="col-12">
@ -243,6 +348,7 @@
<issues-panel [providerId]="movie.imdbId" [isAdmin]="isAdmin"></issues-panel>
</div>
@defer (on viewport) {
<mat-accordion class="mat-elevation-z8 spacing-below">
<mat-expansion-panel *ngIf="movie.recommendations?.results?.length > 0">
<mat-expansion-panel-header>
@ -252,14 +358,18 @@
</mat-expansion-panel-header>
<div class="row card-spacer">
<div class="col-md-2" *ngFor="let r of movie.recommendations?.results">
<div class="sidebar affixable affix-top preview-poster">
<div class="poster">
<a [routerLink]="'/details/movie/' + r.id">
<ombi-image class="real grow" matTooltip="{{r.title}}"
<ombi-image
class="real grow"
matTooltip="{{ r.title }}"
src="https://image.tmdb.org/t/p/w300/{{ r.poster_path }}"
alt="Poster" style="display: block;"> </ombi-image>
alt="Poster"
style="display: block"
>
</ombi-image>
</a>
</div>
</div>
@ -274,14 +384,17 @@
</mat-expansion-panel-header>
<div class="row card-spacer">
<div class="col-md-2" *ngFor="let r of movie.similar.results">
<div class="sidebar affixable affix-top preview-poster">
<div class="poster">
<a [routerLink]="'/details/movie/' + r.id">
<ombi-image class="real grow" matTooltip="{{r.title}}"
<ombi-image
class="real grow"
matTooltip="{{ r.title }}"
src="https://image.tmdb.org/t/p/w300/{{ r.poster_path }}"
alt="Poster" style="display: block;"></ombi-image>
alt="Poster"
style="display: block"
></ombi-image>
</a>
</div>
</div>
@ -289,12 +402,16 @@
</div>
</mat-expansion-panel>
</mat-accordion>
} @placeholder {
<div class="loading-spinner">
<mat-spinner [color]="'accent'"></mat-spinner>
</div>
}
</div>
</div>
</div>
</div>
<div class="bottom-page-gap">
</div>
<div class="bottom-page-gap"></div>
</section>
</div>

@ -1,25 +1,25 @@
import { Component, OnInit, ViewEncapsulation } from "@angular/core";
import { ImageService, SearchV2Service, RequestService, MessageService, RadarrService, SettingsStateService } from "../../../services";
import { ActivatedRoute, Router } from "@angular/router";
import { DomSanitizer } from "@angular/platform-browser";
import { ICrewViewModel, ISearchMovieResultV2 } from "../../../interfaces/ISearchMovieResultV2";
import { MatDialog } from "@angular/material/dialog";
import { YoutubeTrailerComponent } from "../shared/youtube-trailer.component";
import { AuthService } from "../../../auth/auth.service";
import { IMovieRequests, RequestType, IAdvancedData } from "../../../interfaces";
import { DenyDialogComponent } from "../shared/deny-dialog/deny-dialog.component";
import { NewIssueComponent } from "../shared/new-issue/new-issue.component";
import { TranslateService } from "@ngx-translate/core";
import { MovieAdvancedOptionsComponent } from "./panels/movie-advanced-options/movie-advanced-options.component";
import { RequestServiceV2 } from "../../../services/requestV2.service";
import { firstValueFrom, forkJoin } from "rxjs";
import { AdminRequestDialogComponent } from "../../../shared/admin-request-dialog/admin-request-dialog.component";
import { FeaturesFacade } from "../../../state/features/features.facade";
import { Component, OnInit, ViewEncapsulation } from '@angular/core';
import { ImageService, SearchV2Service, RequestService, MessageService, RadarrService, SettingsStateService } from '../../../services';
import { ActivatedRoute, Router } from '@angular/router';
import { DomSanitizer } from '@angular/platform-browser';
import { ICrewViewModel, ISearchMovieResultV2 } from '../../../interfaces/ISearchMovieResultV2';
import { MatDialog } from '@angular/material/dialog';
import { YoutubeTrailerComponent } from '../shared/youtube-trailer.component';
import { AuthService } from '../../../auth/auth.service';
import { IMovieRequests, RequestType, IAdvancedData } from '../../../interfaces';
import { DenyDialogComponent } from '../shared/deny-dialog/deny-dialog.component';
import { NewIssueComponent } from '../shared/new-issue/new-issue.component';
import { TranslateService } from '@ngx-translate/core';
import { MovieAdvancedOptionsComponent } from './panels/movie-advanced-options/movie-advanced-options.component';
import { RequestServiceV2 } from '../../../services/requestV2.service';
import { firstValueFrom, forkJoin } from 'rxjs';
import { AdminRequestDialogComponent } from '../../../shared/admin-request-dialog/admin-request-dialog.component';
import { FeaturesFacade } from '../../../state/features/features.facade';
@Component({
templateUrl: "./movie-details.component.html",
styleUrls: ["../../media-details.component.scss"],
encapsulation: ViewEncapsulation.None
templateUrl: './movie-details.component.html',
styleUrls: ['../../media-details.component.scss'],
encapsulation: ViewEncapsulation.None,
})
export class MovieDetailsComponent implements OnInit {
public movie: ISearchMovieResultV2;
@ -29,35 +29,44 @@ export class MovieDetailsComponent implements OnInit{
public advancedOptions: IAdvancedData;
public showAdvanced: boolean; // Set on the UI
public issuesEnabled: boolean;
public roleName4k = "Request4KMovie";
public roleName4k = 'Request4KMovie';
public is4KEnabled = false;
public requestType = RequestType.movie;
private theMovidDbId: number;
private imdbId: string;
private snapMovieId: string;
constructor(private searchService: SearchV2Service, private route: ActivatedRoute, private router: Router,
private sanitizer: DomSanitizer, private imageService: ImageService,
public dialog: MatDialog, private requestService: RequestService,
private requestService2: RequestServiceV2, private radarrService: RadarrService,
public messageService: MessageService, private auth: AuthService, private settingsState: SettingsStateService,
private translate: TranslateService, private featureFacade: FeaturesFacade) {
constructor(
private searchService: SearchV2Service,
private route: ActivatedRoute,
private router: Router,
private sanitizer: DomSanitizer,
private imageService: ImageService,
public dialog: MatDialog,
private requestService: RequestService,
private requestService2: RequestServiceV2,
private radarrService: RadarrService,
public messageService: MessageService,
private auth: AuthService,
private settingsState: SettingsStateService,
private translate: TranslateService,
private featureFacade: FeaturesFacade,
) {
this.snapMovieId = this.route.snapshot.params.movieDbId;
this.route.params.subscribe(async (params: any) => {
if (typeof params.movieDbId === 'string' || params.movieDbId instanceof String) {
if (params.movieDbId.startsWith("tt")) {
if (params.movieDbId.startsWith('tt')) {
this.imdbId = params.movieDbId;
// Check if we user navigated to another movie and if so reload the component
if (this.imdbId !== this.snapMovieId) {
this.reloadComponent()
this.reloadComponent();
}
}
}
this.theMovidDbId = params.movieDbId;
// Check if we user navigated to another movie and if so reload the component
if (params.movieDbId !== this.snapMovieId) {
this.reloadComponent()
this.reloadComponent();
}
});
}
@ -72,14 +81,14 @@ export class MovieDetailsComponent implements OnInit{
async ngOnInit() {
this.is4KEnabled = this.featureFacade.is4kEnabled();
this.issuesEnabled = this.settingsState.getIssue();
this.isAdmin = this.auth.hasRole("admin") || this.auth.hasRole("poweruser");
this.isAdmin = this.auth.hasRole('admin') || this.auth.hasRole('poweruser');
if (this.isAdmin) {
this.showAdvanced = await firstValueFrom(this.radarrService.isRadarrEnabled());
}
if (this.imdbId) {
this.searchService.getMovieByImdbId(this.imdbId).subscribe(async x => {
this.searchService.getMovieByImdbId(this.imdbId).subscribe(async (x) => {
this.movie = x;
this.checkPoster();
this.movie.credits.crew = this.orderCrew(this.movie.credits.crew);
@ -91,7 +100,7 @@ export class MovieDetailsComponent implements OnInit{
this.loadBanner();
});
} else {
this.searchService.getFullMovieDetails(this.theMovidDbId).subscribe(async x => {
this.searchService.getFullMovieDetails(this.theMovidDbId).subscribe(async (x) => {
this.movie = x;
this.checkPoster();
this.movie.credits.crew = this.orderCrew(this.movie.credits.crew);
@ -111,15 +120,23 @@ export class MovieDetailsComponent implements OnInit{
is4K = false;
}
if (this.isAdmin) {
const dialog = this.dialog.open(AdminRequestDialogComponent, { width: "700px", data: { type: RequestType.movie, id: this.movie.id, is4K: is4K }, panelClass: 'modal-panel' });
const dialog = this.dialog.open(AdminRequestDialogComponent, {
width: '700px',
data: { type: RequestType.movie, id: this.movie.id, is4K: is4K },
panelClass: 'modal-panel',
});
dialog.afterClosed().subscribe(async (result) => {
if (result) {
const requestResult = await firstValueFrom(this.requestService.requestMovie({ theMovieDbId: this.theMovidDbId,
const requestResult = await firstValueFrom(
this.requestService.requestMovie({
theMovieDbId: this.theMovidDbId,
languageCode: this.translate.currentLang,
qualityPathOverride: result.radarrPathId,
requestOnBehalf: result.username?.id,
rootFolderOverride: result.radarrFolderId,
is4KRequest: is4K }));
is4KRequest: is4K,
}),
);
if (requestResult.result) {
if (is4K) {
this.movie.has4KRequest = true;
@ -127,7 +144,7 @@ export class MovieDetailsComponent implements OnInit{
this.movie.requested = true;
}
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);
} else {
this.messageService.sendRequestEngineResultError(requestResult);
@ -135,7 +152,16 @@ export class MovieDetailsComponent implements OnInit{
}
});
} else {
const result = await firstValueFrom(this.requestService.requestMovie({ theMovieDbId: this.theMovidDbId, languageCode: this.translate.currentLang, requestOnBehalf: userId, qualityPathOverride: undefined, rootFolderOverride: undefined, is4KRequest: is4K }));
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 (is4K) {
this.movie.has4KRequest = true;
@ -144,7 +170,7 @@ export class MovieDetailsComponent implements OnInit{
}
this.movie.requestId = result.requestId;
this.movieRequest = await this.requestService.getMovieRequest(this.movie.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');
} else {
this.messageService.sendRequestEngineResultError(result);
}
@ -154,17 +180,17 @@ export class MovieDetailsComponent implements OnInit{
public openDialog() {
this.dialog.open(YoutubeTrailerComponent, {
width: '560px',
data: this.movie.videos.results[0].key
data: this.movie.videos.results[0].key,
});
}
public async deny() {
const dialogRef = this.dialog.open(DenyDialogComponent, {
width: '250px',
data: { requestId: this.movieRequest.id, requestType: RequestType.movie }
data: { requestId: this.movieRequest.id, requestType: RequestType.movie },
});
dialogRef.afterClosed().subscribe(result => {
dialogRef.afterClosed().subscribe((result) => {
this.movieRequest.denied = result.denied;
this.movieRequest.deniedReason = result.reason;
});
@ -177,7 +203,13 @@ export class MovieDetailsComponent implements OnInit{
}
const dialogRef = this.dialog.open(NewIssueComponent, {
width: '500px',
data: { requestId: this.movieRequest ? this.movieRequest.id : null, requestType: RequestType.movie, providerId: provider, title: this.movie.title, posterPath: this.movie.posterPath }
data: {
requestId: this.movieRequest ? this.movieRequest.id : null,
requestType: RequestType.movie,
providerId: provider,
title: this.movie.title,
posterPath: this.movie.posterPath,
},
});
}
@ -189,27 +221,26 @@ export class MovieDetailsComponent implements OnInit{
} else {
this.movie.approved = true;
}
this.messageService.send(this.translate.instant("Requests.SuccessfullyApproved"), "Ok");
this.messageService.send(this.translate.instant('Requests.SuccessfullyApproved'), 'Ok');
} else {
this.messageService.sendRequestEngineResultError(result);
}
}
public async markAvailable(is4K: boolean) {
const result = await firstValueFrom(this.requestService.markMovieAvailable({ id: this.movieRequest.id, is4K }))
const result = await firstValueFrom(this.requestService.markMovieAvailable({ id: this.movieRequest.id, is4K }));
if (result.result) {
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 {
this.messageService.sendRequestEngineResultError(result);
}
}
public async markUnavailable(is4K: boolean) {
const result = await firstValueFrom(this.requestService.markMovieUnavailable({ id: this.movieRequest.id, is4K }));
if (result.result) {
@ -218,7 +249,7 @@ export class MovieDetailsComponent implements OnInit{
} else {
this.movie.available = false;
}
this.messageService.send(this.translate.instant("Requests.NowUnavailable"), "Ok");
this.messageService.send(this.translate.instant('Requests.NowUnavailable'), 'Ok');
} else {
this.messageService.sendRequestEngineResultError(result);
}
@ -227,29 +258,40 @@ export class MovieDetailsComponent implements OnInit{
public setAdvancedOptions(data: IAdvancedData) {
this.advancedOptions = data;
if (data.rootFolderId) {
this.movieRequest.qualityOverrideTitle = data.profiles.filter(x => x.id == data.profileId)[0].name;
this.movieRequest.qualityOverrideTitle = data.profiles.filter((x) => x.id == data.profileId)[0].name;
}
if (data.profileId) {
this.movieRequest.rootPathOverrideTitle = data.rootFolders.filter(x => x.id == data.rootFolderId)[0].path;
this.movieRequest.rootPathOverrideTitle = data.rootFolders.filter((x) => x.id == data.rootFolderId)[0].path;
}
}
public async openAdvancedOptions() {
const dialog = this.dialog.open(MovieAdvancedOptionsComponent, { width: "700px", data: <IAdvancedData>{ movieRequest: this.movieRequest }, panelClass: 'modal-panel' })
await dialog.afterClosed().subscribe(async result => {
const dialog = this.dialog.open(MovieAdvancedOptionsComponent, {
width: '700px',
data: <IAdvancedData>{ movieRequest: this.movieRequest },
panelClass: 'modal-panel',
});
await dialog.afterClosed().subscribe(async (result) => {
if (result) {
result.rootFolder = result.rootFolders.filter(f => f.id === +result.rootFolderId)[0];
result.profile = result.profiles.filter(f => f.id === +result.profileId)[0];
await this.requestService2.updateMovieAdvancedOptions({ qualityOverride: result.profileId, rootPathOverride: result.rootFolderId, languageProfile: 0, requestId: this.movieRequest.id }).toPromise();
result.rootFolder = result.rootFolders.filter((f) => f.id === +result.rootFolderId)[0];
result.profile = result.profiles.filter((f) => f.id === +result.profileId)[0];
await this.requestService2
.updateMovieAdvancedOptions({
qualityOverride: result.profileId,
rootPathOverride: result.rootFolderId,
languageProfile: 0,
requestId: this.movieRequest.id,
})
.toPromise();
this.setAdvancedOptions(result);
}
});
}
public reProcessRequest(is4K: boolean) {
this.requestService2.reprocessRequest(this.movieRequest.id, RequestType.movie, is4K).subscribe(result => {
this.requestService2.reprocessRequest(this.movieRequest.id, RequestType.movie, is4K).subscribe((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 {
this.messageService.sendRequestEngineResultError(result);
}
@ -257,53 +299,52 @@ export class MovieDetailsComponent implements OnInit{
}
public notify() {
this.requestService.subscribeToMovie(this.movieRequest.id).subscribe(result => {
this.requestService.subscribeToMovie(this.movieRequest.id).subscribe((result) => {
if (result) {
this.movie.subscribed = true;
this.messageService.send(this.translate.instant("Requests.SuccessfulNotify", {title: this.movie.title}), "Ok");
this.messageService.send(this.translate.instant('Requests.SuccessfulNotify', { title: this.movie.title }), 'Ok');
} else {
this.messageService.send(this.translate.instant("Requests.CouldntNotify", {title: this.movie.title}), "Ok");
this.messageService.send(this.translate.instant('Requests.CouldntNotify', { title: this.movie.title }), 'Ok');
}
});
}
public unNotify() {
this.requestService.unSubscribeToMovie(this.movieRequest.id).subscribe(result => {
this.requestService.unSubscribeToMovie(this.movieRequest.id).subscribe((result) => {
if (result) {
this.movie.subscribed = false;
this.messageService.send(this.translate.instant("Requests.SuccessfulUnNotify", {title: this.movie.title}), "Ok");
this.messageService.send(this.translate.instant('Requests.SuccessfulUnNotify', { title: this.movie.title }), 'Ok');
} else {
this.messageService.send(this.translate.instant("Requests.CouldntNotify", {title: this.movie.title}), "Ok");
this.messageService.send(this.translate.instant('Requests.CouldntNotify', { title: this.movie.title }), 'Ok');
}
});
}
private loadBanner() {
this.imageService.getMovieBanner(this.theMovidDbId.toString()).subscribe(x => {
this.imageService.getMovieBanner(this.theMovidDbId.toString()).subscribe((x) => {
if (!this.movie.backdropPath) {
this.movie.background = this.sanitizer.bypassSecurityTrustStyle
("url(" + x + ")");
this.movie.background = this.sanitizer.bypassSecurityTrustStyle('url(' + x + ')');
} else {
this.movie.background = this.sanitizer.bypassSecurityTrustStyle
("url(https://image.tmdb.org/t/p/original/" + this.movie.backdropPath + ")");
this.movie.background = this.sanitizer.bypassSecurityTrustStyle(
'url(https://image.tmdb.org/t/p/original/' + this.movie.backdropPath + ')',
);
}
});
}
private checkPoster() {
if (this.movie.posterPath == null) {
this.movie.posterPath = "../../../images/default_movie_poster.png";
this.movie.posterPath = '../../../images/default_movie_poster.png';
} else {
this.movie.posterPath = 'https://image.tmdb.org/t/p/w300/' + this.movie.posterPath;
}
else {
this.movie.posterPath = "https://image.tmdb.org/t/p/w300/" + this.movie.posterPath
};
}
private loadAdvancedInfo() {
const profile = this.radarrService.getQualityProfilesFromSettings();
const folders = this.radarrService.getRootFoldersFromSettings();
forkJoin([profile, folders]).subscribe(x => {
const radarrProfiles = x[0];
const radarrRootFolders = x[1];
forkJoin([profile, folders]).subscribe((x) => {
const radarrProfiles = x[0] ?? [];
const radarrRootFolders = x[1] ?? [];
const profile = radarrProfiles.filter((p) => {
return p.id === this.movieRequest.qualityOverride;
@ -318,15 +359,14 @@ export class MovieDetailsComponent implements OnInit{
if (path.length > 0) {
this.movieRequest.rootPathOverrideTitle = path[0].path;
}
});
}
private orderCrew(crew: ICrewViewModel[]): ICrewViewModel[] {
return crew.sort((a, b) => {
if (a.job === "Director") {
if (a.job === 'Director') {
return -1;
} else if (b.job === "Director") {
} else if (b.job === 'Director') {
return 1;
} else {
return 0;

@ -9,13 +9,15 @@
</div>
</div>
<ng-template #main>
<div>
<top-banner [background]="tv.background" [available]="tv.available" [title]="tv.title"
[releaseDate]="tv.firstAired" [tagline]="tv.tagline"></top-banner>
<top-banner
[background]="tv.background"
[available]="tv.available"
[title]="tv.title"
[releaseDate]="tv.firstAired"
[tagline]="tv.tagline"
></top-banner>
<div class="social-icons-container">
<social-icons
[homepage]="tv.homepage"
@ -38,55 +40,124 @@
<div class="small-middle-container">
<div class="row justify-content-center justify-content-sm-start header-container">
<div class="details-poster-container">
<media-poster [posterPath]=tv.images.original></media-poster>
<media-poster [posterPath]="tv.images.original"></media-poster>
</div>
<!--Next to poster-->
<div class="details-button-container">
<div class="col-12 media-row">
<ng-container *ngIf="tv.fullyAvailable || tv.partlyAvailable">
<a id="viewOnPlexButton" *ngIf="tv.plexUrl" href="{{tv.plexUrl}}" mat-raised-button target="_blank" class="btn-spacing viewon-btn plex">
<a
id="viewOnPlexButton"
*ngIf="tv.plexUrl"
href="{{ tv.plexUrl }}"
mat-raised-button
target="_blank"
class="btn-spacing viewon-btn plex"
>
{{ 'Search.ViewOnPlex' | translate }}
<i class="far fa-play-circle fa-2x"></i>
</a>
<a id="viewOnEmbyButton" *ngIf="tv.embyUrl" href="{{tv.embyUrl}}" mat-raised-button target="_blank" class="btn-spacing viewon-btn emby">
<a
id="viewOnEmbyButton"
*ngIf="tv.embyUrl"
href="{{ tv.embyUrl }}"
mat-raised-button
target="_blank"
class="btn-spacing viewon-btn emby"
>
{{ 'Search.ViewOnEmby' | translate }}
<i class="far fa-play-circle fa-2x"></i>
</a>
<a id="viewOnJellyfinButton" *ngIf="tv.jellyfinUrl" href="{{tv.jellyfinUrl}}" mat-raised-button target="_blank" class="btn-spacing viewon-btn jellyfin">
<a
id="viewOnJellyfinButton"
*ngIf="tv.jellyfinUrl"
href="{{ tv.jellyfinUrl }}"
mat-raised-button
target="_blank"
class="btn-spacing viewon-btn jellyfin"
>
{{ 'Search.ViewOnJellyfin' | translate }}
<i class="far fa-play-circle fa-2x"></i>
</a>
</ng-container>
<button *ngIf="(!tv.fullyAvailable || (tv.fullyAvailable && tv.partlyAvailable)) && !allEpisodesRequestedOrAvailable()" mat-raised-button id="requestBtn" class="btn-spacing" color="primary"
(click)="request()"><i class="fas fa-plus"></i>
{{ 'Common.Request' | translate }}</button>
<button
*ngIf="(!tv.fullyAvailable || (tv.fullyAvailable && tv.partlyAvailable)) && !allEpisodesRequestedOrAvailable()"
mat-raised-button
id="requestBtn"
class="btn-spacing"
color="primary"
(click)="request()"
>
<i class="fas fa-plus"></i> {{ 'Common.Request' | translate }}
</button>
<button *ngIf="!tv.denied && allEpisodesRequestedOrAvailable()" mat-raised-button class="btn-spacing" color="warn" [disabled]>
<button
*ngIf="!tv.denied && allEpisodesRequestedOrAvailable()"
mat-raised-button
class="btn-spacing"
color="warn"
[disabled]
>
<i class="fas fa-check"></i>
{{ 'Common.Requested' | translate }}</button>
{{ 'Common.Requested' | translate }}
</button>
<button *ngIf="tv.fullyAvailable && !tv.partlyAvailable" id="availableBtn" mat-raised-button class="btn-spacing" color="accent"
[disabled]>
<i class="fas fa-check"></i> {{'Common.Available' | translate }}</button>
<button
*ngIf="tv.fullyAvailable && !tv.partlyAvailable"
id="availableBtn"
mat-raised-button
class="btn-spacing"
color="accent"
[disabled]
>
<i class="fas fa-check"></i> {{ 'Common.Available' | translate }}
</button>
<button *ngIf="tv.partlyAvailable && !tv.fullyAvailable" id="partiallyAvailableBtn" mat-raised-button
class="btn-spacing" color="accent" [disabled]>
<i class="fas fa-check"></i> {{'Common.PartiallyAvailable' | translate }}</button>
<button
*ngIf="tv.partlyAvailable && !tv.fullyAvailable"
id="partiallyAvailableBtn"
mat-raised-button
class="btn-spacing"
color="accent"
[disabled]
>
<i class="fas fa-check"></i> {{ 'Common.PartiallyAvailable' | translate }}
</button>
<!-- There are unaired episodes-->
<button *ngIf="tv.partlyAvailable && tv.fullyAvailable" id="partiallyAvailableBtn" mat-raised-button
class="btn-spacing" color="accent" [disabled]>
<i class="fas fa-check"></i> {{'Common.PartiallyAvailable' | translate }}</button>
<button
*ngIf="tv.partlyAvailable && tv.fullyAvailable"
id="partiallyAvailableBtn"
mat-raised-button
class="btn-spacing"
color="accent"
[disabled]
>
<i class="fas fa-check"></i> {{ 'Common.PartiallyAvailable' | translate }}
</button>
<!-- end unaired episodes-->
<button id="deniedButton" *ngIf="tv.denied" [matTooltip]="tv.deniedReason" mat-raised-button class="btn-spacing" color="warn">
<button
id="deniedButton"
*ngIf="tv.denied"
[matTooltip]="tv.deniedReason"
mat-raised-button
class="btn-spacing"
color="warn"
>
<i class="fas fa-times"></i> {{ 'Common.Denied' | translate }}
</button>
<button mat-raised-button class="btn-spacing" color="danger" id="reportIssueBtn" *ngIf="issuesEnabled" (click)="issue()">
<i class="fas fa-exclamation"></i> {{
'Requests.ReportIssue' | translate }}</button>
<button
mat-raised-button
class="btn-spacing"
color="danger"
id="reportIssueBtn"
*ngIf="issuesEnabled"
(click)="issue()"
>
<i class="fas fa-exclamation"></i> {{ 'Requests.ReportIssue' | translate }}
</button>
</div>
</div>
</div>
@ -95,8 +166,7 @@
<div class="col-12 col-md-2">
<mat-card class="mat-elevation-z8 spacing-below">
<mat-card-content>
<tv-information-panel [tv]="tv" [request]="showRequest"
[advancedOptions]="showAdvanced"></tv-information-panel>
<tv-information-panel [tv]="tv" [request]="showRequest" [advancedOptions]="showAdvanced"></tv-information-panel>
</mat-card-content>
</mat-card>
</div>
@ -116,22 +186,23 @@
</div>
</div>
<div class="row">
<div class="col-12 col-md-2">
<!--Just some space yo-->
</div>
<div class="col-12 col-md-10">
@defer {
<tv-request-grid id="requests-grid" [tvRequest]="tvRequest" [isAdmin]="isAdmin" [tv]="tv"></tv-request-grid>
} @placeholder {
<div class="loading-spinner">
<mat-spinner [color]="'accent'"></mat-spinner>
</div>
}
</div>
<div class="col-12 col-md-2">
<!--Just some space yo-->
</div>
<div class="col-12 col-md-10">
<div class="issuesPanel">
@ -144,28 +215,26 @@
{{ 'Requests.Title' | translate }}
</mat-panel-title>
</mat-expansion-panel-header>
<tv-requests-panel [tvRequest]="tvRequest" [isAdmin]="isAdmin" [manageOwnRequests]="manageOwnRequests"></tv-requests-panel>
</mat-expansion-panel>
@defer (on viewport) {
<tv-requests-panel
[tvRequest]="tvRequest"
[isAdmin]="isAdmin"
[manageOwnRequests]="manageOwnRequests"
></tv-requests-panel>
} @placeholder {
<div class="loading-spinner">
<mat-spinner [color]="'accent'"></mat-spinner>
</div>
}
</mat-expansion-panel>
</mat-accordion>
</div>
</div>
</div>
<div class="bottom-page-gap">
</div>
<div class="bottom-page-gap"></div>
</section>
</div>
</ng-template>
</div>

@ -4,6 +4,7 @@ using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Ombi.Api.Radarr;
using Ombi.Api.Radarr.Models;
using Ombi.Api.Radarr.Models.V3;
using Ombi.Attributes;
using Ombi.Core.Settings;
using Ombi.Helpers;
@ -77,7 +78,7 @@ namespace Ombi.Controllers.V1.External
{
return Ok(await _radarrV3Api.GetProfiles(settings.ApiKey, settings.FullUri));
}
return null;
return Ok(new List<RadarrV3QualityProfile>());
}
/// <summary>
@ -94,7 +95,7 @@ namespace Ombi.Controllers.V1.External
{
return Ok(await _radarrV3Api.GetProfiles(settings.ApiKey, settings.FullUri));
}
return null;
return Ok(new List<RadarrV3QualityProfile>());
}
/// <summary>

Loading…
Cancel
Save