mirror of
https://github.com/iib0011/omni-tools.git
synced 2025-12-17 17:56:38 +00:00
feat: implement audio converter tool with support for MP3, AAC, and WAV formats
This commit is contained in:
parent
8fc9081487
commit
dffabc8134
8
public/locales/en/converters.json
Normal file
8
public/locales/en/converters.json
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"audioconverter": {
|
||||||
|
"title": "Audio Converter",
|
||||||
|
"description": "Convert audio files between different formats.",
|
||||||
|
"shortDescription": "Convert audio files to various formats.",
|
||||||
|
"longDescription": "This tool allows you to convert audio files from one format to another, supporting a wide range of audio formats for seamless conversion."
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -17,11 +17,23 @@
|
|||||||
"fileCopied": "File copied",
|
"fileCopied": "File copied",
|
||||||
"selectFileDescription": "Click here to select a {{type}} from your device, press Ctrl+V to use a {{type}} from your clipboard, or drag and drop a file from desktop"
|
"selectFileDescription": "Click here to select a {{type}} from your device, press Ctrl+V to use a {{type}} from your clipboard, or drag and drop a file from desktop"
|
||||||
},
|
},
|
||||||
|
"converters": {
|
||||||
|
"audioconverter": {
|
||||||
|
"title": "Audio Converter",
|
||||||
|
"description": "Convert audio files between different formats.",
|
||||||
|
"shortDescription": "Convert audio files to various formats.",
|
||||||
|
"longDescription": "This tool allows you to convert audio files from one format to another, supporting a wide range of audio formats for seamless conversion."
|
||||||
|
}
|
||||||
|
},
|
||||||
"categories": {
|
"categories": {
|
||||||
"audio": {
|
"audio": {
|
||||||
"description": "Tools for working with audio – extract audio from video, adjusting audio speed, merging multiple audio files and much more.",
|
"description": "Tools for working with audio – extract audio from video, adjusting audio speed, merging multiple audio files and much more.",
|
||||||
"title": "Audio Tools"
|
"title": "Audio Tools"
|
||||||
},
|
},
|
||||||
|
"converters": {
|
||||||
|
"description": "Tools for converting data between different formats – convert images, audio, video, text, and more.",
|
||||||
|
"title": "Converter Tools"
|
||||||
|
},
|
||||||
"csv": {
|
"csv": {
|
||||||
"description": "Tools for working with CSV files - convert CSV to different formats, manipulate CSV data, validate CSV structure, and process CSV files efficiently.",
|
"description": "Tools for working with CSV files - convert CSV to different formats, manipulate CSV data, validate CSV structure, and process CSV files efficiently.",
|
||||||
"title": "CSV Tools"
|
"title": "CSV Tools"
|
||||||
|
|||||||
@ -1,44 +0,0 @@
|
|||||||
import { describe, it, expect, vi } from 'vitest';
|
|
||||||
|
|
||||||
// Mock FFmpeg and fetchFile
|
|
||||||
vi.mock('@ffmpeg/ffmpeg', () => ({
|
|
||||||
FFmpeg: vi.fn().mockImplementation(() => ({
|
|
||||||
load: vi.fn().mockResolvedValue(undefined),
|
|
||||||
writeFile: vi.fn().mockResolvedValue(undefined),
|
|
||||||
exec: vi.fn().mockResolvedValue(undefined),
|
|
||||||
readFile: vi.fn().mockReturnValue(new Uint8Array([1, 2, 3, 4, 5])),
|
|
||||||
unlink: vi.fn().mockResolvedValue(undefined)
|
|
||||||
}))
|
|
||||||
}));
|
|
||||||
|
|
||||||
vi.mock('@ffmpeg/util', () => ({
|
|
||||||
fetchFile: vi.fn().mockResolvedValue(new Uint8Array([1, 2, 3, 4, 5]))
|
|
||||||
}));
|
|
||||||
|
|
||||||
// Import service
|
|
||||||
import { AACtoMp3 } from './service';
|
|
||||||
|
|
||||||
describe('convertAACtoMP3', () => {
|
|
||||||
it('should return a new MP3 File when given a valid AAC file', async () => {
|
|
||||||
const mockAACData = new Uint8Array([0, 1, 2, 3, 4, 5]);
|
|
||||||
const mockFile = new File([mockAACData], 'sample.aac', {
|
|
||||||
type: 'audio/aac'
|
|
||||||
});
|
|
||||||
|
|
||||||
const result = await AACtoMp3(mockFile);
|
|
||||||
|
|
||||||
expect(result).toBeInstanceOf(File);
|
|
||||||
expect(result.name).toBe('sample.mp3');
|
|
||||||
expect(result.type).toBe('audio/mpeg');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should throw error if file type is not AAC', async () => {
|
|
||||||
const mockFile = new File(['dummy'], 'song.wav', {
|
|
||||||
type: 'audio/wav'
|
|
||||||
});
|
|
||||||
|
|
||||||
await expect(() => AACtoMp3(mockFile)).rejects.toThrowError(
|
|
||||||
'Only .aac files are allowed.' // FIXED to match actual error
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@ -1,59 +0,0 @@
|
|||||||
import React, { useState } from 'react';
|
|
||||||
import ToolContent from '@components/ToolContent';
|
|
||||||
import { ToolComponentProps } from '@tools/defineTool';
|
|
||||||
import ToolAudioInput from '@components/input/ToolAudioInput';
|
|
||||||
import ToolFileResult from '@components/result/ToolFileResult';
|
|
||||||
|
|
||||||
import { AACtoMp3 } from './service';
|
|
||||||
|
|
||||||
export default function AACMP3({ title, longDescription }: ToolComponentProps) {
|
|
||||||
const [input, setInput] = useState<File | null>(null);
|
|
||||||
const [result, setResult] = useState<File | null>(null);
|
|
||||||
const [loading, setLoading] = useState(false);
|
|
||||||
|
|
||||||
const compute = async (
|
|
||||||
_optionsValues: {},
|
|
||||||
input: File | null
|
|
||||||
): Promise<void> => {
|
|
||||||
if (!input) return;
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (!input.name.toLowerCase().endsWith('.aac')) {
|
|
||||||
setInput(null);
|
|
||||||
alert('please upload .aac files are allowed.');
|
|
||||||
setResult(null);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setLoading(true);
|
|
||||||
const resultFile = await AACtoMp3(input);
|
|
||||||
setResult(resultFile);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Conversion failed:', error);
|
|
||||||
setResult(null);
|
|
||||||
}
|
|
||||||
setLoading(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ToolContent
|
|
||||||
title={title}
|
|
||||||
input={input}
|
|
||||||
inputComponent={
|
|
||||||
<ToolAudioInput
|
|
||||||
value={input}
|
|
||||||
onChange={setInput}
|
|
||||||
title={'Upload Your AAC File'}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
resultComponent={
|
|
||||||
<ToolFileResult value={result} title={'Mp3 Output'} loading={loading} />
|
|
||||||
}
|
|
||||||
initialValues={{}}
|
|
||||||
getGroups={null}
|
|
||||||
setInput={setInput}
|
|
||||||
compute={compute}
|
|
||||||
toolInfo={{ title: `What is a ${title}?`, description: longDescription }}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -1,15 +0,0 @@
|
|||||||
import { defineTool } from '@tools/defineTool';
|
|
||||||
import { lazy } from 'react';
|
|
||||||
|
|
||||||
export const tool = defineTool('audio', {
|
|
||||||
i18n: {
|
|
||||||
name: 'audio:AACMP3.title',
|
|
||||||
description: 'audio:AACMP3.description',
|
|
||||||
shortDescription: 'audio:AACMP3.shortDescription',
|
|
||||||
longDescription: 'audio:AACMP3.longDescription'
|
|
||||||
},
|
|
||||||
path: 'AAC-MP3',
|
|
||||||
icon: 'bi:filetype-mp3',
|
|
||||||
keywords: ['AAC', 'MP3', 'convert', 'audio', 'file conversion'],
|
|
||||||
component: lazy(() => import('./index'))
|
|
||||||
});
|
|
||||||
@ -1,35 +0,0 @@
|
|||||||
import { FFmpeg } from '@ffmpeg/ffmpeg';
|
|
||||||
import { fetchFile } from '@ffmpeg/util';
|
|
||||||
|
|
||||||
const ffmpeg = new FFmpeg();
|
|
||||||
let isLoaded = false;
|
|
||||||
|
|
||||||
export async function AACtoMp3(input: File): Promise<File> {
|
|
||||||
if (!isLoaded) {
|
|
||||||
await ffmpeg.load();
|
|
||||||
isLoaded = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
const inName = 'input.aac';
|
|
||||||
const outName = 'output.mp3';
|
|
||||||
|
|
||||||
await ffmpeg.writeFile(inName, await fetchFile(input));
|
|
||||||
|
|
||||||
await ffmpeg.exec([
|
|
||||||
'-i',
|
|
||||||
inName,
|
|
||||||
'-c:a',
|
|
||||||
'libmp3lame',
|
|
||||||
'-b:a',
|
|
||||||
'192k',
|
|
||||||
outName
|
|
||||||
]);
|
|
||||||
|
|
||||||
const data = await ffmpeg.readFile(outName);
|
|
||||||
|
|
||||||
const mp3 = new File([data], input.name.replace(/\.aac$/i, '.mp3'), {
|
|
||||||
type: 'audio/mpeg'
|
|
||||||
});
|
|
||||||
|
|
||||||
return mp3;
|
|
||||||
}
|
|
||||||
@ -1,4 +1,3 @@
|
|||||||
import { tool as audioAACMP3 } from './AAC-MP3/meta';
|
|
||||||
import { tool as audioMergeAudio } from './merge-audio/meta';
|
import { tool as audioMergeAudio } from './merge-audio/meta';
|
||||||
import { tool as audioTrim } from './trim/meta';
|
import { tool as audioTrim } from './trim/meta';
|
||||||
import { tool as audioChangeSpeed } from './change-speed/meta';
|
import { tool as audioChangeSpeed } from './change-speed/meta';
|
||||||
@ -8,6 +7,5 @@ export const audioTools = [
|
|||||||
audioExtractAudio,
|
audioExtractAudio,
|
||||||
audioChangeSpeed,
|
audioChangeSpeed,
|
||||||
audioTrim,
|
audioTrim,
|
||||||
audioMergeAudio,
|
audioMergeAudio
|
||||||
audioAACMP3
|
|
||||||
];
|
];
|
||||||
|
|||||||
@ -0,0 +1,64 @@
|
|||||||
|
import { expect, describe, it, vi, beforeEach } from 'vitest';
|
||||||
|
|
||||||
|
// Mock FFmpeg since it doesn't support Node.js in tests
|
||||||
|
vi.mock('@ffmpeg/ffmpeg', () => ({
|
||||||
|
FFmpeg: vi.fn().mockImplementation(() => ({
|
||||||
|
loaded: false,
|
||||||
|
load: vi.fn().mockResolvedValue(undefined),
|
||||||
|
writeFile: vi.fn().mockResolvedValue(undefined),
|
||||||
|
exec: vi.fn().mockResolvedValue(undefined),
|
||||||
|
readFile: vi.fn().mockResolvedValue(new Uint8Array([10, 20, 30, 40, 50])),
|
||||||
|
deleteFile: vi.fn().mockResolvedValue(undefined)
|
||||||
|
}))
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock('@ffmpeg/util', () => ({
|
||||||
|
fetchFile: vi.fn().mockResolvedValue(new Uint8Array([10, 20, 30, 40, 50]))
|
||||||
|
}));
|
||||||
|
|
||||||
|
import { convertAudio } from './service';
|
||||||
|
|
||||||
|
describe('convertAudio', () => {
|
||||||
|
let mockInputFile: File;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
const mockAudioData = new Uint8Array([1, 2, 3, 4, 5]);
|
||||||
|
mockInputFile = new File([mockAudioData], 'input.aac', {
|
||||||
|
type: 'audio/aac'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should convert to MP3 format correctly', async () => {
|
||||||
|
const outputFormat = 'mp3' as const;
|
||||||
|
const result = await convertAudio(mockInputFile, outputFormat);
|
||||||
|
|
||||||
|
expect(result).toBeInstanceOf(File);
|
||||||
|
expect(result.name).toBe('input.mp3'); // base name + outputFormat extension
|
||||||
|
expect(result.type).toBe('audio/mpeg');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should convert to AAC format correctly', async () => {
|
||||||
|
const outputFormat = 'aac' as const;
|
||||||
|
const result = await convertAudio(mockInputFile, outputFormat);
|
||||||
|
|
||||||
|
expect(result).toBeInstanceOf(File);
|
||||||
|
expect(result.name).toBe('input.aac');
|
||||||
|
expect(result.type).toBe('audio/aac');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should convert to WAV format correctly', async () => {
|
||||||
|
const outputFormat = 'wav' as const;
|
||||||
|
const result = await convertAudio(mockInputFile, outputFormat);
|
||||||
|
|
||||||
|
expect(result).toBeInstanceOf(File);
|
||||||
|
expect(result.name).toBe('input.wav');
|
||||||
|
expect(result.type).toBe('audio/wav');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw error for unsupported formats', async () => {
|
||||||
|
// @ts-expect-error - intentionally passing unsupported format
|
||||||
|
await expect(convertAudio(mockInputFile, 'flac')).rejects.toThrow(
|
||||||
|
'Unsupported output format'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
112
src/pages/tools/converters/audio-converter/index.tsx
Normal file
112
src/pages/tools/converters/audio-converter/index.tsx
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import { Box } from '@mui/material';
|
||||||
|
import ToolContent from '@components/ToolContent';
|
||||||
|
import { ToolComponentProps } from '@tools/defineTool';
|
||||||
|
import ToolAudioInput from '@components/input/ToolAudioInput';
|
||||||
|
import ToolFileResult from '@components/result/ToolFileResult';
|
||||||
|
import SelectWithDesc from '@components/options/SelectWithDesc';
|
||||||
|
import { convertAudio } from './service';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { GetGroupsType } from '@components/options/ToolOptions';
|
||||||
|
|
||||||
|
type InitialValuesType = {
|
||||||
|
outputFormat: 'mp3' | 'aac' | 'wav';
|
||||||
|
};
|
||||||
|
|
||||||
|
const initialValues: InitialValuesType = {
|
||||||
|
outputFormat: 'mp3'
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function AudioConverter({
|
||||||
|
title,
|
||||||
|
longDescription
|
||||||
|
}: ToolComponentProps) {
|
||||||
|
const { t } = useTranslation('audio');
|
||||||
|
const [input, setInput] = useState<File | null>(null);
|
||||||
|
const [result, setResult] = useState<File | null>(null);
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
|
// Explicitly type getGroups to match GetGroupsType<InitialValuesType>
|
||||||
|
const getGroups: GetGroupsType<InitialValuesType> = ({
|
||||||
|
values,
|
||||||
|
updateField
|
||||||
|
}) => [
|
||||||
|
{
|
||||||
|
id: 'output-format',
|
||||||
|
title: t('audioConverter.outputFormat', 'Output Format'),
|
||||||
|
description: t(
|
||||||
|
'audioConverter.outputFormatDescription',
|
||||||
|
'Select the desired output audio format'
|
||||||
|
),
|
||||||
|
component: (
|
||||||
|
<Box>
|
||||||
|
<SelectWithDesc
|
||||||
|
selected={values.outputFormat}
|
||||||
|
onChange={(value) =>
|
||||||
|
updateField(
|
||||||
|
'outputFormat',
|
||||||
|
value as InitialValuesType['outputFormat']
|
||||||
|
)
|
||||||
|
}
|
||||||
|
options={[
|
||||||
|
{ label: 'MP3', value: 'mp3' },
|
||||||
|
{ label: 'AAC', value: 'aac' },
|
||||||
|
{ label: 'WAV', value: 'wav' }
|
||||||
|
]}
|
||||||
|
description={t(
|
||||||
|
'audioConverter.outputFormatDescription',
|
||||||
|
'Select the desired output audio format'
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const compute = async (
|
||||||
|
values: InitialValuesType,
|
||||||
|
inputFile: File | null
|
||||||
|
): Promise<void> => {
|
||||||
|
if (!inputFile) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
const resultFile = await convertAudio(inputFile, values.outputFormat);
|
||||||
|
setResult(resultFile);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Conversion failed:', error);
|
||||||
|
setResult(null);
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ToolContent
|
||||||
|
title={title}
|
||||||
|
input={input}
|
||||||
|
inputComponent={
|
||||||
|
<ToolAudioInput
|
||||||
|
value={input}
|
||||||
|
onChange={setInput}
|
||||||
|
title={t('audioConverter.uploadAudio', 'Upload Your Audio File')}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
resultComponent={
|
||||||
|
<ToolFileResult
|
||||||
|
value={result}
|
||||||
|
title={t('audioConverter.outputTitle', 'Converted Audio')}
|
||||||
|
loading={loading}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
initialValues={initialValues}
|
||||||
|
getGroups={getGroups}
|
||||||
|
setInput={setInput}
|
||||||
|
compute={compute}
|
||||||
|
toolInfo={{
|
||||||
|
title: `What is a ${title}?`,
|
||||||
|
description: longDescription
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
15
src/pages/tools/converters/audio-converter/meta.ts
Normal file
15
src/pages/tools/converters/audio-converter/meta.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import { defineTool } from '@tools/defineTool';
|
||||||
|
import { lazy } from 'react';
|
||||||
|
|
||||||
|
export const tool = defineTool('converters', {
|
||||||
|
i18n: {
|
||||||
|
name: 'translation:converters.audioconverter.title',
|
||||||
|
description: 'translation:converters.audioconverter.description',
|
||||||
|
shortDescription: 'translation:converters.audioconverter.shortDescription',
|
||||||
|
longDescription: 'translation:converters.audioconverter.longDescription'
|
||||||
|
},
|
||||||
|
path: 'audio-converter',
|
||||||
|
icon: 'mdi:music-note-outline',
|
||||||
|
keywords: ['audio', 'converter'],
|
||||||
|
component: lazy(() => import('./index'))
|
||||||
|
});
|
||||||
100
src/pages/tools/converters/audio-converter/service.ts
Normal file
100
src/pages/tools/converters/audio-converter/service.ts
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
import { FFmpeg } from '@ffmpeg/ffmpeg';
|
||||||
|
import { fetchFile } from '@ffmpeg/util';
|
||||||
|
|
||||||
|
const ffmpeg = new FFmpeg();
|
||||||
|
let isLoaded = false;
|
||||||
|
|
||||||
|
async function loadFFmpeg() {
|
||||||
|
if (!isLoaded) {
|
||||||
|
await ffmpeg.load();
|
||||||
|
isLoaded = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts input audio file to selected output format ('mp3', 'aac', or 'wav').
|
||||||
|
* Supports any input audio file type accepted by FFmpeg.
|
||||||
|
*
|
||||||
|
* @param input - Source audio File
|
||||||
|
* @param outputFormat - 'mp3' | 'aac' | 'wav'
|
||||||
|
* @returns Converted audio File
|
||||||
|
*/
|
||||||
|
export async function convertAudio(
|
||||||
|
input: File,
|
||||||
|
outputFormat: 'mp3' | 'aac' | 'wav'
|
||||||
|
): Promise<File> {
|
||||||
|
await loadFFmpeg();
|
||||||
|
|
||||||
|
// Use the original input extension for input filename
|
||||||
|
const inputExtMatch = input.name.match(/\.[^.]+$/);
|
||||||
|
const inputExt = inputExtMatch ? inputExtMatch[0] : '.audio';
|
||||||
|
|
||||||
|
const inputFileName = `input${inputExt}`;
|
||||||
|
const outputFileName = `output.${outputFormat}`;
|
||||||
|
|
||||||
|
// Write the input file to FFmpeg FS
|
||||||
|
await ffmpeg.writeFile(inputFileName, await fetchFile(input));
|
||||||
|
|
||||||
|
// Build the FFmpeg args depending on the output format
|
||||||
|
// You can customize the codec and bitrate options per format here
|
||||||
|
let args: string[];
|
||||||
|
|
||||||
|
switch (outputFormat) {
|
||||||
|
case 'mp3':
|
||||||
|
args = [
|
||||||
|
'-i',
|
||||||
|
inputFileName,
|
||||||
|
'-c:a',
|
||||||
|
'libmp3lame',
|
||||||
|
'-b:a',
|
||||||
|
'192k',
|
||||||
|
outputFileName
|
||||||
|
];
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'aac':
|
||||||
|
args = [
|
||||||
|
'-i',
|
||||||
|
inputFileName,
|
||||||
|
'-c:a',
|
||||||
|
'aac',
|
||||||
|
'-b:a',
|
||||||
|
'192k',
|
||||||
|
outputFileName
|
||||||
|
];
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'wav':
|
||||||
|
args = ['-i', inputFileName, '-c:a', 'pcm_s16le', outputFileName];
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new Error(`Unsupported output format: ${outputFormat}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute ffmpeg with arguments
|
||||||
|
await ffmpeg.exec(args);
|
||||||
|
|
||||||
|
// Read the output file from FFmpeg FS
|
||||||
|
const data = await ffmpeg.readFile(outputFileName);
|
||||||
|
|
||||||
|
// Determine MIME type by outputFormat
|
||||||
|
let mimeType = '';
|
||||||
|
switch (outputFormat) {
|
||||||
|
case 'mp3':
|
||||||
|
mimeType = 'audio/mpeg';
|
||||||
|
break;
|
||||||
|
case 'aac':
|
||||||
|
mimeType = 'audio/aac';
|
||||||
|
break;
|
||||||
|
case 'wav':
|
||||||
|
mimeType = 'audio/wav';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new File with the original name but new extension
|
||||||
|
const baseName = input.name.replace(/\.[^.]+$/, '');
|
||||||
|
const convertedFileName = `${baseName}.${outputFormat}`;
|
||||||
|
|
||||||
|
return new File([data], convertedFileName, { type: mimeType });
|
||||||
|
}
|
||||||
3
src/pages/tools/converters/index.ts
Normal file
3
src/pages/tools/converters/index.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import { tool as convertersAudioConverter } from './audio-converter/meta';
|
||||||
|
|
||||||
|
export const convertersTools = [convertersAudioConverter];
|
||||||
@ -30,7 +30,8 @@ export type ToolCategory =
|
|||||||
| 'pdf'
|
| 'pdf'
|
||||||
| 'image-generic'
|
| 'image-generic'
|
||||||
| 'audio'
|
| 'audio'
|
||||||
| 'xml';
|
| 'xml'
|
||||||
|
| 'converters';
|
||||||
|
|
||||||
export interface DefinedTool {
|
export interface DefinedTool {
|
||||||
type: ToolCategory;
|
type: ToolCategory;
|
||||||
|
|||||||
@ -13,6 +13,7 @@ import { timeTools } from '../pages/tools/time';
|
|||||||
import { IconifyIcon } from '@iconify/react';
|
import { IconifyIcon } from '@iconify/react';
|
||||||
import { pdfTools } from '../pages/tools/pdf';
|
import { pdfTools } from '../pages/tools/pdf';
|
||||||
import { xmlTools } from '../pages/tools/xml';
|
import { xmlTools } from '../pages/tools/xml';
|
||||||
|
import { convertersTools } from '../pages/tools/converters';
|
||||||
import { TFunction } from 'i18next';
|
import { TFunction } from 'i18next';
|
||||||
import { FullI18nKey, I18nNamespaces } from '../i18n';
|
import { FullI18nKey, I18nNamespaces } from '../i18n';
|
||||||
|
|
||||||
@ -30,7 +31,8 @@ const toolCategoriesOrder: ToolCategory[] = [
|
|||||||
'png',
|
'png',
|
||||||
'time',
|
'time',
|
||||||
'xml',
|
'xml',
|
||||||
'gif'
|
'gif',
|
||||||
|
'converters'
|
||||||
];
|
];
|
||||||
export const tools: DefinedTool[] = [
|
export const tools: DefinedTool[] = [
|
||||||
...imageTools,
|
...imageTools,
|
||||||
@ -43,7 +45,8 @@ export const tools: DefinedTool[] = [
|
|||||||
...numberTools,
|
...numberTools,
|
||||||
...timeTools,
|
...timeTools,
|
||||||
...audioTools,
|
...audioTools,
|
||||||
...xmlTools
|
...xmlTools,
|
||||||
|
...convertersTools
|
||||||
];
|
];
|
||||||
const categoriesConfig: {
|
const categoriesConfig: {
|
||||||
type: ToolCategory;
|
type: ToolCategory;
|
||||||
@ -134,6 +137,12 @@ const categoriesConfig: {
|
|||||||
icon: 'mdi-light:xml',
|
icon: 'mdi-light:xml',
|
||||||
value: 'translation:categories.xml.description',
|
value: 'translation:categories.xml.description',
|
||||||
title: 'translation:categories.xml.title'
|
title: 'translation:categories.xml.title'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'converters',
|
||||||
|
icon: 'mdi:swap-horizontal',
|
||||||
|
value: 'translation:categories.converters.description',
|
||||||
|
title: 'translation:categories.converters.title'
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
// use for changelogs
|
// use for changelogs
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user