Explorar o código

Handle autoplay failures by showing video controls (#29412)

GitOrigin-RevId: afe5d70d00bfda97cde7e3479875b9cd7be5c46f
Rebeka Dekany hai 9 meses
pai
achega
ec5a283e9e
Modificáronse 1 ficheiros con 37 adicións e 17 borrados
  1. 37 17
      services/web/frontend/js/features/autoplay-video/index.js

+ 37 - 17
services/web/frontend/js/features/autoplay-video/index.js

@@ -1,5 +1,12 @@
 import { debugConsole } from '@/utils/debugging'
 import { debugConsole } from '@/utils/debugging'
 
 
+let hasHandledAutoplayFailure = false
+
+function enableControls(videoEl, reason) {
+  debugConsole.log(`Enabling video controls: ${reason}`)
+  videoEl.setAttribute('controls', '')
+}
+
 function setup(videoEl) {
 function setup(videoEl) {
   const reducedMotionReduce = window.matchMedia(
   const reducedMotionReduce = window.matchMedia(
     '(prefers-reduced-motion: reduce)'
     '(prefers-reduced-motion: reduce)'
@@ -10,7 +17,7 @@ function setup(videoEl) {
     // in console, if user seeks the control seek bar relatively fast
     // in console, if user seeks the control seek bar relatively fast
     // AbortError: The fetching process for the media resource was aborted by the user agent at the user's request.
     // AbortError: The fetching process for the media resource was aborted by the user agent at the user's request.
     // this is only a problem in firefox (tested in macOS), chrome and safari is fine
     // this is only a problem in firefox (tested in macOS), chrome and safari is fine
-    videoEl.setAttribute('controls', '')
+    enableControls(videoEl, 'reduced motion preference')
 
 
     return
     return
   }
   }
@@ -21,11 +28,29 @@ function setup(videoEl) {
 
 
   let videoIsVisible
   let videoIsVisible
 
 
+  function handleAutoplayFailure(error) {
+    // Possible HTMLMediaElement.play() errors
+    // Only show controls for errors where manual play would work
+    // NotAllowedError: autoplay blocked by browser/device settings
+    // AbortError: play attempt interrupted by browser
+    if (error.name === 'NotAllowedError' || error.name === 'AbortError') {
+      if (!hasHandledAutoplayFailure) {
+        hasHandledAutoplayFailure = true
+
+        document.querySelectorAll('[data-ol-autoplay-video]').forEach(video => {
+          enableControls(video, `autoplay blocked (${error.name})`)
+        })
+      }
+    } else {
+      debugConsole.error('Video playback error:', error)
+    }
+  }
+
   videoEl.addEventListener('ended', () => {
   videoEl.addEventListener('ended', () => {
     setTimeout(() => {
     setTimeout(() => {
       videoEl.currentTime = 0
       videoEl.currentTime = 0
-      if (videoIsVisible) {
-        videoEl.play()
+      if (videoIsVisible && !hasHandledAutoplayFailure) {
+        videoEl.play().catch(handleAutoplayFailure)
       }
       }
     }, DELAY_BEFORE_REPLAY)
     }, DELAY_BEFORE_REPLAY)
   })
   })
@@ -35,23 +60,18 @@ function setup(videoEl) {
       for (const change of changes) {
       for (const change of changes) {
         if (change.isIntersecting) {
         if (change.isIntersecting) {
           videoIsVisible = true
           videoIsVisible = true
-          if (videoEl.readyState >= videoEl.HAVE_FUTURE_DATA) {
-            if (!videoEl.ended) {
-              videoEl
-                .play()
-                .catch(error =>
-                  debugConsole.error('Video autoplay failed:', error)
-                )
-            } else {
-              videoEl
-                .play()
-                .catch(error =>
-                  debugConsole.error('Video autoplay failed:', error)
-                )
-            }
+          if (
+            !hasHandledAutoplayFailure &&
+            videoEl.readyState >= videoEl.HAVE_FUTURE_DATA
+          ) {
+            videoEl.play().catch(handleAutoplayFailure)
           }
           }
         } else {
         } else {
           videoIsVisible = false
           videoIsVisible = false
+          // Pause video when it leaves viewport to save resources
+          if (!videoEl.paused) {
+            videoEl.pause()
+          }
         }
         }
       }
       }
     },
     },