feat: add AAC to MP3 audio converter tool

This commit is contained in:
Srivarshan-T
2025-07-20 00:19:13 +05:30
parent fc18dc0dc0
commit 8fc9081487
6 changed files with 162 additions and 1 deletions

View File

@@ -0,0 +1,44 @@
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
);
});
});

View File

@@ -0,0 +1,59 @@
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 }}
/>
);
}

View File

@@ -0,0 +1,15 @@
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'))
});

View File

@@ -0,0 +1,35 @@
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;
}

View File

@@ -1,3 +1,4 @@
import { tool as audioAACMP3 } from './AAC-MP3/meta';
import { tool as audioMergeAudio } from './merge-audio/meta';
import { tool as audioTrim } from './trim/meta';
import { tool as audioChangeSpeed } from './change-speed/meta';
@@ -7,5 +8,6 @@ export const audioTools = [
audioExtractAudio,
audioChangeSpeed,
audioTrim,
audioMergeAudio
audioMergeAudio,
audioAACMP3
];