mirror of
https://github.com/iib0011/omni-tools.git
synced 2025-12-17 09:46:02 +00:00
Merge pull request #216 from Srivarshan-T/feat/aac-to-mp3
feat: add AAC to MP3 audio converter tool
This commit is contained in:
commit
9301259eab
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"
|
||||||
|
|||||||
@ -58,22 +58,6 @@
|
|||||||
"title": "Convertir le temps en secondes"
|
"title": "Convertir le temps en secondes"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"convertUnixToDate": {
|
|
||||||
"addUtcLabel": "Ajouter le suffixe 'UTC'",
|
|
||||||
"addUtcLabelDescription": "Affiche 'UTC' après la date convertie (uniquement en mode UTC)",
|
|
||||||
"description": "Convertit un timestamp Unix en une date lisible par un humain.",
|
|
||||||
"longDescription": "Cet outil permet de convertir un timestamp Unix (en secondes) en une date lisible au format AAAA-MM-JJ HH:MM:SS. Il prend en charge l'affichage en UTC ou dans le fuseau horaire local, ce qui est pratique pour interpréter rapidement des horodatages issus de journaux, d'API ou de systèmes utilisant le temps Unix.",
|
|
||||||
"outputOptions": "Options de sortie",
|
|
||||||
"shortDescription": "Conversion de timestamp Unix en date",
|
|
||||||
"title": "Convertir un timestamp Unix en date",
|
|
||||||
"toolInfo": {
|
|
||||||
"description": "Cet outil convertit un timestamp Unix (en secondes) en une date lisible (par ex. AAAA-MM-JJ HH:MM:SS). Il prend en charge l'affichage en heure locale ou en UTC, ce qui le rend utile pour analyser rapidement des données issues de journaux ou d’APIs.",
|
|
||||||
"title": "Convertir un timestamp Unix en date"
|
|
||||||
},
|
|
||||||
"useLocalTime": "Utiliser l’heure locale",
|
|
||||||
"useLocalTimeDescription": "Affiche la date convertie dans votre fuseau horaire local au lieu de l’heure UTC",
|
|
||||||
"withLabel": "Options"
|
|
||||||
},
|
|
||||||
"crontabGuru": {
|
"crontabGuru": {
|
||||||
"description": "Générez et comprenez les expressions Cron. Créez des planifications Cron pour les tâches automatisées et les tâches système.",
|
"description": "Générez et comprenez les expressions Cron. Créez des planifications Cron pour les tâches automatisées et les tâches système.",
|
||||||
"shortDescription": "Générer et comprendre les expressions cron",
|
"shortDescription": "Générer et comprendre les expressions cron",
|
||||||
@ -114,5 +98,21 @@
|
|||||||
"zeroPaddingDescription": "Faites en sorte que tous les composants de temps aient toujours une largeur de deux chiffres.",
|
"zeroPaddingDescription": "Faites en sorte que tous les composants de temps aient toujours une largeur de deux chiffres.",
|
||||||
"zeroPrintDescription": "Afficher les parties supprimées sous forme de valeurs nulles « 00 ».",
|
"zeroPrintDescription": "Afficher les parties supprimées sous forme de valeurs nulles « 00 ».",
|
||||||
"zeroPrintTruncatedParts": "Parties tronquées sans impression"
|
"zeroPrintTruncatedParts": "Parties tronquées sans impression"
|
||||||
|
},
|
||||||
|
"convertUnixToDate": {
|
||||||
|
"title": "Convertir un timestamp Unix en date",
|
||||||
|
"description": "Convertit un timestamp Unix en une date lisible par un humain.",
|
||||||
|
"shortDescription": "Conversion de timestamp Unix en date",
|
||||||
|
"longDescription": "Cet outil permet de convertir un timestamp Unix (en secondes) en une date lisible au format AAAA-MM-JJ HH:MM:SS. Il prend en charge l'affichage en UTC ou dans le fuseau horaire local, ce qui est pratique pour interpréter rapidement des horodatages issus de journaux, d'API ou de systèmes utilisant le temps Unix.",
|
||||||
|
"withLabel": "Options",
|
||||||
|
"outputOptions": "Options de sortie",
|
||||||
|
"addUtcLabel": "Ajouter le suffixe 'UTC'",
|
||||||
|
"addUtcLabelDescription": "Affiche 'UTC' après la date convertie (uniquement en mode UTC)",
|
||||||
|
"useLocalTime": "Utiliser l’heure locale",
|
||||||
|
"useLocalTimeDescription": "Affiche la date convertie dans votre fuseau horaire local au lieu de l’heure UTC",
|
||||||
|
"toolInfo": {
|
||||||
|
"title": "Convertir un timestamp Unix en date",
|
||||||
|
"description": "Cet outil convertit un timestamp Unix (en secondes) en une date lisible (par ex. AAAA-MM-JJ HH:MM:SS). Il prend en charge l'affichage en heure locale ou en UTC, ce qui le rend utile pour analyser rapidement des données issues de journaux ou d’APIs."
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,13 +2,19 @@ import React, { useRef } from 'react';
|
|||||||
import { Box, Typography } from '@mui/material';
|
import { Box, Typography } from '@mui/material';
|
||||||
import BaseFileInput from './BaseFileInput';
|
import BaseFileInput from './BaseFileInput';
|
||||||
import { BaseFileInputProps } from './file-input-utils';
|
import { BaseFileInputProps } from './file-input-utils';
|
||||||
|
import { AUDIO_FORMATS } from 'pages/tools/converters/audio-converter/types';
|
||||||
|
|
||||||
interface AudioFileInputProps extends Omit<BaseFileInputProps, 'accept'> {
|
interface AudioFileInputProps extends Omit<BaseFileInputProps, 'accept'> {
|
||||||
accept?: string[];
|
accept?: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const AUDIO_ACCEPT_TYPES = [
|
||||||
|
'audio/*',
|
||||||
|
...Object.keys(AUDIO_FORMATS).map((format) => `.${format}`)
|
||||||
|
];
|
||||||
|
|
||||||
export default function ToolAudioInput({
|
export default function ToolAudioInput({
|
||||||
accept = ['audio/*', '.mp3', '.wav', '.aac'],
|
accept = AUDIO_ACCEPT_TYPES,
|
||||||
...props
|
...props
|
||||||
}: AudioFileInputProps) {
|
}: AudioFileInputProps) {
|
||||||
const audioRef = useRef<HTMLAudioElement>(null);
|
const audioRef = useRef<HTMLAudioElement>(null);
|
||||||
|
|||||||
@ -0,0 +1,95 @@
|
|||||||
|
import { expect, describe, it, vi, beforeEach } from 'vitest';
|
||||||
|
import { convertAudio } from './service';
|
||||||
|
import { InitialValuesType } from './types';
|
||||||
|
|
||||||
|
// 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]))
|
||||||
|
}));
|
||||||
|
|
||||||
|
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 options: InitialValuesType = { outputFormat: 'mp3' };
|
||||||
|
const result = await convertAudio(mockInputFile, options);
|
||||||
|
|
||||||
|
expect(result).toBeInstanceOf(File);
|
||||||
|
expect(result.name).toBe('input.mp3');
|
||||||
|
expect(result.type).toBe('audio/mpeg');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should convert to AAC format correctly', async () => {
|
||||||
|
const options: InitialValuesType = { outputFormat: 'aac' };
|
||||||
|
const result = await convertAudio(mockInputFile, options);
|
||||||
|
|
||||||
|
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 options: InitialValuesType = { outputFormat: 'wav' };
|
||||||
|
const result = await convertAudio(mockInputFile, options);
|
||||||
|
|
||||||
|
expect(result).toBeInstanceOf(File);
|
||||||
|
expect(result.name).toBe('input.wav');
|
||||||
|
expect(result.type).toBe('audio/wav');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should convert to FLAC format correctly', async () => {
|
||||||
|
const options: InitialValuesType = { outputFormat: 'flac' };
|
||||||
|
const result = await convertAudio(mockInputFile, options);
|
||||||
|
|
||||||
|
expect(result).toBeInstanceOf(File);
|
||||||
|
expect(result.name).toBe('input.flac');
|
||||||
|
expect(result.type).toBe('audio/flac');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should convert to OGG format correctly', async () => {
|
||||||
|
const options: InitialValuesType = { outputFormat: 'ogg' };
|
||||||
|
const result = await convertAudio(mockInputFile, options);
|
||||||
|
|
||||||
|
expect(result).toBeInstanceOf(File);
|
||||||
|
expect(result.name).toBe('input.ogg');
|
||||||
|
expect(result.type).toBe('audio/ogg');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return original file if input format matches output format', async () => {
|
||||||
|
const options: InitialValuesType = { outputFormat: 'aac' };
|
||||||
|
const result = await convertAudio(mockInputFile, options);
|
||||||
|
|
||||||
|
expect(result).toBe(mockInputFile); // Same instance
|
||||||
|
expect(result.name).toBe('input.aac');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle files without extensions', async () => {
|
||||||
|
const fileNoExt = new File([new Uint8Array([1, 2, 3])], 'audiofile', {
|
||||||
|
type: 'audio/aac'
|
||||||
|
});
|
||||||
|
const options: InitialValuesType = { outputFormat: 'mp3' };
|
||||||
|
const result = await convertAudio(fileNoExt, options);
|
||||||
|
|
||||||
|
expect(result).toBeInstanceOf(File);
|
||||||
|
expect(result.name).toBe('audiofile.mp3');
|
||||||
|
expect(result.type).toBe('audio/mpeg');
|
||||||
|
});
|
||||||
|
});
|
||||||
103
src/pages/tools/converters/audio-converter/index.tsx
Normal file
103
src/pages/tools/converters/audio-converter/index.tsx
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
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';
|
||||||
|
import { InitialValuesType, AUDIO_FORMATS, AudioFormat } from './types';
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
const compute = async (
|
||||||
|
values: InitialValuesType,
|
||||||
|
inputFile: File | null
|
||||||
|
): Promise<void> => {
|
||||||
|
if (!inputFile) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
const resultFile = await convertAudio(inputFile, values);
|
||||||
|
setResult(resultFile);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Conversion failed:', error);
|
||||||
|
setResult(null);
|
||||||
|
} finally {
|
||||||
|
setLoading(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)}
|
||||||
|
options={Object.entries(AUDIO_FORMATS).map(([value]) => ({
|
||||||
|
label: value.toUpperCase(),
|
||||||
|
value: value as AudioFormat
|
||||||
|
}))}
|
||||||
|
description={t(
|
||||||
|
'audioConverter.outputFormatDescription',
|
||||||
|
'Select the desired output audio format'
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
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'))
|
||||||
|
});
|
||||||
82
src/pages/tools/converters/audio-converter/service.ts
Normal file
82
src/pages/tools/converters/audio-converter/service.ts
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
import { FFmpeg } from '@ffmpeg/ffmpeg';
|
||||||
|
import { fetchFile } from '@ffmpeg/util';
|
||||||
|
import { InitialValuesType, AUDIO_FORMATS } from './types';
|
||||||
|
import { getFileExtension } from 'utils/file';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* optimzed call for FFmpeg instance creation,
|
||||||
|
* avoiding to download required FFmpeg binaries on every reload
|
||||||
|
*/
|
||||||
|
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,
|
||||||
|
options: InitialValuesType
|
||||||
|
): Promise<File> {
|
||||||
|
await loadFFmpeg();
|
||||||
|
|
||||||
|
// Use the original input extension for input filename
|
||||||
|
const inputExt = getFileExtension(input.name);
|
||||||
|
|
||||||
|
if (inputExt === options.outputFormat) return input;
|
||||||
|
|
||||||
|
const inputFileName = inputExt ? `input.${inputExt}` : 'input';
|
||||||
|
const outputFileName = `output.${options.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
|
||||||
|
|
||||||
|
const format = AUDIO_FORMATS[options.outputFormat];
|
||||||
|
const { codec, bitrate, mimeType } = format;
|
||||||
|
|
||||||
|
const args = bitrate
|
||||||
|
? ['-i', inputFileName, '-c:a', codec, '-b:a', bitrate, outputFileName]
|
||||||
|
: ['-i', inputFileName, '-c:a', codec, outputFileName];
|
||||||
|
|
||||||
|
// Execute ffmpeg with arguments
|
||||||
|
try {
|
||||||
|
await ffmpeg.exec(args);
|
||||||
|
|
||||||
|
// Read the output file from FFmpeg FS
|
||||||
|
const data = await ffmpeg.readFile(outputFileName);
|
||||||
|
|
||||||
|
// Create a new File with the original name but new extension
|
||||||
|
const baseName = input.name.replace(/\.[^.]+$/, '');
|
||||||
|
const convertedFileName = `${baseName}.${options.outputFormat}`;
|
||||||
|
|
||||||
|
return new File(
|
||||||
|
[new Blob([data as any], { type: mimeType })],
|
||||||
|
convertedFileName,
|
||||||
|
{
|
||||||
|
type: mimeType
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
// Clean up FFmpeg virtual filesystem
|
||||||
|
try {
|
||||||
|
await ffmpeg.deleteFile(inputFileName);
|
||||||
|
await ffmpeg.deleteFile(outputFileName);
|
||||||
|
} catch (e) {
|
||||||
|
// Ignore cleanup errors
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
16
src/pages/tools/converters/audio-converter/types.ts
Normal file
16
src/pages/tools/converters/audio-converter/types.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
export const AUDIO_FORMATS = {
|
||||||
|
// Lossy formats
|
||||||
|
mp3: { codec: 'libmp3lame', bitrate: '192k', mimeType: 'audio/mpeg' },
|
||||||
|
aac: { codec: 'aac', bitrate: '192k', mimeType: 'audio/aac' },
|
||||||
|
ogg: { codec: 'libvorbis', bitrate: '192k', mimeType: 'audio/ogg' },
|
||||||
|
|
||||||
|
// Lossless formats
|
||||||
|
wav: { codec: 'pcm_s16le', bitrate: null, mimeType: 'audio/wav' },
|
||||||
|
flac: { codec: 'flac', bitrate: null, mimeType: 'audio/flac' }
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export type AudioFormat = keyof typeof AUDIO_FORMATS;
|
||||||
|
|
||||||
|
export type InitialValuesType = {
|
||||||
|
outputFormat: AudioFormat;
|
||||||
|
};
|
||||||
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];
|
||||||
@ -33,7 +33,8 @@ export type ToolCategory =
|
|||||||
| 'video'
|
| 'video'
|
||||||
| 'pdf'
|
| 'pdf'
|
||||||
| '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: 'streamline-plump:convert-pdf-1',
|
||||||
|
value: 'translation:categories.converters.description',
|
||||||
|
title: 'translation:categories.converters.title'
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
const CATEGORIES_USER_TYPES_MAPPINGS: Partial<Record<ToolCategory, UserType>> =
|
const CATEGORIES_USER_TYPES_MAPPINGS: Partial<Record<ToolCategory, UserType>> =
|
||||||
@ -145,7 +154,8 @@ const CATEGORIES_USER_TYPES_MAPPINGS: Partial<Record<ToolCategory, UserType>> =
|
|||||||
png: 'generalUsers',
|
png: 'generalUsers',
|
||||||
'image-generic': 'generalUsers',
|
'image-generic': 'generalUsers',
|
||||||
video: 'generalUsers',
|
video: 'generalUsers',
|
||||||
audio: 'generalUsers'
|
audio: 'generalUsers',
|
||||||
|
converters: 'generalUsers'
|
||||||
};
|
};
|
||||||
// Filter tools by user types
|
// Filter tools by user types
|
||||||
export const filterToolsByUserTypes = (
|
export const filterToolsByUserTypes = (
|
||||||
|
|||||||
11
src/utils/file.ts
Normal file
11
src/utils/file.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
/**
|
||||||
|
* Returns the file extension
|
||||||
|
*
|
||||||
|
* @param {string} filename - The filename
|
||||||
|
* @return {string} - the file extension
|
||||||
|
*/
|
||||||
|
export function getFileExtension(filename: string): string {
|
||||||
|
const lastDot = filename.lastIndexOf('.');
|
||||||
|
if (lastDot <= 0) return ''; // No extension
|
||||||
|
return filename.slice(lastDot + 1).toLowerCase();
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user