feat: add linkTitle options to embed player

- Added 'linkTitle' to toggle between clickable and
This commit is contained in:
Yiannis Christodoulou
2026-01-07 13:03:57 +02:00
parent 4b06cabf93
commit eac08a51a8
9 changed files with 181 additions and 138 deletions

View File

@@ -17,6 +17,7 @@ class EmbedInfoOverlay extends Component {
this.showTitle = options.showTitle !== undefined ? options.showTitle : true; this.showTitle = options.showTitle !== undefined ? options.showTitle : true;
this.showRelated = options.showRelated !== undefined ? options.showRelated : true; this.showRelated = options.showRelated !== undefined ? options.showRelated : true;
this.showUserAvatar = options.showUserAvatar !== undefined ? options.showUserAvatar : true; this.showUserAvatar = options.showUserAvatar !== undefined ? options.showUserAvatar : true;
this.linkTitle = options.linkTitle !== undefined ? options.linkTitle : true;
// Initialize after player is ready // Initialize after player is ready
this.player().ready(() => { this.player().ready(() => {
@@ -136,7 +137,7 @@ class EmbedInfoOverlay extends Component {
overflow: hidden; overflow: hidden;
`; `;
if (this.videoUrl) { if (this.videoUrl && this.linkTitle) {
const titleLink = document.createElement('a'); const titleLink = document.createElement('a');
titleLink.href = this.videoUrl; titleLink.href = this.videoUrl;
titleLink.target = '_blank'; titleLink.target = '_blank';

View File

@@ -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, showRelated = true, showUserAvatar = true }) { function VideoJSPlayer({ videoId = 'default-video', showTitle = true, showRelated = true, showUserAvatar = true, linkTitle = 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
@@ -221,10 +221,23 @@ function VideoJSPlayer({ videoId = 'default-video', showTitle = true, showRelate
return showUserAvatar; return showUserAvatar;
}, [isEmbedPlayer, showUserAvatar]); }, [isEmbedPlayer, showUserAvatar]);
// Read linkTitle from URL parameter if available (for embed players)
const getLinkTitleFromURL = useMemo(() => {
if (isEmbedPlayer && typeof window !== 'undefined') {
const urlParams = new URLSearchParams(window.location.search);
const urlLinkTitle = urlParams.get('linkTitle');
if (urlLinkTitle !== null) {
return urlLinkTitle === '1' || urlLinkTitle === 'true';
}
}
return linkTitle;
}, [isEmbedPlayer, linkTitle]);
// 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; const finalShowRelated = isEmbedPlayer ? getShowRelatedFromURL : showRelated;
const finalShowUserAvatar = isEmbedPlayer ? getShowUserAvatarFromURL : showUserAvatar; const finalShowUserAvatar = isEmbedPlayer ? getShowUserAvatarFromURL : showUserAvatar;
const finalLinkTitle = isEmbedPlayer ? getLinkTitleFromURL : linkTitle;
// Utility function to detect touch devices // Utility function to detect touch devices
const isTouchDevice = useMemo(() => { const isTouchDevice = useMemo(() => {
@@ -1332,6 +1345,7 @@ function VideoJSPlayer({ videoId = 'default-video', showTitle = true, showRelate
goToNextVideo, goToNextVideo,
showRelated: finalShowRelated, showRelated: finalShowRelated,
showUserAvatar: finalShowUserAvatar, showUserAvatar: finalShowUserAvatar,
linkTitle: finalLinkTitle,
}); });
customComponents.current.endScreenHandler = endScreenHandler; // Store for cleanup customComponents.current.endScreenHandler = endScreenHandler; // Store for cleanup
@@ -2254,6 +2268,7 @@ function VideoJSPlayer({ videoId = 'default-video', showTitle = true, showRelate
showTitle: finalShowTitle, showTitle: finalShowTitle,
showRelated: finalShowRelated, showRelated: finalShowRelated,
showUserAvatar: finalShowUserAvatar, showUserAvatar: finalShowUserAvatar,
linkTitle: finalLinkTitle,
}); });
} }
// END: Add Embed Info Overlay Component // END: Add Embed Info Overlay Component

View File

@@ -72,6 +72,7 @@ export class EndScreenHandler {
goToNextVideo, goToNextVideo,
showRelated, showRelated,
showUserAvatar, showUserAvatar,
linkTitle,
} = this.options; } = this.options;
// For embed players, show big play button when video ends // For embed players, show big play button when video ends

View File

@@ -36,6 +36,7 @@ const VideoJSEmbed = ({
showTitle, showTitle,
showRelated, showRelated,
showUserAvatar, showUserAvatar,
linkTitle,
hasTheaterMode, hasTheaterMode,
hasNextLink, hasNextLink,
nextLink, nextLink,
@@ -69,6 +70,7 @@ const VideoJSEmbed = ({
const urlMuted = getUrlParameter('muted'); const urlMuted = getUrlParameter('muted');
const urlShowRelated = getUrlParameter('showRelated'); const urlShowRelated = getUrlParameter('showRelated');
const urlShowUserAvatar = getUrlParameter('showUserAvatar'); const urlShowUserAvatar = getUrlParameter('showUserAvatar');
const urlLinkTitle = getUrlParameter('linkTitle');
window.MEDIA_DATA = { window.MEDIA_DATA = {
data: data || {}, data: data || {},
@@ -93,6 +95,7 @@ const VideoJSEmbed = ({
showTitle: showTitle || false, showTitle: showTitle || false,
showRelated: showRelated !== undefined ? showRelated : (urlShowRelated === '1' || urlShowRelated === 'true' || urlShowRelated === null), showRelated: showRelated !== undefined ? showRelated : (urlShowRelated === '1' || urlShowRelated === 'true' || urlShowRelated === null),
showUserAvatar: showUserAvatar !== undefined ? showUserAvatar : (urlShowUserAvatar === '1' || urlShowUserAvatar === 'true' || urlShowUserAvatar === null), showUserAvatar: showUserAvatar !== undefined ? showUserAvatar : (urlShowUserAvatar === '1' || urlShowUserAvatar === 'true' || urlShowUserAvatar === null),
linkTitle: linkTitle !== undefined ? linkTitle : (urlLinkTitle === '1' || urlLinkTitle === 'true' || urlLinkTitle === null),
hasTheaterMode: hasTheaterMode || false, hasTheaterMode: hasTheaterMode || false,
hasNextLink: hasNextLink || false, hasNextLink: hasNextLink || false,
nextLink: nextLink || null, nextLink: nextLink || null,
@@ -104,6 +107,7 @@ const VideoJSEmbed = ({
urlMuted: urlMuted === '1', urlMuted: urlMuted === '1',
urlShowRelated: urlShowRelated === '1' || urlShowRelated === 'true', urlShowRelated: urlShowRelated === '1' || urlShowRelated === 'true',
urlShowUserAvatar: urlShowUserAvatar === '1' || urlShowUserAvatar === 'true', urlShowUserAvatar: urlShowUserAvatar === '1' || urlShowUserAvatar === 'true',
urlLinkTitle: urlLinkTitle === '1' || urlLinkTitle === 'true',
onClickNextCallback: onClickNextCallback || null, onClickNextCallback: onClickNextCallback || null,
onClickPreviousCallback: onClickPreviousCallback || null, onClickPreviousCallback: onClickPreviousCallback || null,
onStateUpdateCallback: onStateUpdateCallback || null, onStateUpdateCallback: onStateUpdateCallback || null,

View File

@@ -22,6 +22,7 @@ export function MediaShareEmbed(props) {
const [showTitle, setShowTitle] = useState(false); const [showTitle, setShowTitle] = useState(false);
const [showRelated, setShowRelated] = useState(true); const [showRelated, setShowRelated] = useState(true);
const [showUserAvatar, setShowUserAvatar] = useState(true); const [showUserAvatar, setShowUserAvatar] = useState(true);
const [linkTitle, setLinkTitle] = 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);
@@ -107,6 +108,10 @@ export function MediaShareEmbed(props) {
setShowUserAvatar(!showUserAvatar); setShowUserAvatar(!showUserAvatar);
} }
function onLinkTitleChange() {
setLinkTitle(!linkTitle);
}
function onAspectRatioChange() { function onAspectRatioChange() {
const newVal = aspectRatioValueRef.current.value; const newVal = aspectRatioValueRef.current.value;
@@ -152,8 +157,8 @@ export function MediaShareEmbed(props) {
<div className="media-embed-wrap"> <div className="media-embed-wrap">
<SiteConsumer> <SiteConsumer>
{(site) => <> {(site) => <>
{/* <VideoViewer key={`embed-${showTitle}-${showRelated}-${showUserAvatar}`} data={MediaPageStore.get('media-data')} siteUrl={site.url} inEmbed={true} showTitle={showTitle} showRelated={showRelated} showUserAvatar={showUserAvatar} /> */} {/* <VideoViewer key={`embed-${showTitle}-${showRelated}-${showUserAvatar}-${linkTitle}`} data={MediaPageStore.get('media-data')} siteUrl={site.url} inEmbed={true} showTitle={showTitle} showRelated={showRelated} showUserAvatar={showUserAvatar} linkTitle={linkTitle} /> */}
<iframe width="100%" height="480px" src={`${links.embed + MediaPageStore.get('media-id')}&showTitle=${showTitle ? '1' : '0'}&showRelated=${showRelated ? '1' : '0'}&showUserAvatar=${showUserAvatar ? '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'}&showUserAvatar=${showUserAvatar ? '1' : '0'}&linkTitle=${linkTitle ? '1' : '0'}`} frameborder="0" allowfullscreen></iframe>
</>} </>}
</SiteConsumer> </SiteConsumer>
</div> </div>
@@ -188,6 +193,7 @@ export function MediaShareEmbed(props) {
'showTitle=' + (showTitle ? '1' : '0') + 'showTitle=' + (showTitle ? '1' : '0') +
'&showRelated=' + (showRelated ? '1' : '0') + '&showRelated=' + (showRelated ? '1' : '0') +
'&showUserAvatar=' + (showUserAvatar ? '1' : '0') + '&showUserAvatar=' + (showUserAvatar ? '1' : '0') +
'&linkTitle=' + (linkTitle ? '1' : '0') +
'" frameborder="0" allowfullscreen></iframe>' '" frameborder="0" allowfullscreen></iframe>'
} }
></textarea> ></textarea>
@@ -209,6 +215,15 @@ export function MediaShareEmbed(props) {
</label> </label>
</div> </div>
{showTitle && (
<div className="options-group" style={{ paddingLeft: '20px' }}>
<label style={{ minHeight: '36px' }}>
<input type="checkbox" checked={linkTitle} onChange={onLinkTitleChange} />
Link title
</label>
</div>
)}
<div className="options-group"> <div className="options-group">
<label style={{ minHeight: '36px' }}> <label style={{ minHeight: '36px' }}>
<input type="checkbox" checked={showRelated} onChange={onShowRelatedChange} /> <input type="checkbox" checked={showRelated} onChange={onShowRelatedChange} />

View File

@@ -415,6 +415,7 @@ export default class VideoViewer extends React.PureComponent {
showTitle: this.props.showTitle, showTitle: this.props.showTitle,
showRelated: this.props.showRelated, showRelated: this.props.showRelated,
showUserAvatar: this.props.showUserAvatar, showUserAvatar: this.props.showUserAvatar,
linkTitle: this.props.linkTitle,
hasTheaterMode: !this.props.inEmbed, hasTheaterMode: !this.props.inEmbed,
hasNextLink: !!nextLink, hasNextLink: !!nextLink,
nextLink: nextLink, nextLink: nextLink,
@@ -441,6 +442,7 @@ VideoViewer.defaultProps = {
showTitle: !0, showTitle: !0,
showRelated: !0, showRelated: !0,
showUserAvatar: !0, showUserAvatar: !0,
linkTitle: !0,
siteUrl: PropTypes.string.isRequired, siteUrl: PropTypes.string.isRequired,
}; };
@@ -449,4 +451,5 @@ VideoViewer.propTypes = {
showTitle: PropTypes.bool, showTitle: PropTypes.bool,
showRelated: PropTypes.bool, showRelated: PropTypes.bool,
showUserAvatar: PropTypes.bool, showUserAvatar: PropTypes.bool,
linkTitle: PropTypes.bool,
}; };

View File

@@ -61,9 +61,12 @@ export const EmbedPage: React.FC = () => {
<SiteConsumer> <SiteConsumer>
{(site) => { {(site) => {
const urlParams = new URLSearchParams(window.location.search); const urlParams = new URLSearchParams(window.location.search);
const showTitle = urlParams.get('showTitle') !== '0'; const urlShowRelated = urlParams.get('showRelated');
const showRelated = urlParams.get('showRelated') !== '0'; const showRelated = urlShowRelated !== '0';
const showUserAvatar = urlParams.get('showUserAvatar') !== '0'; const urlShowUserAvatar = urlParams.get('showUserAvatar');
const showUserAvatar = urlShowUserAvatar !== '0';
const urlLinkTitle = urlParams.get('linkTitle');
const linkTitle = urlLinkTitle !== '0';
return ( return (
<VideoViewer <VideoViewer
data={MediaPageStore.get('media-data')} data={MediaPageStore.get('media-data')}
@@ -72,6 +75,7 @@ export const EmbedPage: React.FC = () => {
showTitle={showTitle} showTitle={showTitle}
showRelated={showRelated} showRelated={showRelated}
showUserAvatar={showUserAvatar} showUserAvatar={showUserAvatar}
linkTitle={linkTitle}
/> />
); );
}} }}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long