I have react component as follows:
import React, { Fragment, useEffect, useRef, useState } from 'react';
import { hasWindow } from '../utils.js';
import { Helmet } from 'react-helmet';
const PLATFORMS = {
YOUTUBE: 'platform',
BUNNY: 'bunny',
VIMEO: 'vimeo'
};
const VideoExternal = ({ video, isOnScreen, item, context }) => {
const [isPlaying, setIsPlaying] = useState(video.autoplay);
const [isPaused, setIsPaused] = useState(false);
const [isMuted, setIsMuted] = useState(video.autoplay);
const [isVideoLoaded, setIsVideoLoaded] = useState(false);
const [isVideoHovered, setIsVideoHovered] = useState(false);
const [showLastImage, setShowLastImage] = useState(false);
const iframeId = `iframe-${video.url}-${video.externalUrl}`;
const iframeRef = useRef(null);
const playerRef = useRef(null);
const getPlatform = (url) => {
if (url.includes('youtube') || url.includes('youtu')) return PLATFORMS.YOUTUBE;
if (url.includes('b-cdn') || url.includes('mediadelivery')) return PLATFORMS.BUNNY;
if (url.includes('vimeo')) return PLATFORMS.VIMEO;
return null;
};
const getBaseUrl = (url) => {
const urlObj = new URL(url);
return `${urlObj.origin}${urlObj.pathname}`;
};
const addQueryParams = (url, platform, autoplay, loop) => {
const urlObj = new URL(url);
switch (platform) {
case PLATFORMS.YOUTUBE:
if (autoplay) urlObj.searchParams.set('autoplay', '1');
if (loop) urlObj.searchParams.set('loop', '1');
isMuted ? urlObj.searchParams.set('mute', '1') : urlObj.searchParams.set('mute', '0');
urlObj.searchParams.set('controls', '0');
urlObj.searchParams.set('enablejsapi', '1');
break;
case PLATFORMS.VIMEO:
if (autoplay) urlObj.searchParams.set('autoplay', '1');
if (loop) urlObj.searchParams.set('loop', '1');
isMuted ? urlObj.searchParams.set('muted', '1') : urlObj.searchParams.set('muted', '0');
urlObj.searchParams.set('controls', '0');
break;
case PLATFORMS.BUNNY:
if (autoplay) urlObj.searchParams.set('autoplay', 'true');
if (loop) urlObj.searchParams.set('loop', 'true');
urlObj.searchParams.set('mute', `${isMuted} ? 'true' : 'false'`);
urlObj.searchParams.set('controls', 'false');
break;
default:
break;
}
return urlObj.toString();
};
const platform = getPlatform(video.externalUrl);
const baseUrl = getBaseUrl(video.externalUrl, platform);
const iframeUrl = addQueryParams(baseUrl, platform, video.autoplay, video.loop);
const initializePlayer = (iframe) => {
if (!iframe) return null;
if (platform === PLATFORMS.YOUTUBE) {
playerRef.current = new YT.Player(iframeId, {
events: {
onReady: () => {
console.log('onReady');
},
onStateChange: event => {
if (!video.loop && event.data === YT.PlayerState.ENDED) {
setIsPlaying(false);
setIsMuted(true);
setShowLastImage(true);
console.log('Video ended.');
}
}
}
});
} else if (platform === PLATFORMS.VIMEO) {
playerRef.current = new Vimeo.Player(iframe);
}
};
useEffect(() => {
if (isOnScreen && isVideoLoaded) {
const iframe = iframeRef?.current;
// Create new player instance
initializePlayer(iframe);
}
}, [isVideoLoaded, isOnScreen]);
// Effect to reset playing and showing the last image on slide change
useEffect(() => {
setIsPlaying(video.autoplay);
setIsMuted(video.autoplay);
setShowLastImage(false);
}, [isOnScreen]);
// Effect to check when video is ended
useEffect(() => {
if (isOnScreen && isVideoLoaded && hasWindow()) {
const iframe = iframeRef?.current;
if (platform === PLATFORMS.YOUTUBE) {
} else if (platform === PLATFORMS.VIMEO) {
playerRef?.current?.on('finish', function () {
if (!video.loop) {
setIsPlaying(false);
setIsMuted(true);
setShowLastImage(true);
}
});
} else if (platform === PLATFORMS.BUNNY) {
}
}
}, [video.loop, isOnScreen, isVideoLoaded]);
const handlePlayClick = () => {
const iframe = iframeRef?.current;
const iframeWindow = iframe?.contentWindow;
if (iframe && isVideoLoaded) {
if (platform === PLATFORMS.YOUTUBE)
playerRef?.current?.playVideo();
} else if (platform === PLATFORMS.VIMEO) {
playerRef?.current?.play();
} else if (platform === PLATFORMS.BUNNY && iframeWindow) {
iframeWindow?.postMessage('{"event":"command","func":"play"}', '*');
}
setIsPaused(false);
setIsPlaying(true);
setIsMuted(false);
setShowLastImage(false);
}
};
const isFirstImageVisible = !isPlaying && !isPaused && !showLastImage && video.firstImage !== '';
const isLastImageVisible = !isPlaying && !isPaused && showLastImage && video.lastImage !== '';
const isPlayButtonVisible = !isPlaying && video.playButtonVisible;
const renderFirstImage = () => {
return <img src={video.firstImage} alt='Start of Video' className='rounded-16 h-full w-full absolute inset-0 object-cover' />;
};
const renderLastImage = () => {
return <img src={video.lastImage} alt='End of Video' className='rounded-16 h-full w-full absolute inset-0 object-cover' />;
};
const renderPlayButton = () => {
return <i className='fad fa-play-circle text-4xl md:text-6xl text-gray-200 hover:text-gray-100 cursor-pointer absolute z-1' onClick={handlePlayClick} />;
};
const handleIframeLoad = () => {
setIsVideoLoaded(true);
};
return (
<Fragment>
<Helmet>
<script src='https://player.vimeo.com/api/player.js'></script>
<script src='https://www.youtube.com/iframe_api'></script>
</Helmet>
<div className='rounded-16 h-full w-full relative flex items-center justify-center' onMouseEnter={() => setIsVideoHovered(true)} onMouseLeave={() => setIsVideoHovered(false)}>
{isFirstImageVisible && renderFirstImage()}
<iframe id={iframeId} ref={iframeRef} src={iframeUrl} allow='autoplay; fullscreen; picture-in-picture' title='Video' className='h-full w-full rounded-16' onLoad={handleIframeLoad}></iframe>
{isPlayButtonVisible && renderPlayButton()}
{isLastImageVisible && renderLastImage()}
</div>
</Fragment>
);
};
export default VideoExternal;
When the component loads the video starts playing (video.autoplay is true), and when video is ended I see console.log('Video ended.');
When I then click on play button and (executes handlePlayClick function) and thus playerRef?.current?.playVideo();
is executed, the video starts playing, but when it ends I do not see console.log(‘isEnded: ‘, true).
Any idea why? Do I need to reinitialize the iframe or is there something else that I’m missing?
Thanks.