From 2f50931bebfd82b77c3e6efed01b9f38f5720c28 Mon Sep 17 00:00:00 2001 From: CodeDevMLH <145071728+CodeDevMLH@users.noreply.github.com> Date: Sat, 14 Feb 2026 01:42:42 +0100 Subject: [PATCH] Fix YouTube player readiness checks and improve polling logic for video playback --- .../Web/mediaBarEnhanced.js | 72 +++++++++++++++---- 1 file changed, 60 insertions(+), 12 deletions(-) diff --git a/Jellyfin.Plugin.MediaBarEnhanced/Web/mediaBarEnhanced.js b/Jellyfin.Plugin.MediaBarEnhanced/Web/mediaBarEnhanced.js index c3e35ce..c5bde24 100644 --- a/Jellyfin.Plugin.MediaBarEnhanced/Web/mediaBarEnhanced.js +++ b/Jellyfin.Plugin.MediaBarEnhanced/Web/mediaBarEnhanced.js @@ -2743,7 +2743,7 @@ const SlideshowManager = { if (!videoBackdrop && !hasRegisteredPlayer) return false; - if (videoBackdrop.tagName === 'VIDEO') { + if (videoBackdrop && videoBackdrop.tagName === 'VIDEO') { videoBackdrop.currentTime = 0; videoBackdrop.muted = STATE.slideshow.isMuted; if (!STATE.slideshow.isMuted) videoBackdrop.volume = 0.4; @@ -2762,7 +2762,9 @@ const SlideshowManager = { // YouTube player const player = STATE.slideshow.videoPlayers && STATE.slideshow.videoPlayers[itemId]; - if (player && typeof player.loadVideoById === 'function' && player._videoId) { + const playerIsReady = player && typeof player.loadVideoById === 'function' && player._videoId; + + if (playerIsReady) { player.loadVideoById({ videoId: player._videoId, startSeconds: player._startTime || 0, @@ -2788,18 +2790,64 @@ const SlideshowManager = { } }, 1000); return true; - } else if (player && typeof player.seekTo === 'function') { - // Fallback if loadVideoById is not available or videoId missing but player object exists - const startTime = player._startTime || 0; - player.seekTo(startTime); - player.playVideo(); - return true; } - // YouTube player not ready yet (still loading from preload) — mark for auto-play when onReady fires - if (videoBackdrop && videoBackdrop.id && videoBackdrop.id.startsWith('youtube-player-') && !player) { - console.log(`YouTube player for ${itemId} not ready yet, marking _pendingPlay`); - videoBackdrop._pendingPlay = true; + // YouTube player exists but NOT fully ready yet. + // new YT.Player() returns an object immediately, but API methods like + // loadVideoById and _videoId aren't available until onReady fires. + // onReady may have ALREADY fired (during preload), so we can't rely on + // _pendingPlay alone. Instead, poll for readiness. + const ytContainer = videoBackdrop || slide.querySelector(`[id^="youtube-player-"]`); + if (ytContainer && (ytContainer.tagName === 'IFRAME' || (ytContainer.id && ytContainer.id.startsWith('youtube-player-')))) { + console.log(`YouTube player for ${itemId} not ready yet, polling for readiness...`); + + // Also set _pendingPlay as fallback in case onReady hasn't fired yet + ytContainer._pendingPlay = true; + + let attempts = 0; + const maxAttempts = 25; // 25 * 200ms = 5 seconds max + const pollInterval = setInterval(() => { + attempts++; + + // Stop if slide is no longer active (user navigated away) + if (!slide.classList.contains('active')) { + clearInterval(pollInterval); + return; + } + + const p = STATE.slideshow.videoPlayers && STATE.slideshow.videoPlayers[itemId]; + if (p && typeof p.loadVideoById === 'function' && p._videoId) { + clearInterval(pollInterval); + console.log(`YouTube player for ${itemId} now ready (after ${attempts * 200}ms), starting playback`); + + if (STATE.slideshow.isPaused) return; + + p.loadVideoById({ + videoId: p._videoId, + startSeconds: p._startTime || 0, + endSeconds: p._endTime + }); + + if (STATE.slideshow.isMuted) { + p.mute(); + } else { + p.unMute(); + p.setVolume(40); + } + + // Pause slideshow timer when video starts if configured + if (CONFIG.waitForTrailerToEnd && STATE.slideshow.slideInterval) { + STATE.slideshow.slideInterval.stop(); + } + return; + } + + if (attempts >= maxAttempts) { + clearInterval(pollInterval); + console.warn(`YouTube player for ${itemId} failed to become ready after ${maxAttempts * 200}ms`); + } + }, 200); + return true; }