bug处理

This commit is contained in:
truewhile
2026-09-08 00:36:56 +08:00
parent ad427fc0d2
commit b23b1d728e
2 changed files with 51 additions and 15 deletions
+18 -4
View File
@@ -100,6 +100,7 @@ export function PlayerControls({
const subtitleMenuOpenRef = useRef(false) const subtitleMenuOpenRef = useRef(false)
const danmakuOpenRef = useRef(false) const danmakuOpenRef = useRef(false)
const playlistOpenRef = useRef(false) const playlistOpenRef = useRef(false)
const pendingSeekRef = useRef<number | null>(null)
useEffect(() => { useEffect(() => {
controlsHoveredRef.current = controlsHovered controlsHoveredRef.current = controlsHovered
@@ -184,16 +185,28 @@ export function PlayerControls({
onMove() onMove()
} }
const syncTime = () => { const syncTime = () => {
if (!isScrubbingRef.current) { if (isScrubbingRef.current) return
setCurrentTime(streamOffset + el.currentTime) if (pendingSeekRef.current !== null) {
const curAbs = streamOffset + el.currentTime
if (Math.abs(curAbs - pendingSeekRef.current) < 3 && el.currentTime > 0.1) {
pendingSeekRef.current = null
setCurrentTime(curAbs)
} else {
setCurrentTime(pendingSeekRef.current)
}
return
} }
setCurrentTime(streamOffset + el.currentTime)
} }
const syncMeta = () => { const syncMeta = () => {
const streamDur = Number.isFinite(el.duration) ? el.duration : 0 const streamDur = Number.isFinite(el.duration) ? el.duration : 0
setDuration(Math.max(knownDuration || 0, streamOffset + streamDur)) setDuration(Math.max(knownDuration || 0, streamOffset + streamDur))
if (!isScrubbingRef.current) { if (isScrubbingRef.current) return
setCurrentTime(streamOffset + el.currentTime) if (pendingSeekRef.current !== null) {
setCurrentTime(pendingSeekRef.current)
return
} }
setCurrentTime(streamOffset + el.currentTime)
} }
const syncVolume = () => { const syncVolume = () => {
setVolume(el.volume) setVolume(el.volume)
@@ -269,6 +282,7 @@ export function PlayerControls({
const el = video() const el = video()
if (!el) return if (!el) return
if (onSeekAbsolute?.(absolute)) { if (onSeekAbsolute?.(absolute)) {
pendingSeekRef.current = absolute
setCurrentTime(absolute) setCurrentTime(absolute)
return return
} }
+33 -11
View File
@@ -259,9 +259,15 @@ export function PlayerPage() {
if (cancelled || !ref.current) return if (cancelled || !ref.current) return
if (HlsCtor.isSupported()) { if (HlsCtor.isSupported()) {
const hls = new HlsCtor({ enableWorker: true, lowLatencyMode: false }) const hls = new HlsCtor({ enableWorker: true, lowLatencyMode: false })
try {
video.currentTime = 0
} catch {
// ignore
}
hls.loadSource(url) hls.loadSource(url)
hls.attachMedia(video) hls.attachMedia(video)
hls.on(HlsCtor.Events.MANIFEST_PARSED, () => { hls.on(HlsCtor.Events.MANIFEST_PARSED, () => {
void video.play().catch(() => undefined)
// .strm 入库时常缺 duration;转码启动时会补探测,这里刷新一次给进度条总时长。 // .strm 入库时常缺 duration;转码启动时会补探测,这里刷新一次给进度条总时长。
if (durationSec > 0) return if (durationSec > 0) return
mediaAPI mediaAPI
@@ -287,14 +293,19 @@ export function PlayerPage() {
} }
hlsRef.current = hls hlsRef.current = hls
} else if (video.canPlayType('application/vnd.apple.mpegurl')) { } else if (video.canPlayType('application/vnd.apple.mpegurl')) {
try {
video.currentTime = 0
} catch {
// ignore
}
video.src = url video.src = url
void video.play().catch(() => undefined)
} else { } else {
setHlsUnavailable(true) setHlsUnavailable(true)
setPlayerError('当前浏览器不支持 HLS,正在尝试直接播放。') setPlayerError('当前浏览器不支持 HLS,正在尝试直接播放。')
toast.error('当前浏览器不支持 HLS,降级到直接播放') toast.error('当前浏览器不支持 HLS,降级到直接播放')
setMode('direct') setMode('direct')
} }
void video.play().catch(() => undefined)
}).catch(() => { }).catch(() => {
if (cancelled) return if (cancelled) return
setHlsUnavailable(true) setHlsUnavailable(true)
@@ -381,22 +392,33 @@ export function PlayerPage() {
} }
}, [resumePosition, initialSeekDone, mode, hlsStartSec]) }, [resumePosition, initialSeekDone, mode, hlsStartSec])
// 使用 ref 实时同步进度计算所需的状态,避免每次 hlsStartSec 改变都触发 cleanup 并误上报旧进度
const hlsStartSecRef = useRef(hlsStartSec)
hlsStartSecRef.current = hlsStartSec
const modeRef = useRef(mode)
modeRef.current = mode
// Persist resume position every 10 seconds while playing, and immediately upon pause/unmount. // Persist resume position every 10 seconds while playing, and immediately upon pause/unmount.
useEffect(() => { useEffect(() => {
if (!media || !ref.current) return if (!id || !ref.current) return
const video = ref.current const video = ref.current
const absolutePositionMs = () => const absolutePositionMs = () => {
Math.floor(((mode === 'hls' ? hlsStartSec : 0) + video.currentTime) * 1000) const currentStartSec = modeRef.current === 'hls' ? hlsStartSecRef.current : 0
const absoluteDurationMs = () => return Math.floor((currentStartSec + (video.currentTime || 0)) * 1000)
Math.floor(Math.max(media.duration_sec || 0, (mode === 'hls' ? hlsStartSec : 0) + (video.duration || 0)) * 1000) }
const absoluteDurationMs = () => {
const currentStartSec = modeRef.current === 'hls' ? hlsStartSecRef.current : 0
const mediaDur = mediaRef.current?.duration_sec || 0
return Math.floor(Math.max(mediaDur, currentStartSec + (video.duration || 0)) * 1000)
}
const handler = () => { const handler = () => {
const now = Date.now() const now = Date.now()
if (now - lastSentRef.current < 10_000) return if (now - lastSentRef.current < 10_000) return
lastSentRef.current = now lastSentRef.current = now
const positionMs = absolutePositionMs() const positionMs = absolutePositionMs()
const durationMs = absoluteDurationMs() const durationMs = absoluteDurationMs()
if (positionMs > 0) { if (positionMs > 0 && mediaRef.current) {
playbackAPI.recordProgress(media.id, positionMs, durationMs).catch(() => undefined) playbackAPI.recordProgress(mediaRef.current.id, positionMs, durationMs).catch(() => undefined)
} }
} }
video.addEventListener('timeupdate', handler) video.addEventListener('timeupdate', handler)
@@ -406,11 +428,11 @@ export function PlayerPage() {
video.removeEventListener('pause', handler) video.removeEventListener('pause', handler)
const positionMs = absolutePositionMs() const positionMs = absolutePositionMs()
const durationMs = absoluteDurationMs() const durationMs = absoluteDurationMs()
if (positionMs > 0 && media) { if (positionMs > 0 && mediaRef.current) {
playbackAPI.recordProgress(media.id, positionMs, durationMs).catch(() => undefined) playbackAPI.recordProgress(mediaRef.current.id, positionMs, durationMs).catch(() => undefined)
} }
} }
}, [media, mode, hlsStartSec]) }, [id])
// 加载剧集/播放列表 // 加载剧集/播放列表
useEffect(() => { useEffect(() => {