import { useEffect, useRef, useState } from 'react' import { useNavigate, useParams, useSearchParams } from 'react-router-dom' import type Hls from 'hls.js' import { ArrowLeft, RefreshCw, Sparkles } from 'lucide-react' import toast from 'react-hot-toast' import { mediaAPI } from '../api/library' import { api, hlsURL, streamURL } from '../api/client' import { playbackAPI } from '../api/playback' import { subtitlesAPI, type SubtitleTrack } from '../api/subtitles' import { systemAPI } from '../api/system' import type { Media } from '../types' type Mode = 'direct' | 'hls' // Fullscreen, dark-themed video page. // // ?mode=hls force HLS even when direct play would work // ?mode=direct force direct play (default for browser-friendly codecs) // // We pick a sensible default based on the source codec: H.264 + AAC in // MP4 / WebM containers play directly; everything else (HEVC, MKV, AV1, // AC3 audio, …) gets routed through ffmpeg → HLS. // // External subtitles next to the source file are auto-discovered and // attached as elements. export function PlayerPage() { const { id = '' } = useParams() const [params, setParams] = useSearchParams() const navigate = useNavigate() const ref = useRef(null) const hlsRef = useRef(null) const lastSentRef = useRef(0) const [media, setMedia] = useState(null) const [mode, setMode] = useState('direct') const [subs, setSubs] = useState([]) const [hlsUnavailable, setHlsUnavailable] = useState(false) const [playerError, setPlayerError] = useState('') // 「客户端直连解码」模式:宿主机不转码,播放器强制 direct play、隐藏 HLS 切换。 const [directOnly, setDirectOnly] = useState(false) // 读取宿主机的「直连解码」开关。开启时全程 direct play,不走 HLS。 useEffect(() => { systemAPI .info() .then((info) => setDirectOnly(Boolean(info.direct_play_only))) .catch(() => setDirectOnly(false)) }, []) // Load metadata and pick a default mode. useEffect(() => { if (!id) return mediaAPI.get(id).then((m) => { setMedia(m) const forced = params.get('mode') as Mode | null const auto = pickMode(m) // 直连解码模式下忽略 ?mode=hls 与自动判定,始终 direct play。 setMode(directOnly ? 'direct' : (forced ?? auto)) setPlayerError('') }) subtitlesAPI .list(id) .then((tracks) => setSubs(tracks ?? [])) .catch(() => setSubs([])) }, [id, params, directOnly]) // Wire up the actual