summaryrefslogtreecommitdiff
path: root/apps/web/src/hooks/use-paste-media.ts
diff options
context:
space:
mode:
authorMaze Winther <mazewinther@gmail.com>2026-04-07 01:09:13 +0200
committerMaze Winther <mazewinther@gmail.com>2026-04-07 01:09:13 +0200
commite4b67094e757917588fb624a30aa046667403add (patch)
tree55452692eac69a5a63b58f2a5cdb701dd7023d68 /apps/web/src/hooks/use-paste-media.ts
parent79df73643185c02493364a077402726d1fa7b9ec (diff)
downloadopencut-e4b67094e757917588fb624a30aa046667403add.tar.gz
refactor: split effects and masks into dedicated rust crates, introduce MediaTime and FrameRate
Diffstat (limited to 'apps/web/src/hooks/use-paste-media.ts')
-rw-r--r--apps/web/src/hooks/use-paste-media.ts228
1 files changed, 114 insertions, 114 deletions
diff --git a/apps/web/src/hooks/use-paste-media.ts b/apps/web/src/hooks/use-paste-media.ts
index 1cba34c0..36d70ebe 100644
--- a/apps/web/src/hooks/use-paste-media.ts
+++ b/apps/web/src/hooks/use-paste-media.ts
@@ -1,114 +1,114 @@
-import { useEffect } from "react";
-import { useEditor } from "@/hooks/use-editor";
-import { processMediaAssets } from "@/lib/media/processing";
-import { showMediaUploadToast } from "@/lib/media/upload-toast";
-import { invokeAction } from "@/lib/actions";
-import { buildElementFromMedia } from "@/lib/timeline/element-utils";
-import { AddMediaAssetCommand } from "@/lib/commands/media";
-import { InsertElementCommand } from "@/lib/commands/timeline";
-import { BatchCommand } from "@/lib/commands";
-import { DEFAULT_NEW_ELEMENT_DURATION_SECONDS } from "@/lib/timeline/creation";
-import { isTypableDOMElement } from "@/utils/browser";
-import type { MediaType } from "@/lib/media/types";
-
-const MEDIA_MIME_PREFIXES: MediaType[] = ["image", "video", "audio"];
-
-function isMediaMimeType({ type }: { type: string }): boolean {
- return MEDIA_MIME_PREFIXES.some((prefix) => type.startsWith(`${prefix}/`));
-}
-
-function extractMediaFilesFromClipboard({
- clipboardData,
-}: {
- clipboardData: DataTransfer | null;
-}): File[] {
- if (!clipboardData?.items) return [];
-
- const files: File[] = [];
- for (const item of clipboardData.items) {
- if (item.kind !== "file") continue;
- if (!isMediaMimeType({ type: item.type })) continue;
-
- const file = item.getAsFile();
- if (file) files.push(file);
- }
- return files;
-}
-
-export function usePasteMedia() {
- const editor = useEditor();
-
- useEffect(() => {
- const handlePaste = async (event: ClipboardEvent) => {
- const activeElement = document.activeElement as HTMLElement;
-
- if (activeElement && isTypableDOMElement({ element: activeElement })) {
- return;
- }
-
- const files = extractMediaFilesFromClipboard({
- clipboardData: event.clipboardData,
- });
- if (files.length === 0) {
- event.preventDefault();
- invokeAction("paste-copied");
- return;
- }
-
- event.preventDefault();
-
- const activeProject = editor.project.getActive();
- if (!activeProject) return;
-
- try {
- await showMediaUploadToast({
- filesCount: files.length,
- promise: async () => {
- const processedAssets = await processMediaAssets({ files });
- const startTime = editor.playback.getCurrentTime();
-
- for (const asset of processedAssets) {
- const addMediaCmd = new AddMediaAssetCommand(
- activeProject.metadata.id,
- asset,
- );
- const assetId = addMediaCmd.getAssetId();
- const duration =
- asset.duration ?? DEFAULT_NEW_ELEMENT_DURATION_SECONDS;
- const trackType = asset.type === "audio" ? "audio" : "video";
-
- const element = buildElementFromMedia({
- mediaId: assetId,
- mediaType: asset.type,
- name: asset.name,
- duration,
- startTime,
- buffer:
- asset.type === "audio"
- ? new AudioBuffer({ length: 1, sampleRate: 44100 })
- : undefined,
- });
-
- const insertCmd = new InsertElementCommand({
- element,
- placement: { mode: "auto", trackType },
- });
- const batchCmd = new BatchCommand([addMediaCmd, insertCmd]);
- editor.command.execute({ command: batchCmd });
- }
-
- return {
- uploadedCount: processedAssets.length,
- assetNames: processedAssets.map((asset) => asset.name),
- };
- },
- });
- } catch (error) {
- console.error("Failed to paste media:", error);
- }
- };
-
- window.addEventListener("paste", handlePaste);
- return () => window.removeEventListener("paste", handlePaste);
- }, [editor]);
-}
+import { useEffect } from "react";
+import { useEditor } from "@/hooks/use-editor";
+import { processMediaAssets } from "@/lib/media/processing";
+import { showMediaUploadToast } from "@/lib/media/upload-toast";
+import { invokeAction } from "@/lib/actions";
+import { buildElementFromMedia } from "@/lib/timeline/element-utils";
+import { AddMediaAssetCommand } from "@/lib/commands/media";
+import { InsertElementCommand } from "@/lib/commands/timeline";
+import { BatchCommand } from "@/lib/commands";
+import { DEFAULT_NEW_ELEMENT_DURATION } from "@/lib/timeline/creation";
+import { isTypableDOMElement } from "@/utils/browser";
+import type { MediaType } from "@/lib/media/types";
+
+const MEDIA_MIME_PREFIXES: MediaType[] = ["image", "video", "audio"];
+
+function isMediaMimeType({ type }: { type: string }): boolean {
+ return MEDIA_MIME_PREFIXES.some((prefix) => type.startsWith(`${prefix}/`));
+}
+
+function extractMediaFilesFromClipboard({
+ clipboardData,
+}: {
+ clipboardData: DataTransfer | null;
+}): File[] {
+ if (!clipboardData?.items) return [];
+
+ const files: File[] = [];
+ for (const item of clipboardData.items) {
+ if (item.kind !== "file") continue;
+ if (!isMediaMimeType({ type: item.type })) continue;
+
+ const file = item.getAsFile();
+ if (file) files.push(file);
+ }
+ return files;
+}
+
+export function usePasteMedia() {
+ const editor = useEditor();
+
+ useEffect(() => {
+ const handlePaste = async (event: ClipboardEvent) => {
+ const activeElement = document.activeElement as HTMLElement;
+
+ if (activeElement && isTypableDOMElement({ element: activeElement })) {
+ return;
+ }
+
+ const files = extractMediaFilesFromClipboard({
+ clipboardData: event.clipboardData,
+ });
+ if (files.length === 0) {
+ event.preventDefault();
+ invokeAction("paste-copied");
+ return;
+ }
+
+ event.preventDefault();
+
+ const activeProject = editor.project.getActive();
+ if (!activeProject) return;
+
+ try {
+ await showMediaUploadToast({
+ filesCount: files.length,
+ promise: async () => {
+ const processedAssets = await processMediaAssets({ files });
+ const startTime = editor.playback.getCurrentTime();
+
+ for (const asset of processedAssets) {
+ const addMediaCmd = new AddMediaAssetCommand(
+ activeProject.metadata.id,
+ asset,
+ );
+ const assetId = addMediaCmd.getAssetId();
+ const duration =
+ asset.duration ?? DEFAULT_NEW_ELEMENT_DURATION;
+ const trackType = asset.type === "audio" ? "audio" : "video";
+
+ const element = buildElementFromMedia({
+ mediaId: assetId,
+ mediaType: asset.type,
+ name: asset.name,
+ duration,
+ startTime,
+ buffer:
+ asset.type === "audio"
+ ? new AudioBuffer({ length: 1, sampleRate: 44100 })
+ : undefined,
+ });
+
+ const insertCmd = new InsertElementCommand({
+ element,
+ placement: { mode: "auto", trackType },
+ });
+ const batchCmd = new BatchCommand([addMediaCmd, insertCmd]);
+ editor.command.execute({ command: batchCmd });
+ }
+
+ return {
+ uploadedCount: processedAssets.length,
+ assetNames: processedAssets.map((asset) => asset.name),
+ };
+ },
+ });
+ } catch (error) {
+ console.error("Failed to paste media:", error);
+ }
+ };
+
+ window.addEventListener("paste", handlePaste);
+ return () => window.removeEventListener("paste", handlePaste);
+ }, [editor]);
+}