remove trailer button and move the imdb trailers to the new carousel

This commit is contained in:
Pas 2025-12-01 18:41:28 -07:00
parent e7e49f81cc
commit 24413a805d
8 changed files with 79 additions and 47 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 KiB

View file

@ -423,6 +423,7 @@
"airs": "Airs",
"endsAt": "Ends at {{time}}",
"trailer": "Trailer",
"trailers": "Trailers",
"similar": "Similar",
"collection": {
"movies": "Movies",

View file

@ -391,6 +391,7 @@ export interface TMDBVideo {
type: string;
official: boolean;
published_at: string;
thumbnail?: string;
}
export interface TMDBVideosResponse {

View file

@ -7,12 +7,14 @@ import { TMDBContentTypes, TMDBVideo } from "@/backend/metadata/types/tmdb";
interface TrailerCarouselProps {
mediaId: string;
mediaType: TMDBContentTypes;
onTrailerClick: (videoKey: string) => void;
imdbData?: any;
onTrailerClick: (videoKey: string, isImdbTrailer?: boolean) => void;
}
export function TrailerCarousel({
mediaId,
mediaType,
imdbData,
onTrailerClick,
}: TrailerCarouselProps) {
const { t } = useTranslation();
@ -36,38 +38,73 @@ export function TrailerCarousel({
loadVideos();
}, [mediaId, mediaType]);
if (videos.length === 0) return null;
// Combine TMDB videos and IMDb trailer
const allTrailers = [
...videos,
...(imdbData?.trailer_url
? [
{
id: "imdb-trailer",
key: imdbData.trailer_url,
name: "IMDb Trailer",
site: "IMDb",
size: 1080,
type: "Trailer",
official: true,
published_at: new Date().toISOString(),
thumbnail: imdbData.trailer_thumbnail,
},
]
: []),
];
if (allTrailers.length === 0) return null;
return (
<div className="space-y-4 pt-8">
<h3 className="text-lg font-semibold text-white/90">
{t("details.trailers", "Trailers")}
{t("details.trailers")}
</h3>
<div className="flex overflow-x-auto scrollbar-none pb-4 gap-4">
{videos.map((video) => (
<button
key={video.id}
type="button"
onClick={() => onTrailerClick(video.key)}
className="flex-shrink-0 hover:opacity-80 transition-opacity rounded-lg overflow-hidden"
>
<div className="relative h-52 w-96 overflow-hidden bg-black/60">
<img
src={`https://img.youtube.com/vi/${video.key}/hqdefault.jpg`}
alt={video.name}
className="h-full w-full object-cover"
loading="lazy"
/>
<div className="absolute inset-0 bg-gradient-to-b from-black/60 via-transparent to-transparent" />
<div className="absolute top-3 left-3 right-3">
<h4 className="text-white font-medium text-sm leading-tight line-clamp-2 text-left">
{video.name}
</h4>
{/* <p className="text-white/80 text-xs mt-1">{video.type}</p> */}
{allTrailers.map((video) => {
const isImdbTrailer = video.id === "imdb-trailer";
let thumbnailUrl: string;
if (isImdbTrailer) {
// Use IMDb thumbnail if available, otherwise use a generic trailer placeholder
thumbnailUrl = video.thumbnail || "/thumbnail-placeholder.png";
} else {
// Use YouTube thumbnail for TMDB videos
thumbnailUrl = `https://img.youtube.com/vi/${video.key}/hqdefault.jpg`;
}
return (
<button
key={video.id}
type="button"
onClick={() => onTrailerClick(video.key, isImdbTrailer)}
className="flex-shrink-0 hover:opacity-80 transition-opacity rounded-lg overflow-hidden"
>
<div className="relative h-52 w-96 overflow-hidden bg-black/60">
<img
src={thumbnailUrl}
alt={video.name}
className="h-full w-full object-cover"
loading="lazy"
/>
<div className="absolute inset-0 bg-gradient-to-b from-black/60 via-transparent to-transparent" />
<div className="absolute top-3 left-3 right-3">
<h4 className="text-white font-medium text-sm leading-tight line-clamp-2 text-left">
{video.name}
</h4>
{/* <p className="text-white/80 text-xs mt-1 text-left">
{isImdbTrailer ? "IMDb Trailer" : video.type}
</p> */}
</div>
</div>
</div>
</button>
))}
</button>
);
})}
</div>
</div>
);

View file

@ -268,7 +268,6 @@ export function DetailsContent({ data, minimal = false }: DetailsContentProps) {
<DetailsBody
data={data}
onPlayClick={handlePlayClick}
onTrailerClick={() => setShowTrailer(true)}
onShareClick={handleShareClick}
showProgress={showProgress}
voteAverage={data.voteAverage}
@ -379,8 +378,16 @@ export function DetailsContent({ data, minimal = false }: DetailsContentProps) {
? TMDBContentTypes.MOVIE
: TMDBContentTypes.TV
}
onTrailerClick={(videoKey) => {
const trailerUrl = `https://www.youtube.com/embed/${videoKey}?autoplay=1&rel=0`;
imdbData={imdbData}
onTrailerClick={(videoKey, isImdbTrailer) => {
let trailerUrl: string;
if (isImdbTrailer) {
// IMDb trailer is already a full URL
trailerUrl = videoKey;
} else {
// TMDB trailer needs to be converted to YouTube embed URL
trailerUrl = `https://www.youtube.com/embed/${videoKey}?autoplay=1&rel=0`;
}
setShowTrailer(true);
setImdbData((prev: any) => ({
...prev,

View file

@ -16,7 +16,6 @@ import { DetailsBodyProps } from "../../types";
export function DetailsBody({
data,
onPlayClick,
onTrailerClick,
onShareClick,
showProgress,
voteAverage,
@ -232,19 +231,6 @@ export function DetailsBody({
</span>
</Button>
<div className="flex items-center gap-1 flex-shrink-0">
{imdbData?.trailer_url && (
<button
type="button"
onClick={onTrailerClick}
className="p-2 opacity-75 transition-opacity duration-300 hover:scale-110 hover:cursor-pointer hover:opacity-95"
title={t("details.trailer")}
>
<IconPatch
icon={Icons.FILM}
className="transition-transform duration-300 hover:scale-110 hover:cursor-pointer"
/>
</button>
)}
<MediaBookmarkButton
media={{
id: data.id?.toString() || "",

View file

@ -110,7 +110,6 @@ export interface EpisodeCarouselProps {
export interface DetailsBodyProps {
data: DetailsContent;
onPlayClick: () => void;
onTrailerClick: () => void;
onShareClick: () => void;
showProgress: ShowProgressResult | null;
voteAverage?: number;

View file

@ -55,6 +55,7 @@ interface IMDbMetadata {
plot?: string;
poster_url?: string;
trailer_url?: string;
trailer_thumbnail?: string;
url?: string;
genre?: string[];
cast?: string[];
@ -251,9 +252,9 @@ export async function scrapeIMDb(
metadata.imdb_rating = aboveTheFold.ratingsSummary?.aggregateRating || null;
metadata.votes = aboveTheFold.ratingsSummary?.voteCount || null;
metadata.poster_url = aboveTheFold.primaryImage?.url || "";
metadata.trailer_url =
aboveTheFold.primaryVideos?.edges?.[0]?.node?.playbackURLs?.[0]?.url ||
"";
const trailerNode = aboveTheFold.primaryVideos?.edges?.[0]?.node;
metadata.trailer_url = trailerNode?.playbackURLs?.[0]?.url || "";
metadata.trailer_thumbnail = trailerNode?.thumbnail?.url || "";
// Extract arrays
metadata.genre = aboveTheFold.genres?.genres?.map((g: any) => g.text) || [];