import { Box } from '@mui/material'; import React, { useCallback, useState } from 'react'; import * as Yup from 'yup'; 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 { compressVideo, VideoResolution } from './service'; import SimpleRadio from '@components/options/SimpleRadio'; import Slider from 'rc-slider'; import 'rc-slider/assets/index.css'; import { useTranslation } from 'react-i18next'; export const initialValues = { width: 480 as VideoResolution, crf: 23, preset: 'medium' }; export const validationSchema = Yup.object({ width: Yup.number() .oneOf( [240, 360, 480, 720, 1080], 'Width must be one of the standard resolutions' ) .required('Width is required'), crf: Yup.number() .min(0, 'CRF must be at least 0') .max(51, 'CRF must be at most 51') .required('CRF is required'), preset: Yup.string() .oneOf( [ 'ultrafast', 'superfast', 'veryfast', 'faster', 'fast', 'medium', 'slow', 'slower', 'veryslow' ], 'Preset must be a valid ffmpeg preset' ) .required('Preset is required') }); const resolutionOptions: { value: VideoResolution; label: string }[] = [ { value: 240, label: '240p' }, { value: 360, label: '360p' }, { value: 480, label: '480p' }, { value: 720, label: '720p' }, { value: 1080, label: '1080p' } ]; const presetOptions = [ { value: 'ultrafast', label: 'Ultrafast (Lowest Quality, Smallest Size)' }, { value: 'superfast', label: 'Superfast' }, { value: 'veryfast', label: 'Very Fast' }, { value: 'faster', label: 'Faster' }, { value: 'fast', label: 'Fast' }, { value: 'medium', label: 'Medium (Balanced)' }, { value: 'slow', label: 'Slow' }, { value: 'slower', label: 'Slower' }, { value: 'veryslow', label: 'Very Slow (Highest Quality, Largest Size)' } ]; export default function CompressVideo({ title }: ToolComponentProps) { const { t } = useTranslation('video'); const [input, setInput] = useState(null); const [result, setResult] = useState(null); const [loading, setLoading] = useState(false); const compute = async ( optionsValues: typeof initialValues, input: File | null ) => { if (!input) return; setLoading(true); try { const compressedFile = await compressVideo(input, { width: optionsValues.width, crf: optionsValues.crf, preset: optionsValues.preset }); setResult(compressedFile); } catch (error) { console.error('Error compressing video:', error); } finally { setLoading(false); } }; const debouncedCompute = useCallback(debounce(compute, 1000), []); const getGroups: GetGroupsType = ({ values, updateField }) => [ { title: t('compress.resolution'), component: ( {resolutionOptions.map((option) => ( { updateField('width', option.value); }} /> ))} ) }, { title: t('compress.quality'), component: ( { updateField('crf', typeof value === 'number' ? value : value[0]); }} marks={{ 0: t('compress.lossless'), 23: t('compress.default'), 51: t('compress.worst') }} /> ) } // { // title: 'Encoding Preset', // component: ( // updateField('preset', value)} // options={presetOptions} // description={ // 'Determines the compression speed. Slower presets provide better compression (quality per filesize) but take more time.' // } // /> // ) // } ]; return ( } resultComponent={ } initialValues={initialValues} getGroups={getGroups} compute={debouncedCompute} setInput={setInput} validationSchema={validationSchema} /> ); }