Made a start to the TV Detail page

pull/3895/head
TidusJar 5 years ago
parent 80595503c8
commit e7e8cc3e85

@ -4,6 +4,7 @@ export interface ISearchTvResultV2 {
id: number;
title: string; // used in the request
aliases: string[];
background: any;
banner: string;
seriesId: number;
status: string;
@ -20,7 +21,7 @@ export interface ISearchTvResultV2 {
siteRating: number;
trailer: string;
homepage: string;
certifcation: string;
certification: string;
seasonRequests: INewSeasonRequests[];
requestAll: boolean;
approved: boolean;

@ -6,12 +6,14 @@ import { SearchService, RequestService } from "../services";
import {CarouselModule} from 'primeng/carousel';
import { SharedModule } from "../shared/shared.module";
import { MovieDetailsComponent } from "./movie-details.component";
import { MovieDetailsComponent } from "./movie/movie-details.component";
import { TvDetailsComponent } from "./tv/tv-details.component";
import { PipeModule } from "../pipes/pipe.module";
import { MovieDetailsTrailerComponent } from "./movie-details-trailer.component";
import { YoutubeTrailerComponent } from "./youtube-trailer.component";
const routes: Routes = [
{ path: "movie/:movieDbId", component: MovieDetailsComponent },
{ path: "tv/:tvdbId", component: TvDetailsComponent },
];
@NgModule({
imports: [
@ -22,13 +24,14 @@ const routes: Routes = [
],
declarations: [
MovieDetailsComponent,
MovieDetailsTrailerComponent
YoutubeTrailerComponent,
TvDetailsComponent
],
exports: [
RouterModule,
],
entryComponents: [
MovieDetailsTrailerComponent
YoutubeTrailerComponent
],
providers: [
SearchService,

@ -1,15 +0,0 @@
import { Component, Inject } from "@angular/core";
import { MatDialogRef, MAT_DIALOG_DATA } from "@angular/material";
import { ISearchMovieResultV2 } from "../interfaces/ISearchMovieResultV2";
@Component({
selector: "movie-trailer",
templateUrl: "./movie-details-trailer.component.html",
})
export class MovieDetailsTrailerComponent {
constructor(
public dialogRef: MatDialogRef<MovieDetailsTrailerComponent>,
@Inject(MAT_DIALOG_DATA) public data: ISearchMovieResultV2) {}
}

@ -88,7 +88,7 @@
'Common.Request' | translate }}</button>
</ng-template>
</span>
<span><button mat-raised-button class="btn-spacing" color="warn">Deny</button></span>
<span *ngIf="isAdmin"><button mat-raised-button class="btn-spacing" color="warn" (click)="deny()">Deny</button></span>
<span *ngIf="movie.available">
<a *ngIf="movie.plexUrl" mat-raised-button style="text-align: right"
class="btn-spacing btn-greem" href="{{movie.plexUrl}}" target="_blank"><i

@ -1,23 +1,25 @@
import { Component, ViewEncapsulation } from "@angular/core";
import { ImageService, SearchV2Service, RequestService, NotificationService, MessageService } from "../services";
import { Component } from "@angular/core";
import { ImageService, SearchV2Service, RequestService, MessageService } from "../../services";
import { ActivatedRoute } from "@angular/router";
import { DomSanitizer } from "@angular/platform-browser";
import { ISearchMovieResultV2 } from "../interfaces/ISearchMovieResultV2";
import { MatDialog, MatSnackBar } from "@angular/material";
import { MovieDetailsTrailerComponent } from "./movie-details-trailer.component";
import { ISearchMovieResultV2 } from "../../interfaces/ISearchMovieResultV2";
import { MatDialog } from "@angular/material";
import { YoutubeTrailerComponent } from "../youtube-trailer.component";
import { AuthService } from "../../auth/auth.service";
@Component({
templateUrl: "./movie-details.component.html",
styleUrls: ["./movie-details.component.scss"],
styleUrls: ["../media-details.component.scss"],
})
export class MovieDetailsComponent {
public movie: ISearchMovieResultV2;
public isAdmin: boolean;
private theMovidDbId: number;
constructor(private searchService: SearchV2Service, private route: ActivatedRoute,
private sanitizer: DomSanitizer, private imageService: ImageService,
public dialog: MatDialog, private requestService: RequestService,
public messageService: MessageService) {
public messageService: MessageService, private auth: AuthService) {
this.route.params.subscribe((params: any) => {
this.theMovidDbId = params.movieDbId;
this.load();
@ -25,6 +27,8 @@ export class MovieDetailsComponent {
}
public load() {
this.isAdmin = this.auth.hasRole("admin") || this.auth.hasRole("poweruser");
this.searchService.getFullMovieDetails(this.theMovidDbId).subscribe(x => {
this.movie = x;
this.imageService.getMovieBanner(this.theMovidDbId.toString()).subscribe(x => {
@ -46,9 +50,9 @@ export class MovieDetailsComponent {
}
public openDialog() {
this.dialog.open(MovieDetailsTrailerComponent, {
this.dialog.open(YoutubeTrailerComponent, {
width: '560px',
data: this.movie
data: this.movie.videos.results[0].key
});
}
}

@ -0,0 +1,272 @@
<div *ngIf="tv">
<section id="summary-wrapper">
<div class="full-screenshot enabled" [style.background-image]="tv.background"></div>
<div class="shadow-base" [ngClass]="tv.available ? 'available-bottom-border' : ''"></div>
<div class="container summary">
<div class="container">
<div class="row">
<div
class="col-xl-12 col-lg-11 offset-lg-1 col-md-8 offset-md-4 col-sm-7 offset-sm-5 col-6 offset-6">
<h1>{{tv.title}} <span *ngIf="tv.firstAired" class="grey-text align-middle">({{tv.firstAired | amLocal | amDateFormat: 'YYYY'}})</span>
</h1>
<h5 class="tagline grey-text">{{tv.certification}}</h5>
</div>
</div>
</div>
</div>
</section>
<section id="info-wrapper">
<div class="small-middle-container">
<div class="row">
<div class="col-md-2 col-sm-3 hidden-xs">
<div class="sidebar sidebar-poster affixable affix-top" style="width: 173px;">
<div class="poster">
<img class="real" [src]="tv.images.medium" alt="Poster"
style="display: block;">
</div>
<!--Underneith poster-->
<br />
</div>
</div>
<!--Next to poster-->
<div class="col-10 col-lg-3 col-xl-3 media-row">
<a *ngIf="tv.homepage" class="media-icons" h href="{{tv.homepage}}" target="_blank">
<i matTooltip="Homepage" class="fa fa-home fa-2x grow"></i>
</a>
<a *ngIf="tv.id" href="https://www.thetvdb.org/tv/{{tv.id}}" class="media-icons"
target="_blank">
<i matTooltip="The TV DB" class="fa fa-tv fa-2x grow"></i>
</a>
<a *ngIf="tv.trailer" class="media-icons" (click)="openDialog()"><i
matTooltip="Trailer" class="fa fa-youtube-play fa-2x grow"></i></a>
<a *ngIf="tv.imdbId" class="media-icons" href="https://imdb.com/title/{{tv.imdbId}}"
target="_blank">
<i matTooltip="Imdb" class="fa fa-imdb fa-2x grow"></i>
</a>
</div>
<div class="col-12 col-lg-7 col-xl-7 media-row">
<button mat-raised-button class="btn-green btn-spacing" *ngIf="tv.available"> {{
'Common.Available' | translate }}</button>
<span *ngIf="!tv.available">
<span *ngIf="tv.requested || tv.approved; then requestedBtn else notRequestedBtn"></span>
<ng-template #requestedBtn>
<button mat-raised-button class="btn-spacing btn-orange" [disabled]><i
class="fa fa-check"></i>
{{ 'Common.Requested' | translate }}</button>
</ng-template>
<ng-template #notRequestedBtn>
<button mat-raised-button class="btn-spacing" (click)="request()">
<i *ngIf="tv.requestProcessing" class="fa fa-circle-o-notch fa-spin fa-fw"></i> <i
*ngIf="!tv.requestProcessing && !tv.processed" class="fa fa-plus"></i>
<i *ngIf="tv.processed && !tv.requestProcessing" class="fa fa-check"></i> {{
'Common.Request' | translate }}</button>
</ng-template>
</span>
<span *ngIf="tv.available">
<a *ngIf="tv.plexUrl" mat-raised-button style="text-align: right"
class="btn-spacing btn-greem" href="{{tv.plexUrl}}" target="_blank"><i
class="fa fa-eye"></i> {{'Search.ViewOnPlex' |
translate}}</a>
<a *ngIf="tv.embyUrl" mat-raised-button class="btn-green btn-spacing"
href="{{tv.embyUrl}}" target="_blank"><i class="fa fa-eye"></i> {{'Search.ViewOnEmby' |
translate}}</a>
</span>
</div>
</div>
<div class="row">
<div class="col-12 col-md-3">
<!-- <button *ngIf="tv.belongsToCollection" mat-raised-button
class="btn-spacing-below full-width mat-elevation-z8">{{tv.belongsToCollection.name}}</button> -->
<mat-card class="card-full mat-elevation-z8">
<mat-card-content>
<div>
<span><strong>First Aired:</strong>
{{tv.firstAired | date: 'mediumDate'}}</span></div>
<div>
<span *ngIf="tv.rating"><strong>Rating:</strong> {{tv.rating}}/10</span>
</div>
<div>
<span><strong>Status:</strong> {{tv.status}}</span>
</div>
<div>
<span><strong>Runtime:</strong> {{tv.runtime}} Minutes</span>
</div>
<div>
<span *ngIf="tv.status"><strong>Status:</strong> {{tv.status}}</span>
</div>
<div>
<span *ngIf="tv.genre"><strong>Genres:</strong>
<span *ngFor="let genre of tv.genre">
{{genre}} |
</span>
</span>
</div>
</mat-card-content>
</mat-card>
</div>
<div class="col-12 col-md-9">
<mat-card class="card-full mat-elevation-z8">
<mat-card-content>
{{tv.overview}}
</mat-card-content>
</mat-card>
</div>
</div>
<div class="row card-spacer media-row">
<!-- <div class="col-12 col-md-3">
<mat-card class="mat-elevation-z8 keywords-panel">
<mat-card-content>
<div>
<span>
<strong>Keywords/Tags:</strong>
<mat-chip-list>
<mat-chip *ngFor="let keyword of tv.keywords.keywordsValue">
{{keyword.name}}
</mat-chip>
</mat-chip-list>
</span>
</div>
</mat-card-content>
</mat-card>
</div> -->
<div class="col-12 col-md-9">
<mat-card class="card-full mat-elevation-z8">
<mat-card-header>Cast</mat-card-header>
<mat-card-content>
<p-carousel [value]="tv.cast" [numVisible]="5" easing="easeOutStrong">
<ng-template let-item pTemplate="item">
<div class="row justify-content-md-center">
<div class="col-12">
<img class="cast-profile-img" *ngIf="item.character.image.medium"
[src]="item.character.image.medium">
<!-- TODO get profile image default -->
</div>
<div class="col-12">
<span><strong>Character:</strong> {{item.character.name}}</span>
</div>
<div class="col-12">
<span><strong>Actor:</strong> {{item.person.name}}</span>
</div>
</div>
</ng-template>
</p-carousel>
</mat-card-content>
</mat-card>
</div>
</div>
<div class="row card-spacer media-row">
<div class="col-12 col-md-3"></div>
<!-- <div class="col-12 col-md-9">
<mat-accordion>
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title>
Recommendations
</mat-panel-title>
</mat-expansion-panel-header>
<div class="row card-spacer" *ngIf="tv.recommendations.results.length > 0">
<div class="col-md-2" *ngFor="let r of tv.recommendations.results">
<div class="sidebar affixable affix-top preview-poster">
<div class="poster">
<a [routerLink]="'/details/tv/'+r.id">
<img class="real grow" matTooltip="{{r.title}}"
src="https://image.tmdb.org/t/p/w300/{{r.poster_path}}" alt="Poster"
style="display: block;">
</a>
</div>
</div>
</div>
</div>
</mat-expansion-panel>
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title>
Similar
</mat-panel-title>
</mat-expansion-panel-header>
<div class="row card-spacer" *ngIf="tv.similar.results.length > 0">
<div class="col-md-2" *ngFor="let r of tv.similar.results">
<div class="sidebar affixable affix-top preview-poster">
<div class="poster ">
<a [routerLink]="'/details/tv/'+r.id">
<img class="real grow" matTooltip="{{r.title}}"
src="https://image.tmdb.org/t/p/w300/{{r.poster_path}}" alt="Poster"
style="display: block;">
</a>
</div>
</div>
</div>
</div>
</mat-expansion-panel>
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title>
Videos
</mat-panel-title>
</mat-expansion-panel-header>
<div class="row card-spacer" *ngIf="tv.videos.results.length > 0">
<div class="col-md-6" *ngFor="let video of tv.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>
</mat-expansion-panel>
</mat-accordion>
</div> -->
</div>
</div>
<div class="bottom-page-gap">
</div>
</section>
</div>

@ -0,0 +1,53 @@
import { Component } from "@angular/core";
import { ImageService, SearchV2Service, RequestService, MessageService } from "../../services";
import { ActivatedRoute } from "@angular/router";
import { DomSanitizer } from "@angular/platform-browser";
import { ISearchTvResultV2 } from "../../interfaces/ISearchTvResultV2";
import { MatDialog } from "@angular/material";
import { YoutubeTrailerComponent } from "../youtube-trailer.component";
@Component({
templateUrl: "./tv-details.component.html",
styleUrls: ["../media-details.component.scss"],
})
export class TvDetailsComponent {
public tv: ISearchTvResultV2;
private tvdbId: number;
constructor(private searchService: SearchV2Service, private route: ActivatedRoute,
private sanitizer: DomSanitizer, private imageService: ImageService,
public dialog: MatDialog, private requestService: RequestService,
public messageService: MessageService) {
this.route.params.subscribe((params: any) => {
this.tvdbId = params.tvdbId;
this.load();
});
}
public async load() {
this.tv = await this.searchService.getTvInfo(this.tvdbId);
const tvBanner = await this.imageService.getTvBanner(this.tvdbId).toPromise();
this.tv.background = this.sanitizer.bypassSecurityTrustStyle("url(" + tvBanner + ")");
}
public async request() {
// var result = await this.requestService.requestTv({}).toPromise();
// if (result.result) {
// this.movie.requested = true;
// this.messageService.send(result.message, "Ok");
// } else {
// this.messageService.send(result.errorMessage, "Ok");
// }
}
public openDialog() {
debugger;
let trailerLink = this.tv.trailer;
trailerLink = trailerLink.split('?v=')[1];
this.dialog.open(YoutubeTrailerComponent, {
width: '560px',
data: trailerLink
});
}
}

@ -1,2 +1,2 @@
<iframe width="100%" height="315px" [src]="'https://www.youtube.com/embed/' + data.videos.results[0].key | safe" frameborder="0"
<iframe width="100%" height="315px" [src]="'https://www.youtube.com/embed/' + youtubeLink | safe" frameborder="0"
allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

@ -0,0 +1,14 @@
import { Component, Inject } from "@angular/core";
import { MatDialogRef, MAT_DIALOG_DATA } from "@angular/material";
@Component({
selector: "youtube-trailer",
templateUrl: "./youtube-trailer.component.html",
})
export class YoutubeTrailerComponent {
constructor(
public dialogRef: MatDialogRef<YoutubeTrailerComponent>,
@Inject(MAT_DIALOG_DATA) public youtubeLink: string) {}
}

@ -24,6 +24,8 @@
"quotemark": [ true, "double", "avoid-template" ],
"no-console": false,
"no-non-null-assertion": false,
},
"compilerOptions": {
"experimentalDecorators":true,
}
}

@ -57,7 +57,7 @@
<ItemGroup>
<PackageReference Include="AutoMapper" Version="6.1.1" />
<PackageReference Include="CommandLineParser" Version="2.1.1-beta" />
<PackageReference Include="Hangfire.AspNetCore" Version="1.6.21" />
<PackageReference Include="Hangfire.AspNetCore" Version="1.6.22" />
<PackageReference Include="Hangfire.Console" Version="1.3.10" />
<PackageReference Include="Hangfire.MemoryStorage.Core" Version="1.4.0" />
<PackageReference Include="Hangfire.RecurringJobExtensions" Version="1.1.6" />

Loading…
Cancel
Save