summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHyteq <DerpsDer.es@gmail.com>2025-06-23 09:35:32 +0300
committerHyteq <DerpsDer.es@gmail.com>2025-06-23 09:35:32 +0300
commitf688c7ef5d8b4b97a74162d97508092901311379 (patch)
tree1fd2641f8a549370b2ac2fc0a12348d5bd121820
parent68864c144221d1cff996c9d7a2499e7b7e741086 (diff)
downloadopencut-f688c7ef5d8b4b97a74162d97508092901311379.tar.gz
fix: video player properly reflecting the edited timeline, video start
-rw-r--r--apps/web/src/components/editor/preview-panel.tsx54
-rw-r--r--apps/web/src/components/editor/timeline.tsx25
-rw-r--r--apps/web/src/components/ui/video-player.tsx6
3 files changed, 49 insertions, 36 deletions
diff --git a/apps/web/src/components/editor/preview-panel.tsx b/apps/web/src/components/editor/preview-panel.tsx
index 593758e1..74bf5f98 100644
--- a/apps/web/src/components/editor/preview-panel.tsx
+++ b/apps/web/src/components/editor/preview-panel.tsx
@@ -11,39 +11,59 @@ import { Play, Pause } from "lucide-react";
export function PreviewPanel() {
const { tracks } = useTimelineStore();
const { mediaItems } = useMediaStore();
- const { isPlaying, toggle } = usePlaybackStore();
+ const { isPlaying, toggle, currentTime } = usePlaybackStore();
- const firstClip = tracks[0]?.clips[0];
- const firstMediaItem = firstClip
- ? mediaItems.find((item) => item.id === firstClip.mediaId)
+ // 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)
: null;
- const aspectRatio = firstMediaItem?.aspectRatio || 16 / 9;
+ const aspectRatio = activeMediaItem?.aspectRatio || 16 / 9;
const renderContent = () => {
- if (!firstMediaItem) {
+ if (!activeMediaItem || !activeClip) {
return (
<div className="absolute inset-0 flex items-center justify-center text-muted-foreground/50">
- Drop media to start editing
+ {tracks.length === 0 ? "Drop media to start editing" : "No clip at current time"}
</div>
);
}
- if (firstMediaItem.type === "video") {
+ // Calculate the relative time within the clip (accounting for trim)
+ const relativeTime = Math.max(0, currentTime - activeClip.startTime + activeClip.trimStart);
+
+ if (activeMediaItem.type === "video") {
return (
<VideoPlayer
- src={firstMediaItem.url}
- poster={firstMediaItem.thumbnailUrl}
+ src={activeMediaItem.url}
+ poster={activeMediaItem.thumbnailUrl}
className="w-full h-full"
+ startTime={relativeTime}
+ key={`${activeClip.id}-${activeClip.trimStart}-${activeClip.trimEnd}`}
/>
);
}
- if (firstMediaItem.type === "image") {
+ if (activeMediaItem.type === "image") {
return (
<ImageTimelineTreatment
- src={firstMediaItem.url}
- alt={firstMediaItem.name}
+ src={activeMediaItem.url}
+ alt={activeMediaItem.name}
targetAspectRatio={aspectRatio}
className="w-full h-full"
backgroundType="blur"
@@ -51,12 +71,12 @@ export function PreviewPanel() {
);
}
- if (firstMediaItem.type === "audio") {
+ if (activeMediaItem.type === "audio") {
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>
- <p className="text-muted-foreground">{firstMediaItem.name}</p>
+ <p className="text-muted-foreground">{activeMediaItem.name}</p>
<Button
variant="outline"
className="mt-4"
@@ -88,10 +108,10 @@ export function PreviewPanel() {
{renderContent()}
</div>
- {firstMediaItem && (
+ {activeMediaItem && (
<div className="mt-4 text-center">
<p className="text-sm text-muted-foreground">
- {firstMediaItem.name}
+ {activeMediaItem.name}
</p>
<p className="text-xs text-muted-foreground/70">
{aspectRatio.toFixed(2)} • {aspectRatio > 1 ? "Landscape" : aspectRatio < 1 ? "Portrait" : "Square"}
diff --git a/apps/web/src/components/editor/timeline.tsx b/apps/web/src/components/editor/timeline.tsx
index ed9128dc..be532991 100644
--- a/apps/web/src/components/editor/timeline.tsx
+++ b/apps/web/src/components/editor/timeline.tsx
@@ -259,19 +259,6 @@ export function Timeline() {
>
{/* Timeline Header */}
<div className="py-3 relative bg-muted/30 border-b">
- {/* Playhead */}
- {duration > 0 && (
- <div
- className="absolute top-0 bottom-0 w-0.5 bg-red-500 pointer-events-none z-10"
- style={{
- left: `${(currentTime / (duration / zoomLevel)) * 100}%`,
- transform: 'translateX(-50%)'
- }}
- >
- <div className="absolute -top-1 left-1/2 transform -translate-x-1/2 w-3 h-3 bg-red-500 rounded-full border-2 border-white shadow-sm" />
- </div>
- )}
-
{/* Zoom indicator */}
<div className="absolute top-1 right-2 text-xs text-muted-foreground">
{zoomLevel.toFixed(1)}x
@@ -301,14 +288,15 @@ export function Timeline() {
)}
{/* Playhead for tracks area */}
- {tracks.length > 0 && duration > 0 && (
+ {tracks.length > 0 && (
<div
- className="absolute top-0 bottom-0 w-0.5 bg-red-500/80 pointer-events-none z-10"
+ className="absolute top-0 bottom-0 w-0.5 bg-red-500 pointer-events-none z-20"
style={{
- left: `${(currentTime / (duration / zoomLevel)) * 100}%`,
- transform: 'translateX(-50%)'
+ left: `${currentTime * 50 * zoomLevel + 128}px`,
}}
- />
+ >
+ <div className="absolute -top-8 left-1/2 transform -translate-x-1/2 w-3 h-3 bg-red-500 rounded-full border-2 border-white shadow-sm" />
+ </div>
)}
</div>
</div>
@@ -321,6 +309,7 @@ function TimelineTrackComponent({ track, zoomLevel }: { track: TimelineTrack, zo
const { mediaItems } = useMediaStore();
const { moveClipToTrack, updateClipTrim, updateClipStartTime } = useTimelineStore();
const [isDropping, setIsDropping] = useState(false);
+ const [dropPosition, setDropPosition] = useState<number | null>(null);
const [resizing, setResizing] = useState<{
clipId: string;
side: 'left' | 'right';
diff --git a/apps/web/src/components/ui/video-player.tsx b/apps/web/src/components/ui/video-player.tsx
index 127cc5db..d31f7113 100644
--- a/apps/web/src/components/ui/video-player.tsx
+++ b/apps/web/src/components/ui/video-player.tsx
@@ -9,9 +9,10 @@ interface VideoPlayerProps {
src: string;
poster?: string;
className?: string;
+ startTime?: number;
}
-export function VideoPlayer({ src, poster, className = "" }: VideoPlayerProps) {
+export function VideoPlayer({ src, poster, className = "", startTime = 0 }: VideoPlayerProps) {
const videoRef = useRef<HTMLVideoElement>(null);
const { isPlaying, currentTime, volume, play, pause, setVolume, setDuration, setCurrentTime } = usePlaybackStore();
@@ -25,6 +26,9 @@ export function VideoPlayer({ src, poster, className = "" }: VideoPlayerProps) {
const handleLoadedMetadata = () => {
setDuration(video.duration);
+ if (startTime > 0) {
+ video.currentTime = startTime;
+ }
};
const handleSeekEvent = (e: CustomEvent) => {