完善观影室

This commit is contained in:
mtvpls
2025-12-07 00:16:21 +08:00
parent 003050d134
commit 4363f4cdae
15 changed files with 1685 additions and 718 deletions
+75 -5
View File
@@ -6,6 +6,8 @@ import { Heart } from 'lucide-react';
import { useRouter, useSearchParams } from 'next/navigation';
import { Suspense, useEffect, useRef, useState } from 'react';
import { usePlaySync } from '@/hooks/usePlaySync';
import {
deleteFavorite,
@@ -328,7 +330,28 @@ function PlayPageClient() {
needPreferRef.current = needPrefer;
}, [needPrefer]);
// 集数相关
const [currentEpisodeIndex, setCurrentEpisodeIndex] = useState(0);
const [currentEpisodeIndex, setCurrentEpisodeIndex] = useState(() => {
const episodeParam = searchParams.get('episode');
if (episodeParam) {
const episode = parseInt(episodeParam, 10);
return episode > 0 ? episode - 1 : 0; // URL 中是 1-based,内部是 0-based
}
return 0;
});
// 监听 URL 参数变化,更新集数索引(用于房员跟随换集)
useEffect(() => {
const episodeParam = searchParams.get('episode');
if (episodeParam) {
const episode = parseInt(episodeParam, 10);
const newIndex = episode > 0 ? episode - 1 : 0;
console.log('[PlayPage] Checking episode from URL:', { urlEpisode: episode, currentIndex: currentEpisodeIndex, newIndex });
if (newIndex !== currentEpisodeIndex) {
console.log('[PlayPage] URL episode changed, updating index to:', newIndex);
setCurrentEpisodeIndex(newIndex);
}
}
}, [searchParams, currentEpisodeIndex]);
const currentSourceRef = useRef(currentSource);
const currentIdRef = useRef(currentId);
@@ -429,6 +452,9 @@ function PlayPageClient() {
'initing' | 'sourceChanging'
>('initing');
// 播放器就绪状态(用于触发 usePlaySync 的事件监听器设置)
const [playerReady, setPlayerReady] = useState(false);
// 播放进度保存相关
const saveIntervalRef = useRef<NodeJS.Timeout | null>(null);
const lastSaveTimeRef = useRef<number>(0);
@@ -439,6 +465,19 @@ function PlayPageClient() {
// Wake Lock 相关
const wakeLockRef = useRef<WakeLockSentinel | null>(null);
// 观影室同步功能
const playSync = usePlaySync({
artPlayerRef,
videoId: currentId || '', // 使用 currentId 状态而不是 searchParams
videoName: videoTitle || detail?.title || '正在加载...',
videoYear: videoYear || detail?.year || '',
searchTitle: searchTitle || '',
currentEpisode: currentEpisodeIndex + 1,
currentSource: currentSource || '',
videoUrl: videoUrl || '',
playerReady: playerReady, // 传递播放器就绪状态
});
// -----------------------------------------------------------------------------
// 工具函数(Utils
// -----------------------------------------------------------------------------
@@ -2721,6 +2760,13 @@ function PlayPageClient() {
html: '<i class="art-icon flex"><svg width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M6 18l8.5-6L6 6v12zM16 6v12h2V6h-2z" fill="currentColor"/></svg></i>',
tooltip: '播放下一集',
click: function () {
// 房员禁用下一集按钮
if (playSync.shouldDisableControls) {
if (artPlayerRef.current) {
artPlayerRef.current.notice.show = '房员无法切换集数,请等待房主操作';
}
return;
}
handleNextEpisode();
},
},
@@ -2731,6 +2777,10 @@ function PlayPageClient() {
artPlayerRef.current.on('ready', async () => {
setError(null);
// 标记播放器已就绪,触发 usePlaySync 设置事件监听器
setPlayerReady(true);
console.log('[PlayPage] Player ready, triggering sync setup');
// 从 art.storage 读取弹幕设置并应用
if (artPlayerRef.current) {
const storedDanmakuSettings = artPlayerRef.current.storage.get('danmaku_settings');
@@ -2905,8 +2955,17 @@ function PlayPageClient() {
}
});
// 监听视频播放结束事件,自动播放下一集
// 监听视频播放结束事件,自动播放下一集(房员禁用)
artPlayerRef.current.on('video:ended', () => {
// 房员禁用自动播放下一集
if (playSync.shouldDisableControls) {
console.log('[PlayPage] Member cannot auto-play next episode');
if (artPlayerRef.current) {
artPlayerRef.current.notice.show = '等待房主切换下一集';
}
return;
}
const d = detailRef.current;
const idx = currentEpisodeIndexRef.current;
if (d && d.episodes && idx < d.episodes.length - 1) {
@@ -3686,18 +3745,29 @@ function PlayPageClient() {
{/* 选集和换源 - 在移动端始终显示,在 lg 及以上可折叠 */}
<div
className={`h-[300px] lg:h-full md:overflow-hidden transition-all duration-300 ease-in-out ${
className={`h-[300px] lg:h-full md:overflow-hidden transition-all duration-300 ease-in-out relative ${
isEpisodeSelectorCollapsed
? 'md:col-span-1 lg:hidden lg:opacity-0 lg:scale-95'
: 'md:col-span-1 lg:opacity-100 lg:scale-100'
}`}
>
{/* 观影室房员禁用层 */}
{playSync.isInRoom && playSync.shouldDisableControls && (
<div className="absolute inset-0 z-10 flex items-center justify-center bg-black/60 backdrop-blur-sm">
<div className="text-center p-4">
<p className="text-white text-lg font-bold mb-2">👥 </p>
<p className="text-gray-300 text-sm">
{playSync.isOwner ? '您是房主,可以控制播放' : '房主控制中,无法切换集数和播放源'}
</p>
</div>
</div>
)}
<EpisodeSelector
totalEpisodes={totalEpisodes}
episodes_titles={detail?.episodes_titles || []}
value={currentEpisodeIndex + 1}
onChange={handleEpisodeChange}
onSourceChange={handleSourceChange}
onChange={playSync.shouldDisableControls ? () => {} : handleEpisodeChange}
onSourceChange={playSync.shouldDisableControls ? () => {} : handleSourceChange}
currentSource={currentSource}
currentId={currentId}
videoTitle={searchTitle || videoTitle}