tidusjar 2 months ago
parent 5a0c15dfbc
commit 55ffa68d9d

@ -1,43 +1,55 @@
<div class="small-middle-container">
<div class="section">
<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>
<div class="section">
<h2>{{'Discovery.RecentlyRequestedTab' | translate}}</h2>
<div>
<ombi-recently-list [id]="'recentlyRequested'"></ombi-recently-list>
</div>
</div>
<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>
</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>
</div>
</div>
<div class="section">
<h2>{{'Discovery.PopularTab' | translate}}</h2>
<div>
<carousel-list [id]="'popular'" [isAdmin]="isAdmin" [discoverType]="DiscoverType.Popular"></carousel-list>
</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>
<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.TrendingTab' | translate}}</h2>
<div >
<carousel-list [id]="'trending'" [isAdmin]="isAdmin" [discoverType]="DiscoverType.Trending"></carousel-list>
</div>
</div>
<div class="section">
<h2>{{'Discovery.UpcomingTab' | translate}}</h2>
<div>
<carousel-list [id]="'upcoming'" [isAdmin]="isAdmin" [discoverType]="DiscoverType.Upcoming"></carousel-list>
</div>
</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>

@ -1,300 +1,417 @@
<div *ngIf="!movie" class="justify-content-md-center top-spacing loading-spinner">
<mat-spinner [color]="'accent'"></mat-spinner>
<mat-spinner [color]="'accent'"></mat-spinner>
</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>
<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>
</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>
</div>
<top-banner [background]="movie.background" [available]="movie.available" [title]="movie.title"
[releaseDate]="movie.releaseDate" [tagline]="movie.tagline"></top-banner>
<div class="social-icons-container">
<!--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"
>
{{ '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"
>
{{ '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"
>
{{ '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>
<span *ngIf="!movie.available">
<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]
>
<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)"
>
<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.Request' | translate }}
</button>
</ng-template>
</span>
<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>
<!-- 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>
</div>
<section id="info-wrapper">
<div class="small-middle-container">
<span *ngIf="!movie.available4K">
<span *ngIf="movie.has4KRequest || movie.approved4K; then requestedBtn4K; else notRequestedBtn4K"></span>
<ng-template #requestedBtn4K>
<button
id="requestedBtn4K"
mat-raised-button
*ngIf="movieRequest && !movieRequest.denied4K"
class="btn-spacing"
color="warn"
[disabled]
>
<i class="fas fa-check"></i>
{{ 'Common.Requested4K' | translate }}
</button>
</ng-template>
<ng-template #notRequestedBtn4K>
<button
*ngIf="!movie.has4KRequest"
id="requestBtn4k"
mat-raised-button
class="btn-spacing"
color="primary"
(click)="request(true)"
>
<i *ngIf="movie.requestProcessing" class="fas fa-circle-notch fa-spin fa-fw"></i>
<i *ngIf="!movie.requestProcessing && !movie.processed" class="fas fa-plus"></i>
<i *ngIf="movie.processed && !movie.requestProcessing" class="fas fa-check"></i>
{{ 'Common.Request4K' | translate }}
</button>
</ng-template>
</span>
</span>
</span>
<div class="row justify-content-center justify-content-sm-start header-container">
<div class="details-poster-container">
<media-poster [posterPath]=movie.posterPath></media-poster>
</div>
<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>
</span>
<!--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">
{{'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">
{{'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">
{{'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>
<span *ngIf="!movie.available">
<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]>
<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)">
<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.Request' | translate }}
</button>
</ng-template>
</span>
<span *ngIf="isAdmin && hasRequest">
<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"
>
<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"
>
<i class="fas fa-minus"></i> {{ 'Requests.MarkUnavailable' | translate }}
</button>
<!-- 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>
<!-- 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"
>
<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"
>
<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"
>
<i class="fas fa-minus"></i> {{ 'Requests.MarkUnavailable4K' | translate }}
</button>
</span>
</span>
<span *ngIf="!movie.available4K">
<span
*ngIf="movie.has4KRequest || movie.approved4K; then requestedBtn4K else notRequestedBtn4K"></span>
<ng-template #requestedBtn4K>
<button id="requestedBtn4K" mat-raised-button
*ngIf="movieRequest && !movieRequest.denied4K" class="btn-spacing"
color="warn" [disabled]>
<i class="fas fa-check"></i>
{{ 'Common.Requested4K' | translate }}
</button>
</ng-template>
<ng-template #notRequestedBtn4K>
<button *ngIf="!movie.has4KRequest" id="requestBtn4k" mat-raised-button
class="btn-spacing" color="primary" (click)="request(true)">
<i *ngIf="movie.requestProcessing"
class="fas fa-circle-notch fa-spin fa-fw"></i>
<i *ngIf="!movie.requestProcessing && !movie.processed"
class="fas fa-plus"></i>
<i *ngIf="movie.processed && !movie.requestProcessing"
class="fas fa-check"></i>
{{'Common.Request4K' | translate }}
</button>
</ng-template>
</span>
</span>
</span>
<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>
<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>
</span>
<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>
<span *ngIf="isAdmin && hasRequest">
<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">
<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">
<i class="fas fa-minus"></i> {{ 'Requests.MarkUnavailable' | translate }}
</button>
<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"
>
<i class="fas fa-list"></i> {{ 'MediaDetails.ViewCollection' | translate }}
</button>
</div>
</div>
</div>
<!-- 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">
<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">
<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">
<i class="fas fa-minus"></i> {{ 'Requests.MarkUnavailable4K' | translate }}
</button>
</span>
</span>
<div class="row">
<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>
</mat-card-content>
</mat-card>
</div>
<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>
<div class="col-12 col-md-10">
<div class="row">
<div class="col-12">
<mat-card class="mat-elevation-z8 spacing-below">
<mat-card-content>
{{ movie.overview }}
</mat-card-content>
</mat-card>
</div>
</div>
<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>
@defer{
<div class="row">
<div class="col-12">
<cast-carousel [cast]="movie.credits.cast"></cast-carousel>
</div>
</div>
<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">
<i class="fas fa-list"></i> {{'MediaDetails.ViewCollection' | translate}}
</button>
</div>
</div>
</div>
<div class="row">
<div class="col-12">
<crew-carousel [crew]="movie.credits.crew"></crew-carousel>
</div>
</div>
}
<div class="row">
<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>
</mat-card-content>
</mat-card>
</div>
<div class="col-12 col-md-10">
<div class="row">
<div class="col-12">
<mat-card class=" mat-elevation-z8 spacing-below">
<mat-card-content>
{{movie.overview}}
</mat-card-content>
</mat-card>
</div>
</div>
<div class="row">
<div class="col-12">
<cast-carousel [cast]="movie.credits.cast"></cast-carousel>
</div>
</div>
<div class="row">
<div class="col-12">
<crew-carousel [crew]="movie.credits.crew"></crew-carousel>
</div>
</div>
<!-- <div class="row card-spacer" *ngIf="movie.videos?.results?.length > 0">
<!-- <div class="row card-spacer" *ngIf="movie.videos?.results?.length > 0">
<div class="col-md-6" *ngFor="let video of movie.videos?.results">
<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">
<ng-template let-result pTemplate="item">
<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>
</ng-template>
</p-carousel>
</mat-card-content>
</mat-card>
</div>
</div>
}
<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">
<ng-template let-result pTemplate="item">
<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>
</ng-template>
</p-carousel>
</mat-card-content>
</mat-card>
</div>
</div>
<div class="row">
<div class="col-12">
<div class="issuesPanel">
<issues-panel [providerId]="movie.imdbId" [isAdmin]="isAdmin"></issues-panel>
</div>
<div class="row">
<div class="col-12">
<div class="issuesPanel">
<issues-panel [providerId]="movie.imdbId" [isAdmin]="isAdmin"></issues-panel>
</div>
<mat-accordion class=" mat-elevation-z8 spacing-below ">
<mat-expansion-panel *ngIf="movie.recommendations?.results?.length> 0">
<mat-expansion-panel-header>
<mat-panel-title>
{{'MediaDetails.RecommendationsTitle' | translate}}
</mat-panel-title>
</mat-expansion-panel-header>
@defer (on viewport) {
<mat-accordion class="mat-elevation-z8 spacing-below">
<mat-expansion-panel *ngIf="movie.recommendations?.results?.length > 0">
<mat-expansion-panel-header>
<mat-panel-title>
{{ 'MediaDetails.RecommendationsTitle' | translate }}
</mat-panel-title>
</mat-expansion-panel-header>
<div class="row card-spacer ">
<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 }}"
src="https://image.tmdb.org/t/p/w300/{{ r.poster_path }}"
alt="Poster"
style="display: block"
>
</ombi-image>
</a>
</div>
</div>
</div>
</div>
</mat-expansion-panel>
<mat-expansion-panel *ngIf="movie.similar?.results?.length > 0">
<mat-expansion-panel-header>
<mat-panel-title>
{{ 'MediaDetails.SimilarTitle' | translate }}
</mat-panel-title>
</mat-expansion-panel-header>
<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}}"
src="https://image.tmdb.org/t/p/w300/{{r.poster_path}}"
alt="Poster" style="display: block;"> </ombi-image>
</a>
</div>
</div>
</div>
</div>
</mat-expansion-panel>
<mat-expansion-panel *ngIf="movie.similar?.results?.length > 0">
<mat-expansion-panel-header>
<mat-panel-title>
{{'MediaDetails.SimilarTitle' | translate}}
</mat-panel-title>
</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}}"
src="https://image.tmdb.org/t/p/w300/{{r.poster_path}}"
alt="Poster" style="display: block;"></ombi-image>
</a>
</div>
</div>
</div>
</div>
</mat-expansion-panel>
</mat-accordion>
</div>
</div>
</div>
</div>
</div>
<div class="bottom-page-gap">
</div>
</section>
<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 }}"
src="https://image.tmdb.org/t/p/w300/{{ r.poster_path }}"
alt="Poster"
style="display: block"
></ombi-image>
</a>
</div>
</div>
</div>
</div>
</mat-expansion-panel>
</mat-accordion>
} @placeholder {
<div class="loading-spinner">
<mat-spinner [color]="'accent'"></mat-spinner>
</div>
}
</div>
</div>
</div>
</div>
</div>
<div class="bottom-page-gap"></div>
</section>
</div>

@ -1,336 +1,376 @@
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;
public hasRequest: boolean;
public movieRequest: IMovieRequests;
public isAdmin: boolean;
public advancedOptions: IAdvancedData;
public showAdvanced: boolean; // Set on the UI
public issuesEnabled: boolean;
public roleName4k = "Request4KMovie";
public is4KEnabled = false;
public requestType = RequestType.movie;
private theMovidDbId: number;
private imdbId: string;
private snapMovieId: string;
export class MovieDetailsComponent implements OnInit {
public movie: ISearchMovieResultV2;
public hasRequest: boolean;
public movieRequest: IMovieRequests;
public isAdmin: boolean;
public advancedOptions: IAdvancedData;
public showAdvanced: boolean; // Set on the UI
public issuesEnabled: boolean;
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,
) {
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')) {
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.theMovidDbId = params.movieDbId;
// Check if we user navigated to another movie and if so reload the component
if (params.movieDbId !== this.snapMovieId) {
this.reloadComponent();
}
});
}
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")) {
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.theMovidDbId = params.movieDbId;
// Check if we user navigated to another movie and if so reload the component
if (params.movieDbId !== this.snapMovieId) {
this.reloadComponent()
}
});
}
reloadComponent() {
let currentUrl = this.router.url;
this.router.routeReuseStrategy.shouldReuseRoute = () => false;
this.router.onSameUrlNavigation = 'reload';
this.router.navigate([currentUrl]);
}
reloadComponent() {
let currentUrl = this.router.url;
this.router.routeReuseStrategy.shouldReuseRoute = () => false;
this.router.onSameUrlNavigation = 'reload';
this.router.navigate([currentUrl]);
}
async ngOnInit() {
this.is4KEnabled = this.featureFacade.is4kEnabled();
this.issuesEnabled = this.settingsState.getIssue();
this.isAdmin = this.auth.hasRole('admin') || this.auth.hasRole('poweruser');
async ngOnInit() {
this.is4KEnabled = this.featureFacade.is4kEnabled();
this.issuesEnabled = this.settingsState.getIssue();
this.isAdmin = this.auth.hasRole("admin") || this.auth.hasRole("poweruser");
if (this.isAdmin) {
this.showAdvanced = await firstValueFrom(this.radarrService.isRadarrEnabled());
}
if (this.isAdmin) {
this.showAdvanced = await firstValueFrom(this.radarrService.isRadarrEnabled());
}
if (this.imdbId) {
this.searchService.getMovieByImdbId(this.imdbId).subscribe(async (x) => {
this.movie = x;
this.checkPoster();
this.movie.credits.crew = this.orderCrew(this.movie.credits.crew);
if (this.movie.requestId > 0) {
// Load up this request
this.hasRequest = true;
this.movieRequest = await this.requestService.getMovieRequest(this.movie.requestId);
}
this.loadBanner();
});
} else {
this.searchService.getFullMovieDetails(this.theMovidDbId).subscribe(async (x) => {
this.movie = x;
this.checkPoster();
this.movie.credits.crew = this.orderCrew(this.movie.credits.crew);
if (this.movie.requestId > 0) {
// Load up this request
this.hasRequest = true;
this.movieRequest = await this.requestService.getMovieRequest(this.movie.requestId);
this.loadAdvancedInfo();
}
this.loadBanner();
});
}
}
if (this.imdbId) {
this.searchService.getMovieByImdbId(this.imdbId).subscribe(async x => {
this.movie = x;
this.checkPoster();
this.movie.credits.crew = this.orderCrew(this.movie.credits.crew);
if (this.movie.requestId > 0) {
// Load up this request
this.hasRequest = true;
this.movieRequest = await this.requestService.getMovieRequest(this.movie.requestId);
}
this.loadBanner();
});
} else {
this.searchService.getFullMovieDetails(this.theMovidDbId).subscribe(async x => {
this.movie = x;
this.checkPoster();
this.movie.credits.crew = this.orderCrew(this.movie.credits.crew);
if (this.movie.requestId > 0) {
// Load up this request
this.hasRequest = true;
this.movieRequest = await this.requestService.getMovieRequest(this.movie.requestId);
this.loadAdvancedInfo();
}
this.loadBanner();
});
}
}
public async request(is4K: boolean, userId?: string) {
if (!this.is4KEnabled) {
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',
});
dialog.afterClosed().subscribe(async (result) => {
if (result) {
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,
}),
);
if (requestResult.result) {
if (is4K) {
this.movie.has4KRequest = true;
} else {
this.movie.requested = true;
}
this.movie.requestId = requestResult.requestId;
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);
}
}
});
} else {
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;
} else {
this.movie.requested = true;
}
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');
} else {
this.messageService.sendRequestEngineResultError(result);
}
}
}
public async request(is4K: boolean, userId?: string) {
if (!this.is4KEnabled) {
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' });
dialog.afterClosed().subscribe(async (result) => {
if (result) {
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 }));
if (requestResult.result) {
if (is4K) {
this.movie.has4KRequest = true;
} else {
this.movie.requested = true;
}
this.movie.requestId = requestResult.requestId;
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);
}
}
});
} else {
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;
} else {
this.movie.requested = true;
}
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");
} else {
this.messageService.sendRequestEngineResultError(result);
}
}
}
public openDialog() {
this.dialog.open(YoutubeTrailerComponent, {
width: '560px',
data: this.movie.videos.results[0].key,
});
}
public openDialog() {
this.dialog.open(YoutubeTrailerComponent, {
width: '560px',
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 },
});
public async deny() {
const dialogRef = this.dialog.open(DenyDialogComponent, {
width: '250px',
data: { requestId: this.movieRequest.id, requestType: RequestType.movie }
});
dialogRef.afterClosed().subscribe((result) => {
this.movieRequest.denied = result.denied;
this.movieRequest.deniedReason = result.reason;
});
}
dialogRef.afterClosed().subscribe(result => {
this.movieRequest.denied = result.denied;
this.movieRequest.deniedReason = result.reason;
});
}
public async issue() {
let provider = this.movie.id.toString();
if (this.movie.imdbId) {
provider = this.movie.imdbId;
}
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,
},
});
}
public async issue() {
let provider = this.movie.id.toString();
if (this.movie.imdbId) {
provider = this.movie.imdbId;
}
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 }
});
}
public async approve(is4K: boolean) {
const result = await firstValueFrom(this.requestService.approveMovie({ id: this.movieRequest.id, is4K }));
if (result.result) {
if (is4K) {
this.movie.approved4K = true;
} else {
this.movie.approved = true;
}
this.messageService.send(this.translate.instant('Requests.SuccessfullyApproved'), 'Ok');
} else {
this.messageService.sendRequestEngineResultError(result);
}
}
public async approve(is4K: boolean) {
const result = await firstValueFrom(this.requestService.approveMovie({ id: this.movieRequest.id, is4K }));
if (result.result) {
if (is4K) {
this.movie.approved4K = true;
} else {
this.movie.approved = true;
}
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 }));
if (result.result) {
if (is4K) {
this.movie.available4K = true;
} else {
this.movie.available = true;
}
this.messageService.send(this.translate.instant('Requests.NowAvailable'), 'Ok');
} else {
this.messageService.sendRequestEngineResultError(result);
}
}
public async markAvailable(is4K: boolean) {
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");
} 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) {
if (is4K) {
this.movie.available4K = false;
} else {
this.movie.available = false;
}
this.messageService.send(this.translate.instant('Requests.NowUnavailable'), 'Ok');
} else {
this.messageService.sendRequestEngineResultError(result);
}
}
public setAdvancedOptions(data: IAdvancedData) {
this.advancedOptions = data;
if (data.rootFolderId) {
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;
}
}
public async markUnavailable(is4K: boolean) {
const result = await firstValueFrom(this.requestService.markMovieUnavailable({ id: this.movieRequest.id, is4K }));
if (result.result) {
if (is4K) {
this.movie.available4K = false;
} else {
this.movie.available = false;
}
this.messageService.send(this.translate.instant("Requests.NowUnavailable"), "Ok");
} else {
this.messageService.sendRequestEngineResultError(result);
}
}
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) => {
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();
this.setAdvancedOptions(result);
}
});
}
public setAdvancedOptions(data: IAdvancedData) {
this.advancedOptions = data;
if (data.rootFolderId) {
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;
}
}
public reProcessRequest(is4K: boolean) {
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');
} else {
this.messageService.sendRequestEngineResultError(result);
}
});
}
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 => {
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();
this.setAdvancedOptions(result);
}
});
}
public notify() {
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');
} else {
this.messageService.send(this.translate.instant('Requests.CouldntNotify', { title: this.movie.title }), 'Ok');
}
});
}
public reProcessRequest(is4K: boolean) {
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");
} else {
this.messageService.sendRequestEngineResultError(result);
}
});
}
public unNotify() {
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');
} else {
this.messageService.send(this.translate.instant('Requests.CouldntNotify', { title: this.movie.title }), 'Ok');
}
});
}
public notify() {
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");
} else {
this.messageService.send(this.translate.instant("Requests.CouldntNotify", {title: this.movie.title}), "Ok");
}
});
}
private loadBanner() {
this.imageService.getMovieBanner(this.theMovidDbId.toString()).subscribe((x) => {
if (!this.movie.backdropPath) {
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 + ')',
);
}
});
}
private checkPoster() {
if (this.movie.posterPath == null) {
this.movie.posterPath = '../../../images/default_movie_poster.png';
} 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();
public unNotify() {
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");
} else {
this.messageService.send(this.translate.instant("Requests.CouldntNotify", {title: this.movie.title}), "Ok");
}
});
}
forkJoin([profile, folders]).subscribe((x) => {
const radarrProfiles = x[0] ?? [];
const radarrRootFolders = x[1] ?? [];
private loadBanner() {
this.imageService.getMovieBanner(this.theMovidDbId.toString()).subscribe(x => {
if (!this.movie.backdropPath) {
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 + ")");
}
});
}
private checkPoster() {
if (this.movie.posterPath == null) {
this.movie.posterPath = "../../../images/default_movie_poster.png";
}
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();
const profile = radarrProfiles.filter((p) => {
return p.id === this.movieRequest.qualityOverride;
});
if (profile.length > 0) {
this.movieRequest.qualityOverrideTitle = profile[0].name;
}
forkJoin([profile, folders]).subscribe(x => {
const radarrProfiles = x[0];
const radarrRootFolders = x[1];
const path = radarrRootFolders.filter((folder) => {
return folder.id === this.movieRequest.rootPathOverride;
});
if (path.length > 0) {
this.movieRequest.rootPathOverrideTitle = path[0].path;
}
});
}
const profile = radarrProfiles.filter((p) => {
return p.id === this.movieRequest.qualityOverride;
});
if (profile.length > 0) {
this.movieRequest.qualityOverrideTitle = profile[0].name;
}
const path = radarrRootFolders.filter((folder) => {
return folder.id === this.movieRequest.rootPathOverride;
});
if (path.length > 0) {
this.movieRequest.rootPathOverrideTitle = path[0].path;
}
});
}
private orderCrew(crew: ICrewViewModel[]): ICrewViewModel[] {
return crew.sort((a, b) => {
if (a.job === "Director") {
return -1;
} else if (b.job === "Director") {
return 1;
} else {
return 0;
}
});
}
private orderCrew(crew: ICrewViewModel[]): ICrewViewModel[] {
return crew.sort((a, b) => {
if (a.job === 'Director') {
return -1;
} else if (b.job === 'Director') {
return 1;
} else {
return 0;
}
});
}
}

@ -1,171 +1,240 @@
<div *ngIf="!tv" class="justify-content-md-center top-spacing loading-spinner">
<mat-spinner [color]="'accent'"></mat-spinner>
<mat-spinner [color]="'accent'"></mat-spinner>
</div>
<div *ngIf="tv" class="main-content-container">
<div *ngIf="tv.id === 0; else main">
<div class="small-middle-container no-info">
<h1><i class="far fa-frown-o" aria-hidden="true"></i></h1>
<h3> {{ 'MediaDetails.NotEnoughInfo' | translate }}</h3>
</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>
<div class="social-icons-container">
<social-icons
[homepage]="tv.homepage"
[theMoviedbId]="tv.id"
[hasTrailer]="tv.trailer"
[twitter]="tv.externalIds?.twitterId"
[facebook]="tv.externalIds?.facebookId"
[instagram]="tv.externalIds?.instagramId"
(openTrailer)="openDialog()"
[imdbId]="tv.imdbId"
[isAdmin]="isAdmin"
[canShowAdvanced]="showAdvanced && showRequest"
[type]="requestType"
(onAdvancedOptions)="openAdvancedOptions()"
>
</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]=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">
{{'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">
{{'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">
{{'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.denied && allEpisodesRequestedOrAvailable()" mat-raised-button class="btn-spacing" color="warn" [disabled]>
<i class="fas fa-check"></i>
{{ '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.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>
<!-- end unaired episodes-->
<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>
</div>
</div>
</div>
<div class="row">
<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>
</mat-card-content>
</mat-card>
</div>
<div class="col-12 col-md-10">
<div class="row">
<div class="col-12">
<mat-card class="mat-elevation-z8 spacing-below">
<mat-card-content>
{{tv.overview}}
</mat-card-content>
</mat-card>
</div>
<div class="col-12">
<cast-carousel [cast]="tv.cast"></cast-carousel>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-12 col-md-2">
<!--Just some space yo-->
</div>
<div class="col-12 col-md-10">
<tv-request-grid id="requests-grid" [tvRequest]="tvRequest" [isAdmin]="isAdmin" [tv]="tv"></tv-request-grid>
</div>
<div class="col-12 col-md-2">
<!--Just some space yo-->
</div>
<div class="col-12 col-md-10">
<div class="issuesPanel">
<issues-panel [providerId]="tv.id" [isAdmin]="isAdmin"></issues-panel>
</div>
<mat-accordion id="requests-panel">
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title>
{{'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>
</mat-accordion>
</div>
</div>
</div>
<div class="bottom-page-gap">
</div>
</section>
</div>
</ng-template>
<div *ngIf="tv.id === 0; else main">
<div class="small-middle-container no-info">
<h1><i class="far fa-frown-o" aria-hidden="true"></i></h1>
<h3>{{ 'MediaDetails.NotEnoughInfo' | translate }}</h3>
</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>
<div class="social-icons-container">
<social-icons
[homepage]="tv.homepage"
[theMoviedbId]="tv.id"
[hasTrailer]="tv.trailer"
[twitter]="tv.externalIds?.twitterId"
[facebook]="tv.externalIds?.facebookId"
[instagram]="tv.externalIds?.instagramId"
(openTrailer)="openDialog()"
[imdbId]="tv.imdbId"
[isAdmin]="isAdmin"
[canShowAdvanced]="showAdvanced && showRequest"
[type]="requestType"
(onAdvancedOptions)="openAdvancedOptions()"
>
</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]="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"
>
{{ '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"
>
{{ '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"
>
{{ '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.denied && allEpisodesRequestedOrAvailable()"
mat-raised-button
class="btn-spacing"
color="warn"
[disabled]
>
<i class="fas fa-check"></i>
{{ '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.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>
<!-- end unaired episodes-->
<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>
</div>
</div>
</div>
<div class="row">
<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>
</mat-card-content>
</mat-card>
</div>
<div class="col-12 col-md-10">
<div class="row">
<div class="col-12">
<mat-card class="mat-elevation-z8 spacing-below">
<mat-card-content>
{{ tv.overview }}
</mat-card-content>
</mat-card>
</div>
<div class="col-12">
<cast-carousel [cast]="tv.cast"></cast-carousel>
</div>
</div>
</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">
<issues-panel [providerId]="tv.id" [isAdmin]="isAdmin"></issues-panel>
</div>
<mat-accordion id="requests-panel">
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title>
{{ 'Requests.Title' | translate }}
</mat-panel-title>
</mat-expansion-panel-header>
@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>
</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