This commit is contained in:
truewhile
2026-10-21 09:19:14 +08:00
parent 7793d50906
commit 3057664a48
3 changed files with 31 additions and 11 deletions
+5 -2
View File
@@ -37,7 +37,7 @@ import {
type SubtitlePosition, type SubtitlePosition,
type SubtitleStylePreset, type SubtitleStylePreset,
} from '../utils/subtitleDisplay' } from '../utils/subtitleDisplay'
import { pickPlayerMode, needsTranscodeForBrowser, isDirectStreamMedia, isStrmMedia, type PlayerMode } from './playerPageModel' import { pickPlayerMode, needsTranscodeForBrowser, isDirectStreamMedia, isStrmMedia, shouldShowPlayerDiagnostics, type PlayerMode } from './playerPageModel'
import { classifyDirectPlayError, directPlayFailureCopy, directStallTimeoutMs, DIRECT_SEEK_GRACE_MS } from './directPlayError' import { classifyDirectPlayError, directPlayFailureCopy, directStallTimeoutMs, DIRECT_SEEK_GRACE_MS } from './directPlayError'
import type { DirectPlayFailureReason } from './directPlayError' import type { DirectPlayFailureReason } from './directPlayError'
import { canSeekDirectNow, DIRECT_SEEK_WAIT_MS } from './directPlaySeek' import { canSeekDirectNow, DIRECT_SEEK_WAIT_MS } from './directPlaySeek'
@@ -181,6 +181,7 @@ export function PlayerPage() {
const [playerError, setPlayerError] = useState('') const [playerError, setPlayerError] = useState('')
// 媒体元数据加载失败(404 / 无权限等):舞台区直接展示错误而不是永远「加载中」 // 媒体元数据加载失败(404 / 无权限等):舞台区直接展示错误而不是永远「加载中」
const [loadError, setLoadError] = useState('') const [loadError, setLoadError] = useState('')
const diagnosticsVisible = shouldShowPlayerDiagnostics(playerError, loadError)
// 「客户端直连解码」模式:宿主机不转码,播放器强制 direct play、隐藏 HLS 切换。 // 「客户端直连解码」模式:宿主机不转码,播放器强制 direct play、隐藏 HLS 切换。
const [directOnly, setDirectOnly] = useState(false) const [directOnly, setDirectOnly] = useState(false)
const [directOnlyKnown, setDirectOnlyKnown] = useState(false) const [directOnlyKnown, setDirectOnlyKnown] = useState(false)
@@ -2608,6 +2609,7 @@ export function PlayerPage() {
} }
/> />
) : null} ) : null}
{diagnosticsVisible && (
<button <button
type="button" type="button"
onClick={() => setDiagnosticsOpen((open) => !open)} onClick={() => setDiagnosticsOpen((open) => !open)}
@@ -2617,7 +2619,8 @@ export function PlayerPage() {
> >
<Activity size={15} />诊断 <Activity size={15} />诊断
</button> </button>
{diagnosticsOpen && ( )}
{diagnosticsVisible && diagnosticsOpen && (
<aside className="absolute right-2.5 top-[6.75rem] z-[80] flex max-h-[calc(100%-8.5rem)] w-[min(92vw,420px)] flex-col overflow-hidden rounded-2xl border border-white/15 bg-[#111827]/95 text-white shadow-2xl backdrop-blur sm:right-3.5 sm:top-[7.25rem]"> <aside className="absolute right-2.5 top-[6.75rem] z-[80] flex max-h-[calc(100%-8.5rem)] w-[min(92vw,420px)] flex-col overflow-hidden rounded-2xl border border-white/15 bg-[#111827]/95 text-white shadow-2xl backdrop-blur sm:right-3.5 sm:top-[7.25rem]">
<div className="flex items-center justify-between border-b border-white/10 px-4 py-3"> <div className="flex items-center justify-between border-b border-white/10 px-4 py-3">
<div><h2 className="text-sm font-bold">播放诊断</h2><p className="mt-1 text-[11px] text-white/60">记录最近 40 条本地播放事件</p></div> <div><h2 className="text-sm font-bold">播放诊断</h2><p className="mt-1 text-[11px] text-white/60">记录最近 40 条本地播放事件</p></div>
+13
View File
@@ -0,0 +1,13 @@
import { shouldShowPlayerDiagnostics } from './playerPageModel.ts'
function check(name: string, actual: boolean, expected: boolean) {
if (actual !== expected) throw new Error(`playerDiagnostics: ${name}`)
}
check('hidden by default and during normal playback', shouldShowPlayerDiagnostics('', ''), false)
check('visible when playback fails', shouldShowPlayerDiagnostics('视频播放失败', ''), true)
check('visible when media cannot be loaded', shouldShowPlayerDiagnostics('', '无法加载该媒体'), true)
check('visible when both loading and playback fail', shouldShowPlayerDiagnostics('视频播放失败', '无法加载该媒体'), true)
check('hidden again after errors are cleared', shouldShowPlayerDiagnostics('', ''), false)
console.log('playerDiagnostics.test.ts ok')
+4
View File
@@ -3,6 +3,10 @@ import { isRemoteEmbyID } from '../utils/remoteEmby'
export type PlayerMode = 'direct' | 'hls' export type PlayerMode = 'direct' | 'hls'
export function shouldShowPlayerDiagnostics(playerError: string, loadError: string): boolean {
return Boolean(playerError || loadError)
}
const directContainers = ['mp4', 'webm', 'm4v'] const directContainers = ['mp4', 'webm', 'm4v']
const directVideoCodecs = ['h264', 'avc', 'avc1'] const directVideoCodecs = ['h264', 'avc', 'avc1']
const directAudioCodecs = ['aac', 'mp3', 'opus'] const directAudioCodecs = ['aac', 'mp3', 'opus']