feat(audio): add MIME types for mp3/wav/ogg/flac/aac/m4a
This commit is contained in:
@@ -37,6 +37,12 @@ const OUTPUT_MIME_TYPES = {
|
|||||||
'tar.bz2': 'application/x-bzip2',
|
'tar.bz2': 'application/x-bzip2',
|
||||||
'7z': 'application/x-7z-compressed',
|
'7z': 'application/x-7z-compressed',
|
||||||
'tar.7z': 'application/x-7z-compressed',
|
'tar.7z': 'application/x-7z-compressed',
|
||||||
|
mp3: 'audio/mpeg',
|
||||||
|
wav: 'audio/wav',
|
||||||
|
ogg: 'audio/ogg',
|
||||||
|
flac: 'audio/flac',
|
||||||
|
aac: 'audio/aac',
|
||||||
|
m4a: 'audio/x-m4a',
|
||||||
};
|
};
|
||||||
|
|
||||||
export async function detectInputMime(filePath) {
|
export async function detectInputMime(filePath) {
|
||||||
|
|||||||
@@ -277,3 +277,87 @@ describe('resolveInputFormat — compound archive extensions', () => {
|
|||||||
await fs.unlink(fixturePath);
|
await fs.unlink(fixturePath);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('outputMimeType — audio', () => {
|
||||||
|
it('returns the correct MIME type for each audio target format', () => {
|
||||||
|
expect(outputMimeType('mp3')).toBe('audio/mpeg');
|
||||||
|
expect(outputMimeType('wav')).toBe('audio/wav');
|
||||||
|
expect(outputMimeType('ogg')).toBe('audio/ogg');
|
||||||
|
expect(outputMimeType('flac')).toBe('audio/flac');
|
||||||
|
expect(outputMimeType('aac')).toBe('audio/aac');
|
||||||
|
expect(outputMimeType('m4a')).toBe('audio/x-m4a');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('resolveInputFormat — audio formats', () => {
|
||||||
|
it('accepts a WAV file (RIFF....WAVE) declared as wav', async () => {
|
||||||
|
const fixturePath = path.join(import.meta.dirname, 'fixtures', 'sample-audio.wav');
|
||||||
|
await fs.writeFile(fixturePath, Buffer.concat([Buffer.from('RIFF'), Buffer.alloc(4), Buffer.from('WAVE')]));
|
||||||
|
|
||||||
|
const result = await resolveInputFormat(fixturePath, 'wav');
|
||||||
|
expect(result).toEqual({ mime: 'audio/wav', valid: true });
|
||||||
|
|
||||||
|
await fs.unlink(fixturePath);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('accepts a FLAC file (fLaC magic) declared as flac', async () => {
|
||||||
|
const fixturePath = path.join(import.meta.dirname, 'fixtures', 'sample-audio.flac');
|
||||||
|
await fs.writeFile(fixturePath, Buffer.from('fLaC'));
|
||||||
|
|
||||||
|
const result = await resolveInputFormat(fixturePath, 'flac');
|
||||||
|
expect(result).toEqual({ mime: 'audio/flac', valid: true });
|
||||||
|
|
||||||
|
await fs.unlink(fixturePath);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('accepts an MP3 file (MPEG layer 3 frame sync) declared as mp3', async () => {
|
||||||
|
const fixturePath = path.join(import.meta.dirname, 'fixtures', 'sample-audio.mp3');
|
||||||
|
// Frame sync 0xFF 0xFB: byte1 & 0xE0 == 0xE0 (sync), byte1 & 0x06 == 0x02 (layer 3, not ADTS).
|
||||||
|
await fs.writeFile(fixturePath, Buffer.from([0xff, 0xfb, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00]));
|
||||||
|
|
||||||
|
const result = await resolveInputFormat(fixturePath, 'mp3');
|
||||||
|
expect(result).toEqual({ mime: 'audio/mpeg', valid: true });
|
||||||
|
|
||||||
|
await fs.unlink(fixturePath);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('accepts an AAC/ADTS file declared as aac', async () => {
|
||||||
|
const fixturePath = path.join(import.meta.dirname, 'fixtures', 'sample-audio.aac');
|
||||||
|
// Frame sync 0xFF 0xF1: byte1 & 0xE0 == 0xE0 (sync), byte1 & 0x16 == 0x10 (ADTS, not layer 3).
|
||||||
|
await fs.writeFile(fixturePath, Buffer.from([0xff, 0xf1, 0x4c, 0x80, 0x01, 0x3f, 0xfc, 0x00]));
|
||||||
|
|
||||||
|
const result = await resolveInputFormat(fixturePath, 'aac');
|
||||||
|
expect(result).toEqual({ mime: 'audio/aac', valid: true });
|
||||||
|
|
||||||
|
await fs.unlink(fixturePath);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('accepts an OGG/Vorbis file declared as ogg', async () => {
|
||||||
|
const fixturePath = path.join(import.meta.dirname, 'fixtures', 'sample-audio.ogg');
|
||||||
|
// 'OggS' + 24 ignored bytes (file-type's ignore(28) counts from the 'OggS' magic itself,
|
||||||
|
// confirmed empirically against the installed file-type package) + packet-type field '\x01vorbis'.
|
||||||
|
const body = Buffer.concat([
|
||||||
|
Buffer.from('OggS'),
|
||||||
|
Buffer.alloc(24),
|
||||||
|
Buffer.concat([Buffer.from([0x01]), Buffer.from('vorbis'), Buffer.alloc(1)]),
|
||||||
|
]);
|
||||||
|
await fs.writeFile(fixturePath, body);
|
||||||
|
|
||||||
|
const result = await resolveInputFormat(fixturePath, 'ogg');
|
||||||
|
expect(result).toEqual({ mime: 'audio/ogg', valid: true });
|
||||||
|
|
||||||
|
await fs.unlink(fixturePath);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('accepts an M4A file (ISO-BMFF ftyp box, M4A brand) declared as m4a', async () => {
|
||||||
|
const fixturePath = path.join(import.meta.dirname, 'fixtures', 'sample-audio.m4a');
|
||||||
|
// 4-byte box size (unchecked) + 'ftyp' + major brand 'M4A ' (space-padded, 4 bytes).
|
||||||
|
const body = Buffer.concat([Buffer.alloc(4), Buffer.from('ftyp'), Buffer.from('M4A ')]);
|
||||||
|
await fs.writeFile(fixturePath, body);
|
||||||
|
|
||||||
|
const result = await resolveInputFormat(fixturePath, 'm4a');
|
||||||
|
expect(result).toEqual({ mime: 'audio/x-m4a', valid: true });
|
||||||
|
|
||||||
|
await fs.unlink(fixturePath);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user