diff --git a/public/app.js b/public/app.js index 79209b9..5e61999 100644 --- a/public/app.js +++ b/public/app.js @@ -53,8 +53,8 @@ let applyingWatchStateUntil = 0; let pauseEnforcementUntil = 0; let syncedPlaybackRetryUntil = 0; let desiredYoutubePlaying = false; -let pendingWatchState = null; -let pendingWatchStateOptions = null; +let youtubeReadyResolvers = []; +let watchApplySequence = 0; let youtubePlaybackRequestId = 0; let watchPlaylist = []; const peers = new Map(); @@ -346,16 +346,10 @@ async function ensureYoutubePlayer(initialState = {}) { youtubeReady = true; const iframe = youtubePlayer?.getIframe?.(); iframe?.setAttribute("allow", "autoplay; encrypted-media; fullscreen; picture-in-picture"); + youtubeReadyResolvers.splice(0).forEach((resolve) => resolve()); if (shouldAutoplay) { requestYoutubePlayback(youtubePlayer, initialVideoId, initialPosition); } - if (pendingWatchState) { - const state = pendingWatchState; - const pendingOptions = pendingWatchStateOptions || {}; - pendingWatchState = null; - pendingWatchStateOptions = null; - applyWatchState(state, pendingOptions).catch(console.error); - } }, onStateChange: handleYoutubeStateChange } @@ -370,6 +364,13 @@ async function ensureYoutubePlayer(initialState = {}) { return youtubePlayer; } +function whenYoutubeReady() { + if (youtubeReady) return Promise.resolve(); + return new Promise((resolve) => { + youtubeReadyResolvers.push(resolve); + }); +} + function loadYoutubeVideo(player, videoId, position) { player.loadVideoById({ videoId, @@ -523,6 +524,7 @@ function handleYoutubeStateChange(event) { } async function applyWatchState(state, { suppressEvents = true } = {}) { + const applySequence = ++watchApplySequence; const shouldPlay = state?.action === "play" || (!state?.action && Boolean(state?.playing)); watchPlaylist = normalizedPlaylist(state?.playlist); currentYoutubeVideoId = state?.videoId || ""; @@ -560,6 +562,8 @@ async function applyWatchState(state, { suppressEvents = true } = {}) { position, autoplay: shouldPlay }); + await whenYoutubeReady(); + if (applySequence !== watchApplySequence) return; if (suppressEvents) { applyingWatchStateUntil = Date.now() + 3000; } @@ -575,41 +579,36 @@ async function applyWatchState(state, { suppressEvents = true } = {}) { pauseEnforcementUntil = Math.max(pauseEnforcementUntil, Date.now() + 3000); } - if (youtubeReady) { - if (shouldPlay) { - setYoutubeStartPromptVisible(true); - const sameVideo = player.getVideoData?.().video_id === currentYoutubeVideoId; - const playerState = player.getPlayerState?.(); - if (sameVideo && ![YT.PlayerState.UNSTARTED, YT.PlayerState.CUED].includes(playerState)) { - player.seekTo(position, true); - } else { - loadYoutubeVideo(player, currentYoutubeVideoId, position); - } - if (playerState !== YT.PlayerState.PLAYING) { - player.playVideo(); - } - requestYoutubePlayback(player, currentYoutubeVideoId, position); + if (shouldPlay) { + setYoutubeStartPromptVisible(true); + const sameVideo = player.getVideoData?.().video_id === currentYoutubeVideoId; + const playerState = player.getPlayerState?.(); + if (sameVideo && ![YT.PlayerState.UNSTARTED, YT.PlayerState.CUED].includes(playerState)) { + player.seekTo(position, true); } else { - if (player.getVideoData?.().video_id !== currentYoutubeVideoId) { - player.cueVideoById(currentYoutubeVideoId, position); - } else { - player.pauseVideo(); - player.seekTo(position, true); - } - player.pauseVideo(); - setTimeout(() => { - player.pauseVideo(); - }, 150); - setTimeout(() => { - if (!desiredYoutubePlaying) player.pauseVideo(); - }, 1200); - setTimeout(() => { - if (!desiredYoutubePlaying) player.pauseVideo(); - }, 2500); + loadYoutubeVideo(player, currentYoutubeVideoId, position); } + if (playerState !== YT.PlayerState.PLAYING) { + player.playVideo(); + } + requestYoutubePlayback(player, currentYoutubeVideoId, position); } else { - pendingWatchState = state; - pendingWatchStateOptions = { suppressEvents }; + if (player.getVideoData?.().video_id !== currentYoutubeVideoId) { + player.cueVideoById(currentYoutubeVideoId, position); + } else { + player.pauseVideo(); + player.seekTo(position, true); + } + player.pauseVideo(); + setTimeout(() => { + player.pauseVideo(); + }, 150); + setTimeout(() => { + if (!desiredYoutubePlaying) player.pauseVideo(); + }, 1200); + setTimeout(() => { + if (!desiredYoutubePlaying) player.pauseVideo(); + }, 2500); } }