mirror of
https://github.com/mediacms-io/mediacms.git
synced 2026-01-20 15:22:58 -05:00
feat: add showRelated option to video player and embed UI
- Added 'showRelated' parameter to control related videos visibility at video end - Implemented UI toggle in MediaShareEmbed for the 'showRelated' option - Updated EndScreenHandler to honor the 'showRelated' setting - Modified EmbedPage and VideoJSEmbed to pass the parameter from URL to player
This commit is contained in:
@@ -15,6 +15,7 @@ class EmbedInfoOverlay extends Component {
|
|||||||
this.videoTitle = options.videoTitle || 'Video';
|
this.videoTitle = options.videoTitle || 'Video';
|
||||||
this.videoUrl = options.videoUrl || '';
|
this.videoUrl = options.videoUrl || '';
|
||||||
this.showTitle = options.showTitle !== undefined ? options.showTitle : true;
|
this.showTitle = options.showTitle !== undefined ? options.showTitle : true;
|
||||||
|
this.showRelated = options.showRelated !== undefined ? options.showRelated : true;
|
||||||
|
|
||||||
// Initialize after player is ready
|
// Initialize after player is ready
|
||||||
this.player().ready(() => {
|
this.player().ready(() => {
|
||||||
|
|||||||
@@ -170,7 +170,7 @@ const enableStandardButtonTooltips = (player) => {
|
|||||||
}, 500); // Delay to ensure all components are ready
|
}, 500); // Delay to ensure all components are ready
|
||||||
};
|
};
|
||||||
|
|
||||||
function VideoJSPlayer({ videoId = 'default-video', showTitle = true }) {
|
function VideoJSPlayer({ videoId = 'default-video', showTitle = true, showRelated = true }) {
|
||||||
const videoRef = useRef(null);
|
const videoRef = useRef(null);
|
||||||
const playerRef = useRef(null); // Track the player instance
|
const playerRef = useRef(null); // Track the player instance
|
||||||
const userPreferences = useRef(new UserPreferences()); // User preferences instance
|
const userPreferences = useRef(new UserPreferences()); // User preferences instance
|
||||||
@@ -197,8 +197,21 @@ function VideoJSPlayer({ videoId = 'default-video', showTitle = true }) {
|
|||||||
return showTitle;
|
return showTitle;
|
||||||
}, [isEmbedPlayer, showTitle]);
|
}, [isEmbedPlayer, showTitle]);
|
||||||
|
|
||||||
|
// Read showRelated from URL parameter if available (for embed players)
|
||||||
|
const getShowRelatedFromURL = useMemo(() => {
|
||||||
|
if (isEmbedPlayer && typeof window !== 'undefined') {
|
||||||
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
|
const urlShowRelated = urlParams.get('showRelated');
|
||||||
|
if (urlShowRelated !== null) {
|
||||||
|
return urlShowRelated === '1' || urlShowRelated === 'true';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return showRelated;
|
||||||
|
}, [isEmbedPlayer, showRelated]);
|
||||||
|
|
||||||
// Use URL parameter value if available, otherwise use prop value
|
// Use URL parameter value if available, otherwise use prop value
|
||||||
const finalShowTitle = isEmbedPlayer ? getShowTitleFromURL : showTitle;
|
const finalShowTitle = isEmbedPlayer ? getShowTitleFromURL : showTitle;
|
||||||
|
const finalShowRelated = isEmbedPlayer ? getShowRelatedFromURL : showRelated;
|
||||||
|
|
||||||
// Utility function to detect touch devices
|
// Utility function to detect touch devices
|
||||||
const isTouchDevice = useMemo(() => {
|
const isTouchDevice = useMemo(() => {
|
||||||
@@ -1304,6 +1317,7 @@ function VideoJSPlayer({ videoId = 'default-video', showTitle = true }) {
|
|||||||
currentVideo,
|
currentVideo,
|
||||||
relatedVideos,
|
relatedVideos,
|
||||||
goToNextVideo,
|
goToNextVideo,
|
||||||
|
showRelated: finalShowRelated,
|
||||||
});
|
});
|
||||||
customComponents.current.endScreenHandler = endScreenHandler; // Store for cleanup
|
customComponents.current.endScreenHandler = endScreenHandler; // Store for cleanup
|
||||||
|
|
||||||
@@ -2224,6 +2238,7 @@ function VideoJSPlayer({ videoId = 'default-video', showTitle = true }) {
|
|||||||
videoTitle: currentVideo.title,
|
videoTitle: currentVideo.title,
|
||||||
videoUrl: currentVideo.url,
|
videoUrl: currentVideo.url,
|
||||||
showTitle: finalShowTitle,
|
showTitle: finalShowTitle,
|
||||||
|
showRelated: finalShowRelated,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// END: Add Embed Info Overlay Component
|
// END: Add Embed Info Overlay Component
|
||||||
|
|||||||
@@ -63,7 +63,15 @@ export class EndScreenHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleVideoEnded() {
|
handleVideoEnded() {
|
||||||
const { isEmbedPlayer, userPreferences, mediaData, currentVideo, relatedVideos, goToNextVideo } = this.options;
|
const {
|
||||||
|
isEmbedPlayer,
|
||||||
|
userPreferences,
|
||||||
|
mediaData,
|
||||||
|
currentVideo,
|
||||||
|
relatedVideos,
|
||||||
|
goToNextVideo,
|
||||||
|
showRelated,
|
||||||
|
} = this.options;
|
||||||
|
|
||||||
// For embed players, show big play button when video ends
|
// For embed players, show big play button when video ends
|
||||||
if (isEmbedPlayer) {
|
if (isEmbedPlayer) {
|
||||||
@@ -73,6 +81,34 @@ export class EndScreenHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If showRelated is false, we don't show the end screen or autoplay countdown
|
||||||
|
if (showRelated === false) {
|
||||||
|
// But we still want to keep the control bar visible and hide the poster
|
||||||
|
setTimeout(() => {
|
||||||
|
if (this.player && !this.player.isDisposed()) {
|
||||||
|
const playerEl = this.player.el();
|
||||||
|
if (playerEl) {
|
||||||
|
// Hide poster elements
|
||||||
|
const posterElements = playerEl.querySelectorAll('.vjs-poster');
|
||||||
|
posterElements.forEach((posterEl) => {
|
||||||
|
posterEl.style.display = 'none';
|
||||||
|
posterEl.style.visibility = 'hidden';
|
||||||
|
posterEl.style.opacity = '0';
|
||||||
|
});
|
||||||
|
|
||||||
|
// Keep control bar visible
|
||||||
|
const controlBar = this.player.getChild('controlBar');
|
||||||
|
if (controlBar) {
|
||||||
|
controlBar.show();
|
||||||
|
controlBar.el().style.opacity = '1';
|
||||||
|
controlBar.el().style.pointerEvents = 'auto';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 50);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Keep controls active after video ends
|
// Keep controls active after video ends
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (this.player && !this.player.isDisposed()) {
|
if (this.player && !this.player.isDisposed()) {
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ const VideoJSEmbed = ({
|
|||||||
enableAutoplay,
|
enableAutoplay,
|
||||||
inEmbed,
|
inEmbed,
|
||||||
showTitle,
|
showTitle,
|
||||||
|
showRelated,
|
||||||
hasTheaterMode,
|
hasTheaterMode,
|
||||||
hasNextLink,
|
hasNextLink,
|
||||||
nextLink,
|
nextLink,
|
||||||
@@ -65,6 +66,7 @@ const VideoJSEmbed = ({
|
|||||||
const urlTimestamp = getUrlParameter('t');
|
const urlTimestamp = getUrlParameter('t');
|
||||||
const urlAutoplay = getUrlParameter('autoplay');
|
const urlAutoplay = getUrlParameter('autoplay');
|
||||||
const urlMuted = getUrlParameter('muted');
|
const urlMuted = getUrlParameter('muted');
|
||||||
|
const urlShowRelated = getUrlParameter('showRelated');
|
||||||
|
|
||||||
window.MEDIA_DATA = {
|
window.MEDIA_DATA = {
|
||||||
data: data || {},
|
data: data || {},
|
||||||
@@ -86,6 +88,8 @@ const VideoJSEmbed = ({
|
|||||||
subtitlesInfo: subtitlesInfo || [],
|
subtitlesInfo: subtitlesInfo || [],
|
||||||
enableAutoplay: enableAutoplay || (urlAutoplay === '1'),
|
enableAutoplay: enableAutoplay || (urlAutoplay === '1'),
|
||||||
inEmbed: inEmbed || false,
|
inEmbed: inEmbed || false,
|
||||||
|
showTitle: showTitle || false,
|
||||||
|
showRelated: showRelated !== undefined ? showRelated : (urlShowRelated === '1' || urlShowRelated === 'true' || urlShowRelated === null),
|
||||||
hasTheaterMode: hasTheaterMode || false,
|
hasTheaterMode: hasTheaterMode || false,
|
||||||
hasNextLink: hasNextLink || false,
|
hasNextLink: hasNextLink || false,
|
||||||
nextLink: nextLink || null,
|
nextLink: nextLink || null,
|
||||||
@@ -95,6 +99,7 @@ const VideoJSEmbed = ({
|
|||||||
urlTimestamp: urlTimestamp ? parseInt(urlTimestamp, 10) : null,
|
urlTimestamp: urlTimestamp ? parseInt(urlTimestamp, 10) : null,
|
||||||
urlAutoplay: urlAutoplay === '1',
|
urlAutoplay: urlAutoplay === '1',
|
||||||
urlMuted: urlMuted === '1',
|
urlMuted: urlMuted === '1',
|
||||||
|
urlShowRelated: urlShowRelated === '1' || urlShowRelated === 'true',
|
||||||
onClickNextCallback: onClickNextCallback || null,
|
onClickNextCallback: onClickNextCallback || null,
|
||||||
onClickPreviousCallback: onClickPreviousCallback || null,
|
onClickPreviousCallback: onClickPreviousCallback || null,
|
||||||
onStateUpdateCallback: onStateUpdateCallback || null,
|
onStateUpdateCallback: onStateUpdateCallback || null,
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ export function MediaShareEmbed(props) {
|
|||||||
const [maxHeight, setMaxHeight] = useState(window.innerHeight - 144 + 56);
|
const [maxHeight, setMaxHeight] = useState(window.innerHeight - 144 + 56);
|
||||||
const [keepAspectRatio, setKeepAspectRatio] = useState(false);
|
const [keepAspectRatio, setKeepAspectRatio] = useState(false);
|
||||||
const [showTitle, setShowTitle] = useState(false);
|
const [showTitle, setShowTitle] = useState(false);
|
||||||
|
const [showRelated, setShowRelated] = useState(true);
|
||||||
const [aspectRatio, setAspectRatio] = useState('16:9');
|
const [aspectRatio, setAspectRatio] = useState('16:9');
|
||||||
const [embedWidthValue, setEmbedWidthValue] = useState(embedVideoDimensions.width);
|
const [embedWidthValue, setEmbedWidthValue] = useState(embedVideoDimensions.width);
|
||||||
const [embedWidthUnit, setEmbedWidthUnit] = useState(embedVideoDimensions.widthUnit);
|
const [embedWidthUnit, setEmbedWidthUnit] = useState(embedVideoDimensions.widthUnit);
|
||||||
@@ -97,6 +98,10 @@ export function MediaShareEmbed(props) {
|
|||||||
setShowTitle(!showTitle);
|
setShowTitle(!showTitle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onShowRelatedChange() {
|
||||||
|
setShowRelated(!showRelated);
|
||||||
|
}
|
||||||
|
|
||||||
function onAspectRatioChange() {
|
function onAspectRatioChange() {
|
||||||
const newVal = aspectRatioValueRef.current.value;
|
const newVal = aspectRatioValueRef.current.value;
|
||||||
|
|
||||||
@@ -142,8 +147,8 @@ export function MediaShareEmbed(props) {
|
|||||||
<div className="media-embed-wrap">
|
<div className="media-embed-wrap">
|
||||||
<SiteConsumer>
|
<SiteConsumer>
|
||||||
{(site) => <>
|
{(site) => <>
|
||||||
{/* <VideoViewer key={`embed-${showTitle}`} data={MediaPageStore.get('media-data')} siteUrl={site.url} inEmbed={true} showTitle={showTitle} /> */}
|
{/* <VideoViewer key={`embed-${showTitle}-${showRelated}`} data={MediaPageStore.get('media-data')} siteUrl={site.url} inEmbed={true} showTitle={showTitle} showRelated={showRelated} /> */}
|
||||||
<iframe width="100%" height="480px" src={`${links.embed + MediaPageStore.get('media-id')}&showTitle=${showTitle ? '1' : '0'}`} frameborder="0" allowfullscreen></iframe>
|
<iframe width="100%" height="480px" src={`${links.embed + MediaPageStore.get('media-id')}&showTitle=${showTitle ? '1' : '0'}&showRelated=${showRelated ? '1' : '0'}`} frameborder="0" allowfullscreen></iframe>
|
||||||
</>}
|
</>}
|
||||||
</SiteConsumer>
|
</SiteConsumer>
|
||||||
</div>
|
</div>
|
||||||
@@ -174,7 +179,9 @@ export function MediaShareEmbed(props) {
|
|||||||
'" src="' +
|
'" src="' +
|
||||||
links.embed +
|
links.embed +
|
||||||
MediaPageStore.get('media-id') +
|
MediaPageStore.get('media-id') +
|
||||||
(showTitle ? (links.embed.includes('?') ? '&showTitle=1' : '?showTitle=1') : '') +
|
(links.embed.includes('?') ? '&' : '?') +
|
||||||
|
'showTitle=' + (showTitle ? '1' : '0') +
|
||||||
|
'&showRelated=' + (showRelated ? '1' : '0') +
|
||||||
'" frameborder="0" allowfullscreen></iframe>'
|
'" frameborder="0" allowfullscreen></iframe>'
|
||||||
}
|
}
|
||||||
></textarea>
|
></textarea>
|
||||||
@@ -196,6 +203,13 @@ export function MediaShareEmbed(props) {
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className="options-group">
|
||||||
|
<label style={{ minHeight: '36px' }}>
|
||||||
|
<input type="checkbox" checked={showRelated} onChange={onShowRelatedChange} />
|
||||||
|
Show related
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="options-group">
|
<div className="options-group">
|
||||||
<label style={{ minHeight: '36px' }}>
|
<label style={{ minHeight: '36px' }}>
|
||||||
<input type="checkbox" checked={keepAspectRatio} onChange={onKeepAspectRatioChange} />
|
<input type="checkbox" checked={keepAspectRatio} onChange={onKeepAspectRatioChange} />
|
||||||
|
|||||||
@@ -412,6 +412,8 @@ export default class VideoViewer extends React.PureComponent {
|
|||||||
subtitlesInfo: this.props.data.subtitles_info,
|
subtitlesInfo: this.props.data.subtitles_info,
|
||||||
enableAutoplay: !this.props.inEmbed,
|
enableAutoplay: !this.props.inEmbed,
|
||||||
inEmbed: this.props.inEmbed,
|
inEmbed: this.props.inEmbed,
|
||||||
|
showTitle: this.props.showTitle,
|
||||||
|
showRelated: this.props.showRelated,
|
||||||
hasTheaterMode: !this.props.inEmbed,
|
hasTheaterMode: !this.props.inEmbed,
|
||||||
hasNextLink: !!nextLink,
|
hasNextLink: !!nextLink,
|
||||||
nextLink: nextLink,
|
nextLink: nextLink,
|
||||||
|
|||||||
@@ -59,9 +59,20 @@ export const EmbedPage: React.FC = () => {
|
|||||||
|
|
||||||
{loadedVideo && (
|
{loadedVideo && (
|
||||||
<SiteConsumer>
|
<SiteConsumer>
|
||||||
{(site) => (
|
{(site) => {
|
||||||
<VideoViewer data={MediaPageStore.get('media-data')} siteUrl={site.url} containerStyles={containerStyles} />
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
)}
|
const showTitle = urlParams.get('showTitle') !== '0';
|
||||||
|
const showRelated = urlParams.get('showRelated') !== '0';
|
||||||
|
return (
|
||||||
|
<VideoViewer
|
||||||
|
data={MediaPageStore.get('media-data')}
|
||||||
|
siteUrl={site.url}
|
||||||
|
containerStyles={containerStyles}
|
||||||
|
showTitle={showTitle}
|
||||||
|
showRelated={showRelated}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}}
|
||||||
</SiteConsumer>
|
</SiteConsumer>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user