import { useRouter } from "next/navigation"; import type { RefObject } from "react"; import { useEffect, useRef } from "react"; import type { LoopMode, Neighbors } from "../types.ts"; import { cleanupExpiredProgressStorage, releaseMediaElement } from "../utils"; interface UseVideoPlayerProps { enabled?: boolean; awemeId: string; videoRef: RefObject; volume: number; rate: number; loopMode: LoopMode; neighbors: Neighbors; progressRestored: boolean; setIsPlaying: (playing: boolean) => void; setProgress: (progress: number) => void; setProgressRestored: (restored: boolean) => void; } export function useVideoPlayer({ enabled = true, awemeId, videoRef, volume, rate, loopMode, neighbors, progressRestored, setIsPlaying, setProgress, setProgressRestored, }: UseVideoPlayerProps): void { const router = useRouter(); const restoredAwemeIdRef = useRef(null); const lastProgressRef = useRef(0); // 挂载时清理过期的播放进度缓存 useEffect(() => { if (!enabled) return; cleanupExpiredProgressStorage(); }, [enabled]); // 恢复播放进度 useEffect(() => { if (!enabled) return; if (progressRestored && restoredAwemeIdRef.current === awemeId) return; const v = videoRef.current; if (!v) return; const onLoadedMetadata = (): void => { if (restoredAwemeIdRef.current === awemeId) return; restoredAwemeIdRef.current = awemeId; try { const key = `aweme_progress_${awemeId}`; const saved = localStorage.getItem(key); if (!saved) { setProgressRestored(true); return; } const parsed = JSON.parse(saved) as { time?: unknown; timestamp?: unknown; }; const time = typeof parsed.time === "number" ? parsed.time : 0; const timestamp = typeof parsed.timestamp === "number" ? parsed.timestamp : 0; const now = Date.now(); const fiveMinutes = 5 * 60 * 1000; if ( now - timestamp < fiveMinutes && time > 1 && time < v.duration - 1 ) { v.currentTime = time; } else if (now - timestamp >= fiveMinutes) { localStorage.removeItem(key); } } catch (e) { console.error("恢复播放进度失败", e); } setProgressRestored(true); }; if (v.readyState >= 1) { onLoadedMetadata(); return; } v.addEventListener("loadedmetadata", onLoadedMetadata, { once: true }); return () => v.removeEventListener("loadedmetadata", onLoadedMetadata); }, [awemeId, enabled, progressRestored, videoRef, setProgressRestored]); // 保存播放进度并在卸载/切页时主动释放媒体解码缓冲区 useEffect(() => { if (!enabled) return; const v = videoRef.current; if (!v) return; const saveProgress = (): void => { if (!v.duration || Number.isNaN(v.duration) || v.currentTime < 1) return; try { const key = `aweme_progress_${awemeId}`; const value = JSON.stringify({ time: v.currentTime, timestamp: Date.now(), }); localStorage.setItem(key, value); } catch (e) { console.error("保存播放进度失败", e); } }; const interval = window.setInterval(saveProgress, 2000); const onBeforeUnload = (): void => saveProgress(); window.addEventListener("beforeunload", onBeforeUnload); return () => { window.clearInterval(interval); window.removeEventListener("beforeunload", onBeforeUnload); saveProgress(); releaseMediaElement(v); }; }, [awemeId, enabled, videoRef]); // 监听播放状态和进度 useEffect(() => { if (!enabled) return; const v = videoRef.current; if (!v) return; const onTime = (): void => { if (!v.duration || Number.isNaN(v.duration)) return; const nextProgress = Math.max(0, Math.min(1, v.currentTime / v.duration)); if (Math.abs(nextProgress - lastProgressRef.current) >= 0.001) { lastProgressRef.current = nextProgress; setProgress(nextProgress); } }; const onPlay = (): void => setIsPlaying(true); const onPause = (): void => setIsPlaying(false); const onEnded = (): void => { if (loopMode === "sequential" && neighbors?.next) { router.push(`/aweme/${neighbors.next.aweme_id}`); } }; v.addEventListener("timeupdate", onTime); v.addEventListener("loadedmetadata", onTime); v.addEventListener("play", onPlay); v.addEventListener("pause", onPause); v.addEventListener("ended", onEnded); return () => { v.removeEventListener("timeupdate", onTime); v.removeEventListener("loadedmetadata", onTime); v.removeEventListener("play", onPlay); v.removeEventListener("pause", onPause); v.removeEventListener("ended", onEnded); }; }, [ enabled, loopMode, neighbors?.next, router, videoRef, setIsPlaying, setProgress, ]); // 自动播放检测 useEffect(() => { if (!enabled) return; const v = videoRef.current; if (!v) return; const checkAutoplay = async (): Promise => { try { await v.play(); setIsPlaying(true); } catch { setIsPlaying(false); } }; if (v.readyState >= 1) { void checkAutoplay(); return; } const onLoaded = (): void => { void checkAutoplay(); }; v.addEventListener("loadedmetadata", onLoaded, { once: true }); return () => v.removeEventListener("loadedmetadata", onLoaded); }, [awemeId, enabled, videoRef, setIsPlaying]); // 更新音量和倍速 useEffect(() => { if (!enabled) return; const v = videoRef.current; if (v) v.volume = volume; }, [enabled, volume, videoRef]); useEffect(() => { if (!enabled) return; const v = videoRef.current; if (v) v.playbackRate = rate; }, [enabled, rate, videoRef]); }