From fd28651c78a96f3eb9beb0ae93199d15fb8dc508 Mon Sep 17 00:00:00 2001 From: nevolodia Date: Sun, 25 May 2025 13:39:21 +0200 Subject: [PATCH] Crop video main page added. --- src/pages/tools/video/crop-video/index.tsx | 167 +++++++++++++++++++++ 1 file changed, 167 insertions(+) create mode 100644 src/pages/tools/video/crop-video/index.tsx diff --git a/src/pages/tools/video/crop-video/index.tsx b/src/pages/tools/video/crop-video/index.tsx new file mode 100644 index 0000000..e756fdc --- /dev/null +++ b/src/pages/tools/video/crop-video/index.tsx @@ -0,0 +1,167 @@ +import { Box, TextField, Typography, Alert } from '@mui/material'; +import { useCallback, useState, useEffect } from 'react'; +import ToolFileResult from '@components/result/ToolFileResult'; +import ToolContent from '@components/ToolContent'; +import { ToolComponentProps } from '@tools/defineTool'; +import { GetGroupsType } from '@components/options/ToolOptions'; +import { debounce } from 'lodash'; +import ToolVideoInput from '@components/input/ToolVideoInput'; +import { cropVideo, getVideoDimensions } from './service'; +import { InitialValuesType } from './types'; + +export const initialValues: InitialValuesType = { + x: 0, + y: 0, + width: 100, + height: 100 +}; + +export default function CropVideo({ title }: ToolComponentProps) { + const [input, setInput] = useState(null); + const [result, setResult] = useState(null); + const [loading, setLoading] = useState(false); + const [videoDimensions, setVideoDimensions] = useState<{ + width: number; + height: number; + } | null>(null); + + useEffect(() => { + if (input) { + getVideoDimensions(input) + .then((dimensions) => { + setVideoDimensions(dimensions); + }) + .catch((error) => { + console.error('Error getting video dimensions:', error); + }); + } else { + setVideoDimensions(null); + } + }, [input]); + + const compute = async ( + optionsValues: InitialValuesType, + input: File | null + ) => { + if (!input) return; + + setLoading(true); + + try { + const croppedFile = await cropVideo(input, optionsValues); + setResult(croppedFile); + } catch (error) { + console.error('Error cropping video:', error); + } finally { + setLoading(false); + } + }; + + const debouncedCompute = useCallback(debounce(compute, 1000), [ + videoDimensions + ]); + + const getGroups: GetGroupsType = ({ + values, + updateField + }) => [ + { + title: 'Video Information', + component: ( + + {videoDimensions ? ( + + Video dimensions: {videoDimensions.width} ×{' '} + {videoDimensions.height} pixels + + ) : ( + + Load a video to see dimensions + + )} + + ) + }, + { + title: 'Crop Coordinates', + component: ( + + + updateField('x', parseInt(e.target.value) || 0)} + size="small" + inputProps={{ min: 0 }} + /> + updateField('y', parseInt(e.target.value) || 0)} + size="small" + inputProps={{ min: 0 }} + /> + + + + updateField('width', parseInt(e.target.value) || 0) + } + size="small" + inputProps={{ min: 1 }} + /> + + updateField('height', parseInt(e.target.value) || 0) + } + size="small" + inputProps={{ min: 1 }} + /> + + + ) + } + ]; + + return ( + + } + resultComponent={ + loading ? ( + + ) : ( + + ) + } + initialValues={initialValues} + getGroups={getGroups} + compute={debouncedCompute} + setInput={setInput} + /> + ); +}