mirror of
https://github.com/p-stream/p-stream.git
synced 2026-01-11 20:10:32 +00:00
remove trailer button and move the imdb trailers to the new carousel
This commit is contained in:
parent
e7e49f81cc
commit
24413a805d
8 changed files with 79 additions and 47 deletions
BIN
public/thumbnail-placeholder.png
Normal file
BIN
public/thumbnail-placeholder.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 123 KiB |
|
|
@ -423,6 +423,7 @@
|
|||
"airs": "Airs",
|
||||
"endsAt": "Ends at {{time}}",
|
||||
"trailer": "Trailer",
|
||||
"trailers": "Trailers",
|
||||
"similar": "Similar",
|
||||
"collection": {
|
||||
"movies": "Movies",
|
||||
|
|
|
|||
|
|
@ -391,6 +391,7 @@ export interface TMDBVideo {
|
|||
type: string;
|
||||
official: boolean;
|
||||
published_at: string;
|
||||
thumbnail?: string;
|
||||
}
|
||||
|
||||
export interface TMDBVideosResponse {
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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() || "",
|
||||
|
|
|
|||
|
|
@ -110,7 +110,6 @@ export interface EpisodeCarouselProps {
|
|||
export interface DetailsBodyProps {
|
||||
data: DetailsContent;
|
||||
onPlayClick: () => void;
|
||||
onTrailerClick: () => void;
|
||||
onShareClick: () => void;
|
||||
showProgress: ShowProgressResult | null;
|
||||
voteAverage?: number;
|
||||
|
|
|
|||
|
|
@ -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) || [];
|
||||
|
|
|
|||
Loading…
Reference in a new issue