forked from 77media/video-flow
fix: 解决 use-workflow-data.tsx 合并冲突,保留手动打开 AI 编辑器逻辑(不自动触发)
This commit is contained in:
commit
8c7fb3f994
@ -75,6 +75,10 @@ function isShotVideoGeneration(data: any): data is ShotVideoGeneration {
|
|||||||
return data && 'prompt_json' in data && 'urls' in data && 'completed_count' in data && 'total_count' in data;
|
return data && 'prompt_json' in data && 'urls' in data && 'completed_count' in data && 'total_count' in data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function displayText(title: string, content: string, isLast: boolean = false) {
|
||||||
|
return `${content ? `${title}: ${content}${isLast ? '' : '\n'}` : ''}`
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 系统消息转换为blocks数组
|
* 系统消息转换为blocks数组
|
||||||
*/
|
*/
|
||||||
@ -153,7 +157,7 @@ function transformSystemMessage(
|
|||||||
if (isShotSketchGeneration(customData)) {
|
if (isShotSketchGeneration(customData)) {
|
||||||
blocks = [{
|
blocks = [{
|
||||||
type: 'text',
|
type: 'text',
|
||||||
text: `🎬 Storyboard static frame generation \nShot type: ${customData.shot_type}\nAtmosphere: ${customData.atmosphere}\nKey action: ${customData.key_action}`
|
text: `🎬 Storyboard static frame generation \n${displayText('Shot type', customData.shot_type)}${displayText('Atmosphere', customData.atmosphere)}${displayText('Key action', customData.key_action, true)}`
|
||||||
}, {
|
}, {
|
||||||
type: 'image',
|
type: 'image',
|
||||||
url: customData.url
|
url: customData.url
|
||||||
|
|||||||
@ -48,7 +48,7 @@ export function TopBar({ collapsed }: { collapsed: boolean }) {
|
|||||||
const [isLogin, setIsLogin] = useState(false);
|
const [isLogin, setIsLogin] = useState(false);
|
||||||
const [isManagingSubscription, setIsManagingSubscription] = useState(false);
|
const [isManagingSubscription, setIsManagingSubscription] = useState(false);
|
||||||
const [subscriptionStatus, setSubscriptionStatus] = useState<string>("");
|
const [subscriptionStatus, setSubscriptionStatus] = useState<string>("");
|
||||||
const [credits, setCredits] = useState<number>(100);
|
const [credits, setCredits] = useState<number>(0);
|
||||||
const [isLoadingSubscription, setIsLoadingSubscription] = useState(false);
|
const [isLoadingSubscription, setIsLoadingSubscription] = useState(false);
|
||||||
|
|
||||||
// 获取用户订阅信息
|
// 获取用户订阅信息
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useState, useEffect, useRef, useCallback } from 'react';
|
import { useState, useEffect, useRef, useCallback } from 'react';
|
||||||
import { Loader2, Check, Pencil, Trash } from 'lucide-react';
|
import { Loader2, Check, Pencil, Trash, Download } from 'lucide-react';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
import './style/create-to-video2.css';
|
import './style/create-to-video2.css';
|
||||||
|
|
||||||
@ -10,6 +10,9 @@ import { ChatInputBox } from '@/components/ChatInputBox/ChatInputBox';
|
|||||||
import cover_image1 from '@/public/assets/cover_image3.jpg';
|
import cover_image1 from '@/public/assets/cover_image3.jpg';
|
||||||
import { motion } from 'framer-motion';
|
import { motion } from 'framer-motion';
|
||||||
import { Tooltip, Button } from 'antd';
|
import { Tooltip, Button } from 'antd';
|
||||||
|
import { GlassIconButton } from '@/components/ui/glass-icon-button';
|
||||||
|
import { downloadVideo } from '@/utils/tools';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ideaText已迁移到ChatInputBox组件中
|
// ideaText已迁移到ChatInputBox组件中
|
||||||
@ -217,6 +220,18 @@ export default function CreateToVideo2() {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* 下载按钮 右上角 */}
|
||||||
|
{(project.final_video_url || project.final_simple_video_url) && (
|
||||||
|
<div className="absolute top-1 right-1">
|
||||||
|
<Tooltip placement="top" title="Download">
|
||||||
|
<Button size="small" type="text" className="w-[2.5rem] h-[2.5rem] rounded-full items-center justify-center p-0 hidden group-hover:flex transition-all duration-300 hover:bg-white/15" onClick={() => {
|
||||||
|
downloadVideo(project.final_video_url || project.final_simple_video_url);
|
||||||
|
}}><Download className="w-4 h-4 text-white" /></Button>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
|
||||||
{/* 状态标签 - 左上角 */}
|
{/* 状态标签 - 左上角 */}
|
||||||
<div className="absolute top-3 left-3">
|
<div className="absolute top-3 left-3">
|
||||||
{StatusBadge((project.status === 'COMPLETED' || project.final_simple_video_url) ? 'completed' : project.status === 'FAILED' ? 'failed' : 'pending')}
|
{StatusBadge((project.status === 'COMPLETED' || project.final_simple_video_url) ? 'completed' : project.status === 'FAILED' ? 'failed' : 'pending')}
|
||||||
@ -232,12 +247,12 @@ export default function CreateToVideo2() {
|
|||||||
</h2>
|
</h2>
|
||||||
{/* TODO 编辑标题 */}
|
{/* TODO 编辑标题 */}
|
||||||
{/* <Tooltip title="Edit Title">
|
{/* <Tooltip title="Edit Title">
|
||||||
<Button size="small" type="text" className="p-0 hidden group-hover:block transition-all duration-300"><Pencil className="w-4 h-4 text-white" /></Button>
|
<Button size="small" type="text" className="w-[2.5rem] h-[2.5rem] rounded-full items-center justify-center p-0 hidden group-hover:flex transition-all duration-300 hover:bg-white/15" /></Button>
|
||||||
</Tooltip> */}
|
</Tooltip> */}
|
||||||
</div>
|
</div>
|
||||||
{/* TODO 删除 */}
|
{/* TODO 删除 */}
|
||||||
{/* <Tooltip title="Delete">
|
{/* <Tooltip title="Delete">
|
||||||
<Button size="small" type="text" className="p-0 hidden group-hover:block transition-all duration-300"><Trash className="w-4 h-4 text-white" /></Button>
|
<Button size="small" type="text" className="w-[2.5rem] h-[2.5rem] rounded-full items-center justify-center p-0 hidden group-hover:flex transition-all duration-300 hover:bg-white/15" /></Button>
|
||||||
</Tooltip> */}
|
</Tooltip> */}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -10,7 +10,7 @@ import { mockScriptData } from '@/components/script-renderer/mock';
|
|||||||
import { Skeleton } from '@/components/ui/skeleton';
|
import { Skeleton } from '@/components/ui/skeleton';
|
||||||
import { TaskObject } from '@/api/DTO/movieEdit';
|
import { TaskObject } from '@/api/DTO/movieEdit';
|
||||||
import { Button, Tooltip } from 'antd';
|
import { Button, Tooltip } from 'antd';
|
||||||
import { Video } from 'lucide-react';
|
import { downloadVideo } from '@/utils/tools';
|
||||||
|
|
||||||
interface MediaViewerProps {
|
interface MediaViewerProps {
|
||||||
taskObject: TaskObject;
|
taskObject: TaskObject;
|
||||||
@ -273,14 +273,6 @@ export const MediaViewer = React.memo(function MediaViewer({
|
|||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// 下载视频
|
|
||||||
const downloadVideo = (url: string) => {
|
|
||||||
const a = document.createElement('a');
|
|
||||||
a.href = url;
|
|
||||||
a.download = url.split('/').pop() || '';
|
|
||||||
a.click();
|
|
||||||
};
|
|
||||||
|
|
||||||
// 渲染音量控制组件
|
// 渲染音量控制组件
|
||||||
const renderVolumeControls = () => (
|
const renderVolumeControls = () => (
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
@ -371,6 +363,12 @@ export const MediaViewer = React.memo(function MediaViewer({
|
|||||||
onClick={() => handleEditClick('3', 'final')}
|
onClick={() => handleEditClick('3', 'final')}
|
||||||
/>
|
/>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
{/* 下载按钮 */}
|
||||||
|
<Tooltip placement="top" title="Download video">
|
||||||
|
<GlassIconButton icon={Download} size='sm' onClick={() => {
|
||||||
|
downloadVideo(taskObject.final.url);
|
||||||
|
}} />
|
||||||
|
</Tooltip>
|
||||||
{showGotoCutButton && (
|
{showGotoCutButton && (
|
||||||
<Tooltip placement="top" title='Go to AI-powered editing platform'>
|
<Tooltip placement="top" title='Go to AI-powered editing platform'>
|
||||||
<GlassIconButton icon={Scissors} size='sm' onClick={onGotoCut} />
|
<GlassIconButton icon={Scissors} size='sm' onClick={onGotoCut} />
|
||||||
|
|||||||
@ -115,25 +115,24 @@ export function useWorkflowData() {
|
|||||||
}
|
}
|
||||||
}, [taskObject.currentStage]);
|
}, [taskObject.currentStage]);
|
||||||
|
|
||||||
const generateEditPlan = useCallback(async (isInit?: boolean) => {
|
// const generateEditPlan = useCallback(async (isInit?: boolean) => {
|
||||||
if (isLoaded) {
|
// if (isLoaded) {
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
localStorage.setItem(`isLoaded_plan_${episodeId}`, 'true');
|
// localStorage.setItem(`isLoaded_plan_${episodeId}`, 'true');
|
||||||
isInit && await getGenerateEditPlan({ project_id: episodeId });
|
// isInit && await getGenerateEditPlan({ project_id: episodeId });
|
||||||
openEditPlan();
|
// openEditPlan();
|
||||||
}, [episodeId]);
|
// }, [episodeId]);
|
||||||
|
|
||||||
const openEditPlan = useCallback(async () => {
|
const openEditPlan = useCallback(async () => {
|
||||||
window.open(`https://smartcut.movieflow.ai/ai-editor/${episodeId}?token=${token}&user_id=${useid}`, '_target');
|
window.open(`https://smartcut.movieflow.ai/ai-editor/${episodeId}?token=${token}&user_id=${useid}`, '_target');
|
||||||
}, [episodeId]);
|
}, [episodeId]);
|
||||||
|
|
||||||
// 注释掉自动跳转逻辑,改为手动点击触发
|
// useEffect(() => {
|
||||||
useEffect(() => {
|
// if (!from && canGoToCut && taskObject.status !== 'COMPLETED') {
|
||||||
if (!from && canGoToCut && taskObject.status !== 'COMPLETED') {
|
// generateEditPlan(true);
|
||||||
generateEditPlan(true);
|
// }
|
||||||
}
|
// }, [canGoToCut, taskObject.status]);
|
||||||
}, [canGoToCut, taskObject.status]);
|
|
||||||
|
|
||||||
|
|
||||||
useUpdateEffect(() => {
|
useUpdateEffect(() => {
|
||||||
@ -398,7 +397,7 @@ export function useWorkflowData() {
|
|||||||
|
|
||||||
const { current: taskCurrent } = tempTaskObject;
|
const { current: taskCurrent } = tempTaskObject;
|
||||||
|
|
||||||
taskCurrent.title = title || 'generating...';
|
taskCurrent.title = name || 'generating...';
|
||||||
taskCurrent.tags = tags || [];
|
taskCurrent.tags = tags || [];
|
||||||
taskCurrent.status = status as Status;
|
taskCurrent.status = status as Status;
|
||||||
|
|
||||||
@ -408,8 +407,8 @@ export function useWorkflowData() {
|
|||||||
const titleResponse = await getScriptTitle({ project_id: episodeId });
|
const titleResponse = await getScriptTitle({ project_id: episodeId });
|
||||||
console.log('titleResponse', titleResponse);
|
console.log('titleResponse', titleResponse);
|
||||||
if (titleResponse.successful) {
|
if (titleResponse.successful) {
|
||||||
taskCurrent.title = titleResponse.data.title;
|
taskCurrent.title = titleResponse.data.name;
|
||||||
taskCurrent.tags = titleResponse.data.tags || [];
|
taskCurrent.tags = titleResponse.data.description.tags || [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -77,3 +77,20 @@ export function createScreenAdapter(): void {
|
|||||||
// 将样式添加到head
|
// 将样式添加到head
|
||||||
document.head.appendChild(style);
|
document.head.appendChild(style);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const downloadVideo = async (url: string) => {
|
||||||
|
try {
|
||||||
|
const response = await fetch(url);
|
||||||
|
const blob = await response.blob();
|
||||||
|
const blobUrl = window.URL.createObjectURL(blob);
|
||||||
|
const a = document.createElement('a');
|
||||||
|
a.href = blobUrl;
|
||||||
|
a.download = url.split('/').pop() || 'video.mp4';
|
||||||
|
document.body.appendChild(a);
|
||||||
|
a.click();
|
||||||
|
document.body.removeChild(a);
|
||||||
|
window.URL.revokeObjectURL(blobUrl);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('下载视频失败:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
Loading…
x
Reference in New Issue
Block a user