From 22b7a3efb25b7ab2e59751cb3a00a2c61b3afd40 Mon Sep 17 00:00:00 2001 From: SergeantPanda Date: Tue, 5 Aug 2025 16:43:51 -0500 Subject: [PATCH] Fix youtube trailers for series. --- frontend/src/pages/VODs.jsx | 752 +++++++++++++++-------------- frontend/src/store/useVODStore.jsx | 2 +- 2 files changed, 399 insertions(+), 355 deletions(-) diff --git a/frontend/src/pages/VODs.jsx b/frontend/src/pages/VODs.jsx index 912116ff..a8ac6b18 100644 --- a/frontend/src/pages/VODs.jsx +++ b/frontend/src/pages/VODs.jsx @@ -223,6 +223,8 @@ const SeriesModal = ({ series, opened, onClose }) => { const [loadingDetails, setLoadingDetails] = useState(false); const [activeTab, setActiveTab] = useState(null); const [expandedEpisode, setExpandedEpisode] = useState(null); + const [trailerModalOpened, setTrailerModalOpened] = useState(false); + const [trailerUrl, setTrailerUrl] = useState(''); useEffect(() => { if (opened && series) { @@ -329,385 +331,427 @@ const SeriesModal = ({ series, opened, onClose }) => { setExpandedEpisode(expandedEpisode === episode.id ? null : episode.id); }; + // Helper to get embeddable YouTube URL + const getEmbedUrl = (url) => { + if (!url) return ''; + // Accepts full YouTube URLs or just IDs + const match = url.match(/(?:youtube\.com\/watch\?v=|youtu\.be\/)([\w-]+)/); + const videoId = match ? match[1] : url; + return `https://www.youtube.com/embed/${videoId}`; + }; + if (!series) return null; // Use detailed data if available, otherwise use basic series data const displaySeries = detailedSeries || series; return ( - - - {/* Backdrop image as background */} - {displaySeries.backdrop_path && displaySeries.backdrop_path.length > 0 && ( - <> - {`${displaySeries.name} - {/* Overlay for readability */} - - - )} + <> + + + {/* Backdrop image as background */} + {displaySeries.backdrop_path && displaySeries.backdrop_path.length > 0 && ( + <> + {`${displaySeries.name} + {/* Overlay for readability */} + + + )} - {/* Modal content above backdrop */} - - - {loadingDetails && ( - - - Loading series details and episodes... - - )} - - {/* Series poster and basic info */} - - {(displaySeries.series_image || displaySeries.logo?.url) ? ( - - {displaySeries.name} - - ) : ( - - - + {/* Modal content above backdrop */} + + + {loadingDetails && ( + + + Loading series details and episodes... + )} - - {displaySeries.name} - - {/* Original name if different */} - {displaySeries.o_name && displaySeries.o_name !== displaySeries.name && ( - - Original: {displaySeries.o_name} - - )} - - - {displaySeries.year && {displaySeries.year}} - {displaySeries.rating && {displaySeries.rating}} - {displaySeries.age && {displaySeries.age}} - Series - {displaySeries.episode_count && ( - {displaySeries.episode_count} episodes - )} - - - {/* Release date */} - {displaySeries.release_date && ( - - Release Date: {displaySeries.release_date} - - )} - - {displaySeries.genre && ( - - Genre: {displaySeries.genre} - - )} - - {displaySeries.director && ( - - Director: {displaySeries.director} - - )} - - {displaySeries.actors && ( - - Cast: {displaySeries.actors} - - )} - - {displaySeries.country && ( - - Country: {displaySeries.country} - - )} - - {/* Description */} - {displaySeries.description && ( - - Description - - {displaySeries.description} - + {/* Series poster and basic info */} + + {(displaySeries.series_image || displaySeries.logo?.url) ? ( + + {displaySeries.name} + + ) : ( + + )} - {/* Watch Trailer button if available */} - {displaySeries.youtube_trailer && ( - - )} - - + + {displaySeries.name} - {/* Provider Information */} - {displaySeries.m3u_account && ( - - IPTV Provider - - - {displaySeries.m3u_account.name || displaySeries.m3u_account} - - {displaySeries.m3u_account.account_type && ( - - {displaySeries.m3u_account.account_type === 'XC' ? 'Xtream Codes' : 'Standard M3U'} - + {/* Original name if different */} + {displaySeries.o_name && displaySeries.o_name !== displaySeries.name && ( + + Original: {displaySeries.o_name} + )} - - - )} - + + {displaySeries.year && {displaySeries.year}} + {displaySeries.rating && {displaySeries.rating}} + {displaySeries.age && {displaySeries.age}} + Series + {displaySeries.episode_count && ( + {displaySeries.episode_count} episodes + )} + - Episodes + {/* Release date */} + {displaySeries.release_date && ( + + Release Date: {displaySeries.release_date} + + )} - {loadingDetails ? ( - - + {displaySeries.genre && ( + + Genre: {displaySeries.genre} + + )} + + {displaySeries.director && ( + + Director: {displaySeries.director} + + )} + + {displaySeries.actors && ( + + Cast: {displaySeries.actors} + + )} + + {displaySeries.country && ( + + Country: {displaySeries.country} + + )} + + {/* Description */} + {displaySeries.description && ( + + Description + + {displaySeries.description} + + + )} + + {/* Watch Trailer button if available */} + {displaySeries.youtube_trailer && ( + + )} + - ) : seasons.length > 0 ? ( - - + + {/* Provider Information */} + {displaySeries.m3u_account && ( + + IPTV Provider + + + {displaySeries.m3u_account.name || displaySeries.m3u_account} + + {displaySeries.m3u_account.account_type && ( + + {displaySeries.m3u_account.account_type === 'XC' ? 'Xtream Codes' : 'Standard M3U'} + + )} + + + )} + + + + Episodes + + {loadingDetails ? ( + + + + ) : seasons.length > 0 ? ( + + + {seasons.map(season => ( + + Season {season} + + ))} + + {seasons.map(season => ( - - Season {season} - - ))} - - - {seasons.map(season => ( - - - - - Ep - Title - Duration - Date - Action - - - - {episodesBySeason[season]?.map(episode => ( - - handleEpisodeRowClick(episode)} - > - - - {episode.episode_number || '?'} - - - - - - {episode.name} - - {episode.genre && ( - - {episode.genre} + +
+ + + Ep + Title + Duration + Date + Action + + + + {episodesBySeason[season]?.map(episode => ( + + handleEpisodeRowClick(episode)} + > + + + {episode.episode_number || '?'} + + + + + + {episode.name} - )} - - - - - {formatDuration(episode.duration)} - - - - - {episode.release_date ? new Date(episode.release_date).toLocaleDateString() : 'N/A'} - - - - { - e.stopPropagation(); - handlePlayEpisode(episode); - }} - > - - - - - {expandedEpisode === episode.id && ( - - - - {/* Episode Image and Description Row */} - - {/* Episode Image */} - {episode.movie_image && ( - - - - )} - - {/* Episode Description */} - - {episode.description && ( - - Description - - {episode.description} - - - )} - - - - {/* Additional Episode Details */} - - {episode.rating && ( - - Rating - {episode.rating} - - )} - - {episode.director && ( - - Director - {episode.director} - - )} - - {episode.actors && ( - - Cast - {episode.actors} - - )} - - - {/* Technical Details */} - {(episode.bitrate || episode.video || episode.audio) && ( - - Technical Details - - {episode.bitrate && episode.bitrate > 0 && ( - - Bitrate: {episode.bitrate} kbps - - )} - {episode.video && Object.keys(episode.video).length > 0 && ( - - Video:{' '} - {episode.video.codec_long_name || episode.video.codec_name} - {episode.video.width && episode.video.height - ? `, ${episode.video.width}x${episode.video.height}` - : ''} - - )} - {episode.audio && Object.keys(episode.audio).length > 0 && ( - - Audio:{' '} - {episode.audio.codec_long_name || episode.audio.codec_name} - {episode.audio.channels - ? `, ${episode.audio.channels} channels` - : ''} - - )} - - - )} - - {/* Provider Information */} - {episode.m3u_account && ( - - Provider: - - {episode.m3u_account.name || episode.m3u_account} - - + {episode.genre && ( + + {episode.genre} + )} + + + {formatDuration(episode.duration)} + + + + + {episode.release_date ? new Date(episode.release_date).toLocaleDateString() : 'N/A'} + + + + { + e.stopPropagation(); + handlePlayEpisode(episode); + }} + > + + + - )} - - ))} - -
-
- ))} -
- ) : ( - - No episodes found for this series. - - )} -
+ {expandedEpisode === episode.id && ( + + + + {/* Episode Image and Description Row */} + + {/* Episode Image */} + {episode.movie_image && ( + + {episode.name} + + )} + + {/* Episode Description */} + + {episode.description && ( + + Description + + {episode.description} + + + )} + + + + {/* Additional Episode Details */} + + {episode.rating && ( + + Rating + {episode.rating} + + )} + + {episode.director && ( + + Director + {episode.director} + + )} + + {episode.actors && ( + + Cast + {episode.actors} + + )} + + + {/* Technical Details */} + {(episode.bitrate || episode.video || episode.audio) && ( + + Technical Details + + {episode.bitrate && episode.bitrate > 0 && ( + + Bitrate: {episode.bitrate} kbps + + )} + {episode.video && Object.keys(episode.video).length > 0 && ( + + Video:{' '} + {episode.video.codec_long_name || episode.video.codec_name} + {episode.video.width && episode.video.height + ? `, ${episode.video.width}x${episode.video.height}` + : ''} + + )} + {episode.audio && Object.keys(episode.audio).length > 0 && ( + + Audio:{' '} + {episode.audio.codec_long_name || episode.audio.codec_name} + {episode.audio.channels + ? `, ${episode.audio.channels} channels` + : ''} + + )} + + + )} + + {/* Provider Information */} + {episode.m3u_account && ( + + Provider: + + {episode.m3u_account.name || episode.m3u_account} + + + )} + + + + )} + + ))} + + + + ))} + + ) : ( + + No episodes found for this series. + + )} + +
-
-
+
+ + {/* YouTube Trailer Modal */} + setTrailerModalOpened(false)} + title="Trailer" + size="xl" + centered + withCloseButton + > + + {trailerUrl && ( +