Fix video autoplay: register MANIFEST_PARSED before loadSource, use loadedmetadata for MP4/native-HLS

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 <noreply@anthropic.com>
This commit is contained in:
ghassan 2026-05-16 14:07:23 +03:00
parent e74862a24d
commit 07cee7b481

View File

@ -808,28 +808,34 @@ function getShuffledPrevUrl() {
window._ytpHls = null; window._ytpHls = null;
function tryAutoplay() {
video.muted = true;
video.autoplay = true;
video.play().catch(function(){});
}
function initSource() { function initSource() {
video.muted = true; video.muted = true;
video.autoplay = true; video.autoplay = true;
if (HLS_URL && window.Hls && Hls.isSupported()) { if (HLS_URL && window.Hls && Hls.isSupported()) {
window._ytpHls = new Hls({ startLevel: -1 }); 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.loadSource(HLS_URL);
window._ytpHls.attachMedia(video); window._ytpHls.attachMedia(video);
window._ytpHls.once(Hls.Events.MANIFEST_PARSED, tryAutoplay);
} else if (HLS_URL && video.canPlayType('application/vnd.apple.mpegurl')) { } else if (HLS_URL && video.canPlayType('application/vnd.apple.mpegurl')) {
// Native HLS (Safari) — set src then play on loadedmetadata
video.src = HLS_URL; video.src = HLS_URL;
video.load(); video.load();
video.play().catch(function(){}); video.addEventListener('loadedmetadata', function() {
video.muted = true;
video.play().catch(function(){});
}, { once: true });
} else { } else {
// Plain MP4 — play on loadedmetadata (canplay/MANIFEST_PARSED don't apply)
video.src = MP4_URL; video.src = MP4_URL;
video.load(); 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; video.autoplay = true;
if (hlsUrl && window.Hls && Hls.isSupported()) { if (hlsUrl && window.Hls && Hls.isSupported()) {
window._ytpHls = new Hls({ startLevel: -1 }); window._ytpHls = new Hls({ startLevel: -1 });
window._ytpHls.loadSource(hlsUrl);
window._ytpHls.attachMedia(video);
window._ytpHls.once(Hls.Events.MANIFEST_PARSED, function() { window._ytpHls.once(Hls.Events.MANIFEST_PARSED, function() {
video.muted = false; video.muted = false;
video.play().catch(function(){}); video.play().catch(function(){});
}); });
window._ytpHls.loadSource(hlsUrl);
window._ytpHls.attachMedia(video);
} else if (hlsUrl && video.canPlayType('application/vnd.apple.mpegurl')) { } else if (hlsUrl && video.canPlayType('application/vnd.apple.mpegurl')) {
video.src = hlsUrl; video.src = hlsUrl;
video.load(); video.load();
video.muted = false; video.addEventListener('loadedmetadata', function() {
video.play().catch(function(){}); video.muted = false;
video.play().catch(function(){});
}, { once: true });
} else { } else {
video.src = mp4Url; video.src = mp4Url;
video.load(); video.load();
video.muted = false; video.addEventListener('loadedmetadata', function() {
video.play().catch(function(){}); video.muted = false;
video.play().catch(function(){});
}, { once: true });
} }
}; };
@ -1316,15 +1326,17 @@ function init() {
sessionStorage.removeItem('ytpMiniState'); sessionStorage.removeItem('ytpMiniState');
} }
// canplay fires for ALL source types (HLS.js, MP4, Safari native HLS) // canplay is a belt-and-suspenders fallback — also mute before play
// once the browser has enough data to start — most reliable autoplay trigger
video.addEventListener('canplay', function autoStart() { video.addEventListener('canplay', function autoStart() {
video.removeEventListener('canplay', autoStart); video.removeEventListener('canplay', autoStart);
if (miniSeekTime > 0) { if (miniSeekTime > 0) {
video.currentTime = miniSeekTime; video.currentTime = miniSeekTime;
miniSeekTime = 0; miniSeekTime = 0;
} }
video.play().catch(() => {}); if (video.paused) {
video.muted = true;
video.play().catch(() => {});
}
}); });
// Update duration when metadata is ready // Update duration when metadata is ready