"use client";
import { useState, useRef } from 'react';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
import { Badge } from '@/components/ui/badge';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
import { Slider } from '@/components/ui/slider';
import { ArrowLeft, ArrowRight, Play, Trash2, Replace, Scissors, Volume2, Edit, Upload, Image, Sparkles, ChevronLeft, ChevronRight, Layers, Pause, File, Ruler, UnfoldHorizontal, RefreshCcw, RotateCcw } from 'lucide-react';
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog';
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
import { Checkbox } from '@/components/ui/checkbox';
import { Label } from '@/components/ui/label';
import { Input } from '@/components/ui/input';
interface GenerateShotsStepProps {
onNext: () => void;
onPrevious: () => void;
}
interface Shot {
id: string;
type: string;
duration: number;
shotVideo: string;
generatedVideos: string[];
description: string;
transition: string;
volume: number;
mediaNumber: number;
}
interface Chapter {
id: number;
title: string;
shots: Shot[];
}
// 时间轴组件
const TimelineView = ({
chapters,
selectedShot,
onShotSelect,
onVideoCheck
}: {
chapters: Chapter[];
selectedShot: string;
onShotSelect: (shotId: string) => void;
onVideoCheck?: () => void;
}) => (
{chapters.map((chapter) => (
{chapter.shots.map((shot) => (
onShotSelect(shot.id)}
>
{onVideoCheck && (
)}
00:{shot.duration.toString().padStart(2, '0')}
))}
))}
);
// 媒体信息项组件
const MediaInfoItem = ({
icon,
text,
popoverContent
}: {
icon: React.ReactNode;
text: string;
popoverContent?: React.ReactNode;
}) => (
{icon}
{text}
{popoverContent && (
{popoverContent}
)}
);
// 查看视频弹窗
const CheckVideoDialog = ({
isOpen,
onClose,
currentShot
}: {
isOpen: boolean;
onClose: (open: boolean) => void;
currentShot?: Shot;
}) => (
);
// 替换媒体弹窗
const ReplaceMediaDialog = ({
isOpen,
onClose,
chapters,
selectedShot,
onShotSelect,
activeTab,
setActiveTab
}: {
isOpen: boolean;
onClose: (open: boolean) => void;
chapters: Chapter[];
selectedShot: string;
onShotSelect: (shotId: string) => void;
activeTab: string;
setActiveTab: (tab: string) => void;
}) => {
const replaceMediaTabs = [
{ value: 'uploaded', label: 'Uploaded media' },
{ value: 'stock', label: 'Stock media' },
{ value: 'generative', label: 'Generative media' },
];
return (
);
};
// 媒体属性弹窗
const MediaPropertyDialog = ({
isOpen,
onClose,
chapters,
selectedShot,
onShotSelect,
currentShot,
activeTab,
setActiveTab
}: {
isOpen: boolean;
onClose: (open: boolean) => void;
chapters: Chapter[];
selectedShot: string;
onShotSelect: (shotId: string) => void;
currentShot?: Shot;
activeTab: string;
setActiveTab: (tab: string) => void;
}) => {
const mediaPropertyTabs = [
{ value: 'media', label: 'Media' },
{ value: 'audio', label: 'Audio & SFX' },
];
return (
);
};
const mockChapters: Chapter[] = [
{
id: 1,
title: 'Chapter 1',
shots: [
{
id: '1-1',
type: 'talking-head',
duration: 8,
shotVideo: 'https://cdn.qikongjian.com/videos/1750162598_db834807-3f1c-4c52-8f50-b25396bd73ef_text_to_video_0.mp4',
generatedVideos:[
'https://cdn.qikongjian.com/videos/1750162598_db834807-3f1c-4c52-8f50-b25396bd73ef_text_to_video_0.mp4',
'https://cdn.qikongjian.com/videos/1750162598_db834807-3f1c-4c52-8f50-b25396bd73ef_text_to_video_0.mp4'
],
description: 'Opening welcome shot with character',
transition: 'Selected Automatically by Preset',
volume: 55,
mediaNumber: 1,
},
{
id: '1-2',
type: 'b-roll',
duration: 9,
shotVideo: 'https://cdn.qikongjian.com/videos/1750162598_db834807-3f1c-4c52-8f50-b25396bd73ef_text_to_video_0.mp4',
generatedVideos:[
'https://cdn.qikongjian.com/videos/1750162598_db834807-3f1c-4c52-8f50-b25396bd73ef_text_to_video_0.mp4',
'https://cdn.qikongjian.com/videos/1750162598_db834807-3f1c-4c52-8f50-b25396bd73ef_text_to_video_0.mp4',
'https://cdn.qikongjian.com/videos/1750162598_db834807-3f1c-4c52-8f50-b25396bd73ef_text_to_video_0.mp4'
],
description: 'Technology overview montage',
transition: 'Selected Automatically by Preset',
volume: 55,
mediaNumber: 2,
}
],
},
{
id: 2,
title: 'Chapter 2',
shots: [
{
id: '2-1',
type: 'talking-head',
duration: 8,
shotVideo: 'https://cdn.qikongjian.com/videos/1750162598_db834807-3f1c-4c52-8f50-b25396bd73ef_text_to_video_0.mp4',
generatedVideos:[
'https://cdn.qikongjian.com/videos/1750162598_db834807-3f1c-4c52-8f50-b25396bd73ef_text_to_video_0.mp4'
],
description: 'Opening welcome shot with character',
transition: 'Selected Automatically by Preset',
volume: 55,
mediaNumber: 1,
},
{
id: '2-2',
type: 'b-roll',
duration: 9,
shotVideo: 'https://cdn.qikongjian.com/videos/1750162598_db834807-3f1c-4c52-8f50-b25396bd73ef_text_to_video_0.mp4',
generatedVideos:[
'https://cdn.qikongjian.com/videos/1750162598_db834807-3f1c-4c52-8f50-b25396bd73ef_text_to_video_0.mp4',
'https://cdn.qikongjian.com/videos/1750162598_db834807-3f1c-4c52-8f50-b25396bd73ef_text_to_video_0.mp4',
'https://cdn.qikongjian.com/videos/1750162598_db834807-3f1c-4c52-8f50-b25396bd73ef_text_to_video_0.mp4'
],
description: 'Technology overview montage',
transition: 'Selected Automatically by Preset',
volume: 55,
mediaNumber: 2,
},
{
id: '2-3',
type: 'animation',
duration: 10,
shotVideo: 'https://cdn.qikongjian.com/videos/1750162598_db834807-3f1c-4c52-8f50-b25396bd73ef_text_to_video_0.mp4',
generatedVideos:[
'https://cdn.qikongjian.com/videos/1750162598_db834807-3f1c-4c52-8f50-b25396bd73ef_text_to_video_0.mp4',
'https://cdn.qikongjian.com/videos/1750162598_db834807-3f1c-4c52-8f50-b25396bd73ef_text_to_video_0.mp4'
],
description: 'Animation sequence',
transition: 'Selected Automatically by Preset',
volume: 55,
mediaNumber: 3,
},
{
id: '2-4',
type: 'talking-head',
duration: 8,
shotVideo: 'https://cdn.qikongjian.com/videos/1750162598_db834807-3f1c-4c52-8f50-b25396bd73ef_text_to_video_0.mp4',
generatedVideos:[
'https://cdn.qikongjian.com/videos/1750162598_db834807-3f1c-4c52-8f50-b25396bd73ef_text_to_video_0.mp4',
'https://cdn.qikongjian.com/videos/1750162598_db834807-3f1c-4c52-8f50-b25396bd73ef_text_to_video_0.mp4',
'https://cdn.qikongjian.com/videos/1750162598_db834807-3f1c-4c52-8f50-b25396bd73ef_text_to_video_0.mp4'
],
description: 'Character dialogue',
transition: 'Selected Automatically by Preset',
volume: 55,
mediaNumber: 4,
}
]
}
];
export function GenerateShotsStep({ onNext, onPrevious }: GenerateShotsStepProps) {
const [selectedChapter, setSelectedChapter] = useState(1);
const [selectedShot, setSelectedShot] = useState('1-1');
const [chapters, setChapters] = useState(mockChapters);
const videoRef = useRef(null);
const [isPlaying, setIsPlaying] = useState(false);
const [isCheckVideoOpen, setIsCheckVideoOpen] = useState(false);
const [isReplaceMediaOpen, setIsReplaceMediaOpen] = useState(false);
const [isMediaPropertyOpen, setIsMediaPropertyOpen] = useState(false);
const [activeTabReplaceMedia, setActiveTabReplaceMedia] = useState('uploaded');
const [activeTabMediaProperty, setActiveTabMediaProperty] = useState('media');
const currentChapter = chapters.find(ch => ch.id === selectedChapter);
const currentShot = currentChapter?.shots.find(shot => shot.id === selectedShot);
const handlePlayPause = () => {
if (videoRef.current) {
if (videoRef.current.paused) {
videoRef.current.play();
setIsPlaying(true);
} else {
videoRef.current.pause();
setIsPlaying(false);
}
}
};
const handleTransitionChange = (shotId: string, transition: string) => {
setChapters(chapters.map(ch =>
ch.id === selectedChapter
? {
...ch,
shots: ch.shots.map(shot =>
shot.id === shotId ? { ...shot, transition } : shot
)
}
: ch
));
};
const handleVolumeChange = (shotId: string, volume: number[]) => {
setChapters(chapters.map(ch =>
ch.id === selectedChapter
? {
...ch,
shots: ch.shots.map(shot =>
shot.id === shotId ? { ...shot, volume: volume[0] } : shot
)
}
: ch
));
};
const handleDeleteShot = (shotId: string) => {
setChapters(chapters.map(ch =>
ch.id === selectedChapter
? {
...ch,
shots: ch.shots.filter(shot => shot.id !== shotId)
}
: ch
));
};
const handleOpenReplaceMedia = (tab: string) => {
setActiveTabReplaceMedia(tab);
setIsReplaceMediaOpen(true);
};
const handleOpenMediaProperty = (tab: string) => {
setActiveTabMediaProperty(tab);
setIsMediaPropertyOpen(true);
};
return (
{/* 弹窗组件 */}
{/* Timeline Header */}
setIsCheckVideoOpen(true)}
/>
但我决心要改变它。我的翅膀展开,在千星的光芒中翱翔,降临人生。和星光铸就,我是凤青楗,这就是我重生的故事。不......?
{/* Main Content */}
{/* Replace Media Section */}
{currentShot && (
Replace media {currentShot.mediaNumber} with:
)}
{/* Media Info */}
{currentShot && (
Media info:
}
text={`Chapter 1 / media ${currentShot.mediaNumber} / Generated media`}
popoverContent={
Delete media
Delete and add blank media
}
/>
}
text="00m : 08s : 070ms / 00m : 08s : 070ms"
popoverContent={
}
/>
}
text={`Transition: ${currentShot.transition}`}
popoverContent={
Transition
}
/>
Audio volume: {currentShot.volume}% volume
{/* Media Properties */}
handleOpenMediaProperty('media')}>Media properties
)}
{/* Action Buttons */}
);
}