import { Box } from '@mui/material'; import React, { useState } from 'react'; import ToolContent from '@components/ToolContent'; import { ToolComponentProps } from '@tools/defineTool'; import { GetGroupsType } from '@components/options/ToolOptions'; import { InitialValuesType } from './types'; import ToolVideoInput from '@components/input/ToolVideoInput'; import ToolFileResult from '@components/result/ToolFileResult'; import SimpleRadio from '@components/options/SimpleRadio'; import { FFmpeg } from '@ffmpeg/ffmpeg'; import { fetchFile } from '@ffmpeg/util'; const initialValues: InitialValuesType = { quality: 'mid', fps: '10', scale: '320:-1:flags=bicubic' }; export default function VideoToGif({ title, longDescription }: ToolComponentProps) { const [input, setInput] = useState(null); const [result, setResult] = useState(null); const [loading, setLoading] = useState(false); const compute = (values: InitialValuesType, input: File | null) => { if (!input) return; const { fps, scale } = values; let ffmpeg: FFmpeg | null = null; let ffmpegLoaded = false; const convertVideoToGif = async ( file: File, fps: string, scale: string ): Promise => { setLoading(true); if (!ffmpeg) { ffmpeg = new FFmpeg(); } if (!ffmpegLoaded) { await ffmpeg.load({ wasmURL: 'https://cdn.jsdelivr.net/npm/@ffmpeg/core@0.12.9/dist/esm/ffmpeg-core.wasm' }); ffmpegLoaded = true; } const fileName = file.name; const outputName = 'output.gif'; try { ffmpeg.writeFile(fileName, await fetchFile(file)); await ffmpeg.exec([ '-i', fileName, '-vf', `fps=${fps},scale=${scale},palettegen`, 'palette.png' ]); await ffmpeg.exec([ '-i', fileName, '-i', 'palette.png', '-filter_complex', `fps=${fps},scale=${scale}[x];[x][1:v]paletteuse`, outputName ]); const data = await ffmpeg.readFile(outputName); const blob = new Blob([data], { type: 'image/gif' }); const convertedFile = new File([blob], outputName, { type: 'image/gif' }); await ffmpeg.deleteFile(fileName); await ffmpeg.deleteFile(outputName); setResult(convertedFile); } catch (err) { console.error(`Failed to convert video: ${err}`); throw err; } finally { setLoading(false); } }; convertVideoToGif(input, fps, scale); }; const getGroups: GetGroupsType | null = ({ values, updateField }) => [ { title: 'Set Quality', component: ( { updateField('quality', 'low'); updateField('fps', '5'); updateField('scale', '240:-1:flags=bilinear'); }} checked={values.quality === 'low'} /> { updateField('quality', 'mid'); updateField('fps', '10'); updateField('scale', '320:-1:flags=bicubic'); }} checked={values.quality === 'mid'} /> { updateField('quality', 'high'); updateField('fps', '15'); updateField('scale', '480:-1:flags=lanczos'); }} checked={values.quality === 'high'} /> { updateField('quality', 'ultra'); updateField('fps', '15'); updateField('scale', '640:-1:flags=lanczos'); }} checked={values.quality === 'ultra'} /> ) } ]; return ( } resultComponent={ loading ? ( ) : ( ) } initialValues={initialValues} getGroups={getGroups} setInput={setInput} compute={compute} toolInfo={{ title: `What is a ${title}?`, description: longDescription }} /> ); }