2025-06-22 19:28:03 +02:00
|
|
|
"use client";
|
|
|
|
|
|
|
|
|
|
import { useTimelineStore } from "@/stores/timeline-store";
|
|
|
|
|
import { useMediaStore } from "@/stores/media-store";
|
2025-06-23 09:18:15 +03:00
|
|
|
import { usePlaybackStore } from "@/stores/playback-store";
|
2025-06-22 19:28:03 +02:00
|
|
|
import { ImageTimelineTreatment } from "@/components/ui/image-timeline-treatment";
|
2025-06-23 09:18:15 +03:00
|
|
|
import { VideoPlayer } from "@/components/ui/video-player";
|
2025-06-22 19:28:03 +02:00
|
|
|
import { Button } from "@/components/ui/button";
|
|
|
|
|
import { Play, Pause } from "lucide-react";
|
|
|
|
|
|
2025-06-22 10:02:50 +02:00
|
|
|
export function PreviewPanel() {
|
2025-06-22 19:28:03 +02:00
|
|
|
const { tracks } = useTimelineStore();
|
|
|
|
|
const { mediaItems } = useMediaStore();
|
2025-06-23 09:35:32 +03:00
|
|
|
const { isPlaying, toggle, currentTime } = usePlaybackStore();
|
2025-06-22 19:28:03 +02:00
|
|
|
|
2025-06-23 09:35:32 +03:00
|
|
|
// Find the active clip at the current playback time
|
|
|
|
|
const getActiveClip = () => {
|
|
|
|
|
for (const track of tracks) {
|
|
|
|
|
for (const clip of track.clips) {
|
|
|
|
|
const clipStart = clip.startTime;
|
|
|
|
|
const clipEnd = clip.startTime + (clip.duration - clip.trimStart - clip.trimEnd);
|
|
|
|
|
|
|
|
|
|
if (currentTime >= clipStart && currentTime < clipEnd) {
|
|
|
|
|
return clip;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return null;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const activeClip = getActiveClip();
|
|
|
|
|
const activeMediaItem = activeClip
|
|
|
|
|
? mediaItems.find((item) => item.id === activeClip.mediaId)
|
2025-06-22 19:28:03 +02:00
|
|
|
: null;
|
|
|
|
|
|
2025-06-23 09:35:32 +03:00
|
|
|
const aspectRatio = activeMediaItem?.aspectRatio || 16 / 9;
|
2025-06-22 22:10:50 +02:00
|
|
|
|
2025-06-23 09:18:15 +03:00
|
|
|
const renderContent = () => {
|
2025-06-23 14:40:34 +03:00
|
|
|
if (!activeClip) {
|
2025-06-22 19:28:03 +02:00
|
|
|
return (
|
2025-06-23 09:18:15 +03:00
|
|
|
<div className="absolute inset-0 flex items-center justify-center text-muted-foreground/50">
|
2025-06-23 09:35:32 +03:00
|
|
|
{tracks.length === 0 ? "Drop media to start editing" : "No clip at current time"}
|
2025-06-22 10:02:50 +02:00
|
|
|
</div>
|
2025-06-22 19:28:03 +02:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-23 14:40:34 +03:00
|
|
|
// Handle test clips without media items
|
|
|
|
|
if (!activeMediaItem && activeClip.mediaId === "test") {
|
|
|
|
|
return (
|
|
|
|
|
<div className="absolute inset-0 flex items-center justify-center bg-gradient-to-br from-blue-500/20 to-purple-500/20">
|
|
|
|
|
<div className="text-center">
|
|
|
|
|
<div className="text-6xl mb-4">🎬</div>
|
|
|
|
|
<p className="text-muted-foreground">{activeClip.name}</p>
|
|
|
|
|
<p className="text-xs text-muted-foreground/70 mt-2">Test clip for playback</p>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!activeMediaItem) {
|
|
|
|
|
return (
|
|
|
|
|
<div className="absolute inset-0 flex items-center justify-center text-muted-foreground/50">
|
|
|
|
|
Media not found
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
2025-06-23 09:35:32 +03:00
|
|
|
|
|
|
|
|
if (activeMediaItem.type === "video") {
|
2025-06-23 09:18:15 +03:00
|
|
|
return (
|
|
|
|
|
<VideoPlayer
|
2025-06-23 09:35:32 +03:00
|
|
|
src={activeMediaItem.url}
|
|
|
|
|
poster={activeMediaItem.thumbnailUrl}
|
2025-06-23 09:18:15 +03:00
|
|
|
className="w-full h-full"
|
2025-06-23 14:40:34 +03:00
|
|
|
clipStartTime={activeClip.startTime}
|
|
|
|
|
trimStart={activeClip.trimStart}
|
|
|
|
|
trimEnd={activeClip.trimEnd}
|
|
|
|
|
clipDuration={activeClip.duration}
|
2025-06-23 09:35:32 +03:00
|
|
|
key={`${activeClip.id}-${activeClip.trimStart}-${activeClip.trimEnd}`}
|
2025-06-23 09:18:15 +03:00
|
|
|
/>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-23 09:35:32 +03:00
|
|
|
if (activeMediaItem.type === "image") {
|
2025-06-22 19:28:03 +02:00
|
|
|
return (
|
|
|
|
|
<ImageTimelineTreatment
|
2025-06-23 09:35:32 +03:00
|
|
|
src={activeMediaItem.url}
|
|
|
|
|
alt={activeMediaItem.name}
|
2025-06-22 22:10:50 +02:00
|
|
|
targetAspectRatio={aspectRatio}
|
2025-06-23 09:18:15 +03:00
|
|
|
className="w-full h-full"
|
2025-06-22 19:28:03 +02:00
|
|
|
backgroundType="blur"
|
|
|
|
|
/>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-23 09:35:32 +03:00
|
|
|
if (activeMediaItem.type === "audio") {
|
2025-06-22 19:28:03 +02:00
|
|
|
return (
|
|
|
|
|
<div className="absolute inset-0 flex items-center justify-center bg-gradient-to-br from-green-500/20 to-emerald-500/20">
|
|
|
|
|
<div className="text-center">
|
|
|
|
|
<div className="text-6xl mb-4">🎵</div>
|
2025-06-23 09:35:32 +03:00
|
|
|
<p className="text-muted-foreground">{activeMediaItem.name}</p>
|
2025-06-23 09:18:15 +03:00
|
|
|
<Button
|
|
|
|
|
variant="outline"
|
|
|
|
|
className="mt-4"
|
|
|
|
|
onClick={toggle}
|
|
|
|
|
>
|
|
|
|
|
{isPlaying ? <Pause className="h-4 w-4 mr-2" /> : <Play className="h-4 w-4 mr-2" />}
|
|
|
|
|
{isPlaying ? "Pause" : "Play"}
|
|
|
|
|
</Button>
|
2025-06-22 19:28:03 +02:00
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return (
|
2025-06-22 22:10:50 +02:00
|
|
|
<div className="h-full flex flex-col items-center justify-center p-4 overflow-hidden">
|
|
|
|
|
<div
|
2025-06-23 09:18:15 +03:00
|
|
|
className="bg-black rounded-lg shadow-lg relative overflow-hidden flex-shrink-0"
|
2025-06-22 22:10:50 +02:00
|
|
|
style={{
|
|
|
|
|
aspectRatio: aspectRatio.toString(),
|
|
|
|
|
width: aspectRatio > 1 ? "100%" : "auto",
|
|
|
|
|
height: aspectRatio <= 1 ? "100%" : "auto",
|
|
|
|
|
maxWidth: "100%",
|
|
|
|
|
maxHeight: "100%",
|
|
|
|
|
}}
|
|
|
|
|
>
|
2025-06-23 09:18:15 +03:00
|
|
|
{renderContent()}
|
2025-06-22 10:02:50 +02:00
|
|
|
</div>
|
2025-06-22 19:28:03 +02:00
|
|
|
|
2025-06-23 09:35:32 +03:00
|
|
|
{activeMediaItem && (
|
2025-06-22 19:28:03 +02:00
|
|
|
<div className="mt-4 text-center">
|
|
|
|
|
<p className="text-sm text-muted-foreground">
|
2025-06-23 09:35:32 +03:00
|
|
|
{activeMediaItem.name}
|
2025-06-23 09:18:15 +03:00
|
|
|
</p>
|
|
|
|
|
<p className="text-xs text-muted-foreground/70">
|
|
|
|
|
{aspectRatio.toFixed(2)} • {aspectRatio > 1 ? "Landscape" : aspectRatio < 1 ? "Portrait" : "Square"}
|
2025-06-22 19:28:03 +02:00
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
2025-06-22 10:02:50 +02:00
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|