From c7dcec2560c1f907f399c53097edfbfbdff3dd79 Mon Sep 17 00:00:00 2001 From: Pas <74743263+Pasithea0@users.noreply.github.com> Date: Sun, 9 Nov 2025 13:48:28 -0700 Subject: [PATCH] Improve autoplay handling after PiP and fullscreen on iOS Adds logic to attempt autoplay when entering Picture-in-Picture or fullscreen modes, particularly to address autoplay restrictions on iOS. Introduces a flag to track if autoplay should be retried after loading, and ensures the UI updates appropriately if autoplay remains blocked. --- src/components/player/display/base.ts | 57 ++++++++++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/src/components/player/display/base.ts b/src/components/player/display/base.ts index e62c7f4c..3ccd5756 100644 --- a/src/components/player/display/base.ts +++ b/src/components/player/display/base.ts @@ -96,6 +96,7 @@ export function makeVideoElementDisplayInterface(): DisplayInterface { let lastVolume = 1; let lastValidDuration = 0; // Store the last valid duration to prevent reset during source switches let lastValidTime = 0; // Store the last valid time to prevent reset during source switches + let shouldAutoplayAfterLoad = false; // Flag to track if we should autoplay after loading completes const languagePromises = new Map< string, @@ -335,6 +336,15 @@ export function makeVideoElementDisplayInterface(): DisplayInterface { isPictureInPicture = isInWebkitPip; // Use native tracks in WebKit PiP mode for iOS compatibility emit("needstrack", isInWebkitPip); + + // On iOS, entering PiP may allow autoplay that was previously blocked + if (isInWebkitPip && videoElement.paused && shouldAutoplayAfterLoad) { + shouldAutoplayAfterLoad = false; + videoElement.play().catch(() => { + // If still blocked, emit pause to show play button + emit("pause", undefined); + }); + } } function setSource() { @@ -356,7 +366,22 @@ export function makeVideoElementDisplayInterface(): DisplayInterface { }); videoElement.addEventListener("playing", () => emit("play", undefined)); videoElement.addEventListener("pause", () => emit("pause", undefined)); - videoElement.addEventListener("canplay", () => emit("loading", false)); + videoElement.addEventListener("canplay", () => { + emit("loading", false); + // Attempt autoplay if this was an autoplay transition (startAt = 0) + if (shouldAutoplayAfterLoad && startAt === 0 && videoElement) { + shouldAutoplayAfterLoad = false; // Reset the flag + // Try to play - this will work on most platforms, but iOS may block it + const playPromise = videoElement.play(); + if (playPromise !== undefined) { + playPromise.catch(() => { + // Play was blocked (likely iOS), emit that we're not playing + // The AutoPlayStart component will show a play button + emit("pause", undefined); + }); + } + } + }); videoElement.addEventListener("waiting", () => emit("loading", true)); videoElement.addEventListener("volumechange", () => emit( @@ -465,6 +490,20 @@ export function makeVideoElementDisplayInterface(): DisplayInterface { !!(document as any).webkitFullscreenElement; // safari emit("fullscreen", isFullscreen); if (!isFullscreen) emit("needstrack", false); + + // On iOS, entering fullscreen may allow autoplay that was previously blocked + if ( + isFullscreen && + videoElement && + videoElement.paused && + shouldAutoplayAfterLoad + ) { + shouldAutoplayAfterLoad = false; + videoElement.play().catch(() => { + // If still blocked, emit pause to show play button + emit("pause", undefined); + }); + } } fscreen.addEventListener("fullscreenchange", fullscreenChange); @@ -472,6 +511,20 @@ export function makeVideoElementDisplayInterface(): DisplayInterface { isPictureInPicture = !!document.pictureInPictureElement; // Use native tracks in PiP mode for better compatibility with iOS and other platforms emit("needstrack", isPictureInPicture); + + // Entering PiP may allow autoplay that was previously blocked + if ( + isPictureInPicture && + videoElement && + videoElement.paused && + shouldAutoplayAfterLoad + ) { + shouldAutoplayAfterLoad = false; + videoElement.play().catch(() => { + // If still blocked, emit pause to show play button + emit("pause", undefined); + }); + } } document.addEventListener("enterpictureinpicture", pictureInPictureChange); @@ -502,6 +555,8 @@ export function makeVideoElementDisplayInterface(): DisplayInterface { source = ops.source; emit("loading", true); startAt = ops.startAt; + // Set autoplay flag if starting from beginning (indicates autoplay transition) + shouldAutoplayAfterLoad = ops.startAt === 0; setSource(); }, changeQuality(newAutomaticQuality, newPreferredQuality) {