feat: add crew on movie page (#4722)

* add crew on movie page

* order by director, add default image and fix click

Co-authored-by: tidusjar <tidusjar@gmail.com>
fix-stats-controller
Hadrien 2 years ago committed by GitHub
parent 2f5d54c5bf
commit 1d53261382
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -268,6 +268,12 @@
<e p="cast-carousel.component.scss" t="Include" /> <e p="cast-carousel.component.scss" t="Include" />
<e p="cast-carousel.component.ts" t="Include" /> <e p="cast-carousel.component.ts" t="Include" />
</e> </e>
<e p="crew-carousel" t="Include">
<e p="crew-carousel.component.html" t="Include" />
<e p="crew-carousel.component.scss" t="Include" />
<e p="crew-carousel.component.ts" t="Include" />
</e>
<e p="deny-dialog" t="Include"> <e p="deny-dialog" t="Include">
<e p="deny-dialog.component.html" t="Include" /> <e p="deny-dialog.component.html" t="Include" />
<e p="deny-dialog.component.ts" t="Include" /> <e p="deny-dialog.component.ts" t="Include" />

@ -1,7 +1,7 @@
import { Component, OnInit } from "@angular/core"; import { Component, OnInit } from "@angular/core";
import { ActivatedRoute } from "@angular/router"; import { ActivatedRoute } from "@angular/router";
import { SearchV2Service } from "../../../services"; import { SearchV2Service } from "../../../services";
import { IActorCredits, IActorCast } from "../../../interfaces/ISearchTvResultV2"; import { IActorCredits, IActorCast, IActorCrew } from "../../../interfaces/ISearchTvResultV2";
import { IDiscoverCardResult } from "../../interfaces"; import { IDiscoverCardResult } from "../../interfaces";
import { RequestType } from "../../../interfaces"; import { RequestType } from "../../../interfaces";
import { AuthService } from "../../../auth/auth.service"; import { AuthService } from "../../../auth/auth.service";
@ -38,13 +38,13 @@ export class DiscoverActorComponent implements OnInit {
this.searchService.getMoviesByActor(this.actorId), this.searchService.getMoviesByActor(this.actorId),
this.searchService.getTvByActor(this.actorId) this.searchService.getTvByActor(this.actorId)
]).subscribe(([movie, tv]) => { ]).subscribe(([movie, tv]) => {
this.pushDiscoverResults(movie.cast, RequestType.movie); this.pushDiscoverResults(movie.crew, movie.cast, RequestType.movie);
this.pushDiscoverResults(tv.cast, RequestType.tvShow); this.pushDiscoverResults(tv.crew, tv.cast, RequestType.tvShow);
this.finishLoading(); this.finishLoading();
}); });
} }
pushDiscoverResults(cast: IActorCast[], type: RequestType) { pushDiscoverResults(crew: IActorCrew[], cast: IActorCast[], type: RequestType) {
cast.forEach(m => { cast.forEach(m => {
this.discoverResults.push({ this.discoverResults.push({
available: false, available: false,
@ -62,6 +62,23 @@ export class DiscoverActorComponent implements OnInit {
background: "" background: ""
}); });
}); });
crew.forEach(m => {
this.discoverResults.push({
available: false,
posterPath: m.poster_path ? `https://image.tmdb.org/t/p/w300/${m.poster_path}` : "../../../images/default_movie_poster.png",
requested: false,
title: m.title,
type: type,
id: m.id,
url: null,
rating: 0,
overview: m.overview,
approved: false,
imdbid: "",
denied: false,
background: ""
});
});
} }
private loading() { private loading() {

@ -4,6 +4,7 @@ import { ArtistDetailsComponent } from "./artist/artist-details.component";
import { ArtistInformationPanel } from "./artist/panels/artist-information-panel/artist-information-panel.component"; import { ArtistInformationPanel } from "./artist/panels/artist-information-panel/artist-information-panel.component";
import { ArtistReleasePanel } from "./artist/panels/artist-release-panel/artist-release-panel.component"; import { ArtistReleasePanel } from "./artist/panels/artist-release-panel/artist-release-panel.component";
import { CastCarouselComponent } from "./shared/cast-carousel/cast-carousel.component"; import { CastCarouselComponent } from "./shared/cast-carousel/cast-carousel.component";
import { CrewCarouselComponent } from "./shared/crew-carousel/crew-carousel.component";
import { DenyDialogComponent } from "./shared/deny-dialog/deny-dialog.component"; import { DenyDialogComponent } from "./shared/deny-dialog/deny-dialog.component";
import { IssuesPanelComponent } from "./shared/issues-panel/issues-panel.component"; import { IssuesPanelComponent } from "./shared/issues-panel/issues-panel.component";
import { MediaPosterComponent } from "./shared/media-poster/media-poster.component"; import { MediaPosterComponent } from "./shared/media-poster/media-poster.component";
@ -32,6 +33,7 @@ export const components: any[] = [
SocialIconsComponent, SocialIconsComponent,
MediaPosterComponent, MediaPosterComponent,
CastCarouselComponent, CastCarouselComponent,
CrewCarouselComponent,
DenyDialogComponent, DenyDialogComponent,
TvRequestsPanelComponent, TvRequestsPanelComponent,
MovieAdvancedOptionsComponent, MovieAdvancedOptionsComponent,

@ -197,13 +197,19 @@
</mat-card> </mat-card>
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="col-12"> <div class="col-12">
<cast-carousel [cast]="movie.credits.cast"></cast-carousel> <cast-carousel [cast]="movie.credits.cast"></cast-carousel>
</div> </div>
</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"> <div class="col-md-6" *ngFor="let video of movie.videos?.results">

@ -2,7 +2,7 @@ import { Component, OnInit, ViewEncapsulation } from "@angular/core";
import { ImageService, SearchV2Service, RequestService, MessageService, RadarrService, SettingsStateService } from "../../../services"; import { ImageService, SearchV2Service, RequestService, MessageService, RadarrService, SettingsStateService } from "../../../services";
import { ActivatedRoute, Router } from "@angular/router"; import { ActivatedRoute, Router } from "@angular/router";
import { DomSanitizer } from "@angular/platform-browser"; import { DomSanitizer } from "@angular/platform-browser";
import { ISearchMovieResultV2 } from "../../../interfaces/ISearchMovieResultV2"; import { ICrewViewModel, ISearchMovieResultV2 } from "../../../interfaces/ISearchMovieResultV2";
import { MatDialog } from "@angular/material/dialog"; import { MatDialog } from "@angular/material/dialog";
import { YoutubeTrailerComponent } from "../shared/youtube-trailer.component"; import { YoutubeTrailerComponent } from "../shared/youtube-trailer.component";
import { AuthService } from "../../../auth/auth.service"; import { AuthService } from "../../../auth/auth.service";
@ -82,6 +82,7 @@ export class MovieDetailsComponent implements OnInit{
this.searchService.getMovieByImdbId(this.imdbId).subscribe(async x => { this.searchService.getMovieByImdbId(this.imdbId).subscribe(async x => {
this.movie = x; this.movie = x;
this.checkPoster(); this.checkPoster();
this.movie.credits.crew = this.orderCrew(this.movie.credits.crew);
if (this.movie.requestId > 0) { if (this.movie.requestId > 0) {
// Load up this request // Load up this request
this.hasRequest = true; this.hasRequest = true;
@ -93,6 +94,7 @@ export class MovieDetailsComponent implements OnInit{
this.searchService.getFullMovieDetails(this.theMovidDbId).subscribe(async x => { this.searchService.getFullMovieDetails(this.theMovidDbId).subscribe(async x => {
this.movie = x; this.movie = x;
this.checkPoster(); this.checkPoster();
this.movie.credits.crew = this.orderCrew(this.movie.credits.crew);
if (this.movie.requestId > 0) { if (this.movie.requestId > 0) {
// Load up this request // Load up this request
this.hasRequest = true; this.hasRequest = true;
@ -319,4 +321,16 @@ export class MovieDetailsComponent implements OnInit{
}); });
} }
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;
}
});
}
} }

@ -11,7 +11,7 @@
<a *ngIf="item.profile_path" [routerLink]="'/discover/actor/' + item.id"> <a *ngIf="item.profile_path" [routerLink]="'/discover/actor/' + item.id">
<ombi-image class="cast-profile-img" src="https://image.tmdb.org/t/p/w300{{item.profile_path}}"></ombi-image> <ombi-image class="cast-profile-img" src="https://image.tmdb.org/t/p/w300{{item.profile_path}}"></ombi-image>
</a> </a>
<!-- TODO get profile image default --> <i *ngIf="!item.image && !item.profile_path" class="fa-solid fa-user-large fa-2xl crew-profile-img" aria-hidden="true"></i>
</div> </div>
<div class="col-12"> <div class="col-12">

@ -0,0 +1,32 @@
<mat-card class="mat-elevation-z8 spacing-below">
<mat-card-header>{{'MediaDetails.Crews.CrewTitle' | translate}}</mat-card-header>
<mat-card-content>
<p-carousel [value]="crew" easing="easeOutStrong" [responsiveOptions]="responsiveOptions" [numVisible]="5" >
<ng-template let-item pTemplate="item">
<div class="row justify-content-md-center mat-card mat-card-flat carousel-item">
<div [routerLink]="'/discover/actor/' + item.id" class="bottom-space link">
<a *ngIf="item.image">
<img class="crew-profile-img" src="https://image.tmdb.org/t/p/w300{{item.image}}">
</a>
<a *ngIf="item.profile_path">
<img class="crew-profile-img" src="https://image.tmdb.org/t/p/w300{{item.profile_path}}">
</a>
<i *ngIf="!item.image && !item.profile_path" class="fa-solid fa-user-large fa-2xl crew-profile-img" aria-hidden="true"></i>
</div>
<div class="col-12">
<span *ngIf="item.name"><strong>{{item.name}}</strong></span>
<span *ngIf="item.person"><strong>{{item.person}}</strong></span>
</div>
<div class="col-12">
<span *ngIf="item.job"><small>{{item.job}}</small></span>
<span *ngIf="item.department"><small>{{item.position}}</small></span>
<span *ngIf="item.title"><small>{{item.title}}</small></span>
<span *ngIf="item.overview"><small>{{item.overview}}</small></span>
</div>
</div>
</ng-template>
</p-carousel>
</mat-card-content>
</mat-card>

@ -0,0 +1,87 @@
@import "~@angular/material/theming";
@import "~styles/variables.scss";
::ng-deep body .ui-carousel .ui-carousel-content .ui-carousel-prev,
body .ui-carousel .ui-carousel-content .ui-carousel-next {
background-color: #ffffff;
border: solid 1px rgba(178, 193, 205, 0.64);
-moz-border-radius: 50%;
-webkit-border-radius: 50%;
border-radius: 50%;
margin: 0.2em;
color: #333333;
-moz-transition: color 0.2s;
-o-transition: color 0.2s;
-webkit-transition: color 0.2s;
transition: color 0.2s;
}
::ng-deep body .ui-carousel .ui-carousel-content .ui-carousel-prev:not(.ui-state-disabled):hover,
body .ui-carousel .ui-carousel-content .ui-carousel-next:not(.ui-state-disabled):hover {
background-color: #ffffff;
color: #000;
border-color: solid 1px rgba(178, 193, 205, 0.64);
}
::ng-deep body .ui-carousel .ui-carousel-dots-container .ui-carousel-dot-item>.ui-button {
border-color: transparent;
background-color: transparent;
}
::ng-deep body .ui-carousel .ui-carousel-dots-container .ui-carousel-dot-item .ui-carousel-dot-icon {
width: 20px;
height: 6px;
background-color: rgba(255, 255, 255, 0.44);
margin: 0 0.2em;
}
::ng-deep body .ui-carousel .ui-carousel-dots-container .ui-carousel-dot-item .ui-carousel-dot-icon::before {
content: " ";
}
::ng-deep body .ui-carousel .ui-carousel-dots-container .ui-carousel-dot-item.ui-state-highlight .ui-carousel-dot-icon {
background-color: #FFF;
}
.carousel-item {
text-align: center;
}
::ng-deep .ui-carousel-next {
background-color: #ffffff;
border: solid 1px rgba(178, 193, 205, 0.64);
border-radius: 50%;
margin: 0.2em;
color: #333333;
transition: color 0.2s;
}
::ng-deep .ui-carousel-content button:focus {
outline: none;
}
.bottom-space {
padding-bottom: 10px;
}
@media (min-width: 979px) {
.crew-profile-img {
border-radius: 100%;
width: 200px;
max-height: 200px;
object-fit: cover;
}
}
@media (max-width: 978px) {
.crew-profile-img {
border-radius: 100%;
width: 100px;
max-height: 100px;
object-fit: cover;
}
}
.link {
cursor: pointer;
}

@ -0,0 +1,32 @@
import { Component, Input } from "@angular/core";
@Component({
selector: "crew-carousel",
templateUrl: "./crew-carousel.component.html",
styleUrls: ["./crew-carousel.component.scss"]
})
export class CrewCarouselComponent {
constructor() {
this.responsiveOptions = [
{
breakpoint: '1024px',
numVisible: 5,
numScroll: 5
},
{
breakpoint: '768px',
numVisible: 3,
numScroll: 3
},
{
breakpoint: '560px',
numVisible: 1,
numScroll: 1
}
];
}
@Input() crew: any[];
public responsiveOptions: any[];
}

@ -181,6 +181,11 @@
} }
.crew-profile-img {
border-radius: 100%;
width: 170px;
}
.small-middle-container { .small-middle-container {
margin: auto; margin: auto;

@ -364,6 +364,9 @@
"Casts": { "Casts": {
"CastTitle": "Cast" "CastTitle": "Cast"
}, },
"Crews": {
"CrewTitle": "Crew"
},
"EpisodeSelector": { "EpisodeSelector": {
"AllSeasonsTooltip": "This will request every season for this show", "AllSeasonsTooltip": "This will request every season for this show",
"FirstSeasonTooltip": "This will only request the First Season for this show", "FirstSeasonTooltip": "This will only request the First Season for this show",

@ -364,6 +364,9 @@
"Casts": { "Casts": {
"CastTitle": "Casting" "CastTitle": "Casting"
}, },
"Crews": {
"CrewTitle": "Equipe"
},
"EpisodeSelector": { "EpisodeSelector": {
"AllSeasonsTooltip": "Cette action demandera toutes les saisons de cette série", "AllSeasonsTooltip": "Cette action demandera toutes les saisons de cette série",
"FirstSeasonTooltip": "Cette action ne demandera que la Première Saison de cette série", "FirstSeasonTooltip": "Cette action ne demandera que la Première Saison de cette série",

Loading…
Cancel
Save