Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 17 additions & 9 deletions next-cloudinary/src/components/CldVideoPlayer/CldVideoPlayer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ const CldVideoPlayer = (props: CldVideoPlayerProps) => {
const videoRef = props.videoRef || defaultVideoRef;
const defaultPlayerRef = useRef<CloudinaryVideoPlayer | null>(null);
const playerRef = props.playerRef || defaultPlayerRef;
// The <video> element the player was created on (video.js keeps using it inside the player)
const playerVideoElementRef = useRef<HTMLVideoElement | null>(null);

const playerId = id || `player-${uniqueId.replace(/:/g, '')}`;
let playerClassName = 'cld-video-player cld-fluid';
Expand Down Expand Up @@ -86,8 +88,14 @@ const CldVideoPlayer = (props: CldVideoPlayerProps) => {
*/

const disposePlayer = () => {
if (playerRef.current?.videojs?.cloudinary) {
playerRef.current.videojs.cloudinary.dispose();
// React Strict Mode runs effect cleanups without removing the DOM, then reuses the same
// <video> element. Keep the player in that case; dispose() would remove the element.
if (playerVideoElementRef.current?.isConnected) return;

// dispose() on the player instance also removes it from the video.js registry,
// so a remount with the same id creates a new player instead of reusing a stale one
if (typeof playerRef.current?.dispose === 'function') {
playerRef.current.dispose();
}
// remove from global instances array
playerInstances = playerInstances.filter((instanceId) => instanceId !== playerId);
Expand All @@ -101,14 +109,12 @@ const CldVideoPlayer = (props: CldVideoPlayerProps) => {
*/

const initializePlayer = () => {
if (typeof window !== 'undefined' && 'cloudinary' in window && videoRef.current && !playerInitialized) {
// Check the ref, not the playerInitialized state: onLoad and onReady can both call this
// during the same render, and the state value seen there may be stale
if (typeof window !== 'undefined' && 'cloudinary' in window && videoRef.current && !playerRef.current) {
cloudinaryRef.current = window.cloudinary;

// dispose any existing player instance first to prevent conflicts
if (playerRef.current) {
disposePlayer();
}


playerVideoElementRef.current = videoRef.current;
playerRef.current = cloudinaryRef.current.videoPlayer(videoRef.current, playerOptions);
setPlayerInitialized(true);

Expand Down Expand Up @@ -180,6 +186,8 @@ const CldVideoPlayer = (props: CldVideoPlayerProps) => {
id={`cloudinary-videoplayer-${playerId}`}
src={`https://unpkg.com/cloudinary-video-player@${PLAYER_VERSION}/dist/cld-video-player.min.js`}
onLoad={handleOnLoad}
// onReady also runs on later mounts with the same script id, where onLoad doesn't fire again
onReady={handleOnLoad}
onError={(e) => console.error(`Failed to load Cloudinary Video Player: ${e.message}`)}
/>
</div>
Expand Down
Loading