From 2a9d3c4fdabb88fc6ee6d22a4238c792399b5be8 Mon Sep 17 00:00:00 2001 From: C043 Date: Thu, 22 May 2025 22:47:27 +0200 Subject: [PATCH 01/10] Fixed create-tool script - It was creating all my pwd inside the project before and exiting with an error - Now it work as intened --- scripts/create-tool.mjs | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/scripts/create-tool.mjs b/scripts/create-tool.mjs index 3032f95..ab16ce1 100644 --- a/scripts/create-tool.mjs +++ b/scripts/create-tool.mjs @@ -1,7 +1,7 @@ -import { readFile, writeFile } from 'fs/promises'; -import fs from 'fs'; -import { dirname, join, sep } from 'path'; -import { fileURLToPath } from 'url'; +import { readFile, writeFile } from "fs/promises"; +import fs from "fs"; +import { dirname, join, sep } from "path"; +import { fileURLToPath } from "url"; const currentDirname = dirname(fileURLToPath(import.meta.url)); @@ -10,14 +10,14 @@ const folder = process.argv[3]; const toolsDir = join( currentDirname, - '..', - 'src', - 'pages', - 'tools', - folder ?? '' + "..", + "src", + "pages", + "tools", + folder ?? "" ); if (!toolName) { - throw new Error('Please specify a toolname.'); + throw new Error("Please specify a toolname."); } function capitalizeFirstLetter(string) { @@ -35,7 +35,7 @@ function createFolderStructure(basePath, foldersToCreateIndexCount) { if (!fs.existsSync(currentPath)) { fs.mkdirSync(currentPath, { recursive: true }); } - const indexPath = join(currentPath, 'index.ts'); + const indexPath = join(currentPath, "index.ts"); if ( !fs.existsSync(indexPath) && index < folderArray.length - 1 && @@ -54,15 +54,15 @@ function createFolderStructure(basePath, foldersToCreateIndexCount) { } // Start the recursive folder creation - recursiveCreate('.', 0); + recursiveCreate(toolsDir, 0); } -const toolNameCamelCase = toolName.replace(/-./g, (x) => x[1].toUpperCase()); +const toolNameCamelCase = toolName.replace(/-./g, x => x[1].toUpperCase()); const toolNameTitleCase = - toolName[0].toUpperCase() + toolName.slice(1).replace(/-/g, ' '); + toolName[0].toUpperCase() + toolName.slice(1).replace(/-/g, " "); const toolDir = join(toolsDir, toolName); const type = folder.split(sep)[folder.split(sep).length - 1]; -await createFolderStructure(toolDir, folder.split(sep).length); +await createFolderStructure(toolName, folder.split(sep).length); console.log(`Directory created: ${toolDir}`); const createToolFile = async (name, content) => { @@ -150,7 +150,7 @@ export const tool = defineTool('${type}', { icon: '', description: '', shortDescription: '', - keywords: ['${toolName.split('-').join("', '")}'], + keywords: ['${toolName.split("-").join("', '")}'], longDescription: '', component: lazy(() => import('./index')) }); @@ -209,9 +209,9 @@ import { expect, describe, it } from 'vitest'; // ` // ) -const toolsIndex = join(toolsDir, 'index.ts'); -const indexContent = await readFile(toolsIndex, { encoding: 'utf-8' }).then( - (r) => r.split('\n') +const toolsIndex = join(toolsDir, "index.ts"); +const indexContent = await readFile(toolsIndex, { encoding: "utf-8" }).then(r => + r.split("\n") ); indexContent.splice( @@ -221,5 +221,5 @@ indexContent.splice( toolNameCamelCase )} } from './${toolName}/meta';` ); -writeFile(toolsIndex, indexContent.join('\n')); +writeFile(toolsIndex, indexContent.join("\n")); console.log(`Added import in: ${toolsIndex}`); From 9a0ca5c6117d389ce459fdad84f1d3c54792b7f3 Mon Sep 17 00:00:00 2001 From: C043 Date: Thu, 22 May 2025 22:48:29 +0200 Subject: [PATCH 02/10] [Video Change-speed tool] Initial commit --- .../change-speed/change-speed.service.test.ts | 6 ++ src/pages/tools/video/change-speed/index.tsx | 62 +++++++++++++++++++ src/pages/tools/video/change-speed/meta.ts | 13 ++++ src/pages/tools/video/change-speed/service.ts | 5 ++ src/pages/tools/video/change-speed/types.ts | 3 + src/pages/tools/video/index.ts | 1 + 6 files changed, 90 insertions(+) create mode 100644 src/pages/tools/video/change-speed/change-speed.service.test.ts create mode 100644 src/pages/tools/video/change-speed/index.tsx create mode 100644 src/pages/tools/video/change-speed/meta.ts create mode 100644 src/pages/tools/video/change-speed/service.ts create mode 100644 src/pages/tools/video/change-speed/types.ts diff --git a/src/pages/tools/video/change-speed/change-speed.service.test.ts b/src/pages/tools/video/change-speed/change-speed.service.test.ts new file mode 100644 index 0000000..47fc0e4 --- /dev/null +++ b/src/pages/tools/video/change-speed/change-speed.service.test.ts @@ -0,0 +1,6 @@ +import { expect, describe, it } from 'vitest'; +// import { main } from './service'; +// +// describe('change-speed', () => { +// +// }) \ No newline at end of file diff --git a/src/pages/tools/video/change-speed/index.tsx b/src/pages/tools/video/change-speed/index.tsx new file mode 100644 index 0000000..cb14482 --- /dev/null +++ b/src/pages/tools/video/change-speed/index.tsx @@ -0,0 +1,62 @@ +import { Box } from '@mui/material'; +import React, { useState } from 'react'; +import ToolContent from '@components/ToolContent'; +import { ToolComponentProps } from '@tools/defineTool'; +import ToolTextInput from '@components/input/ToolTextInput'; +import ToolTextResult from '@components/result/ToolTextResult'; +import { GetGroupsType } from '@components/options/ToolOptions'; +import { CardExampleType } from '@components/examples/ToolExamples'; +import { main } from './service'; +import { InitialValuesType } from './types'; + +const initialValues: InitialValuesType = { + // splitSeparator: '\n' +}; + +const exampleCards: CardExampleType[] = [ + { + title: 'Split a String', + description: 'This example shows how to split a string into multiple lines', + sampleText: 'Hello World,Hello World', + sampleResult: `Hello World +Hello World`, + sampleOptions: { + // splitSeparator: ',' + } + } +]; +export default function ChangeSpeed({ + title, + longDescription +}: ToolComponentProps) { + const [input, setInput] = useState(''); + const [result, setResult] = useState(''); + + const compute = (values: InitialValuesType, input: string) => { + setResult(main(input, values)); + }; + + const getGroups: GetGroupsType | null = ({ + values, + updateField + }) => [ + { + title: 'Example Settings', + component: + } + ]; + return ( + } + resultComponent={} + initialValues={initialValues} + exampleCards={exampleCards} + getGroups={getGroups} + setInput={setInput} + compute={compute} + toolInfo={{ title: `What is a ${title}?`, description: longDescription }} + /> + ); +} \ No newline at end of file diff --git a/src/pages/tools/video/change-speed/meta.ts b/src/pages/tools/video/change-speed/meta.ts new file mode 100644 index 0000000..15b9c88 --- /dev/null +++ b/src/pages/tools/video/change-speed/meta.ts @@ -0,0 +1,13 @@ +import { defineTool } from '@tools/defineTool'; +import { lazy } from 'react'; + +export const tool = defineTool('video', { + name: 'Change speed', + path: 'change-speed', + icon: '', + description: '', + shortDescription: '', + keywords: ['change', 'speed'], + longDescription: '', + component: lazy(() => import('./index')) +}); \ No newline at end of file diff --git a/src/pages/tools/video/change-speed/service.ts b/src/pages/tools/video/change-speed/service.ts new file mode 100644 index 0000000..182e773 --- /dev/null +++ b/src/pages/tools/video/change-speed/service.ts @@ -0,0 +1,5 @@ +import { InitialValuesType } from './types'; + +export function main(input: string, options: InitialValuesType): string { + return input; +} \ No newline at end of file diff --git a/src/pages/tools/video/change-speed/types.ts b/src/pages/tools/video/change-speed/types.ts new file mode 100644 index 0000000..861f1c6 --- /dev/null +++ b/src/pages/tools/video/change-speed/types.ts @@ -0,0 +1,3 @@ +export type InitialValuesType = { + // splitSeparator: string; +}; \ No newline at end of file diff --git a/src/pages/tools/video/index.ts b/src/pages/tools/video/index.ts index 3e6659d..4c4316d 100644 --- a/src/pages/tools/video/index.ts +++ b/src/pages/tools/video/index.ts @@ -1,3 +1,4 @@ +import { tool as videoChangeSpeed } from './change-speed/meta'; import { tool as videoFlip } from './flip/meta'; import { rotate } from '../string/rotate/service'; import { gifTools } from './gif'; From e064689e35499baaa30a03744dbe2ff0dad050c0 Mon Sep 17 00:00:00 2001 From: C043 Date: Thu, 22 May 2025 23:34:17 +0200 Subject: [PATCH 03/10] WIP - Setted the tool structure and integration with the project --- src/pages/tools/video/change-speed/index.tsx | 75 ++++++++++++------- src/pages/tools/video/change-speed/meta.ts | 10 +-- src/pages/tools/video/change-speed/service.ts | 7 +- src/pages/tools/video/change-speed/types.ts | 4 +- src/pages/tools/video/index.ts | 4 +- 5 files changed, 64 insertions(+), 36 deletions(-) diff --git a/src/pages/tools/video/change-speed/index.tsx b/src/pages/tools/video/change-speed/index.tsx index cb14482..6222af1 100644 --- a/src/pages/tools/video/change-speed/index.tsx +++ b/src/pages/tools/video/change-speed/index.tsx @@ -1,3 +1,4 @@ +/* eslint-disable prettier/prettier */ import { Box } from '@mui/material'; import React, { useState } from 'react'; import ToolContent from '@components/ToolContent'; @@ -8,55 +9,77 @@ import { GetGroupsType } from '@components/options/ToolOptions'; import { CardExampleType } from '@components/examples/ToolExamples'; import { main } from './service'; import { InitialValuesType } from './types'; +import ToolVideoInput from '@components/input/ToolVideoInput'; +import ToolFileResult from '@components/result/ToolFileResult'; +import TextFieldWithDesc from '@components/options/TextFieldWithDesc'; const initialValues: InitialValuesType = { - // splitSeparator: '\n' + newSpeed: 2 }; -const exampleCards: CardExampleType[] = [ - { - title: 'Split a String', - description: 'This example shows how to split a string into multiple lines', - sampleText: 'Hello World,Hello World', - sampleResult: `Hello World -Hello World`, - sampleOptions: { - // splitSeparator: ',' - } - } -]; +// TODO - Add the ffmpeg logic export default function ChangeSpeed({ title, longDescription }: ToolComponentProps) { - const [input, setInput] = useState(''); - const [result, setResult] = useState(''); + const [input, setInput] = useState(null); + const [result, setResult] = useState(null); + const [loading, setLoading] = useState(false); - const compute = (values: InitialValuesType, input: string) => { - setResult(main(input, values)); + const compute = (optionsValues: InitialValuesType, input: File | null) => { + if (!input) return; + const { newSpeed } = optionsValues; + + // Here we set the output video + setResult(main(input, optionsValues)); }; const getGroups: GetGroupsType | null = ({ values, updateField }) => [ - { - title: 'Example Settings', - component: - } - ]; + { + title: 'New Video Speed', + component: ( + + updateField('newSpeed', Number(val))} + description="Default multiplier: 2 means 2x faster" + type="number" + /> + + ) + } + ]; return ( } - resultComponent={} + inputComponent={ + + } + resultComponent={ + loading ? ( + + ) : ( + + ) + } initialValues={initialValues} - exampleCards={exampleCards} getGroups={getGroups} setInput={setInput} compute={compute} toolInfo={{ title: `What is a ${title}?`, description: longDescription }} /> ); -} \ No newline at end of file +} diff --git a/src/pages/tools/video/change-speed/meta.ts b/src/pages/tools/video/change-speed/meta.ts index 15b9c88..37a88d8 100644 --- a/src/pages/tools/video/change-speed/meta.ts +++ b/src/pages/tools/video/change-speed/meta.ts @@ -4,10 +4,10 @@ import { lazy } from 'react'; export const tool = defineTool('video', { name: 'Change speed', path: 'change-speed', - icon: '', - description: '', - shortDescription: '', + icon: 'material-symbols-light:speed-outline', + description: + 'This online utility lets you change the speed of a video. You can speed it up or slow it down.', + shortDescription: 'Quickly change VIDEO speed', keywords: ['change', 'speed'], - longDescription: '', component: lazy(() => import('./index')) -}); \ No newline at end of file +}); diff --git a/src/pages/tools/video/change-speed/service.ts b/src/pages/tools/video/change-speed/service.ts index 182e773..18d6dd6 100644 --- a/src/pages/tools/video/change-speed/service.ts +++ b/src/pages/tools/video/change-speed/service.ts @@ -1,5 +1,8 @@ import { InitialValuesType } from './types'; -export function main(input: string, options: InitialValuesType): string { +export function main( + input: File | null, + options: InitialValuesType +): File | null { return input; -} \ No newline at end of file +} diff --git a/src/pages/tools/video/change-speed/types.ts b/src/pages/tools/video/change-speed/types.ts index 861f1c6..a673625 100644 --- a/src/pages/tools/video/change-speed/types.ts +++ b/src/pages/tools/video/change-speed/types.ts @@ -1,3 +1,3 @@ export type InitialValuesType = { - // splitSeparator: string; -}; \ No newline at end of file + newSpeed: Number; +}; diff --git a/src/pages/tools/video/index.ts b/src/pages/tools/video/index.ts index 4c4316d..d3507f2 100644 --- a/src/pages/tools/video/index.ts +++ b/src/pages/tools/video/index.ts @@ -7,6 +7,7 @@ import { tool as rotateVideo } from './rotate/meta'; import { tool as compressVideo } from './compress/meta'; import { tool as loopVideo } from './loop/meta'; import { tool as flipVideo } from './flip/meta'; +import { tool as changeSpeed } from './change-speed/meta'; export const videoTools = [ ...gifTools, @@ -14,5 +15,6 @@ export const videoTools = [ rotateVideo, compressVideo, loopVideo, - flipVideo + flipVideo, + changeSpeed ]; From 338bd8d9374713b7fd6d857b8b194c721c752cb6 Mon Sep 17 00:00:00 2001 From: Mario Fragnito Date: Fri, 23 May 2025 09:55:33 +0200 Subject: [PATCH 04/10] Adds ffmpeg logic --- src/pages/tools/video/change-speed/index.tsx | 129 ++++++++++++++++--- 1 file changed, 111 insertions(+), 18 deletions(-) diff --git a/src/pages/tools/video/change-speed/index.tsx b/src/pages/tools/video/change-speed/index.tsx index 6222af1..2428140 100644 --- a/src/pages/tools/video/change-speed/index.tsx +++ b/src/pages/tools/video/change-speed/index.tsx @@ -3,21 +3,19 @@ import { Box } from '@mui/material'; import React, { useState } from 'react'; import ToolContent from '@components/ToolContent'; import { ToolComponentProps } from '@tools/defineTool'; -import ToolTextInput from '@components/input/ToolTextInput'; -import ToolTextResult from '@components/result/ToolTextResult'; import { GetGroupsType } from '@components/options/ToolOptions'; -import { CardExampleType } from '@components/examples/ToolExamples'; import { main } from './service'; import { InitialValuesType } from './types'; import ToolVideoInput from '@components/input/ToolVideoInput'; import ToolFileResult from '@components/result/ToolFileResult'; import TextFieldWithDesc from '@components/options/TextFieldWithDesc'; +import { FFmpeg } from '@ffmpeg/ffmpeg'; +import { fetchFile } from '@ffmpeg/util'; const initialValues: InitialValuesType = { newSpeed: 2 }; -// TODO - Add the ffmpeg logic export default function ChangeSpeed({ title, longDescription @@ -27,8 +25,103 @@ export default function ChangeSpeed({ const [loading, setLoading] = useState(false); const compute = (optionsValues: InitialValuesType, input: File | null) => { + setLoading(true); if (!input) return; const { newSpeed } = optionsValues; + let ffmpeg: FFmpeg | null = null; + let ffmpegLoaded = false; + + const processVideo = async ( + file: File, + newSpeed: number + ): Promise => { + 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; + } + + // Write file to FFmpeg FS + const fileName = file.name; + const outputName = 'output.mp4'; + + try { + ffmpeg.writeFile(fileName, await fetchFile(file)); + + const videoFilter = `setpts=${1 / newSpeed}*PTS`; + const audioFilter = computeAudioFilter(newSpeed); + + // Run FFmpeg command + await ffmpeg.exec([ + '-i', + fileName, + '-vf', + videoFilter, + '-filter:a', + audioFilter, + '-c:v', + 'libx264', + '-crf', + '18', + '-preset', + 'slow', + '-c:a', + 'aac', + '-b:a', + '192k', + outputName + ]); + + const data = await ffmpeg.readFile(outputName); + + // Create new file from processed data + const blob = new Blob([data], { type: 'video/mp4' }); + const newFile = new File( + [blob], + file.name.replace('.mp4', `-${newSpeed}x.mp4`), + { type: 'video/mp4' } + ); + + // Clean up to free memory + await ffmpeg.deleteFile(fileName); + await ffmpeg.deleteFile(outputName); + + setResult(newFile); + } catch (err) { + console.error(`Failed to process video: ${err}`); + throw err; + } finally { + setLoading(false); + } + + // FFmpeg only supports atempo between 0.5 and 2.0, so we chain filters + function computeAudioFilter(speed: number): string { + if (speed <= 2 && speed >= 0.5) { + return `atempo=${speed}`; + } + + // Break into supported chunks + const filters = []; + let remainingSpeed = speed; + while (remainingSpeed > 2.0) { + filters.push('atempo=2.0'); + remainingSpeed /= 2.0; + } + while (remainingSpeed < 0.5) { + filters.push('atempo=0.5'); + remainingSpeed /= 0.5; + } + filters.push(`atempo=${remainingSpeed.toFixed(2)}`); + + return filters.join(','); + } + }; // Here we set the output video setResult(main(input, optionsValues)); @@ -38,20 +131,20 @@ export default function ChangeSpeed({ values, updateField }) => [ - { - title: 'New Video Speed', - component: ( - - updateField('newSpeed', Number(val))} - description="Default multiplier: 2 means 2x faster" - type="number" - /> - - ) - } - ]; + { + title: 'New Video Speed', + component: ( + + updateField('newSpeed', Number(val))} + description="Default multiplier: 2 means 2x faster" + type="number" + /> + + ) + } + ]; return ( Date: Fri, 23 May 2025 12:31:41 +0200 Subject: [PATCH 05/10] Moved setLoading call inside the processVideo fucntion --- src/pages/tools/video/change-speed/index.tsx | 33 ++++++++++---------- src/pages/tools/video/change-speed/types.ts | 2 +- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/src/pages/tools/video/change-speed/index.tsx b/src/pages/tools/video/change-speed/index.tsx index 2428140..15e6551 100644 --- a/src/pages/tools/video/change-speed/index.tsx +++ b/src/pages/tools/video/change-speed/index.tsx @@ -25,7 +25,6 @@ export default function ChangeSpeed({ const [loading, setLoading] = useState(false); const compute = (optionsValues: InitialValuesType, input: File | null) => { - setLoading(true); if (!input) return; const { newSpeed } = optionsValues; let ffmpeg: FFmpeg | null = null; @@ -35,6 +34,8 @@ export default function ChangeSpeed({ file: File, newSpeed: number ): Promise => { + setLoading(true); + if (!ffmpeg) { ffmpeg = new FFmpeg(); } @@ -124,27 +125,27 @@ export default function ChangeSpeed({ }; // Here we set the output video - setResult(main(input, optionsValues)); + processVideo(input, newSpeed) }; const getGroups: GetGroupsType | null = ({ values, updateField }) => [ - { - title: 'New Video Speed', - component: ( - - updateField('newSpeed', Number(val))} - description="Default multiplier: 2 means 2x faster" - type="number" - /> - - ) - } - ]; + { + title: 'New Video Speed', + component: ( + + updateField('newSpeed', Number(val))} + description="Default multiplier: 2 means 2x faster" + type="number" + /> + + ) + } + ]; return ( Date: Fri, 23 May 2025 12:41:32 +0200 Subject: [PATCH 06/10] Removed boilerplate test file --- .../tools/video/change-speed/change-speed.service.test.ts | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 src/pages/tools/video/change-speed/change-speed.service.test.ts diff --git a/src/pages/tools/video/change-speed/change-speed.service.test.ts b/src/pages/tools/video/change-speed/change-speed.service.test.ts deleted file mode 100644 index 47fc0e4..0000000 --- a/src/pages/tools/video/change-speed/change-speed.service.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { expect, describe, it } from 'vitest'; -// import { main } from './service'; -// -// describe('change-speed', () => { -// -// }) \ No newline at end of file From 9d89f8c0b94fa601131e9f340a5248fa1dc3478c Mon Sep 17 00:00:00 2001 From: Mario Fragnito Date: Fri, 23 May 2025 15:35:28 +0200 Subject: [PATCH 07/10] Refactored code for readability --- src/pages/tools/video/change-speed/index.tsx | 81 +++++++++----------- 1 file changed, 38 insertions(+), 43 deletions(-) diff --git a/src/pages/tools/video/change-speed/index.tsx b/src/pages/tools/video/change-speed/index.tsx index 15e6551..6cd795f 100644 --- a/src/pages/tools/video/change-speed/index.tsx +++ b/src/pages/tools/video/change-speed/index.tsx @@ -4,7 +4,6 @@ import React, { useState } from 'react'; import ToolContent from '@components/ToolContent'; import { ToolComponentProps } from '@tools/defineTool'; import { GetGroupsType } from '@components/options/ToolOptions'; -import { main } from './service'; import { InitialValuesType } from './types'; import ToolVideoInput from '@components/input/ToolVideoInput'; import ToolFileResult from '@components/result/ToolFileResult'; @@ -24,6 +23,28 @@ export default function ChangeSpeed({ const [result, setResult] = useState(null); const [loading, setLoading] = useState(false); + // FFmpeg only supports atempo between 0.5 and 2.0, so we chain filters + const computeAudioFilter = (speed: number): string => { + if (speed <= 2 && speed >= 0.5) { + return `atempo=${speed}`; + } + + // Break into supported chunks + const filters = []; + let remainingSpeed = speed; + while (remainingSpeed > 2.0) { + filters.push('atempo=2.0'); + remainingSpeed /= 2.0; + } + while (remainingSpeed < 0.5) { + filters.push('atempo=0.5'); + remainingSpeed /= 0.5; + } + filters.push(`atempo=${remainingSpeed.toFixed(2)}`); + + return filters.join(','); + }; + const compute = (optionsValues: InitialValuesType, input: File | null) => { if (!input) return; const { newSpeed } = optionsValues; @@ -68,14 +89,10 @@ export default function ChangeSpeed({ audioFilter, '-c:v', 'libx264', - '-crf', - '18', '-preset', - 'slow', + 'ultrafast', '-c:a', 'aac', - '-b:a', - '192k', outputName ]); @@ -100,52 +117,30 @@ export default function ChangeSpeed({ } finally { setLoading(false); } - - // FFmpeg only supports atempo between 0.5 and 2.0, so we chain filters - function computeAudioFilter(speed: number): string { - if (speed <= 2 && speed >= 0.5) { - return `atempo=${speed}`; - } - - // Break into supported chunks - const filters = []; - let remainingSpeed = speed; - while (remainingSpeed > 2.0) { - filters.push('atempo=2.0'); - remainingSpeed /= 2.0; - } - while (remainingSpeed < 0.5) { - filters.push('atempo=0.5'); - remainingSpeed /= 0.5; - } - filters.push(`atempo=${remainingSpeed.toFixed(2)}`); - - return filters.join(','); - } }; // Here we set the output video - processVideo(input, newSpeed) + processVideo(input, newSpeed); }; const getGroups: GetGroupsType | null = ({ values, updateField }) => [ - { - title: 'New Video Speed', - component: ( - - updateField('newSpeed', Number(val))} - description="Default multiplier: 2 means 2x faster" - type="number" - /> - - ) - } - ]; + { + title: 'New Video Speed', + component: ( + + updateField('newSpeed', Number(val))} + description="Default multiplier: 2 means 2x faster" + type="number" + /> + + ) + } + ]; return ( Date: Fri, 23 May 2025 20:04:39 +0100 Subject: [PATCH 08/10] chore: revert create-tool.mjs --- scripts/create-tool.mjs | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/scripts/create-tool.mjs b/scripts/create-tool.mjs index ab16ce1..3032f95 100644 --- a/scripts/create-tool.mjs +++ b/scripts/create-tool.mjs @@ -1,7 +1,7 @@ -import { readFile, writeFile } from "fs/promises"; -import fs from "fs"; -import { dirname, join, sep } from "path"; -import { fileURLToPath } from "url"; +import { readFile, writeFile } from 'fs/promises'; +import fs from 'fs'; +import { dirname, join, sep } from 'path'; +import { fileURLToPath } from 'url'; const currentDirname = dirname(fileURLToPath(import.meta.url)); @@ -10,14 +10,14 @@ const folder = process.argv[3]; const toolsDir = join( currentDirname, - "..", - "src", - "pages", - "tools", - folder ?? "" + '..', + 'src', + 'pages', + 'tools', + folder ?? '' ); if (!toolName) { - throw new Error("Please specify a toolname."); + throw new Error('Please specify a toolname.'); } function capitalizeFirstLetter(string) { @@ -35,7 +35,7 @@ function createFolderStructure(basePath, foldersToCreateIndexCount) { if (!fs.existsSync(currentPath)) { fs.mkdirSync(currentPath, { recursive: true }); } - const indexPath = join(currentPath, "index.ts"); + const indexPath = join(currentPath, 'index.ts'); if ( !fs.existsSync(indexPath) && index < folderArray.length - 1 && @@ -54,15 +54,15 @@ function createFolderStructure(basePath, foldersToCreateIndexCount) { } // Start the recursive folder creation - recursiveCreate(toolsDir, 0); + recursiveCreate('.', 0); } -const toolNameCamelCase = toolName.replace(/-./g, x => x[1].toUpperCase()); +const toolNameCamelCase = toolName.replace(/-./g, (x) => x[1].toUpperCase()); const toolNameTitleCase = - toolName[0].toUpperCase() + toolName.slice(1).replace(/-/g, " "); + toolName[0].toUpperCase() + toolName.slice(1).replace(/-/g, ' '); const toolDir = join(toolsDir, toolName); const type = folder.split(sep)[folder.split(sep).length - 1]; -await createFolderStructure(toolName, folder.split(sep).length); +await createFolderStructure(toolDir, folder.split(sep).length); console.log(`Directory created: ${toolDir}`); const createToolFile = async (name, content) => { @@ -150,7 +150,7 @@ export const tool = defineTool('${type}', { icon: '', description: '', shortDescription: '', - keywords: ['${toolName.split("-").join("', '")}'], + keywords: ['${toolName.split('-').join("', '")}'], longDescription: '', component: lazy(() => import('./index')) }); @@ -209,9 +209,9 @@ import { expect, describe, it } from 'vitest'; // ` // ) -const toolsIndex = join(toolsDir, "index.ts"); -const indexContent = await readFile(toolsIndex, { encoding: "utf-8" }).then(r => - r.split("\n") +const toolsIndex = join(toolsDir, 'index.ts'); +const indexContent = await readFile(toolsIndex, { encoding: 'utf-8' }).then( + (r) => r.split('\n') ); indexContent.splice( @@ -221,5 +221,5 @@ indexContent.splice( toolNameCamelCase )} } from './${toolName}/meta';` ); -writeFile(toolsIndex, indexContent.join("\n")); +writeFile(toolsIndex, indexContent.join('\n')); console.log(`Added import in: ${toolsIndex}`); From 9593ee516b4000b54f068e6e3c5fbcd33964c1e9 Mon Sep 17 00:00:00 2001 From: "Ibrahima G. Coulibaly" Date: Fri, 23 May 2025 20:18:04 +0100 Subject: [PATCH 09/10] fix: misc --- src/components/input/BaseFileInput.tsx | 13 ++----------- src/pages/tools/video/change-speed/index.tsx | 6 +++--- src/pages/tools/video/change-speed/meta.ts | 2 +- 3 files changed, 6 insertions(+), 15 deletions(-) diff --git a/src/components/input/BaseFileInput.tsx b/src/components/input/BaseFileInput.tsx index 6346b6d..ebc3840 100644 --- a/src/components/input/BaseFileInput.tsx +++ b/src/components/input/BaseFileInput.tsx @@ -41,7 +41,7 @@ export default function BaseFileInput({ } catch (error) { console.error('Error previewing file:', error); } - } + } else setPreview(null); }, [value]); const handleFileChange = (event: React.ChangeEvent) => { @@ -67,11 +67,6 @@ export default function BaseFileInput({ } }; - function handleClear() { - // @ts-ignore - onChange(null); - } - const handleDrop = (event: React.DragEvent) => { event.preventDefault(); event.stopPropagation(); @@ -213,11 +208,7 @@ export default function BaseFileInput({ )} - + (null); const [loading, setLoading] = useState(false); - // FFmpeg only supports atempo between 0.5 and 2.0, so we chain filters + // FFmpeg only supports a tempo between 0.5 and 2.0, so we chain filters const computeAudioFilter = (speed: number): string => { if (speed <= 2 && speed >= 0.5) { return `atempo=${speed}`; } // Break into supported chunks - const filters = []; + const filters: string[] = []; let remainingSpeed = speed; while (remainingSpeed > 2.0) { filters.push('atempo=2.0'); @@ -55,6 +54,7 @@ export default function ChangeSpeed({ file: File, newSpeed: number ): Promise => { + if (newSpeed === 0) return; setLoading(true); if (!ffmpeg) { diff --git a/src/pages/tools/video/change-speed/meta.ts b/src/pages/tools/video/change-speed/meta.ts index 37a88d8..ff4f0ad 100644 --- a/src/pages/tools/video/change-speed/meta.ts +++ b/src/pages/tools/video/change-speed/meta.ts @@ -7,7 +7,7 @@ export const tool = defineTool('video', { icon: 'material-symbols-light:speed-outline', description: 'This online utility lets you change the speed of a video. You can speed it up or slow it down.', - shortDescription: 'Quickly change VIDEO speed', + shortDescription: 'Quickly change video speed', keywords: ['change', 'speed'], component: lazy(() => import('./index')) }); From 863f00e913088b27d2a682d453c1576639db4929 Mon Sep 17 00:00:00 2001 From: "Ibrahima G. Coulibaly" Date: Fri, 23 May 2025 20:20:51 +0100 Subject: [PATCH 10/10] chore: remove unnecessary prop --- src/pages/tools/video/change-speed/index.tsx | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/pages/tools/video/change-speed/index.tsx b/src/pages/tools/video/change-speed/index.tsx index cc16985..0369e7c 100644 --- a/src/pages/tools/video/change-speed/index.tsx +++ b/src/pages/tools/video/change-speed/index.tsx @@ -154,12 +154,7 @@ export default function ChangeSpeed({ } resultComponent={ loading ? ( - + ) : ( )