From 9c1f85739805720ea48b6e7519cded66c5390d96 Mon Sep 17 00:00:00 2001 From: Anthony GAEREMYNCK <1@anthony.sh> Date: Sat, 1 Aug 2026 21:59:06 +0200 Subject: [PATCH] feat(video): add MIME types for mp4/webm/mov/avi/mkv Co-Authored-By: Claude Sonnet 5 --- src/mime.js | 5 +++ test/mime.test.js | 87 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+) diff --git a/src/mime.js b/src/mime.js index d820c97..5e2cc92 100644 --- a/src/mime.js +++ b/src/mime.js @@ -43,6 +43,11 @@ const OUTPUT_MIME_TYPES = { flac: 'audio/flac', aac: 'audio/aac', m4a: 'audio/x-m4a', + mp4: 'video/mp4', + webm: 'video/webm', + mov: 'video/quicktime', + avi: 'video/x-msvideo', + mkv: 'video/x-matroska', }; export async function detectInputMime(filePath) { diff --git a/test/mime.test.js b/test/mime.test.js index cb74fba..eb05983 100644 --- a/test/mime.test.js +++ b/test/mime.test.js @@ -361,3 +361,90 @@ describe('resolveInputFormat — audio formats', () => { await fs.unlink(fixturePath); }); }); + +describe('outputMimeType — video', () => { + it('returns the correct MIME type for each video target format', () => { + expect(outputMimeType('mp4')).toBe('video/mp4'); + expect(outputMimeType('webm')).toBe('video/webm'); + expect(outputMimeType('mov')).toBe('video/quicktime'); + expect(outputMimeType('avi')).toBe('video/x-msvideo'); + expect(outputMimeType('mkv')).toBe('video/x-matroska'); + }); +}); + +describe('resolveInputFormat — video magic bytes', () => { + it('accepts a minimal mp4 (ftyp box) declared as mp4', async () => { + const fixturePath = path.join(import.meta.dirname, 'fixtures', 'sample-video.mp4'); + await fs.writeFile( + fixturePath, + Buffer.concat([Buffer.from([0, 0, 0, 0x18]), Buffer.from('ftyp'), Buffer.from('isom')]) + ); + + const result = await resolveInputFormat(fixturePath, 'mp4'); + expect(result).toEqual({ mime: 'video/mp4', valid: true }); + + await fs.unlink(fixturePath); + }); + + it('accepts a minimal mov (moov atom) declared as mov', async () => { + const fixturePath = path.join(import.meta.dirname, 'fixtures', 'sample-video.mov'); + await fs.writeFile(fixturePath, Buffer.concat([Buffer.from([0, 0, 0, 0x08]), Buffer.from('moov')])); + + const result = await resolveInputFormat(fixturePath, 'mov'); + expect(result).toEqual({ mime: 'video/quicktime', valid: true }); + + await fs.unlink(fixturePath); + }); + + it('accepts a minimal avi (RIFF/AVI) declared as avi', async () => { + const fixturePath = path.join(import.meta.dirname, 'fixtures', 'sample-video.avi'); + await fs.writeFile( + fixturePath, + Buffer.concat([Buffer.from('RIFF'), Buffer.from([0, 0, 0, 0]), Buffer.from('AVI ')]) + ); + + const result = await resolveInputFormat(fixturePath, 'avi'); + expect(result).toEqual({ mime: 'video/vnd.avi', valid: true }); + + await fs.unlink(fixturePath); + }); + + it('accepts a minimal webm (EBML DocType webm) declared as webm', async () => { + const fixturePath = path.join(import.meta.dirname, 'fixtures', 'sample-video.webm'); + await fs.writeFile( + fixturePath, + Buffer.concat([Buffer.from([0x1a, 0x45, 0xdf, 0xa3, 0x81, 0x42, 0x82, 0x84]), Buffer.from('webm')]) + ); + + const result = await resolveInputFormat(fixturePath, 'webm'); + expect(result).toEqual({ mime: 'video/webm', valid: true }); + + await fs.unlink(fixturePath); + }); + + it('accepts a minimal mkv (EBML DocType matroska) declared as mkv', async () => { + const fixturePath = path.join(import.meta.dirname, 'fixtures', 'sample-video.mkv'); + await fs.writeFile( + fixturePath, + Buffer.concat([Buffer.from([0x1a, 0x45, 0xdf, 0xa3, 0x81, 0x42, 0x82, 0x88]), Buffer.from('matroska')]) + ); + + const result = await resolveInputFormat(fixturePath, 'mkv'); + expect(result).toEqual({ mime: 'video/matroska', valid: true }); + + await fs.unlink(fixturePath); + }); + + it('rejects a webm file declared as mkv', async () => { + const fixturePath = path.join(import.meta.dirname, 'fixtures', 'sample-video-mismatch.webm'); + await fs.writeFile( + fixturePath, + Buffer.concat([Buffer.from([0x1a, 0x45, 0xdf, 0xa3, 0x81, 0x42, 0x82, 0x84]), Buffer.from('webm')]) + ); + + const result = await resolveInputFormat(fixturePath, 'mkv'); + expect(result.valid).toBe(false); + + await fs.unlink(fixturePath); + }); +});