Fix Doom Scroll mode bugs in TV libraries and video autoplay

TV library fix: the unfiltered Doom Scroll path was calling
/api/browse which explicitly rejects non-mixed libraries with a 400
error, leaving the item list empty. Replace it with the same TV API
hierarchy fetch already used by the filtered path (series → seasons →
episodes), matching how the rest of the TV library is loaded.

Autoplay fix (all library types): two interacting bugs caused videos
to silently stall on navigation. First, the play/pause effect had
current?.url in its deps, so navigating while paused would call
pause() on the freshly-mounted video element before the isPaused reset
could take effect. Second, browser autoplay policy blocks unmuted
play() calls and the rejection was silently swallowed with no recovery.
Fix by merging the isPaused reset and the play() call into one
navigation effect, and adding a muted fallback on rejection so playback
always starts — the user can unmute manually afterward.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Garret Patti
2026-04-06 20:21:27 -04:00
parent 4d75e74cab
commit f08950f456
2 changed files with 39 additions and 10 deletions

View File

@@ -73,9 +73,20 @@ export default function DoomScrollView({ items, videoContext = 'mixed', onClose
setTimeout(() => { cooldownRef.current = false }, 300)
}, [goNext, goPrev])
// Reset pause when switching items
// On navigation to a new item: reset pause state and start playing.
// Merging the reset + play() into one effect prevents the old isPaused=true
// value from calling pause() on the freshly-mounted video element before the
// reset fires. If autoplay is blocked by browser policy (common when unmuted),
// fall back to muted and retry — the user can unmute manually afterward.
useEffect(() => {
setIsPaused(false)
if (!videoRef.current) return
videoRef.current.play().catch(() => {
if (!videoRef.current) return
videoRef.current.muted = true
setLocalMuted(true)
videoRef.current.play().catch(() => {})
})
}, [current?.url])
// Sync muted imperatively — React's muted prop is not reliable
@@ -83,7 +94,8 @@ export default function DoomScrollView({ items, videoContext = 'mixed', onClose
if (videoRef.current) videoRef.current.muted = localMuted
}, [localMuted, current?.url])
// Sync play/pause imperatively
// Sync play/pause imperatively for user-initiated pause/unpause only.
// current?.url is intentionally excluded: navigation is handled above.
useEffect(() => {
if (!videoRef.current) return
if (isPaused) {
@@ -91,7 +103,7 @@ export default function DoomScrollView({ items, videoContext = 'mixed', onClose
} else {
videoRef.current.play().catch(() => {})
}
}, [isPaused, current?.url])
}, [isPaused])
// Auto-play timer — resets on each new item, pause, enable/disable, or interval change
useEffect(() => {