From 07cee7b48137ef10b0c71b91f6e785bf7f52a1dd Mon Sep 17 00:00:00 2001 From: ghassan Date: Sat, 16 May 2026 14:07:23 +0300 Subject: [PATCH] Fix video autoplay: register MANIFEST_PARSED before loadSource, use loadedmetadata for MP4/native-HLS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The MANIFEST_PARSED listener was registered after loadSource/attachMedia, so cached manifests could fire the event before the listener was set — meaning tryAutoplay never ran. Fix: register the listener first, then call loadSource. Also switch MP4 and native-HLS paths to play on loadedmetadata (not immediately after load()) which is the earliest safe moment to call play(). Belt-and-suspenders canplay fallback now also explicitly mutes before calling play(). Co-Authored-By: Claude Sonnet 4.6 --- .../views/components/video-player.blade.php | 48 ++++++++++++------- 1 file changed, 30 insertions(+), 18 deletions(-) diff --git a/resources/views/components/video-player.blade.php b/resources/views/components/video-player.blade.php index 048277f..863e9b7 100644 --- a/resources/views/components/video-player.blade.php +++ b/resources/views/components/video-player.blade.php @@ -808,28 +808,34 @@ function getShuffledPrevUrl() { window._ytpHls = null; -function tryAutoplay() { - video.muted = true; - video.autoplay = true; - video.play().catch(function(){}); -} - function initSource() { video.muted = true; video.autoplay = true; if (HLS_URL && window.Hls && Hls.isSupported()) { window._ytpHls = new Hls({ startLevel: -1 }); + // Register MANIFEST_PARSED before loadSource to avoid cache race condition + window._ytpHls.once(Hls.Events.MANIFEST_PARSED, function() { + video.muted = true; + video.play().catch(function(){}); + }); window._ytpHls.loadSource(HLS_URL); window._ytpHls.attachMedia(video); - window._ytpHls.once(Hls.Events.MANIFEST_PARSED, tryAutoplay); } else if (HLS_URL && video.canPlayType('application/vnd.apple.mpegurl')) { + // Native HLS (Safari) — set src then play on loadedmetadata video.src = HLS_URL; video.load(); - video.play().catch(function(){}); + video.addEventListener('loadedmetadata', function() { + video.muted = true; + video.play().catch(function(){}); + }, { once: true }); } else { + // Plain MP4 — play on loadedmetadata (canplay/MANIFEST_PARSED don't apply) video.src = MP4_URL; video.load(); - video.play().catch(function(){}); + video.addEventListener('loadedmetadata', function() { + video.muted = true; + video.play().catch(function(){}); + }, { once: true }); } } @@ -840,22 +846,26 @@ window._ytpLoadSource = function(hlsUrl, mp4Url) { video.autoplay = true; if (hlsUrl && window.Hls && Hls.isSupported()) { window._ytpHls = new Hls({ startLevel: -1 }); - window._ytpHls.loadSource(hlsUrl); - window._ytpHls.attachMedia(video); window._ytpHls.once(Hls.Events.MANIFEST_PARSED, function() { video.muted = false; video.play().catch(function(){}); }); + window._ytpHls.loadSource(hlsUrl); + window._ytpHls.attachMedia(video); } else if (hlsUrl && video.canPlayType('application/vnd.apple.mpegurl')) { video.src = hlsUrl; video.load(); - video.muted = false; - video.play().catch(function(){}); + video.addEventListener('loadedmetadata', function() { + video.muted = false; + video.play().catch(function(){}); + }, { once: true }); } else { video.src = mp4Url; video.load(); - video.muted = false; - video.play().catch(function(){}); + video.addEventListener('loadedmetadata', function() { + video.muted = false; + video.play().catch(function(){}); + }, { once: true }); } }; @@ -1316,15 +1326,17 @@ function init() { sessionStorage.removeItem('ytpMiniState'); } - // canplay fires for ALL source types (HLS.js, MP4, Safari native HLS) - // once the browser has enough data to start — most reliable autoplay trigger + // canplay is a belt-and-suspenders fallback — also mute before play video.addEventListener('canplay', function autoStart() { video.removeEventListener('canplay', autoStart); if (miniSeekTime > 0) { video.currentTime = miniSeekTime; miniSeekTime = 0; } - video.play().catch(() => {}); + if (video.paused) { + video.muted = true; + video.play().catch(() => {}); + } }); // Update duration when metadata is ready