'use client'; import React, { useRef, useEffect, useState } from 'react'; import { motion, AnimatePresence } from 'framer-motion'; import { RefreshCw, User, Loader2, X, Plus, Video, CircleX } from 'lucide-react'; import { cn } from '@/public/lib/utils'; import { PersonDetection, PersonDetectionScene } from './person-detection'; import { ShotsEditor } from './shot-editor/ShotsEditor'; import { CharacterLibrarySelector } from './character-library-selector'; import FloatingGlassPanel from './FloatingGlassPanel'; import { ReplaceCharacterPanel } from './replace-character-panel'; import HorizontalScroller from './HorizontalScroller'; import { useEditData } from '@/components/pages/work-flow/use-edit-data'; import { RoleEntity } from '@/app/service/domain/Entities'; interface ShotTabContentProps { currentSketchIndex: number; roles?: any[]; } export const ShotTabContent = (props: ShotTabContentProps) => { const { currentSketchIndex = 0, roles = [] } = props; const { loading, shotData, setSelectedSegment, regenerateVideoSegment, filterRole, fetchUserRoleLibrary, userRoleLibrary, fetchRoleShots, shotSelectionList, applyRoleToSelectedShots, calculateRecognitionBoxes, setSelectedRole } = useEditData('shot'); const [selectedIndex, setSelectedIndex] = useState(currentSketchIndex); const [detections, setDetections] = useState([]); const [scanState, setScanState] = useState<'idle' | 'scanning' | 'detected' | 'failed' | 'timeout'>('idle'); const [isScanFailed, setIsScanFailed] = useState(false); const [isLoadingLibrary, setIsLoadingLibrary] = useState(false); const [isReplaceLibraryOpen, setIsReplaceLibraryOpen] = useState(false); const [isReplacePanelOpen, setIsReplacePanelOpen] = useState(false); const [selectedCharacter, setSelectedCharacter] = useState(null); const [selectedLibaryRole, setSelectedLibaryRole] = useState(null); const [isLoadingShots, setIsLoadingShots] = useState(false); const shotsEditorRef = useRef(null); const [isRegenerate, setIsRegenerate] = useState(false); const [pendingRegeneration, setPendingRegeneration] = useState(false); useEffect(() => { console.log('shotTabContent-----roles', roles); }, [roles]); useEffect(() => { if (pendingRegeneration) { regenerateVideoSegment(); setPendingRegeneration(false); } }, [shotData[selectedIndex]?.lens]); // 监听当前选中index变化 useEffect(() => { console.log('shotTabContent-----shotData', shotData); if (shotData.length > 0) { // 清空检测状态 和 检测结果 setScanState('idle'); setDetections([]); setSelectedSegment(shotData[selectedIndex]); } }, [selectedIndex, shotData]); // 处理扫描开始 const handleScan = async () => { if (scanState === 'detected') { // 如果已经有检测结果,点击按钮退出检测状态 setScanState('idle'); setDetections([]); // 清除检测结果 return; } setScanState('scanning'); const containerElement = document.getElementById('person-detection-video') as HTMLVideoElement; try { const roleRecognitionResponse = await filterRole(containerElement); console.log('roleRecognitionResponse', roleRecognitionResponse); if (roleRecognitionResponse && roleRecognitionResponse.recognition_result.code === 200) { const recognitionBoxes = calculateRecognitionBoxes(containerElement, roleRecognitionResponse.recognition_result.data); console.log('recognitionBoxes', recognitionBoxes); setDetections(recognitionBoxes.map((person: any) => ({ id: person.person_id, name: person.person_id, description: roleRecognitionResponse.characters_used.find((character: any) => character.character_name === person.person_id)?.character_description || '', imageUrl: roleRecognitionResponse.characters_used.find((character: any) => character.character_name === person.person_id)?.avatar || '', position: { top: person.top, left: person.left, width: person.width, height: person.height } }))); } else { setIsScanFailed(true); } setScanState('detected'); } catch (error) { setIsScanFailed(true); } }; // 处理扫描超时 const handleScanTimeout = () => { setIsScanFailed(true); setScanState('timeout'); setDetections([]); }; // 处理退出扫描 const handleScanExit = () => { setScanState('idle'); setDetections([]); }; // 处理检测到结果 const handleDetectionsChange = (newDetections: PersonDetection[]) => { // console.log('handleDetectionsChange', newDetections); // if (newDetections.length > 0 && scanState === 'scanning') { // setScanState('detected'); // } }; // 处理人物点击 打开角色库 const handlePersonClick = async (person: PersonDetection) => { console.log('person', person); const role: RoleEntity = { id: person.id, name: person.name, generateText: person.description, tags: [], // 由于 person 对象中没有标签信息,我们设置为空数组 imageUrl: person.imageUrl, fromDraft: false, // 默认不是来自草稿箱 isChangeRole: true, // 默认没有发生角色形象的变更 }; console.log('role', role); setSelectedRole(role); setSelectedCharacter(person); setIsLoadingLibrary(true); setIsReplaceLibraryOpen(true); await fetchUserRoleLibrary(role.generateText); setIsLoadingLibrary(false); }; // 从角色库中选择角色 const handleSelectCharacter = (index: number) => { console.log('index', index); setSelectedLibaryRole(userRoleLibrary[index]); setIsReplaceLibraryOpen(false); handleStartReplaceCharacter(); }; const handleStartReplaceCharacter = async () => { setIsLoadingShots(true); setIsReplacePanelOpen(true); // 获取当前角色对应的视频片段 await fetchRoleShots(selectedCharacter?.name || ''); // 打开替换角色面板 setIsLoadingShots(false); }; // 确认替换角色 const handleConfirmReplace = () => { applyRoleToSelectedShots(selectedLibaryRole); setIsReplacePanelOpen(false); }; // 点击按钮重新生成 const handleRegenerate = async () => { console.log('regenerate'); setIsRegenerate(true); const shotInfo = shotsEditorRef.current.getShotInfo(); console.log('shotInfo', shotInfo); setSelectedSegment({ ...shotData[selectedIndex], lens: shotInfo }); setPendingRegeneration(true); }; // 新增分镜 const handleAddShot = () => { console.log('add shot'); shotsEditorRef.current.addShot(); }; // 切换选择分镜 const handleSelectShot = (index: number) => { // 切换前 判断数据是否发生变化 setSelectedIndex(index); }; // 如果loading 显示loading状态 if (loading) { return (

Loading...

); } // 如果没有数据,显示空状态 if (shotData.length === 0) { return (
); } return (
{/* 上部分 */} {/* 分镜缩略图行 */}
handleSelectShot(i)} > {shotData.map((shot, index) => ( {shot.status === 0 && (
)} {shot.status === 1 && (
))}
{/* 视频描述行 - 单行滚动 */}
handleSelectShot(i)} > {shotData.map((shot, index) => { const isActive = selectedIndex === index; return (
Segment {index + 1} {shot.status === 0 && ( )} {shot.status === 2 && ( )} {index < shotData.length - 1 && ( | )}
); })}
{/* 渐变遮罩 */}
{/* 下部分 */} {shotData[selectedIndex] && ( {/* 视频预览和操作 */}
{/* 选中的视频预览 */} <> {shotData[selectedIndex]?.status === 0 && (
Loading...
)} {shotData[selectedIndex]?.status === 1 && ( {/* 人物替换按钮 */} handleScan()} className={`p-2 backdrop-blur-sm transition-colors z-10 rounded-full ${scanState === 'detected' ? 'bg-cyan-500/50 hover:bg-cyan-500/70 text-white' : 'bg-black/50 hover:bg-black/70 text-white' }`} whileHover={{ scale: 1.05 }} whileTap={{ scale: 0.95 }} > {scanState === 'scanning' ? ( ) : scanState === 'detected' ? ( ) : ( )} )} {shotData[selectedIndex]?.status === 2 && (
任务失败,点击重新生成
)}
{/* 基础配置 */}
{/* 重新生成按钮、新增分镜按钮 */}
{/* handleAddShot()} className="flex items-center justify-center gap-2 px-4 py-3 bg-pink-500/10 hover:bg-pink-500/20 text-pink-500 rounded-lg transition-colors" whileHover={{ scale: 1.02 }} whileTap={{ scale: 0.98 }} > Add Shot */} handleRegenerate()} className="flex items-center justify-center gap-2 px-4 py-3 bg-blue-500/10 hover:bg-blue-500/20 text-blue-500 rounded-lg transition-colors disabled:opacity-50 disabled:cursor-not-allowed" whileHover={{ scale: 1.02 }} whileTap={{ scale: 0.98 }} disabled={isRegenerate} > {isRegenerate ? 'Regenerating...' : 'Regenerate'}
)} setIsReplacePanelOpen(false)} > setIsReplacePanelOpen(false)} onConfirm={handleConfirmReplace} />
); }