forked from 77media/video-flow
角色和场景一起展示
This commit is contained in:
parent
93a205135c
commit
628ff70a0c
@ -625,21 +625,25 @@ export interface Role {
|
||||
name: string;
|
||||
url: string;
|
||||
status: number;
|
||||
type: string;
|
||||
}
|
||||
interface Scene {
|
||||
url: string;
|
||||
script: string;
|
||||
status: number;
|
||||
type: string;
|
||||
}
|
||||
interface ShotSketch {
|
||||
url: string;
|
||||
script: string;
|
||||
status: number;
|
||||
type: string;
|
||||
}
|
||||
export interface ShotVideo {
|
||||
video_id: string;
|
||||
urls: string[];
|
||||
video_status: number;
|
||||
type: string;
|
||||
}
|
||||
|
||||
// 执行loading文字映射
|
||||
|
||||
@ -153,7 +153,7 @@
|
||||
|
||||
.media-Ocdu1O {
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
gap: 8px;
|
||||
height: 100%;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
|
||||
@ -77,7 +77,7 @@ const WorkFlow = React.memo(function WorkFlow() {
|
||||
|
||||
return (
|
||||
<ErrorBoundary>
|
||||
<div className="w-full overflow-hidden h-[calc(100vh-6rem)] absolute top-[4rem] left-0 right-0 px-[1rem]">
|
||||
<div className="w-full overflow-hidden h-[calc(100vh-5rem)] absolute top-[4rem] left-0 right-0 px-[1rem]">
|
||||
<div className="w-full h-full">
|
||||
<div className="splashContainer-otuV_A">
|
||||
<div className="content-vPGYx8">
|
||||
@ -131,7 +131,7 @@ const WorkFlow = React.memo(function WorkFlow() {
|
||||
) : isLoading ? (
|
||||
<Skeleton className="w-full aspect-video rounded-lg" />
|
||||
) : (
|
||||
<div className={`heroVideo-FIzuK1 ${['final_video', 'script'].includes(taskObject.currentStage) ? 'h-[calc(100vh-6rem)] w-[calc((100vh-6rem)/9*16)]' : 'h-[calc(100vh-6rem-200px)] w-[calc((100vh-6rem-200px)/9*16)]'}`} style={{ aspectRatio: "16 / 9" }} key={currentSketchIndex}>
|
||||
<div className={`heroVideo-FIzuK1 ${['final_video', 'script'].includes(taskObject.currentStage) ? 'h-[calc(100vh-6rem)] w-[calc((100vh-6rem)/9*16)]' : 'h-[calc(100vh-6rem-200px)] w-[calc((100vh-6rem-200px)/9*16)]'}`} style={{ aspectRatio: "16 / 9" }} key={taskObject.currentStage+'_'+currentSketchIndex}>
|
||||
<ErrorBoundary>
|
||||
<MediaViewer
|
||||
taskObject={taskObject}
|
||||
@ -151,7 +151,7 @@ const WorkFlow = React.memo(function WorkFlow() {
|
||||
)}
|
||||
</div>
|
||||
{taskObject.currentStage !== 'final_video' && taskObject.currentStage !== 'script' && (
|
||||
<div className="h-[112px] w-[calc((100vh-6rem-200px)/9*16)]">
|
||||
<div className="h-[123px] w-[calc((100vh-6rem-200px)/9*16)]">
|
||||
<ThumbnailGrid
|
||||
isDisabledFocus={isEditModalOpen || isPauseWorkFlow || isSmartChatBoxOpen}
|
||||
taskObject={taskObject}
|
||||
|
||||
@ -546,14 +546,29 @@ export const MediaViewer = React.memo(function MediaViewer({
|
||||
)}
|
||||
{/* 只在生成过程中或没有分镜图片时使用ProgressiveReveal */}
|
||||
{currentSketch.status === 1 && (
|
||||
<img
|
||||
key={currentSketchIndex}
|
||||
src={currentSketch.url}
|
||||
alt={`NG-Sketch ${currentSketchIndex + 1}`}
|
||||
className="w-full h-full rounded-lg object-cover"
|
||||
/>
|
||||
<AnimatePresence mode="wait">
|
||||
<motion.img
|
||||
key={currentSketch.url}
|
||||
src={currentSketch.url}
|
||||
className="w-full h-full rounded-lg object-cover"
|
||||
// 用 circle clip-path 实现“扩散”
|
||||
initial={{ clipPath: "circle(0% at 50% 50%)" }}
|
||||
animate={{ clipPath: "circle(150% at 50% 50%)" }}
|
||||
exit={{ opacity: 0 }}
|
||||
transition={{ duration: 1, ease: "easeInOut" }}
|
||||
/>
|
||||
</AnimatePresence>
|
||||
)}
|
||||
|
||||
<div className="absolute top-0 left-0 right-0 p-2">
|
||||
{/* 角色类型 */}
|
||||
{currentSketch.type === 'role' && (
|
||||
<div className="inline-flex items-center px-2 py-1 rounded-full bg-purple-500/20 backdrop-blur-sm">
|
||||
<span className="text-xs text-purple-400">Role: {currentSketch.name}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 操作按钮组 */}
|
||||
<AnimatePresence>
|
||||
<motion.div
|
||||
@ -636,13 +651,13 @@ export const MediaViewer = React.memo(function MediaViewer({
|
||||
return renderScriptContent();
|
||||
}
|
||||
|
||||
if (taskObject.currentStage === 'scene') {
|
||||
return renderSketchContent(taskObject.scenes.data[currentSketchIndex]);
|
||||
if (taskObject.currentStage === 'scene' || taskObject.currentStage === 'character') {
|
||||
return renderSketchContent([...taskObject.roles.data, ...taskObject.scenes.data][currentSketchIndex]);
|
||||
}
|
||||
|
||||
if (taskObject.currentStage === 'shot_sketch') {
|
||||
return renderSketchContent(taskObject.shot_sketch.data[currentSketchIndex]);
|
||||
}
|
||||
|
||||
return renderSketchContent(taskObject.scenes.data[currentSketchIndex]);
|
||||
return null;
|
||||
});
|
||||
|
||||
@ -4,7 +4,7 @@ import React, { useRef, useEffect, useState, useCallback } from 'react';
|
||||
import { motion } from 'framer-motion';
|
||||
import { Skeleton } from '@/components/ui/skeleton';
|
||||
import { ProgressiveReveal, presets } from '@/components/ui/progressive-reveal';
|
||||
import { Loader2, X } from 'lucide-react';
|
||||
import { Loader2, X, SquareUserRound, MapPinHouse, Clapperboard } from 'lucide-react';
|
||||
import { TaskObject } from '@/api/DTO/movieEdit';
|
||||
|
||||
interface ThumbnailGridProps {
|
||||
@ -45,7 +45,7 @@ export function ThumbnailGrid({
|
||||
if (taskObject.currentStage === 'video') {
|
||||
return taskObject.videos.data;
|
||||
} else if (taskObject.currentStage === 'scene' || taskObject.currentStage === 'character') {
|
||||
return taskObject.scenes.data;
|
||||
return [...taskObject.roles.data, ...taskObject.scenes.data];
|
||||
} else if (taskObject.currentStage === 'shot_sketch') {
|
||||
return taskObject.shot_sketch.data;
|
||||
}
|
||||
@ -251,13 +251,32 @@ export function ThumbnailGrid({
|
||||
<img
|
||||
className="w-full h-full object-cover select-none"
|
||||
src={sketch.url}
|
||||
alt={`NG ${index + 1}`}
|
||||
draggable="false"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div className="absolute bottom-0 left-0 right-0 p-2 bg-gradient-to-t from-black/60 to-transparent">
|
||||
<span className="text-xs text-white/90">Scene {index + 1}</span>
|
||||
<div className='absolute top-0 left-0 right-0 p-2'>
|
||||
{/* 角色类型 */}
|
||||
{sketch.type === 'role' && (
|
||||
<div className="inline-flex items-center px-2 py-1 rounded-full bg-purple-500/20 backdrop-blur-sm">
|
||||
<SquareUserRound className="w-3 h-3 text-purple-400 mr-1" />
|
||||
<span className="text-xs text-purple-400">Role</span>
|
||||
</div>
|
||||
)}
|
||||
{/* 场景类型 */}
|
||||
{sketch.type === 'scene' && (
|
||||
<div className="inline-flex items-center px-2 py-1 rounded-full bg-blue-500/20 backdrop-blur-sm">
|
||||
<MapPinHouse className="w-3 h-3 text-blue-400 mr-1" />
|
||||
<span className="text-xs text-blue-400">Scene</span>
|
||||
</div>
|
||||
)}
|
||||
{/* 分镜类型 */}
|
||||
{(!sketch.type || sketch.type === 'shot_sketch') && (
|
||||
<div className="inline-flex items-center px-2 py-1 rounded-full bg-amber-500/20 backdrop-blur-sm">
|
||||
<Clapperboard className="w-3 h-3 text-amber-400 mr-1" />
|
||||
<span className="text-xs text-amber-400">Shot {index + 1}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@ -269,7 +288,7 @@ export function ThumbnailGrid({
|
||||
<div
|
||||
ref={thumbnailsRef}
|
||||
tabIndex={0}
|
||||
className="w-full h-full grid grid-flow-col auto-cols-[20%] gap-4 overflow-x-auto hide-scrollbar px-1 py-1 cursor-grab active:cursor-grabbing focus:outline-none select-none"
|
||||
className="w-full h-full grid grid-flow-col auto-cols-[20%] gap-2 overflow-x-auto hide-scrollbar px-1 py-1 cursor-grab active:cursor-grabbing focus:outline-none select-none"
|
||||
autoFocus
|
||||
onMouseDown={handleMouseDown}
|
||||
onMouseMove={handleMouseMove}
|
||||
@ -279,9 +298,8 @@ export function ThumbnailGrid({
|
||||
onBlur={() => setIsFocused(false)}
|
||||
>
|
||||
{taskObject.currentStage === 'video' && renderVideoThumbnails()}
|
||||
{taskObject.currentStage === 'scene' && renderSketchThumbnails(taskObject.scenes.data)}
|
||||
{(taskObject.currentStage === 'scene' || taskObject.currentStage === 'character') && renderSketchThumbnails(getCurrentData())}
|
||||
{taskObject.currentStage === 'shot_sketch' && renderSketchThumbnails(taskObject.shot_sketch.data)}
|
||||
{taskObject.currentStage === 'character' && renderSketchThumbnails(taskObject.scenes.data)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -215,7 +215,8 @@ export function useWorkflowData() {
|
||||
characterList.push({
|
||||
name: character.character_name,
|
||||
url: character.image_path,
|
||||
status: character.image_path ? 1 : (task.task_status === 'COMPLETED' ? 2 : 0)
|
||||
status: character.image_path ? 1 : (task.task_status === 'COMPLETED' ? 2 : 0),
|
||||
type: 'role'
|
||||
});
|
||||
}
|
||||
taskCurrent.roles.data = characterList;
|
||||
@ -242,7 +243,8 @@ export function useWorkflowData() {
|
||||
sketchList.push({
|
||||
url: sketch.image_path,
|
||||
script: sketch.sketch_name,
|
||||
status: sketch.image_path ? 1 : (task.task_status === 'COMPLETED' ? 2 : 0)
|
||||
status: sketch.image_path ? 1 : (task.task_status === 'COMPLETED' ? 2 : 0),
|
||||
type: 'scene'
|
||||
});
|
||||
}
|
||||
taskCurrent.scenes.data = sketchList;
|
||||
@ -269,7 +271,8 @@ export function useWorkflowData() {
|
||||
sketchList.push({
|
||||
url: sketch.url,
|
||||
script: sketch.description,
|
||||
status: sketch.url ? 1 : (task.task_status === 'COMPLETED' ? 2 : 0)
|
||||
status: sketch.url ? 1 : (task.task_status === 'COMPLETED' ? 2 : 0),
|
||||
type: 'shot_sketch'
|
||||
});
|
||||
}
|
||||
taskCurrent.shot_sketch.data = sketchList;
|
||||
@ -302,6 +305,7 @@ export function useWorkflowData() {
|
||||
urls: video.urls,
|
||||
video_id: video.video_id,
|
||||
video_status: video_status, // 0 生成中 1 生成完成 2 生成失败
|
||||
type: 'video'
|
||||
});
|
||||
}
|
||||
taskCurrent.videos.data = videoList;
|
||||
@ -427,7 +431,8 @@ export function useWorkflowData() {
|
||||
characterList.push({
|
||||
name: character.character_name,
|
||||
url: character.image_path,
|
||||
status: character.image_path ? 1 : (data.character.task_status === 'COMPLETED' ? 2 : 0)
|
||||
status: character.image_path ? 1 : (data.character.task_status === 'COMPLETED' ? 2 : 0),
|
||||
type: 'role'
|
||||
});
|
||||
}
|
||||
taskCurrent.roles.data = characterList;
|
||||
@ -446,7 +451,8 @@ export function useWorkflowData() {
|
||||
sketchList.push({
|
||||
url: sketch.image_path,
|
||||
script: sketch.sketch_name,
|
||||
status: sketch.image_path ? 1 : (data.sketch.task_status === 'COMPLETED' ? 2 : 0)
|
||||
status: sketch.image_path ? 1 : (data.sketch.task_status === 'COMPLETED' ? 2 : 0),
|
||||
type: 'scene'
|
||||
});
|
||||
}
|
||||
taskCurrent.scenes.data = sketchList;
|
||||
@ -466,7 +472,8 @@ export function useWorkflowData() {
|
||||
sketchList.push({
|
||||
url: sketch.url,
|
||||
script: sketch.description,
|
||||
status: sketch.url ? 1 : (data.shot_sketch.task_status === 'COMPLETED' ? 2 : 0)
|
||||
status: sketch.url ? 1 : (data.shot_sketch.task_status === 'COMPLETED' ? 2 : 0),
|
||||
type: 'shot_sketch'
|
||||
});
|
||||
}
|
||||
taskCurrent.shot_sketch.data = sketchList;
|
||||
@ -492,6 +499,7 @@ export function useWorkflowData() {
|
||||
urls: video.urls,
|
||||
video_id: video.video_id,
|
||||
video_status: video_status, // 0 生成中 1 生成完成 2 生成失败
|
||||
type: 'video'
|
||||
});
|
||||
}
|
||||
taskCurrent.videos.data = videoList;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user