From 1f3c7995ea758394bf51fd2f5902ce834fb381cf Mon Sep 17 00:00:00 2001 From: CodeAlpha Intern Date: Fri, 22 May 2026 12:56:13 +0530 Subject: [PATCH 1/2] fix: grab frame button ignores crop, scale, and rotate edits The 'Grab Frame' feature previously drew the raw video element directly onto a canvas, completely ignoring any zoom, crop, rotation, or aspect ratio changes the user applied in the UI. Fix: Replaced the manual canvas drawing logic with the existing 'captureFrameAsPng' utility from frame-export.ts, which correctly computes and applies all recipe transformations before rendering the image. --- src/components/VideoPreview.tsx | 29 +++++++++-------------------- 1 file changed, 9 insertions(+), 20 deletions(-) diff --git a/src/components/VideoPreview.tsx b/src/components/VideoPreview.tsx index 71095330..ae9277ed 100644 --- a/src/components/VideoPreview.tsx +++ b/src/components/VideoPreview.tsx @@ -4,6 +4,7 @@ import { useEffect, useRef, useState, useCallback, RefObject } from "react"; import { EditRecipe } from "@/lib/types"; import { getPresetById } from "@/lib/presets"; +import { captureFrameAsPng } from "@/lib/frame-export"; import { cn } from "@/lib/utils"; import { Camera } from "lucide-react"; @@ -21,34 +22,22 @@ export default function VideoPreview({ file, recipe, videoRef }: Props) { const onLoadedRef = useRef<(() => void) | null>(null); /** Capture the current video frame and download it as a PNG. */ - const handleGrabFrame = useCallback(() => { + const handleGrabFrame = useCallback(async () => { const video = videoRef.current; - if (!video || video.readyState < 2) return; - - const canvas = document.createElement("canvas"); - canvas.width = video.videoWidth; - canvas.height = video.videoHeight; - - const ctx = canvas.getContext("2d"); - if (!ctx) return; - ctx.drawImage(video, 0, 0, canvas.width, canvas.height); - - canvas.toBlob((blob) => { - if (!blob) return; - - const totalSec = Math.floor(video.currentTime); - const mins = String(Math.floor(totalSec / 60)).padStart(2, "0"); - const secs = String(totalSec % 60).padStart(2, "0"); - const filename = `frame-${mins}m${secs}s.png`; + if (!video || video.readyState < 2 || !recipe) return; + try { + const { blob, filename } = await captureFrameAsPng(video, recipe); const url = URL.createObjectURL(blob); const a = document.createElement("a"); a.href = url; a.download = filename; a.click(); URL.revokeObjectURL(url); - }, "image/png"); - }, [videoRef]); + } catch (err) { + console.error("Failed to capture frame:", err); + } + }, [videoRef, recipe]); useEffect(() => { if (!file) return; From f867ba51cad8c2cd0ffcd483386cb4d41ec90250 Mon Sep 17 00:00:00 2001 From: CodeAlpha Intern Date: Sun, 24 May 2026 16:18:07 +0530 Subject: [PATCH 2/2] fix: remove duplicate URL.revokeObjectURL call in file effect --- src/components/VideoPreview.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/VideoPreview.tsx b/src/components/VideoPreview.tsx index a1d9ce8e..11aabcbb 100644 --- a/src/components/VideoPreview.tsx +++ b/src/components/VideoPreview.tsx @@ -60,7 +60,6 @@ export default function VideoPreview({ useEffect(() => { if (!file) return; - if (urlRef.current) URL.revokeObjectURL(urlRef.current); setIsLoading(true); const id = ++lastId.current; const url = URL.createObjectURL(file);