diff options
| author | DevloperAmanSingh <amansinghneh211205@gmail.com> | 2025-06-26 23:40:56 +0530 |
|---|---|---|
| committer | DevloperAmanSingh <amansinghneh211205@gmail.com> | 2025-06-26 23:40:56 +0530 |
| commit | b799615654eb5225365726fd736eeb0fbd5c776e (patch) | |
| tree | 9820252c19f94d0fc8a8fdac97243757d61aa911 | |
| parent | 3224dd974aa0a4906cae5642e8479a3618c49c03 (diff) | |
| download | opencut-b799615654eb5225365726fd736eeb0fbd5c776e.tar.gz | |
feat: enhance playback controls with keyboard shortcuts for clip manipulation and audio separation
| -rw-r--r-- | apps/web/src/hooks/use-playback-controls.ts | 176 |
1 files changed, 166 insertions, 10 deletions
diff --git a/apps/web/src/hooks/use-playback-controls.ts b/apps/web/src/hooks/use-playback-controls.ts index 5742e272..23bffe13 100644 --- a/apps/web/src/hooks/use-playback-controls.ts +++ b/apps/web/src/hooks/use-playback-controls.ts @@ -1,18 +1,174 @@ import { useEffect } from "react"; import { usePlaybackStore } from "@/stores/playback-store"; +import { useTimelineStore } from "@/stores/timeline-store"; +import { toast } from "sonner"; -export function usePlaybackControls() { - const { toggle } = usePlaybackStore(); +export const usePlaybackControls = () => { + const { isPlaying, currentTime, play, pause, seek } = usePlaybackStore(); + + const { + selectedClips, + tracks, + splitClip, + splitAndKeepLeft, + splitAndKeepRight, + separateAudio, + } = useTimelineStore(); useEffect(() => { - const handleKeyDown = (e: KeyboardEvent) => { - if (e.code === "Space" && e.target === document.body) { - e.preventDefault(); - toggle(); + const handleKeyPress = (e: KeyboardEvent) => { + if ( + e.target instanceof HTMLInputElement || + e.target instanceof HTMLTextAreaElement + ) { + return; } + + switch (e.key) { + case " ": + e.preventDefault(); + if (isPlaying) { + pause(); + } else { + play(); + } + break; + + case "s": + if (e.ctrlKey || e.metaKey) { + e.preventDefault(); + handleSplitSelectedClip(); + } + break; + + case "q": + if (e.ctrlKey || e.metaKey) { + e.preventDefault(); + handleSplitAndKeepLeft(); + } + break; + + case "w": + if (e.ctrlKey || e.metaKey) { + e.preventDefault(); + handleSplitAndKeepRight(); + } + break; + + case "d": + if (e.ctrlKey || e.metaKey) { + e.preventDefault(); + handleSeparateAudio(); + } + break; + } + }; + + const handleSplitSelectedClip = () => { + if (selectedClips.length !== 1) { + toast.error("Select exactly one clip to split"); + return; + } + + const { trackId, clipId } = selectedClips[0]; + const track = tracks.find((t) => t.id === trackId); + const clip = track?.clips.find((c) => c.id === clipId); + + if (!clip) return; + + const effectiveStart = clip.startTime; + const effectiveEnd = + clip.startTime + (clip.duration - clip.trimStart - clip.trimEnd); + + if (currentTime <= effectiveStart || currentTime >= effectiveEnd) { + toast.error("Playhead must be within selected clip"); + return; + } + + splitClip(trackId, clipId, currentTime); + toast.success("Clip split at playhead"); + }; + + const handleSplitAndKeepLeft = () => { + if (selectedClips.length !== 1) { + toast.error("Select exactly one clip"); + return; + } + + const { trackId, clipId } = selectedClips[0]; + const track = tracks.find((t) => t.id === trackId); + const clip = track?.clips.find((c) => c.id === clipId); + + if (!clip) return; + + const effectiveStart = clip.startTime; + const effectiveEnd = + clip.startTime + (clip.duration - clip.trimStart - clip.trimEnd); + + if (currentTime <= effectiveStart || currentTime >= effectiveEnd) { + toast.error("Playhead must be within selected clip"); + return; + } + + splitAndKeepLeft(trackId, clipId, currentTime); + toast.success("Split and kept left portion"); + }; + + const handleSplitAndKeepRight = () => { + if (selectedClips.length !== 1) { + toast.error("Select exactly one clip"); + return; + } + + const { trackId, clipId } = selectedClips[0]; + const track = tracks.find((t) => t.id === trackId); + const clip = track?.clips.find((c) => c.id === clipId); + + if (!clip) return; + + const effectiveStart = clip.startTime; + const effectiveEnd = + clip.startTime + (clip.duration - clip.trimStart - clip.trimEnd); + + if (currentTime <= effectiveStart || currentTime >= effectiveEnd) { + toast.error("Playhead must be within selected clip"); + return; + } + + splitAndKeepRight(trackId, clipId, currentTime); + toast.success("Split and kept right portion"); + }; + + const handleSeparateAudio = () => { + if (selectedClips.length !== 1) { + toast.error("Select exactly one video clip to separate audio"); + return; + } + + const { trackId, clipId } = selectedClips[0]; + const track = tracks.find((t) => t.id === trackId); + + if (!track || track.type !== "video") { + toast.error("Select a video clip to separate audio"); + return; + } + + separateAudio(trackId, clipId); + toast.success("Audio separated to audio track"); }; - document.addEventListener("keydown", handleKeyDown); - return () => document.removeEventListener("keydown", handleKeyDown); - }, [toggle]); -}
\ No newline at end of file + document.addEventListener("keydown", handleKeyPress); + return () => document.removeEventListener("keydown", handleKeyPress); + }, [ + isPlaying, + currentTime, + selectedClips, + tracks, + play, + pause, + splitClip, + splitAndKeepLeft, + splitAndKeepRight, + separateAudio, + ]); +}; |
