mirror of
https://github.com/Ombi-app/Ombi.git
synced 2025-08-21 05:43:19 -07:00
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>
This commit is contained in:
parent
2f5d54c5bf
commit
1d53261382
12 changed files with 214 additions and 7 deletions
6
src/.idea/.idea.Ombi/.idea/contentModel.xml
generated
6
src/.idea/.idea.Ombi/.idea/contentModel.xml
generated
|
@ -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,
|
||||||
|
|
|
@ -204,6 +204,12 @@
|
||||||
</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…
Add table
Add a link
Reference in a new issue