mirror of https://github.com/Ombi-app/Ombi
fix: Fixes default image for recently requested items. (#4767)
parent
33d6704789
commit
2e6f35f89a
@ -1,92 +1,154 @@
|
||||
import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from "@angular/core";
|
||||
import {
|
||||
Component,
|
||||
EventEmitter,
|
||||
Input,
|
||||
OnDestroy,
|
||||
OnInit,
|
||||
Output,
|
||||
} from "@angular/core";
|
||||
import { IRecentlyRequested, RequestType } from "../../interfaces";
|
||||
import { ImageService } from "app/services";
|
||||
import { Subject, takeUntil } from "rxjs";
|
||||
import { DomSanitizer, SafeStyle } from "@angular/platform-browser";
|
||||
|
||||
@Component({
|
||||
standalone: false,
|
||||
selector: 'ombi-detailed-card',
|
||||
templateUrl: './detailed-card.component.html',
|
||||
styleUrls: ['./detailed-card.component.scss']
|
||||
})
|
||||
export class DetailedCardComponent implements OnInit, OnDestroy {
|
||||
@Input() public request: IRecentlyRequested;
|
||||
@Input() public isAdmin: boolean = false;
|
||||
@Output() public onClick: EventEmitter<void> = new EventEmitter<void>();
|
||||
@Output() public onApprove: EventEmitter<void> = new EventEmitter<void>();
|
||||
|
||||
public RequestType = RequestType;
|
||||
public loading: false;
|
||||
|
||||
private $imageSub = new Subject<void>();
|
||||
|
||||
public background: SafeStyle;
|
||||
|
||||
constructor(private imageService: ImageService, private sanitizer: DomSanitizer) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
if (!this.request.posterPath) {
|
||||
this.loadImages();
|
||||
} else {
|
||||
this.request.posterPath = `https://image.tmdb.org/t/p/w300${this.request.posterPath}`;
|
||||
this.background = this.sanitizer.bypassSecurityTrustStyle("linear-gradient(rgba(0,0,0,.5), rgba(0,0,0,.5)), url(https://image.tmdb.org/t/p/w300" + this.request.background + ")");
|
||||
}
|
||||
}
|
||||
|
||||
public getStatus(request: IRecentlyRequested) {
|
||||
if (request.available) {
|
||||
return "Common.Available";
|
||||
}
|
||||
if (request.tvPartiallyAvailable) {
|
||||
return "Common.PartiallyAvailable";
|
||||
}
|
||||
if (request.approved) {
|
||||
return "Common.Approved";
|
||||
} else {
|
||||
return "Common.Pending";
|
||||
}
|
||||
}
|
||||
|
||||
public click() {
|
||||
this.onClick.emit();
|
||||
}
|
||||
|
||||
public approve() {
|
||||
this.onApprove.emit();
|
||||
}
|
||||
|
||||
public getClass(request: IRecentlyRequested) {
|
||||
if (request.available || request.tvPartiallyAvailable) {
|
||||
return "success";
|
||||
}
|
||||
if (request.approved) {
|
||||
return "primary";
|
||||
} else {
|
||||
return "info";
|
||||
}
|
||||
}
|
||||
|
||||
public ngOnDestroy() {
|
||||
this.$imageSub.next();
|
||||
this.$imageSub.complete();
|
||||
standalone: false,
|
||||
selector: "ombi-detailed-card",
|
||||
templateUrl: "./detailed-card.component.html",
|
||||
styleUrls: ["./detailed-card.component.scss"],
|
||||
})
|
||||
export class DetailedCardComponent implements OnInit, OnDestroy {
|
||||
@Input() public request: IRecentlyRequested;
|
||||
@Input() public isAdmin: boolean = false;
|
||||
@Output() public onClick: EventEmitter<void> = new EventEmitter<void>();
|
||||
@Output() public onApprove: EventEmitter<void> = new EventEmitter<void>();
|
||||
|
||||
public RequestType = RequestType;
|
||||
public loading: false;
|
||||
|
||||
private $imageSub = new Subject<void>();
|
||||
|
||||
public background: SafeStyle;
|
||||
|
||||
constructor(
|
||||
private imageService: ImageService,
|
||||
private sanitizer: DomSanitizer
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.loadPosterPath();
|
||||
this.loadBackgroundPath();
|
||||
}
|
||||
|
||||
public getStatus(request: IRecentlyRequested) {
|
||||
if (request.available) {
|
||||
return "Common.Available";
|
||||
}
|
||||
if (request.tvPartiallyAvailable) {
|
||||
return "Common.PartiallyAvailable";
|
||||
}
|
||||
if (request.approved) {
|
||||
return "Common.Approved";
|
||||
} else {
|
||||
return "Common.Pending";
|
||||
}
|
||||
}
|
||||
|
||||
public click() {
|
||||
this.onClick.emit();
|
||||
}
|
||||
|
||||
public approve() {
|
||||
this.onApprove.emit();
|
||||
}
|
||||
|
||||
public getClass(request: IRecentlyRequested) {
|
||||
if (request.available || request.tvPartiallyAvailable) {
|
||||
return "success";
|
||||
}
|
||||
if (request.approved) {
|
||||
return "primary";
|
||||
} else {
|
||||
return "info";
|
||||
}
|
||||
}
|
||||
|
||||
public ngOnDestroy() {
|
||||
this.$imageSub.next();
|
||||
this.$imageSub.complete();
|
||||
}
|
||||
|
||||
private loadPosterPath() {
|
||||
if (this.request.posterPath) {
|
||||
this.setPosterPath(this.request.posterPath);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (this.request.type) {
|
||||
case RequestType.movie:
|
||||
this.imageService
|
||||
.getMoviePoster(this.request.mediaId)
|
||||
.pipe(takeUntil(this.$imageSub))
|
||||
.subscribe((x) => this.setPosterPath(x));
|
||||
break;
|
||||
case RequestType.tvShow:
|
||||
this.imageService
|
||||
.getTmdbTvPoster(Number(this.request.mediaId))
|
||||
.pipe(takeUntil(this.$imageSub))
|
||||
.subscribe((x) => this.setPosterPath(x));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private loadImages() {
|
||||
switch (this.request.type) {
|
||||
case RequestType.movie:
|
||||
this.imageService.getMoviePoster(this.request.mediaId).pipe(takeUntil(this.$imageSub)).subscribe(x => this.request.posterPath = x);
|
||||
this.imageService.getMovieBackground(this.request.mediaId).pipe(takeUntil(this.$imageSub)).subscribe(x => {
|
||||
this.background = this.sanitizer.bypassSecurityTrustStyle("linear-gradient(rgba(0,0,0,.5), rgba(0,0,0,.5)), url(" + x + ")");
|
||||
});
|
||||
break;
|
||||
case RequestType.tvShow:
|
||||
this.imageService.getTmdbTvPoster(Number(this.request.mediaId)).pipe(takeUntil(this.$imageSub)).subscribe(x => this.request.posterPath = `https://image.tmdb.org/t/p/w300${x}`);
|
||||
this.imageService.getTmdbTvBackground(Number(this.request.mediaId)).pipe(takeUntil(this.$imageSub)).subscribe(x => {
|
||||
this.background = this.sanitizer.bypassSecurityTrustStyle("linear-gradient(rgba(0,0,0,.5), rgba(0,0,0,.5)), url(https://image.tmdb.org/t/p/w300" + x + ")");
|
||||
});
|
||||
break;
|
||||
}
|
||||
private setPosterPath(posterPath: string) {
|
||||
if (!posterPath) {
|
||||
this.request.posterPath = null;
|
||||
} else {
|
||||
this.request.posterPath = this.getImageUrl(posterPath);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
private loadBackgroundPath() {
|
||||
if (this.request.background) {
|
||||
this.setBackgroundStyle(this.request.background);
|
||||
return;
|
||||
}
|
||||
|
||||
// Set background style while image path is loading.
|
||||
this.setBackgroundStyle(null);
|
||||
switch (this.request.type) {
|
||||
case RequestType.movie:
|
||||
this.imageService
|
||||
.getMovieBackground(this.request.mediaId)
|
||||
.pipe(takeUntil(this.$imageSub))
|
||||
.subscribe((x) => this.setBackgroundStyle(x));
|
||||
break;
|
||||
case RequestType.tvShow:
|
||||
this.imageService
|
||||
.getTmdbTvBackground(Number(this.request.mediaId))
|
||||
.pipe(takeUntil(this.$imageSub))
|
||||
.subscribe((x) => this.setBackgroundStyle(x));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private setBackgroundStyle(backgroundPath: string) {
|
||||
if (backgroundPath) {
|
||||
this.background = this.sanitizer.bypassSecurityTrustStyle(
|
||||
`linear-gradient(rgba(0,0,0,.5), rgba(0,0,0,.5)), url(${this.getImageUrl(
|
||||
backgroundPath
|
||||
)})`
|
||||
);
|
||||
} else {
|
||||
this.background = "linear-gradient(rgba(0,0,0,.5), rgba(0,0,0,.5))";
|
||||
}
|
||||
}
|
||||
|
||||
private getImageUrl(path: string) {
|
||||
if (new RegExp("^(http|https)://").test(path)) {
|
||||
return path;
|
||||
} else {
|
||||
return `https://image.tmdb.org/t/p/w300${path}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,63 +1,77 @@
|
||||
import { OmbiCommonModules } from "../modules";
|
||||
import { ChangeDetectionStrategy, Component, ElementRef, Inject, Input, ViewEncapsulation } from "@angular/core";
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
Inject,
|
||||
Input,
|
||||
ViewEncapsulation,
|
||||
} from "@angular/core";
|
||||
import { RequestType } from "../../interfaces";
|
||||
import { APP_BASE_HREF } from "@angular/common";
|
||||
|
||||
@Component({
|
||||
standalone: true,
|
||||
selector: 'ombi-image',
|
||||
imports: [...OmbiCommonModules],
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
templateUrl: './image.component.html',
|
||||
})
|
||||
export class ImageComponent {
|
||||
|
||||
@Input() public src: string;
|
||||
@Input() public type: RequestType;
|
||||
|
||||
// Attributes from the parent
|
||||
@Input() public class: string;
|
||||
@Input() public id: string;
|
||||
@Input() public alt: string;
|
||||
@Input() public style: string;
|
||||
|
||||
public baseUrl: string = "";
|
||||
|
||||
public defaultTv = "/images/default_tv_poster.png";
|
||||
private defaultMovie = "/images/default_movie_poster.png";
|
||||
private defaultMusic = "i/mages/default-music-placeholder.png";
|
||||
|
||||
private alreadyErrored = false;
|
||||
|
||||
constructor (@Inject(APP_BASE_HREF) public href: string) {
|
||||
if (this.href.length > 1) {
|
||||
this.baseUrl = this.href;
|
||||
}
|
||||
standalone: true,
|
||||
selector: "ombi-image",
|
||||
imports: [...OmbiCommonModules],
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
templateUrl: "./image.component.html",
|
||||
})
|
||||
export class ImageComponent {
|
||||
@Input() public src: string;
|
||||
@Input() public type: RequestType;
|
||||
|
||||
// Attributes from the parent
|
||||
@Input() public class: string;
|
||||
@Input() public id: string;
|
||||
@Input() public alt: string;
|
||||
@Input() public style: string;
|
||||
|
||||
private baseUrl: string = "";
|
||||
|
||||
private defaultTv = "/images/default_tv_poster.png";
|
||||
private defaultMovie = "/images/default_movie_poster.png";
|
||||
private defaultMusic = "/images/default-music-placeholder.png";
|
||||
|
||||
private maxRetries = 1;
|
||||
private retriesPerformed = 0;
|
||||
|
||||
constructor(@Inject(APP_BASE_HREF) private href: string) {
|
||||
if (this.href.length > 1) {
|
||||
this.baseUrl = this.href;
|
||||
}
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
if (!this.src) {
|
||||
// Prevent unnecessary error handling when src is not specified.
|
||||
this.src = this.getPlaceholderImage();
|
||||
}
|
||||
}
|
||||
|
||||
public onError(event: any) {
|
||||
event.target.src = this.getPlaceholderImage();
|
||||
|
||||
if (!this.src || this.retriesPerformed === this.maxRetries) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Retry the original image.
|
||||
this.retriesPerformed++;
|
||||
const timeout = setTimeout(() => {
|
||||
clearTimeout(timeout);
|
||||
event.target.src = this.src;
|
||||
}, Math.floor(Math.random() * (7000 - 1000 + 1)) + 1000);
|
||||
}
|
||||
|
||||
public onError(event: any) {
|
||||
if (this.alreadyErrored) {
|
||||
return;
|
||||
}
|
||||
// set to a placeholder
|
||||
switch(this.type) {
|
||||
case RequestType.movie:
|
||||
event.target.src = this.baseUrl + this.defaultMovie;
|
||||
break;
|
||||
case RequestType.tvShow:
|
||||
event.target.src = this.baseUrl + this.defaultTv;
|
||||
break;
|
||||
case RequestType.album:
|
||||
event.target.src = this.baseUrl + this.defaultMusic;
|
||||
break;
|
||||
}
|
||||
|
||||
this.alreadyErrored = true;
|
||||
// Retry the original image
|
||||
const timeout = setTimeout(() => {
|
||||
clearTimeout(timeout);
|
||||
event.target.src = this.src;
|
||||
}, Math.floor(Math.random() * (7000 - 1000 + 1)) + 1000);
|
||||
private getPlaceholderImage() {
|
||||
switch (this.type) {
|
||||
case RequestType.movie:
|
||||
return this.baseUrl + this.defaultMovie;
|
||||
case RequestType.tvShow:
|
||||
return this.baseUrl + this.defaultTv;
|
||||
case RequestType.album:
|
||||
return this.baseUrl + this.defaultMusic;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in new issue