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", "airs": "Airs",
"endsAt": "Ends at {{time}}", "endsAt": "Ends at {{time}}",
"trailer": "Trailer", "trailer": "Trailer",
"trailers": "Trailers",
"similar": "Similar", "similar": "Similar",
"collection": { "collection": {
"movies": "Movies", "movies": "Movies",

View file

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

View file

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

View file

@ -268,7 +268,6 @@ export function DetailsContent({ data, minimal = false }: DetailsContentProps) {
<DetailsBody <DetailsBody
data={data} data={data}
onPlayClick={handlePlayClick} onPlayClick={handlePlayClick}
onTrailerClick={() => setShowTrailer(true)}
onShareClick={handleShareClick} onShareClick={handleShareClick}
showProgress={showProgress} showProgress={showProgress}
voteAverage={data.voteAverage} voteAverage={data.voteAverage}
@ -379,8 +378,16 @@ export function DetailsContent({ data, minimal = false }: DetailsContentProps) {
? TMDBContentTypes.MOVIE ? TMDBContentTypes.MOVIE
: TMDBContentTypes.TV : TMDBContentTypes.TV
} }
onTrailerClick={(videoKey) => { imdbData={imdbData}
const trailerUrl = `https://www.youtube.com/embed/${videoKey}?autoplay=1&rel=0`; 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); setShowTrailer(true);
setImdbData((prev: any) => ({ setImdbData((prev: any) => ({
...prev, ...prev,

View file

@ -16,7 +16,6 @@ import { DetailsBodyProps } from "../../types";
export function DetailsBody({ export function DetailsBody({
data, data,
onPlayClick, onPlayClick,
onTrailerClick,
onShareClick, onShareClick,
showProgress, showProgress,
voteAverage, voteAverage,
@ -232,19 +231,6 @@ export function DetailsBody({
</span> </span>
</Button> </Button>
<div className="flex items-center gap-1 flex-shrink-0"> <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 <MediaBookmarkButton
media={{ media={{
id: data.id?.toString() || "", id: data.id?.toString() || "",

View file

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

View file

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