seek via underlying media element for resume

This commit is contained in:
2026-06-06 21:54:01 +03:00
parent 5d12a5edc6
commit c9f6d92a7f
+47 -9
View File
@@ -348,6 +348,19 @@ export default function PlayerPage() {
} }
return '' return ''
})() })()
if (typeof window !== 'undefined' && playbackInfo) {
const transcodingUrl = resolvedSource?.TranscodingUrl
const supportsDirectPlay = resolvedSource?.SupportsDirectPlay
const hasRuntimeTicks = transcodingUrl ? transcodingUrl.includes('runtimeTicks=') : null
console.log('[player] stream resolved', {
id,
startTimeTicks,
supportsDirectPlay,
transcodingUrlPresent: !!transcodingUrl,
transcodingUrlHasRuntimeTicks: hasRuntimeTicks,
streamUrl: streamUrl.replace(/api_key=[^&]+/, 'api_key=***'),
})
}
/* Reset transient flags on item change */ /* Reset transient flags on item change */
useEffect(() => { useEffect(() => {
@@ -1164,8 +1177,13 @@ export default function PlayerPage() {
if (p && pendingSeekRef.current != null) { if (p && pendingSeekRef.current != null) {
const target = pendingSeekRef.current const target = pendingSeekRef.current
pendingSeekRef.current = null pendingSeekRef.current = null
if (p.currentTime < target - 0.5 || p.currentTime > target + 0.5) { const mediaEl = (p as any).media as HTMLMediaElement | undefined
try { p.currentTime = target } catch { /* ignore */ } const before = mediaEl ? mediaEl.currentTime : p.currentTime
if (before < target - 0.5 || before > target + 0.5) {
try {
if (mediaEl) mediaEl.currentTime = target
else p.currentTime = target
} catch { /* ignore */ }
} }
p.play().catch(() => {}) p.play().catch(() => {})
} }
@@ -1403,13 +1421,29 @@ export default function PlayerPage() {
setResumePromptOpen(false) setResumePromptOpen(false)
const p = playerRef.current const p = playerRef.current
const pos = Number(item?.UserData?.PlaybackPositionTicks ?? 0) const pos = Number(item?.UserData?.PlaybackPositionTicks ?? 0)
const target = pos > 0 ? pos / 10_000_000 : 0
if (typeof window !== 'undefined') {
console.log('[player] resume prompt clicked', {
id,
pos,
target,
mediaElementCurrentTime: (p as any)?.media?.currentTime,
streamUrlStart: streamUrl.includes('StartTimeTicks=') || streamUrl.includes('runtimeTicks='),
})
}
if (p) { if (p) {
// Queue the seek: if the video isn't ready yet, onCanPlay // Vidstack has a known issue (GH #941) where setting
// picks it up. The setter works on a paused player in // currentTime on the MediaPlayerInstance for a direct-play
// vidstack, but buffering the seek target can race the // MP4 source restarts the video from the beginning. The
// canPlay event for the initial source. // workaround is to set currentTime on the underlying
pendingSeekRef.current = pos > 0 ? pos / 10_000_000 : 0 // HTMLMediaElement. For HLS it works either way; we use
try { p.currentTime = pendingSeekRef.current } catch { /* ignore */ } // the underlying element for consistency.
pendingSeekRef.current = target
const mediaEl = (p as any).media as HTMLMediaElement | undefined
try {
if (mediaEl) mediaEl.currentTime = target
else p.currentTime = target
} catch { /* ignore */ }
p.play().catch(() => {}) p.play().catch(() => {})
} }
}, },
@@ -1418,7 +1452,11 @@ export default function PlayerPage() {
const p = playerRef.current const p = playerRef.current
if (p) { if (p) {
pendingSeekRef.current = 0 pendingSeekRef.current = 0
try { p.currentTime = 0 } catch { /* ignore */ } const mediaEl = (p as any).media as HTMLMediaElement | undefined
try {
if (mediaEl) mediaEl.currentTime = 0
else p.currentTime = 0
} catch { /* ignore */ }
p.play().catch(() => {}) p.play().catch(() => {})
} }
}, },