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 && (
- <>
-
- {/* Overlay for readability */}
-
- >
- )}
+ <>
+
+
+ {/* Backdrop image as background */}
+ {displaySeries.backdrop_path && displaySeries.backdrop_path.length > 0 && (
+ <>
+
+ {/* Overlay for readability */}
+
+ >
+ )}
- {/* Modal content above backdrop */}
-
-
- {loadingDetails && (
-
-
- Loading series details and episodes...
-
- )}
-
- {/* Series poster and basic info */}
-
- {(displaySeries.series_image || displaySeries.logo?.url) ? (
-
-
-
- ) : (
-
-
-
+ {/* 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) ? (
+
+
+
+ ) : (
+
+
)}
- {/* 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 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 && (
+
+ )}
+
+
+ >
);
};
diff --git a/frontend/src/store/useVODStore.jsx b/frontend/src/store/useVODStore.jsx
index ceb23ff7..9a2e2c52 100644
--- a/frontend/src/store/useVODStore.jsx
+++ b/frontend/src/store/useVODStore.jsx
@@ -291,7 +291,7 @@ const useVODStore = create((set, get) => ({
o_name: response.o_name || '',
age: response.age || '',
m3u_account: response.m3u_account || '',
- youtube_trailer: response.youtube_trailer || '',
+ youtube_trailer: response.custom_properties?.youtube_trailer || '',
};
let episodesData = {};