video-flow-b/components/workflow/add-music-step.tsx
2025-06-19 17:15:03 +08:00

317 lines
10 KiB
TypeScript

"use client";
import { useState } from 'react';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
import { Badge } from '@/components/ui/badge';
import { Slider } from '@/components/ui/slider';
import { Progress } from '@/components/ui/progress';
import { ArrowLeft, ArrowRight, Play, Pause, Music, Upload, Wand2, Volume2 } from 'lucide-react';
interface AddMusicStepProps {
onNext: () => void;
onPrevious: () => void;
}
const mockChapters = [
{
id: 1,
title: 'Introduction',
duration: 45,
music: {
id: 1,
name: 'Uplifting Corporate',
genre: 'Corporate',
duration: 45,
volume: 30,
fadeIn: 2,
fadeOut: 3,
generated: true,
},
},
{
id: 2,
title: 'Core Concepts',
duration: 80,
music: {
id: 2,
name: 'Tech Ambient',
genre: 'Ambient',
duration: 80,
volume: 25,
fadeIn: 3,
fadeOut: 2,
generated: true,
},
},
{
id: 3,
title: 'Practical Applications',
duration: 75,
music: {
id: 3,
name: 'Modern Innovation',
genre: 'Electronic',
duration: 75,
volume: 35,
fadeIn: 2,
fadeOut: 4,
generated: true,
},
},
{
id: 4,
title: 'Future Outlook',
duration: 50,
music: {
id: 4,
name: 'Inspiring Future',
genre: 'Cinematic',
duration: 50,
volume: 40,
fadeIn: 1,
fadeOut: 5,
generated: true,
},
},
];
const musicGenres = [
'Corporate', 'Ambient', 'Electronic', 'Cinematic', 'Jazz', 'Folk', 'Rock', 'Classical'
];
export function AddMusicStep({ onNext, onPrevious }: AddMusicStepProps) {
const [chapters, setChapters] = useState(mockChapters);
const [playingChapter, setPlayingChapter] = useState<number | null>(null);
const handleVolumeChange = (chapterId: number, volume: number[]) => {
setChapters(chapters.map(ch =>
ch.id === chapterId
? { ...ch, music: { ...ch.music, volume: volume[0] } }
: ch
));
};
const handleFadeChange = (chapterId: number, type: 'fadeIn' | 'fadeOut', value: number[]) => {
setChapters(chapters.map(ch =>
ch.id === chapterId
? { ...ch, music: { ...ch.music, [type]: value[0] } }
: ch
));
};
const regenerateMusic = (chapterId: number) => {
const randomGenre = musicGenres[Math.floor(Math.random() * musicGenres.length)];
const randomName = `AI Generated ${randomGenre} ${Math.floor(Math.random() * 100)}`;
setChapters(chapters.map(ch =>
ch.id === chapterId
? {
...ch,
music: {
...ch.music,
name: randomName,
genre: randomGenre,
generated: true
}
}
: ch
));
};
const togglePlay = (chapterId: number) => {
setPlayingChapter(playingChapter === chapterId ? null : chapterId);
};
return (
<div className="space-y-6">
<Card>
<CardHeader>
<CardTitle className="flex items-center space-x-2">
<Music className="h-5 w-5" />
<span>Background Music</span>
</CardTitle>
</CardHeader>
<CardContent>
<p className="text-muted-foreground mb-6">
AI has automatically generated background music for each chapter.
You can adjust volumes, fade effects, or replace with custom music.
</p>
<div className="space-y-6">
{chapters.map((chapter) => (
<Card key={chapter.id} className="border-l-4 border-l-blue-500/20">
<CardHeader>
<div className="flex items-center justify-between">
<div className="flex items-center space-x-3">
<Badge variant="outline">Chapter {chapter.id}</Badge>
<h3 className="font-semibold">{chapter.title}</h3>
<Badge variant="secondary">{chapter.duration}s</Badge>
</div>
<div className="flex items-center space-x-2">
<Button
variant="ghost"
size="sm"
onClick={() => togglePlay(chapter.id)}
>
{playingChapter === chapter.id ? (
<Pause className="h-4 w-4" />
) : (
<Play className="h-4 w-4" />
)}
</Button>
</div>
</div>
</CardHeader>
<CardContent className="space-y-4">
{/* Music Info */}
<div className="bg-muted p-4 rounded-lg">
<div className="flex items-center justify-between mb-2">
<div className="flex items-center space-x-2">
<Music className="h-4 w-4 text-blue-600" />
<span className="font-medium">{chapter.music.name}</span>
<Badge variant="outline" className="text-xs">
{chapter.music.genre}
</Badge>
{chapter.music.generated && (
<Badge variant="secondary" className="text-xs">
AI Generated
</Badge>
)}
</div>
<span className="text-sm text-muted-foreground">
{chapter.music.duration}s
</span>
</div>
{playingChapter === chapter.id && (
<div className="space-y-2">
<Progress value={Math.random() * 100} className="h-1" />
<div className="flex justify-between text-xs text-muted-foreground">
<span>0:00</span>
<span>{Math.floor(chapter.duration / 60)}:{(chapter.duration % 60).toString().padStart(2, '0')}</span>
</div>
</div>
)}
</div>
{/* Controls */}
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
{/* Volume */}
<div className="space-y-2">
<label className="text-sm font-medium flex items-center">
<Volume2 className="mr-1 h-4 w-4" />
Volume ({chapter.music.volume}%)
</label>
<Slider
value={[chapter.music.volume]}
onValueChange={(value) => handleVolumeChange(chapter.id, value)}
max={100}
step={5}
className="w-full"
/>
</div>
{/* Fade In */}
<div className="space-y-2">
<label className="text-sm font-medium">
Fade In ({chapter.music.fadeIn}s)
</label>
<Slider
value={[chapter.music.fadeIn]}
onValueChange={(value) => handleFadeChange(chapter.id, 'fadeIn', value)}
max={10}
step={0.5}
className="w-full"
/>
</div>
{/* Fade Out */}
<div className="space-y-2">
<label className="text-sm font-medium">
Fade Out ({chapter.music.fadeOut}s)
</label>
<Slider
value={[chapter.music.fadeOut]}
onValueChange={(value) => handleFadeChange(chapter.id, 'fadeOut', value)}
max={10}
step={0.5}
className="w-full"
/>
</div>
</div>
{/* Action Buttons */}
<div className="flex space-x-2 pt-2">
<Button
variant="outline"
size="sm"
onClick={() => regenerateMusic(chapter.id)}
>
<Wand2 className="mr-2 h-4 w-4" />
Regenerate
</Button>
<Button variant="outline" size="sm">
<Upload className="mr-2 h-4 w-4" />
Upload Custom
</Button>
<Button variant="outline" size="sm">
Browse Library
</Button>
</div>
</CardContent>
</Card>
))}
</div>
</CardContent>
</Card>
{/* Global Music Settings */}
<Card>
<CardHeader>
<CardTitle>Global Settings</CardTitle>
</CardHeader>
<CardContent>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div className="space-y-3">
<label className="text-sm font-medium">Master Volume</label>
<Slider
defaultValue={[70]}
max={100}
step={5}
className="w-full"
/>
<p className="text-xs text-muted-foreground">
Adjust overall music volume relative to voice
</p>
</div>
<div className="space-y-3">
<label className="text-sm font-medium">Cross-fade Between Chapters</label>
<Slider
defaultValue={[2]}
max={5}
step={0.5}
className="w-full"
/>
<p className="text-xs text-muted-foreground">
Smooth transitions between chapter music
</p>
</div>
</div>
</CardContent>
</Card>
{/* Action Buttons */}
<div className="flex justify-between">
<Button variant="outline" onClick={onPrevious}>
<ArrowLeft className="mr-2 h-4 w-4" />
Back to Scenes
</Button>
<Button onClick={onNext}>
Final Composition
<ArrowRight className="ml-2 h-4 w-4" />
</Button>
</div>
</div>
);
}