summaryrefslogtreecommitdiff
path: root/apps/web/src/hooks/use-playback-controls.ts
diff options
context:
space:
mode:
authoriza <59828082+izadoesdev@users.noreply.github.com>2025-06-27 07:02:28 +0300
committerGitHub <noreply@github.com>2025-06-27 07:02:28 +0300
commitf53f2802ebeda4e4ae71f42e6f92b1a50dee8515 (patch)
tree4277024b083849cb55d824596d2e4564c463b982 /apps/web/src/hooks/use-playback-controls.ts
parent87e90a5e24b6fbfe37be7d057617411071b56abf (diff)
parentc32daa4f2ec967902f2ce12d291f8034ca8bce38 (diff)
downloadopencut-f53f2802ebeda4e4ae71f42e6f92b1a50dee8515.tar.gz
Merge pull request #135 from DevloperAmanSingh/feature/player-controls
Feature: More Players Controls are added now .
Diffstat (limited to 'apps/web/src/hooks/use-playback-controls.ts')
-rw-r--r--apps/web/src/hooks/use-playback-controls.ts182
1 files changed, 169 insertions, 13 deletions
diff --git a/apps/web/src/hooks/use-playback-controls.ts b/apps/web/src/hooks/use-playback-controls.ts
index 5742e272..2c689470 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 { useEffect, useCallback } 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();
- useEffect(() => {
- const handleKeyDown = (e: KeyboardEvent) => {
- if (e.code === "Space" && e.target === document.body) {
- e.preventDefault();
- toggle();
+ const {
+ selectedClips,
+ tracks,
+ splitClip,
+ splitAndKeepLeft,
+ splitAndKeepRight,
+ separateAudio,
+ } = useTimelineStore();
+
+ const handleSplitSelectedClip = useCallback(() => {
+ 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");
+ }, [selectedClips, tracks, currentTime, splitClip]);
+
+ const handleSplitAndKeepLeftCallback = useCallback(() => {
+ 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");
+ }, [selectedClips, tracks, currentTime, splitAndKeepLeft]);
+
+ const handleSplitAndKeepRightCallback = useCallback(() => {
+ 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");
+ }, [selectedClips, tracks, currentTime, splitAndKeepRight]);
+
+ const handleSeparateAudioCallback = useCallback(() => {
+ 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");
+ }, [selectedClips, tracks, separateAudio]);
+
+ const handleKeyPress = useCallback(
+ (e: KeyboardEvent) => {
+ if (
+ e.target instanceof HTMLInputElement ||
+ e.target instanceof HTMLTextAreaElement
+ ) {
+ return;
}
- };
- document.addEventListener("keydown", handleKeyDown);
- return () => document.removeEventListener("keydown", handleKeyDown);
- }, [toggle]);
-} \ No newline at end of file
+ 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();
+ handleSplitAndKeepLeftCallback();
+ }
+ break;
+
+ case "w":
+ if (e.ctrlKey || e.metaKey) {
+ e.preventDefault();
+ handleSplitAndKeepRightCallback();
+ }
+ break;
+
+ case "d":
+ if (e.ctrlKey || e.metaKey) {
+ e.preventDefault();
+ handleSeparateAudioCallback();
+ }
+ break;
+ }
+ },
+ [
+ isPlaying,
+ play,
+ pause,
+ handleSplitSelectedClip,
+ handleSplitAndKeepLeftCallback,
+ handleSplitAndKeepRightCallback,
+ handleSeparateAudioCallback,
+ ]
+ );
+
+ useEffect(() => {
+ document.addEventListener("keydown", handleKeyPress);
+ return () => document.removeEventListener("keydown", handleKeyPress);
+ }, [handleKeyPress]);
+};