Merge branch 'iib0011:main' into feature/json2xml

This commit is contained in:
Luís Jesus
2025-03-26 11:23:53 +00:00
committed by GitHub
32 changed files with 1492 additions and 128 deletions

View File

@@ -1,16 +1,15 @@
import { Box } from '@mui/material';
import React, { useState } from 'react';
import * as Yup from 'yup';
import ToolFileInput from '@components/input/ToolFileInput';
import ToolFileResult from '@components/result/ToolFileResult';
import ToolOptions, { GetGroupsType } from '@components/options/ToolOptions';
import { GetGroupsType } from '@components/options/ToolOptions';
import ColorSelector from '@components/options/ColorSelector';
import Color from 'color';
import TextFieldWithDesc from 'components/options/TextFieldWithDesc';
import ToolInputAndResult from '@components/ToolInputAndResult';
import { areColorsSimilar } from 'utils/color';
import ToolContent from '@components/ToolContent';
import { ToolComponentProps } from '@tools/defineTool';
import ToolImageInput from '@components/input/ToolImageInput';
const initialValues = {
fromColor: 'white',
@@ -125,7 +124,7 @@ export default function ChangeColorsInPng({ title }: ToolComponentProps) {
input={input}
validationSchema={validationSchema}
inputComponent={
<ToolFileInput
<ToolImageInput
value={input}
onChange={setInput}
accept={['image/png']}

View File

@@ -1,5 +1,5 @@
import React, { useEffect, useState } from 'react';
import ToolFileInput from '@components/input/ToolFileInput';
import ToolImageInput from '@components/input/ToolImageInput';
import ToolFileResult from '@components/result/ToolFileResult';
import { changeOpacity } from './service';
import ToolContent from '@components/ToolContent';
@@ -94,7 +94,7 @@ export default function ChangeOpacity({ title }: ToolComponentProps) {
title={title}
input={input}
inputComponent={
<ToolFileInput
<ToolImageInput
value={input}
onChange={setInput}
accept={['image/png']}

View File

@@ -1,7 +1,7 @@
import { Box } from '@mui/material';
import React, { useState } from 'react';
import * as Yup from 'yup';
import ToolFileInput from '@components/input/ToolFileInput';
import ToolImageInput from '@components/input/ToolImageInput';
import ToolFileResult from '@components/result/ToolFileResult';
import TextFieldWithDesc from 'components/options/TextFieldWithDesc';
import imageCompression from 'browser-image-compression';
@@ -56,7 +56,7 @@ export default function ChangeColorsInPng({ title }: ToolComponentProps) {
title={title}
input={input}
inputComponent={
<ToolFileInput
<ToolImageInput
value={input}
onChange={setInput}
accept={['image/png']}

View File

@@ -1,5 +1,5 @@
import { Box } from '@mui/material';
import ToolFileInput from 'components/input/ToolFileInput';
import ToolImageInput from 'components/input/ToolImageInput';
import CheckboxWithDesc from 'components/options/CheckboxWithDesc';
import ColorSelector from 'components/options/ColorSelector';
import TextFieldWithDesc from 'components/options/TextFieldWithDesc';
@@ -101,7 +101,7 @@ export default function ConvertJgpToPng({ title }: ToolComponentProps) {
title={title}
input={input}
inputComponent={
<ToolFileInput
<ToolImageInput
value={input}
onChange={setInput}
accept={['image/jpeg']}

View File

@@ -1,7 +1,7 @@
import { Box } from '@mui/material';
import React, { useState } from 'react';
import * as Yup from 'yup';
import ToolFileInput from '@components/input/ToolFileInput';
import ToolImageInput from '@components/input/ToolImageInput';
import ToolFileResult from '@components/result/ToolFileResult';
import ColorSelector from '@components/options/ColorSelector';
import Color from 'color';
@@ -109,7 +109,7 @@ export default function CreateTransparent({ title }: ToolComponentProps) {
<ToolContent
title={title}
inputComponent={
<ToolFileInput
<ToolImageInput
value={input}
onChange={setInput}
accept={['image/png']}

View File

@@ -1,7 +1,7 @@
import { Box } from '@mui/material';
import React, { useState } from 'react';
import * as Yup from 'yup';
import ToolFileInput from '@components/input/ToolFileInput';
import ToolImageInput from '@components/input/ToolImageInput';
import ToolFileResult from '@components/result/ToolFileResult';
import { GetGroupsType, UpdateField } from '@components/options/ToolOptions';
import TextFieldWithDesc from '@components/options/TextFieldWithDesc';
@@ -197,7 +197,7 @@ export default function CropPng({ title }: ToolComponentProps) {
values: InitialValuesType,
updateField: UpdateField<InitialValuesType>
) => (
<ToolFileInput
<ToolImageInput
value={input}
onChange={setInput}
accept={['image/png']}

View File

@@ -4,6 +4,7 @@ import { tool as convertJgpToPng } from './convert-jgp-to-png/meta';
import { tool as pngCreateTransparent } from './create-transparent/meta';
import { tool as changeColorsInPng } from './change-colors-in-png/meta';
import { tool as changeOpacity } from './change-opacity/meta';
import { tool as removeBackground } from './remove-background/meta';
export const pngTools = [
pngCompressPng,
@@ -11,5 +12,6 @@ export const pngTools = [
changeColorsInPng,
convertJgpToPng,
changeOpacity,
pngCrop
pngCrop,
removeBackground
];

View File

@@ -0,0 +1,87 @@
import { Box, CircularProgress, Typography } from '@mui/material';
import React, { 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 ToolImageInput from '@components/input/ToolImageInput';
import { removeBackground } from '@imgly/background-removal';
const initialValues = {};
const validationSchema = Yup.object({});
export default function RemoveBackgroundFromPng({ title }: ToolComponentProps) {
const [input, setInput] = useState<File | null>(null);
const [result, setResult] = useState<File | null>(null);
const [isProcessing, setIsProcessing] = useState<boolean>(false);
const compute = async (_optionsValues: typeof initialValues, input: any) => {
if (!input) return;
setIsProcessing(true);
try {
// Convert the input file to a Blob URL
const inputUrl = URL.createObjectURL(input);
// Process the image with the background removal library
const blob = await removeBackground(inputUrl, {
progress: (progress) => {
console.log(`Background removal progress: ${progress}`);
}
});
// Create a new file from the blob
const newFile = new File(
[blob],
input.name.replace(/\.[^/.]+$/, '') + '-no-bg.png',
{
type: 'image/png'
}
);
setResult(newFile);
} catch (err) {
console.error('Error removing background:', err);
throw new Error(
'Failed to remove background. Please try a different image or try again later.'
);
} finally {
setIsProcessing(false);
}
};
return (
<ToolContent
title={title}
initialValues={initialValues}
getGroups={null}
compute={compute}
input={input}
validationSchema={validationSchema}
inputComponent={
<ToolImageInput
value={input}
onChange={setInput}
accept={['image/png', 'image/jpeg', 'image/jpg']}
title={'Input Image'}
/>
}
resultComponent={
<ToolFileResult
title={'Transparent PNG'}
value={result}
extension={'png'}
loading={isProcessing}
loadingText={'Removing background'}
/>
}
toolInfo={{
title: 'Remove Background from PNG',
description:
'This tool uses AI to automatically remove the background from your images, creating a transparent PNG. Perfect for product photos, profile pictures, and design assets.'
}}
/>
);
}

View File

@@ -0,0 +1,13 @@
import { defineTool } from '@tools/defineTool';
import { lazy } from 'react';
export const tool = defineTool('png', {
name: 'Remove Background from PNG',
path: 'remove-background',
icon: 'mdi:image-remove',
description:
"World's simplest online tool to remove backgrounds from PNG images. Just upload your image and our AI-powered tool will automatically remove the background, giving you a transparent PNG. Perfect for product photos, profile pictures, and design assets.",
shortDescription: 'Automatically remove backgrounds from images',
keywords: ['remove', 'background', 'png', 'transparent', 'image', 'ai'],
component: lazy(() => import('./index'))
});

View File

@@ -0,0 +1,4 @@
import { meta as splitPdfMeta } from './split-pdf/meta';
import { DefinedTool } from '@tools/defineTool';
export const pdfTools: DefinedTool[] = [splitPdfMeta];

View File

@@ -0,0 +1,181 @@
import { Box, Typography } from '@mui/material';
import React, { useEffect, useRef, useState } from 'react';
import ToolFileInput from '@components/input/ToolFileInput';
import ToolFileResult from '@components/result/ToolFileResult';
import TextFieldWithDesc from '@components/options/TextFieldWithDesc';
import ToolContent from '@components/ToolContent';
import { ToolComponentProps } from '@tools/defineTool';
import { parsePageRanges, splitPdf } from './service';
import { CardExampleType } from '@components/examples/ToolExamples';
import { PDFDocument } from 'pdf-lib';
import { FormikProps } from 'formik';
import ToolPdfInput from '@components/input/ToolPdfInput';
type InitialValuesType = {
pageRanges: string;
};
const initialValues: InitialValuesType = {
pageRanges: ''
};
const exampleCards: CardExampleType<InitialValuesType>[] = [
{
title: 'Extract Specific Pages',
description: 'Extract pages 1, 5, 6, 7, and 8 from a PDF document.',
sampleText: '',
sampleResult: '',
sampleOptions: {
pageRanges: '1,5-8'
}
},
{
title: 'Extract First and Last Pages',
description: 'Extract only the first and last pages from a PDF document.',
sampleText: '',
sampleResult: '',
sampleOptions: {
pageRanges: '1,10'
}
},
{
title: 'Extract a Range of Pages',
description: 'Extract a continuous range of pages from a PDF document.',
sampleText: '',
sampleResult: '',
sampleOptions: {
pageRanges: '3-7'
}
}
];
export default function SplitPdf({ title }: ToolComponentProps) {
const [input, setInput] = useState<File | null>(null);
const [result, setResult] = useState<File | null>(null);
const [isProcessing, setIsProcessing] = useState<boolean>(false);
const [totalPages, setTotalPages] = useState<number>(0);
const [pageRangePreview, setPageRangePreview] = useState<string>('');
// Get the total number of pages when a PDF is uploaded
useEffect(() => {
const getPdfInfo = async () => {
if (!input) {
setTotalPages(0);
return;
}
try {
const arrayBuffer = await input.arrayBuffer();
const pdf = await PDFDocument.load(arrayBuffer);
setTotalPages(pdf.getPageCount());
} catch (error) {
console.error('Error getting PDF info:', error);
setTotalPages(0);
}
};
getPdfInfo();
}, [input]);
const onValuesChange = (values: InitialValuesType) => {
const { pageRanges } = values;
if (!totalPages || !pageRanges?.trim()) {
setPageRangePreview('');
return;
}
try {
const count = parsePageRanges(pageRanges, totalPages).length;
setPageRangePreview(
`${count} page${count !== 1 ? 's' : ''} will be extracted`
);
} catch (error) {
setPageRangePreview('');
}
};
const compute = async (values: InitialValuesType, input: File | null) => {
if (!input) return;
try {
setIsProcessing(true);
const splitResult = await splitPdf(input, values.pageRanges);
setResult(splitResult);
} catch (error) {
throw new Error('Error splitting PDF:' + error);
} finally {
setIsProcessing(false);
}
};
return (
<ToolContent
title={title}
input={input}
setInput={setInput}
initialValues={initialValues}
compute={compute}
exampleCards={exampleCards}
inputComponent={
<ToolPdfInput
value={input}
onChange={setInput}
accept={['application/pdf']}
title={'Input PDF'}
/>
}
resultComponent={
<ToolFileResult
title={'Output PDF with selected pages'}
value={result}
extension={'pdf'}
loading={isProcessing}
loadingText={'Extracting pages'}
/>
}
getGroups={({ values, updateField }) => [
{
title: 'Page Selection',
component: (
<Box>
{totalPages > 0 && (
<Typography variant="body2" sx={{ mb: 1 }}>
PDF has {totalPages} page{totalPages !== 1 ? 's' : ''}
</Typography>
)}
<TextFieldWithDesc
value={values.pageRanges}
onOwnChange={(val) => {
updateField('pageRanges', val);
}}
description={
'Enter page numbers or ranges separated by commas (e.g., 1,3,5-7)'
}
placeholder={'e.g., 1,5-8'}
/>
{pageRangePreview && (
<Typography
variant="body2"
sx={{ mt: 1, color: 'primary.main' }}
>
{pageRangePreview}
</Typography>
)}
</Box>
)
}
]}
onValuesChange={onValuesChange}
toolInfo={{
title: 'How to Use the Split PDF Tool',
description: `This tool allows you to extract specific pages from a PDF document. You can specify individual page numbers (e.g., 1,3,5) or page ranges (e.g., 2-6) or a combination of both (e.g., 1,3-5,8).
Leave the page ranges field empty to include all pages from the PDF.
Examples:
- "1,5,9" extracts pages 1, 5, and 9
- "1-5" extracts pages 1 through 5
- "1,3-5,8-10" extracts pages 1, 3, 4, 5, 8, 9, and 10`
}}
/>
);
}

View File

@@ -0,0 +1,13 @@
import { defineTool } from '@tools/defineTool';
import { lazy } from 'react';
export const meta = defineTool('pdf', {
name: 'Split PDF',
shortDescription: 'Extract specific pages from a PDF file',
description:
'Extract specific pages from a PDF file using page numbers or ranges (e.g., 1,5-8)',
icon: 'mdi:file-pdf-box',
component: lazy(() => import('./index')),
keywords: ['pdf', 'split', 'extract', 'pages', 'range', 'document'],
path: 'split-pdf'
});

View File

@@ -0,0 +1,43 @@
import { parsePageRanges } from './service';
describe('parsePageRanges', () => {
test('should return all pages when input is empty', () => {
expect(parsePageRanges('', 5)).toEqual([1, 2, 3, 4, 5]);
});
test('should parse single page numbers', () => {
expect(parsePageRanges('1,3,5', 5)).toEqual([1, 3, 5]);
});
test('should parse page ranges', () => {
expect(parsePageRanges('2-4', 5)).toEqual([2, 3, 4]);
});
test('should parse mixed page numbers and ranges', () => {
expect(parsePageRanges('1,3-5', 5)).toEqual([1, 3, 4, 5]);
});
test('should handle whitespace', () => {
expect(parsePageRanges(' 1, 3 - 5 ', 5)).toEqual([1, 3, 4, 5]);
});
test('should ignore invalid page numbers', () => {
expect(parsePageRanges('1,a,3', 5)).toEqual([1, 3]);
});
test('should ignore out-of-range page numbers', () => {
expect(parsePageRanges('1,6,3', 5)).toEqual([1, 3]);
});
test('should limit ranges to valid pages', () => {
expect(parsePageRanges('0-6', 5)).toEqual([1, 2, 3, 4, 5]);
});
test('should handle reversed ranges', () => {
expect(parsePageRanges('4-2', 5)).toEqual([2, 3, 4]);
});
test('should remove duplicates', () => {
expect(parsePageRanges('1,1,2,2-4,3', 5)).toEqual([1, 2, 3, 4]);
});
});

View File

@@ -0,0 +1,74 @@
import { PDFDocument } from 'pdf-lib';
/**
* Parses a page range string and returns an array of page numbers
* @param pageRangeStr String like "1,3-5,7" to extract pages 1, 3, 4, 5, and 7
* @param totalPages Total number of pages in the PDF
* @returns Array of page numbers to extract
*/
export function parsePageRanges(
pageRangeStr: string,
totalPages: number
): number[] {
if (!pageRangeStr.trim()) {
return Array.from({ length: totalPages }, (_, i) => i + 1);
}
const pageNumbers = new Set<number>();
const ranges = pageRangeStr.split(',');
for (const range of ranges) {
const trimmedRange = range.trim();
if (trimmedRange.includes('-')) {
const [start, end] = trimmedRange.split('-').map(Number);
if (!isNaN(start) && !isNaN(end)) {
// Handle both forward and reversed ranges
const normalizedStart = Math.min(start, end);
const normalizedEnd = Math.max(start, end);
for (
let i = Math.max(1, normalizedStart);
i <= Math.min(totalPages, normalizedEnd);
i++
) {
pageNumbers.add(i);
}
}
} else {
const pageNum = parseInt(trimmedRange, 10);
if (!isNaN(pageNum) && pageNum >= 1 && pageNum <= totalPages) {
pageNumbers.add(pageNum);
}
}
}
return [...pageNumbers].sort((a, b) => a - b);
}
/**
* Splits a PDF file based on specified page ranges
* @param pdfFile The input PDF file
* @param pageRanges String specifying which pages to extract (e.g., "1,3-5,7")
* @returns Promise resolving to a new PDF file with only the selected pages
*/
export async function splitPdf(
pdfFile: File,
pageRanges: string
): Promise<File> {
const arrayBuffer = await pdfFile.arrayBuffer();
const sourcePdf = await PDFDocument.load(arrayBuffer);
const totalPages = sourcePdf.getPageCount();
const pagesToExtract = parsePageRanges(pageRanges, totalPages);
const newPdf = await PDFDocument.create();
const copiedPages = await newPdf.copyPages(
sourcePdf,
pagesToExtract.map((pageNum) => pageNum - 1)
);
copiedPages.forEach((page) => newPdf.addPage(page));
const newPdfBytes = await newPdf.save();
const newFileName = pdfFile.name.replace('.pdf', '-extracted.pdf');
return new File([newPdfBytes], newFileName, { type: 'application/pdf' });
}

View File

@@ -0,0 +1,40 @@
import { expect, describe, it } from 'vitest';
import { convertHoursToDays } from './service';
describe('convertHoursToDays', () => {
it('should convert hours to days with default accuracy', () => {
const input = '48';
const result = convertHoursToDays(input, '1', false);
expect(result).toBe('2');
});
it('should convert hours to days with specified accuracy', () => {
const input = '50';
const result = convertHoursToDays(input, '2', false);
expect(result).toBe('2.08');
});
it('should append "days" postfix when daysFlag is true', () => {
const input = '72';
const result = convertHoursToDays(input, '1', true);
expect(result).toBe('3 days');
});
it('should handle multiple lines of input', () => {
const input = '24\n48\n72';
const result = convertHoursToDays(input, '1', true);
expect(result).toBe('1 days\n2 days\n3 days');
});
it('should handle invalid input gracefully', () => {
const input = 'abc';
const result = convertHoursToDays(input, '1', false);
expect(result).toBe('');
});
it('should handle empty input', () => {
const input = '';
const result = convertHoursToDays(input, '1', false);
expect(result).toBe('');
});
});

View File

@@ -0,0 +1,136 @@
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 CheckboxWithDesc from '@components/options/CheckboxWithDesc';
import TextFieldWithDesc from '@components/options/TextFieldWithDesc';
import { convertHoursToDays } from './service';
const initialValues = {
daysFlag: false,
accuracy: '1'
};
type InitialValuesType = typeof initialValues;
const exampleCards: CardExampleType<InitialValuesType>[] = [
{
title: 'Hours to Integer Days',
description:
'In this example, we convert ten hour values to ten day values. Each input hour is divisible by 24 without a remainder, so all converted output values are full days. To better communicate the time units, we use the word "hours" in the input data and also add the word "days" to the output data.',
sampleText: `24 hours
48 hours
72 hours
96 hours
120 hours
144 hours
168 hours
192 hours
216 hours
240 hours`,
sampleResult: `1 day
2 days
3 days
4 days
5 days
6 days
7 days
8 days
9 days
10 days`,
sampleOptions: { daysFlag: true, accuracy: '2' }
},
{
title: 'Decimal Days',
description:
'In this example, we convert five decimal fraction day values to hours. Conversion of partial days is similar to the conversion of full days they are all multiplied by 24. We turn off the option that appends the "hours" string after the converted values and get only the numerical hour values in the output.',
sampleText: `1 hr
100 hr
9999 hr
12345 hr
333333 hr`,
sampleResult: `0.0417 days
4.1667 days
416.625 days
514.375 days
13888.875 days`,
sampleOptions: { daysFlag: true, accuracy: '4' }
},
{
title: 'Partial Hours',
description:
'In the modern Gregorian calendar, a common year has 365 days and a leap year has 366 days. This makes the true average length of a year to be 365.242199 days. In this example, we load this number in the input field and convert it to the hours. It turns out that there 8765.812776 hours in an average year.',
sampleText: `0.5
0.01
0.99`,
sampleResult: `0.02083333
0.00041667
0.04125`,
sampleOptions: { daysFlag: false, accuracy: '8' }
}
];
export default function ConvertDaysToHours({
title,
longDescription
}: ToolComponentProps) {
const [input, setInput] = useState<string>('');
const [result, setResult] = useState<string>('');
const compute = (optionsValues: typeof initialValues, input: string) => {
setResult(
convertHoursToDays(input, optionsValues.accuracy, optionsValues.daysFlag)
);
};
const getGroups: GetGroupsType<InitialValuesType> | null = ({
values,
updateField
}) => [
{
title: 'Day Value Accuracy',
component: (
<Box>
<TextFieldWithDesc
description={
'If the calculated days is a decimal number, then how many digits should be left after the decimal point?.'
}
value={values.accuracy}
onOwnChange={(val) => updateField('accuracy', val)}
type={'text'}
/>
</Box>
)
},
{
title: 'Days Postfix',
component: (
<Box>
<CheckboxWithDesc
onChange={(val) => updateField('daysFlag', val)}
checked={values.daysFlag}
title={'Append Days Postfix'}
description={'Display numeric day values with the postfix "days".'}
/>
</Box>
)
}
];
return (
<ToolContent
title={title}
input={input}
inputComponent={<ToolTextInput value={input} onChange={setInput} />}
resultComponent={<ToolTextResult value={result} />}
initialValues={initialValues}
getGroups={getGroups}
setInput={setInput}
compute={compute}
toolInfo={{ title: `What is a ${title}?`, description: longDescription }}
exampleCards={exampleCards}
/>
);
}

View File

@@ -0,0 +1,15 @@
import { defineTool } from '@tools/defineTool';
import { lazy } from 'react';
export const tool = defineTool('time', {
path: 'convert-hours-to-days',
name: 'Convert Hours to Days',
icon: 'mdi:hours-24',
description:
'With this browser-based application, you can calculate how many days there are in the given number of hours. Given one or more hour values in the input, it converts them into days via the simple math formula: days = hours/24. It works with arbitrary large hour values and you can also customize the decimal day precision.',
shortDescription: 'Convert hours to days easily.',
keywords: ['convert', 'hours', 'days'],
longDescription:
"This is a quick online utility for converting hours to days. To figure out the number of days in the specified hours, the program divides them by 24. For example, if the input hours value is 48, then by doing 48/24, it finds that there are 2 days, or if the hours value is 120, then it's 120/24 = 5 days. If the hours value is not divisible by 24, then the number of days is displayed as a decimal number. For example, 36 hours is 36/24 = 1.5 days and 100 hours is approximately 4.167 days. You can specify the precision of the decimal fraction calculation in the options. You can also enable the option that adds the postfix 'days' to all the output values. Timeabulous!",
component: lazy(() => import('./index'))
});

View File

@@ -0,0 +1,33 @@
import { containsOnlyDigits } from '@utils/string';
function compute(input: string, accuracy: number) {
if (!containsOnlyDigits(input)) {
return '';
}
const hours = parseFloat(input);
const days = (hours / 24).toFixed(accuracy);
return parseFloat(days);
}
export function convertHoursToDays(
input: string,
accuracy: string,
daysFlag: boolean
): string {
if (!containsOnlyDigits(accuracy)) {
throw new Error('Accuracy contains non digits.');
}
const result: string[] = [];
const lines = input.split('\n');
lines.forEach((line) => {
const parts = line.split(' ');
const hours = parts[0]; // Extract the number before the space
const days = compute(hours, Number(accuracy));
result.push(daysFlag ? `${days} days` : `${days}`);
});
return result.join('\n');
}

View File

@@ -1,3 +1,4 @@
import { tool as daysDoHours } from './convert-days-to-hours/meta';
import { tool as hoursToDays } from './convert-hours-to-days/meta';
export const timeTools = [daysDoHours];
export const timeTools = [daysDoHours, hoursToDays];

View File

@@ -1,7 +1,6 @@
import { Box } from '@mui/material';
import React, { useCallback, useEffect, useState } from 'react';
import React, { useCallback, useState } from 'react';
import * as Yup from 'yup';
import ToolFileInput from '@components/input/ToolFileInput';
import ToolFileResult from '@components/result/ToolFileResult';
import ToolContent from '@components/ToolContent';
import { ToolComponentProps } from '@tools/defineTool';
@@ -11,6 +10,7 @@ import { updateNumberField } from '@utils/string';
import { FFmpeg } from '@ffmpeg/ffmpeg';
import { fetchFile } from '@ffmpeg/util';
import { debounce } from 'lodash';
import ToolVideoInput from '@components/input/ToolVideoInput';
const ffmpeg = new FFmpeg();
@@ -35,14 +35,16 @@ export default function TrimVideo({ title }: ToolComponentProps) {
optionsValues: typeof initialValues,
input: File | null
) => {
console.log('compute', optionsValues, input);
if (!input) return;
const { trimStart, trimEnd } = optionsValues;
try {
if (!ffmpeg.loaded) {
await ffmpeg.load();
await ffmpeg.load({
wasmURL:
'https://cdn.jsdelivr.net/npm/@ffmpeg/core@0.12.9/dist/esm/ffmpeg-core.wasm'
});
}
const inputName = 'input.mp4';
@@ -111,12 +113,11 @@ export default function TrimVideo({ title }: ToolComponentProps) {
input={input}
renderCustomInput={({ trimStart, trimEnd }, setFieldValue) => {
return (
<ToolFileInput
<ToolVideoInput
value={input}
onChange={setInput}
accept={['video/mp4', 'video/webm', 'video/ogg']}
title={'Input Video'}
type="video"
showTrimControls={true}
onTrimChange={(trimStart, trimEnd) => {
setFieldValue('trimStart', trimStart);