mirror of
https://github.com/iib0011/omni-tools.git
synced 2025-12-29 16:16:02 +00:00
feat: add AAC to MP3 audio converter tool
This commit is contained in:
44
src/pages/tools/audio/AAC-MP3/AAC-MP3.service.test.ts
Normal file
44
src/pages/tools/audio/AAC-MP3/AAC-MP3.service.test.ts
Normal 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
|
||||
);
|
||||
});
|
||||
});
|
||||
59
src/pages/tools/audio/AAC-MP3/index.tsx
Normal file
59
src/pages/tools/audio/AAC-MP3/index.tsx
Normal 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 }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
15
src/pages/tools/audio/AAC-MP3/meta.ts
Normal file
15
src/pages/tools/audio/AAC-MP3/meta.ts
Normal 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'))
|
||||
});
|
||||
35
src/pages/tools/audio/AAC-MP3/service.ts
Normal file
35
src/pages/tools/audio/AAC-MP3/service.ts
Normal 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;
|
||||
}
|
||||
@@ -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
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user