feat(video): add MIME types for mp4/webm/mov/avi/mkv
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -43,6 +43,11 @@ const OUTPUT_MIME_TYPES = {
|
|||||||
flac: 'audio/flac',
|
flac: 'audio/flac',
|
||||||
aac: 'audio/aac',
|
aac: 'audio/aac',
|
||||||
m4a: 'audio/x-m4a',
|
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) {
|
export async function detectInputMime(filePath) {
|
||||||
|
|||||||
@@ -361,3 +361,90 @@ describe('resolveInputFormat — audio formats', () => {
|
|||||||
await fs.unlink(fixturePath);
|
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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user