feat: Expand supported image and audio formats, update UI color palette, and refresh application assets.
This commit is contained in:
@@ -7,7 +7,10 @@ import * as Comlink from 'comlink';
|
||||
import { FFmpeg } from '@ffmpeg/ffmpeg';
|
||||
import { toBlobURL } from '@ffmpeg/util';
|
||||
|
||||
type AudioFormat = 'mp3' | 'wav' | 'ogg' | 'm4a' | 'flac';
|
||||
// All supported audio formats
|
||||
type AudioFormat =
|
||||
| 'mp3' | 'wav' | 'ogg' | 'm4a' | 'flac'
|
||||
| 'aac' | 'aiff' | 'alac' | 'wma' | 'opus' | 'm4r' | 'amr';
|
||||
|
||||
let ffmpeg: FFmpeg | null = null;
|
||||
let ffmpegLoading: Promise<void> | null = null;
|
||||
@@ -30,6 +33,8 @@ async function ensureFFmpegLoaded(): Promise<FFmpeg> {
|
||||
coreURL: await toBlobURL(`${baseURL}/ffmpeg-core.js`, 'text/javascript'),
|
||||
wasmURL: await toBlobURL(`${baseURL}/ffmpeg-core.wasm`, 'application/wasm'),
|
||||
});
|
||||
|
||||
console.log('[AudioWorker] FFmpeg WASM initialized');
|
||||
})();
|
||||
|
||||
await ffmpegLoading;
|
||||
@@ -42,34 +47,66 @@ function getFFmpegArgs(
|
||||
bitrate?: number
|
||||
): string[] {
|
||||
const args: string[] = [];
|
||||
const br = bitrate || 192;
|
||||
|
||||
// Codec selection based on output format
|
||||
switch (outputFormat) {
|
||||
case 'mp3':
|
||||
args.push('-c:a', 'libmp3lame');
|
||||
args.push('-b:a', `${bitrate || 192}k`);
|
||||
args.push('-b:a', `${br}k`);
|
||||
break;
|
||||
case 'wav':
|
||||
args.push('-c:a', 'pcm_s16le');
|
||||
break;
|
||||
case 'ogg':
|
||||
args.push('-c:a', 'libvorbis');
|
||||
args.push('-q:a', '4'); // Quality level 0-10
|
||||
args.push('-q:a', '4');
|
||||
break;
|
||||
case 'm4a':
|
||||
args.push('-c:a', 'aac');
|
||||
args.push('-b:a', `${bitrate || 192}k`);
|
||||
args.push('-b:a', `${br}k`);
|
||||
break;
|
||||
case 'flac':
|
||||
args.push('-c:a', 'flac');
|
||||
args.push('-compression_level', '5');
|
||||
break;
|
||||
case 'aac':
|
||||
args.push('-c:a', 'aac');
|
||||
args.push('-b:a', `${br}k`);
|
||||
break;
|
||||
case 'aiff':
|
||||
args.push('-c:a', 'pcm_s16be');
|
||||
break;
|
||||
case 'alac':
|
||||
// ALAC in M4A container
|
||||
args.push('-c:a', 'alac');
|
||||
break;
|
||||
case 'wma':
|
||||
args.push('-c:a', 'wmav2');
|
||||
args.push('-b:a', `${br}k`);
|
||||
break;
|
||||
case 'opus':
|
||||
args.push('-c:a', 'libopus');
|
||||
args.push('-b:a', `${br}k`);
|
||||
break;
|
||||
case 'm4r':
|
||||
// M4R is just AAC with different extension (iPhone ringtones)
|
||||
args.push('-c:a', 'aac');
|
||||
args.push('-b:a', `${br}k`);
|
||||
break;
|
||||
case 'amr':
|
||||
args.push('-c:a', 'libopencore_amrnb');
|
||||
args.push('-ar', '8000'); // AMR requires 8kHz sample rate
|
||||
args.push('-ac', '1'); // AMR is mono only
|
||||
break;
|
||||
}
|
||||
|
||||
return args;
|
||||
}
|
||||
|
||||
function getExtension(format: AudioFormat): string {
|
||||
// ALAC uses m4a container
|
||||
if (format === 'alac') return 'm4a';
|
||||
return format;
|
||||
}
|
||||
|
||||
@@ -80,11 +117,31 @@ function getMimeType(format: AudioFormat): string {
|
||||
ogg: 'audio/ogg',
|
||||
m4a: 'audio/mp4',
|
||||
flac: 'audio/flac',
|
||||
aac: 'audio/aac',
|
||||
aiff: 'audio/aiff',
|
||||
alac: 'audio/x-m4a',
|
||||
wma: 'audio/x-ms-wma',
|
||||
opus: 'audio/opus',
|
||||
m4r: 'audio/x-m4r',
|
||||
amr: 'audio/amr',
|
||||
};
|
||||
return mimeMap[format];
|
||||
}
|
||||
|
||||
const audioConverter = {
|
||||
/**
|
||||
* Initialize the worker (can be called early to warm up)
|
||||
*/
|
||||
async init(): Promise<boolean> {
|
||||
try {
|
||||
await ensureFFmpegLoaded();
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('[AudioWorker] Init failed:', error);
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Convert audio file using FFmpeg
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user