fix resume starting from beginning
This commit is contained in:
@@ -63,6 +63,9 @@ export default function PlayerPage() {
|
||||
const playerRef = useRef<MediaPlayerInstance>(null)
|
||||
const qc = useQueryClient()
|
||||
const progressRef = useRef(0)
|
||||
// Seek the in-player resume prompt asks for, but only applied on the
|
||||
// next canPlay so the seek survives when the source isn't ready yet.
|
||||
const pendingSeekRef = useRef<number | null>(null)
|
||||
|
||||
// Start paused so the play icon shows until the player actually begins
|
||||
// playback - if autoplay succeeds, the onPlay handler flips this to false.
|
||||
@@ -291,6 +294,21 @@ export default function PlayerPage() {
|
||||
const resume = searchParams.get('resume') === 'true'
|
||||
const positionTicks = item?.UserData?.PlaybackPositionTicks
|
||||
const startTimeTicks = resume && positionTicks ? Number(positionTicks) : undefined
|
||||
// If the user clicked Resume from a deep link, the item hasn't
|
||||
// returned yet on the first render and startTimeTicks is undefined.
|
||||
// Block the first PlaybackInfo call until the saved position is
|
||||
// known - otherwise the server returns a stream that starts at 0
|
||||
// and the video buffers before the second query refires.
|
||||
const playbackInfoReady = !resume || startTimeTicks !== undefined
|
||||
if (typeof window !== 'undefined') {
|
||||
console.log('[player] resume state', {
|
||||
id,
|
||||
resume,
|
||||
positionTicks,
|
||||
startTimeTicks,
|
||||
playbackInfoReady,
|
||||
})
|
||||
}
|
||||
|
||||
/* Resolve the proper stream URL from Jellyfin's PlaybackInfo endpoint.
|
||||
* The server picks direct-play / direct-stream / transcoded HLS based on
|
||||
@@ -312,6 +330,7 @@ export default function PlayerPage() {
|
||||
startTimeTicks,
|
||||
streamAudioIndex ?? undefined,
|
||||
maxBitrate,
|
||||
playbackInfoReady,
|
||||
)
|
||||
const resolvedSource = playbackInfo?.MediaSources?.[0]
|
||||
const streamUrl = (() => {
|
||||
@@ -337,6 +356,7 @@ export default function PlayerPage() {
|
||||
setAudioIndex(null)
|
||||
setStreamAudioIndex(null)
|
||||
setEndCardOpen(false)
|
||||
pendingSeekRef.current = null
|
||||
usePlayerRuntimeStore.getState().resetForNewItem()
|
||||
}, [id, setPanel])
|
||||
|
||||
@@ -1140,6 +1160,15 @@ export default function PlayerPage() {
|
||||
// Apply the saved playback rate (default 1) so users who like
|
||||
// 1.5x get it on every episode without re-setting.
|
||||
if (p) applyPlaybackRate(playbackRate)
|
||||
// Apply any deferred seek queued by the resume prompt.
|
||||
if (p && pendingSeekRef.current != null) {
|
||||
const target = pendingSeekRef.current
|
||||
pendingSeekRef.current = null
|
||||
if (p.currentTime < target - 0.5 || p.currentTime > target + 0.5) {
|
||||
try { p.currentTime = target } catch { /* ignore */ }
|
||||
}
|
||||
p.play().catch(() => {})
|
||||
}
|
||||
}}
|
||||
onEnded={() => {
|
||||
// Queue takes priority - playlist play/shuffle should always
|
||||
@@ -1374,14 +1403,24 @@ export default function PlayerPage() {
|
||||
setResumePromptOpen(false)
|
||||
const p = playerRef.current
|
||||
const pos = Number(item?.UserData?.PlaybackPositionTicks ?? 0)
|
||||
if (p && pos > 0) p.currentTime = pos / 10_000_000
|
||||
p?.play().catch(() => {})
|
||||
if (p) {
|
||||
// Queue the seek: if the video isn't ready yet, onCanPlay
|
||||
// picks it up. The setter works on a paused player in
|
||||
// vidstack, but buffering the seek target can race the
|
||||
// canPlay event for the initial source.
|
||||
pendingSeekRef.current = pos > 0 ? pos / 10_000_000 : 0
|
||||
try { p.currentTime = pendingSeekRef.current } catch { /* ignore */ }
|
||||
p.play().catch(() => {})
|
||||
}
|
||||
},
|
||||
onRestart: () => {
|
||||
setResumePromptOpen(false)
|
||||
const p = playerRef.current
|
||||
if (p) p.currentTime = 0
|
||||
p?.play().catch(() => {})
|
||||
if (p) {
|
||||
pendingSeekRef.current = 0
|
||||
try { p.currentTime = 0 } catch { /* ignore */ }
|
||||
p.play().catch(() => {})
|
||||
}
|
||||
},
|
||||
}}
|
||||
recap={{
|
||||
|
||||
Reference in New Issue
Block a user