forked from 77media/video-flow
修复 视频片段重新生成 点击 Apply 没有传 video_id 给接口
This commit is contained in:
parent
540737210d
commit
ed79e49abe
@ -37,6 +37,10 @@ export class VideoSegmentEntityAdapter {
|
|||||||
task_status: string = "";
|
task_status: string = "";
|
||||||
/** 任务结果 */
|
/** 任务结果 */
|
||||||
task_result: Array<{
|
task_result: Array<{
|
||||||
|
/** 任务ID */
|
||||||
|
video_id: string;
|
||||||
|
/** 任务状态 */
|
||||||
|
video_status: number|null;
|
||||||
/** 叙事目标 */
|
/** 叙事目标 */
|
||||||
narrative_goal: string;
|
narrative_goal: string;
|
||||||
/** 镜头1描述 */
|
/** 镜头1描述 */
|
||||||
@ -175,6 +179,8 @@ export class VideoSegmentEntityAdapter {
|
|||||||
*/
|
*/
|
||||||
static fromVideoSegmentEntity(entities: VideoSegmentEntity[]): VideoSegmentEntityAdapter {
|
static fromVideoSegmentEntity(entities: VideoSegmentEntity[]): VideoSegmentEntityAdapter {
|
||||||
const taskResults: Array<{
|
const taskResults: Array<{
|
||||||
|
video_id: string;
|
||||||
|
video_status: number|null;
|
||||||
narrative_goal: string;
|
narrative_goal: string;
|
||||||
shot_1: string;
|
shot_1: string;
|
||||||
shot_2: string;
|
shot_2: string;
|
||||||
@ -235,6 +241,8 @@ export class VideoSegmentEntityAdapter {
|
|||||||
|
|
||||||
taskResults.push({
|
taskResults.push({
|
||||||
narrative_goal: narrative_goal,
|
narrative_goal: narrative_goal,
|
||||||
|
video_id: entity.id,
|
||||||
|
video_status: entity.status,
|
||||||
shot_1: shots.shot_1 || "",
|
shot_1: shots.shot_1 || "",
|
||||||
shot_2: shots.shot_2 || "",
|
shot_2: shots.shot_2 || "",
|
||||||
shot_3: shots.shot_3 || "",
|
shot_3: shots.shot_3 || "",
|
||||||
|
|||||||
@ -60,7 +60,15 @@ export class TextToShotAdapter {
|
|||||||
let currentText = text;
|
let currentText = text;
|
||||||
|
|
||||||
// 按角色名称长度降序排序,避免短名称匹配到长名称的一部分
|
// 按角色名称长度降序排序,避免短名称匹配到长名称的一部分
|
||||||
const sortedRoles = [...roles].sort((a, b) => b.name.length - a.name.length);
|
// 既要兼容 首字母大写 其余小写、还要兼容 全部大写
|
||||||
|
const sortedRoles = [...roles].sort((a, b) => b.name.length - a.name.length).map(role => ({
|
||||||
|
...role,
|
||||||
|
name: role.name.charAt(0).toUpperCase() + role.name.slice(1).toLowerCase()
|
||||||
|
})).concat([...roles].map(role => ({
|
||||||
|
...role,
|
||||||
|
name: role.name.toUpperCase()
|
||||||
|
})));
|
||||||
|
|
||||||
|
|
||||||
while (currentText.length > 0) {
|
while (currentText.length > 0) {
|
||||||
let matchFound = false;
|
let matchFound = false;
|
||||||
|
|||||||
@ -432,10 +432,9 @@ export const MediaViewer = React.memo(function MediaViewer({
|
|||||||
<div
|
<div
|
||||||
className="relative w-full h-full rounded-lg group"
|
className="relative w-full h-full rounded-lg group"
|
||||||
>
|
>
|
||||||
{/* 只在生成过程中或没有视频时使用ProgressiveReveal */}
|
{/* 背景模糊的图片 */}
|
||||||
<div className="relative w-full h-full">
|
{taskObject.videos.data[currentSketchIndex].video_status !== 1 && (
|
||||||
{/* 背景模糊的图片 */}
|
<div className="absolute inset-0 overflow-hidden z-20" style={{background: `url(${taskObject.shot_sketch.data[currentSketchIndex]?.url}) no-repeat center center`}}>
|
||||||
<div className="absolute inset-0 overflow-hidden" style={{background: `url(${taskSketch[currentSketchIndex]?.url}) no-repeat center center`}}>
|
|
||||||
{/* 生成中 */}
|
{/* 生成中 */}
|
||||||
{taskObject.videos.data[currentSketchIndex].video_status === 0 && (
|
{taskObject.videos.data[currentSketchIndex].video_status === 0 && (
|
||||||
<div className="absolute inset-0 bg-black/10 flex items-center justify-center">
|
<div className="absolute inset-0 bg-black/10 flex items-center justify-center">
|
||||||
@ -455,39 +454,39 @@ export const MediaViewer = React.memo(function MediaViewer({
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
|
||||||
{/* 视频 多个 取第一个 */}
|
{/* 视频 多个 取第一个 */}
|
||||||
{ taskObject.videos.data[currentSketchIndex].urls && (
|
{ taskObject.videos.data[currentSketchIndex].urls && (
|
||||||
<motion.div
|
<motion.div
|
||||||
initial={{ clipPath: "inset(0 100% 0 0)" }}
|
initial={{ clipPath: "inset(0 100% 0 0)" }}
|
||||||
animate={{ clipPath: "inset(0 0% 0 0)" }}
|
animate={{ clipPath: "inset(0 0% 0 0)" }}
|
||||||
transition={{ duration: 0.8, ease: [0.43, 0.13, 0.23, 0.96] }}
|
transition={{ duration: 0.8, ease: [0.43, 0.13, 0.23, 0.96] }}
|
||||||
className="relative z-10 w-full h-full"
|
className="relative z-10 w-full h-full"
|
||||||
>
|
>
|
||||||
<video
|
<video
|
||||||
ref={mainVideoRef}
|
ref={mainVideoRef}
|
||||||
key={taskObject.videos.data[currentSketchIndex].urls[0]}
|
key={taskObject.videos.data[currentSketchIndex].urls[0]}
|
||||||
className="w-full h-full rounded-lg object-cover object-center relative z-10"
|
className="w-full h-full rounded-lg object-cover object-center relative z-10"
|
||||||
src={taskObject.videos.data[currentSketchIndex].urls[0]}
|
src={taskObject.videos.data[currentSketchIndex].urls[0]}
|
||||||
autoPlay={isVideoPlaying}
|
autoPlay={isVideoPlaying}
|
||||||
loop={true}
|
loop={true}
|
||||||
playsInline
|
playsInline
|
||||||
onLoadedData={() => applyVolumeSettings(mainVideoRef.current!)}
|
onLoadedData={() => applyVolumeSettings(mainVideoRef.current!)}
|
||||||
onEnded={() => {
|
onEnded={() => {
|
||||||
if (isVideoPlaying) {
|
if (isVideoPlaying) {
|
||||||
// 自动切换到下一个视频的逻辑在父组件处理
|
// 自动切换到下一个视频的逻辑在父组件处理
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 操作按钮组 */}
|
{/* 操作按钮组 */}
|
||||||
<AnimatePresence>
|
<AnimatePresence>
|
||||||
<motion.div
|
<motion.div
|
||||||
className="absolute top-4 right-4 gap-2 z-[11] hidden group-hover:flex"
|
className="absolute top-4 right-4 gap-2 z-[21] hidden group-hover:flex"
|
||||||
initial={{ opacity: 0, y: -10 }}
|
initial={{ opacity: 0, y: -10 }}
|
||||||
animate={{ opacity: 1, y: 0 }}
|
animate={{ opacity: 1, y: 0 }}
|
||||||
exit={{ opacity: 0, y: -10 }}
|
exit={{ opacity: 0, y: -10 }}
|
||||||
@ -501,10 +500,10 @@ export const MediaViewer = React.memo(function MediaViewer({
|
|||||||
</AnimatePresence>
|
</AnimatePresence>
|
||||||
|
|
||||||
{/* 底部控制区域 */}
|
{/* 底部控制区域 */}
|
||||||
{ taskObject.videos.data[currentSketchIndex] && (
|
{ taskObject.videos.data[currentSketchIndex].video_status === 1 && (
|
||||||
<AnimatePresence>
|
<AnimatePresence>
|
||||||
<motion.div
|
<motion.div
|
||||||
className="absolute bottom-4 left-4 z-[11] flex items-center gap-3"
|
className="absolute bottom-4 left-4 z-[21] flex items-center gap-3"
|
||||||
initial={{ opacity: 0, scale: 0.8 }}
|
initial={{ opacity: 0, scale: 0.8 }}
|
||||||
animate={{ opacity: 1, scale: 1 }}
|
animate={{ opacity: 1, scale: 1 }}
|
||||||
exit={{ opacity: 0, scale: 0.8 }}
|
exit={{ opacity: 0, scale: 0.8 }}
|
||||||
@ -534,8 +533,6 @@ export const MediaViewer = React.memo(function MediaViewer({
|
|||||||
|
|
||||||
// 渲染分镜草图
|
// 渲染分镜草图
|
||||||
const renderSketchContent = (currentSketch: any) => {
|
const renderSketchContent = (currentSketch: any) => {
|
||||||
const defaultBgColors = ['RGB(45, 50, 70)', 'RGB(75, 80, 100)', 'RGB(105, 110, 130)'];
|
|
||||||
const bgColors = defaultBgColors;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
|||||||
@ -307,7 +307,6 @@ export function useWorkflowData() {
|
|||||||
}
|
}
|
||||||
taskCurrent.roles.data = characterList;
|
taskCurrent.roles.data = characterList;
|
||||||
if (task.task_status === 'COMPLETED') {
|
if (task.task_status === 'COMPLETED') {
|
||||||
console.log('----------角色生成完成,有几个分镜', sketchCount);
|
|
||||||
// 角色生成完成
|
// 角色生成完成
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -337,7 +336,6 @@ export function useWorkflowData() {
|
|||||||
taskCurrent.shot_sketch.data = sketchList;
|
taskCurrent.shot_sketch.data = sketchList;
|
||||||
if (task.task_status === 'COMPLETED') {
|
if (task.task_status === 'COMPLETED') {
|
||||||
// 草图生成完成
|
// 草图生成完成
|
||||||
console.log('----------草图生成完成', sketchCount);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -126,7 +126,7 @@ export function CharacterToken(props: ReactNodeViewProps) {
|
|||||||
>
|
>
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
{roles.map((role) => {
|
{roles.map((role) => {
|
||||||
const isSelected = role.name === name;
|
const isSelected = role.name.toLowerCase() === name.toLowerCase();
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={role.name}
|
key={role.name}
|
||||||
|
|||||||
@ -226,14 +226,14 @@ export const ShotsEditor = forwardRef<any, ShotsEditorProps>(function ShotsEdito
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* <button
|
<button
|
||||||
data-alt="add-shot-button"
|
data-alt="add-shot-button"
|
||||||
onClick={handleAddShot}
|
onClick={addShot}
|
||||||
className="flex items-center gap-1.5 px-3 py-1.5 text-sm font-medium text-white/60 hover:text-white hover:bg-white/5 rounded-md transition-colors"
|
className="flex items-center gap-1.5 px-3 py-1.5 text-sm font-medium text-white/60 hover:text-white hover:bg-white/5 rounded-md transition-colors"
|
||||||
>
|
>
|
||||||
<Plus className="w-4 h-4" />
|
<Plus className="w-4 h-4" />
|
||||||
新增镜头
|
Add
|
||||||
</button> */}
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 分镜内容 */}
|
{/* 分镜内容 */}
|
||||||
|
|||||||
@ -18,10 +18,8 @@ interface ShotTabContentProps {
|
|||||||
roles?: any[];
|
roles?: any[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ShotTabContent({
|
export const ShotTabContent = (props: ShotTabContentProps) => {
|
||||||
currentSketchIndex = 0,
|
const { currentSketchIndex = 0, roles = [] } = props;
|
||||||
roles = []
|
|
||||||
}: ShotTabContentProps) {
|
|
||||||
const {
|
const {
|
||||||
loading,
|
loading,
|
||||||
shotData,
|
shotData,
|
||||||
@ -48,7 +46,7 @@ export function ShotTabContent({
|
|||||||
const [selectedLibaryRole, setSelectedLibaryRole] = useState<any>(null);
|
const [selectedLibaryRole, setSelectedLibaryRole] = useState<any>(null);
|
||||||
const [isLoadingShots, setIsLoadingShots] = useState(false);
|
const [isLoadingShots, setIsLoadingShots] = useState(false);
|
||||||
const shotsEditorRef = useRef<any>(null);
|
const shotsEditorRef = useRef<any>(null);
|
||||||
const videoRef = useRef<HTMLVideoElement>(null);
|
const [isRegenerate, setIsRegenerate] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.log('shotTabContent-----roles', roles);
|
console.log('shotTabContent-----roles', roles);
|
||||||
@ -163,15 +161,17 @@ export function ShotTabContent({
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 点击按钮重新生成
|
// 点击按钮重新生成
|
||||||
const handleRegenerate = () => {
|
const handleRegenerate = async () => {
|
||||||
console.log('regenerate');
|
console.log('regenerate');
|
||||||
|
setIsRegenerate(true);
|
||||||
const shotInfo = shotsEditorRef.current.getShotInfo();
|
const shotInfo = shotsEditorRef.current.getShotInfo();
|
||||||
console.log('shotTabContent-----shotInfo', shotInfo);
|
console.log('shotTabContent-----shotInfo', shotInfo);
|
||||||
setSelectedSegment({
|
setSelectedSegment({
|
||||||
...shotData[selectedIndex],
|
...shotData[selectedIndex],
|
||||||
lens: shotInfo
|
lens: shotInfo
|
||||||
});
|
});
|
||||||
regenerateVideoSegment();
|
await regenerateVideoSegment();
|
||||||
|
setIsRegenerate(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
// 新增分镜
|
// 新增分镜
|
||||||
@ -394,8 +394,8 @@ export function ShotTabContent({
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
{/* 重新生成按钮、新增分镜按钮 */}
|
{/* 重新生成按钮、新增分镜按钮 */}
|
||||||
<div className="grid grid-cols-2 gap-2">
|
<div className="grid grid-cols-1 gap-2">
|
||||||
<motion.button
|
{/* <motion.button
|
||||||
onClick={() => handleAddShot()}
|
onClick={() => handleAddShot()}
|
||||||
className="flex items-center justify-center gap-2 px-4 py-3 bg-pink-500/10 hover:bg-pink-500/20
|
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"
|
text-pink-500 rounded-lg transition-colors"
|
||||||
@ -404,16 +404,17 @@ export function ShotTabContent({
|
|||||||
>
|
>
|
||||||
<Plus className="w-4 h-4" />
|
<Plus className="w-4 h-4" />
|
||||||
<span>Add Shot</span>
|
<span>Add Shot</span>
|
||||||
</motion.button>
|
</motion.button> */}
|
||||||
<motion.button
|
<motion.button
|
||||||
onClick={() => handleRegenerate()}
|
onClick={() => handleRegenerate()}
|
||||||
className="flex items-center justify-center gap-2 px-4 py-3 bg-blue-500/10 hover:bg-blue-500/20
|
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"
|
text-blue-500 rounded-lg transition-colors"
|
||||||
whileHover={{ scale: 1.02 }}
|
whileHover={{ scale: 1.02 }}
|
||||||
whileTap={{ scale: 0.98 }}
|
whileTap={{ scale: 0.98 }}
|
||||||
>
|
disabled={isRegenerate}
|
||||||
<RefreshCw className="w-4 h-4" />
|
>
|
||||||
<span>Regenerate</span>
|
<RefreshCw className="w-4 h-4" />
|
||||||
|
<span>{isRegenerate ? 'Regenerating...' : 'Regenerate'}</span>
|
||||||
</motion.button>
|
</motion.button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user