forked from 77media/video-flow
168 lines
6.2 KiB
TypeScript
168 lines
6.2 KiB
TypeScript
"use client";
|
|
|
|
import { useState } from 'react';
|
|
import { Button } from '@/components/ui/button';
|
|
import { Card } from '@/components/ui/card';
|
|
import { Textarea } from '@/components/ui/textarea';
|
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
|
import { ArrowLeft, Loader2 } from 'lucide-react';
|
|
import { useRouter } from 'next/navigation';
|
|
import { ModeEnum, ResolutionEnum } from "@/app/model/enums";
|
|
import { createScriptEpisode, CreateScriptEpisodeRequest, updateScriptEpisode, UpdateScriptEpisodeRequest } from "@/api/script_episode";
|
|
import { convertScriptToScene } from "@/api/video_flow";
|
|
|
|
export function ScriptToVideo() {
|
|
const router = useRouter();
|
|
const [script, setScript] = useState('');
|
|
const [selectedMode, setSelectedMode] = useState<ModeEnum>(ModeEnum.AUTOMATIC);
|
|
const [selectedResolution, setSelectedResolution] = useState<ResolutionEnum>(ResolutionEnum.HD_720P);
|
|
const [isCreating, setIsCreating] = useState(false);
|
|
|
|
const handleBack = () => {
|
|
router.push('/create');
|
|
};
|
|
|
|
const handleCreate = async () => {
|
|
if (!script.trim()) {
|
|
alert('请输入剧本内容');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
setIsCreating(true);
|
|
|
|
// Create episode
|
|
const episodeData: CreateScriptEpisodeRequest = {
|
|
title: "Script Episode",
|
|
script_id: 0, // This should come from a project
|
|
status: 1,
|
|
summary: script
|
|
};
|
|
|
|
const episodeResponse = await createScriptEpisode(episodeData);
|
|
if (episodeResponse.code !== 0) {
|
|
alert(`创建剧集失败: ${episodeResponse.message}`);
|
|
return;
|
|
}
|
|
|
|
const episodeId = episodeResponse.data.id;
|
|
|
|
// Convert script to scenes
|
|
const convertResponse = await convertScriptToScene(script, episodeId, 0);
|
|
|
|
if (convertResponse.code === 0) {
|
|
// Update episode with generated data
|
|
const updateEpisodeData: UpdateScriptEpisodeRequest = {
|
|
id: episodeId,
|
|
atmosphere: convertResponse.data.atmosphere,
|
|
summary: convertResponse.data.summary,
|
|
scene: convertResponse.data.scene,
|
|
characters: convertResponse.data.characters,
|
|
};
|
|
|
|
await updateScriptEpisode(updateEpisodeData);
|
|
|
|
// Navigate to workflow
|
|
router.push(`/create/work-flow?episodeId=${episodeId}`);
|
|
} else {
|
|
alert(`转换失败: ${convertResponse.message}`);
|
|
}
|
|
} catch (error) {
|
|
console.error('创建过程出错:', error);
|
|
alert("创建项目时发生错误,请稍后重试");
|
|
} finally {
|
|
setIsCreating(false);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div className="min-h-screen bg-gradient-to-br from-slate-900 via-purple-900 to-slate-900 p-6">
|
|
<div className="max-w-4xl mx-auto">
|
|
{/* Header */}
|
|
<div className="flex items-center gap-4 mb-8">
|
|
<Button
|
|
variant="ghost"
|
|
size="sm"
|
|
onClick={handleBack}
|
|
className="text-white/70 hover:text-white"
|
|
>
|
|
<ArrowLeft className="h-4 w-4" />
|
|
</Button>
|
|
<h1 className="text-2xl font-bold text-white">Script to Video</h1>
|
|
</div>
|
|
|
|
{/* Main Content */}
|
|
<Card className="p-6 bg-white/5 border-white/10">
|
|
<div className="space-y-6">
|
|
{/* Script Input */}
|
|
<div>
|
|
<label className="block text-sm font-medium text-white/70 mb-2">
|
|
Script Content
|
|
</label>
|
|
<Textarea
|
|
value={script}
|
|
onChange={(e) => setScript(e.target.value)}
|
|
placeholder="Enter your script here..."
|
|
className="min-h-[200px] bg-white/5 border-white/20 text-white placeholder:text-white/50"
|
|
rows={10}
|
|
/>
|
|
</div>
|
|
|
|
{/* Settings */}
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
<div>
|
|
<label className="block text-sm font-medium text-white/70 mb-2">
|
|
Mode
|
|
</label>
|
|
<Select value={selectedMode.toString()} onValueChange={(value) => setSelectedMode(Number(value) as ModeEnum)}>
|
|
<SelectTrigger className="bg-white/5 border-white/20 text-white">
|
|
<SelectValue />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem value={ModeEnum.AUTOMATIC.toString()}>Automatic</SelectItem>
|
|
<SelectItem value={ModeEnum.MANUAL.toString()}>Manual</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="block text-sm font-medium text-white/70 mb-2">
|
|
Resolution
|
|
</label>
|
|
<Select value={selectedResolution.toString()} onValueChange={(value) => setSelectedResolution(Number(value) as ResolutionEnum)}>
|
|
<SelectTrigger className="bg-white/5 border-white/20 text-white">
|
|
<SelectValue />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem value={ResolutionEnum.HD_720P.toString()}>720P HD</SelectItem>
|
|
<SelectItem value={ResolutionEnum.FULL_HD_1080P.toString()}>1080P Full HD</SelectItem>
|
|
<SelectItem value={ResolutionEnum.UHD_2K.toString()}>2K UHD</SelectItem>
|
|
<SelectItem value={ResolutionEnum.UHD_4K.toString()}>4K UHD</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Create Button */}
|
|
<div className="flex justify-end">
|
|
<Button
|
|
onClick={handleCreate}
|
|
disabled={isCreating || !script.trim()}
|
|
className="bg-purple-600 hover:bg-purple-700"
|
|
>
|
|
{isCreating ? (
|
|
<>
|
|
<Loader2 className="h-4 w-4 mr-2 animate-spin" />
|
|
Creating...
|
|
</>
|
|
) : (
|
|
'Create Video'
|
|
)}
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</Card>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|