完善下载:分镜下载、最终视频下载、列表中视频下载

This commit is contained in:
北枳 2025-09-03 19:10:25 +08:00
parent 2bf142f664
commit 2820ce0c4d
4 changed files with 43 additions and 13 deletions

View File

@ -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>

View File

@ -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} />

View File

@ -397,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;

View File

@ -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);
}
};