@@ -26,6 +26,12 @@ async function makeOoxmlZip(opts: { mainMime: string; partPath: string }): Promi
2626 return await zip . generateAsync ( { type : "nodebuffer" } ) ;
2727}
2828
29+ // file-type classifies this generic ISO-BMFF brand as video/mp4 without track metadata.
30+ const ISOM_BRAND_BUFFER = Buffer . from (
31+ "0000001c6674797069736f6d0000000069736f6d0000000000000000" ,
32+ "hex" ,
33+ ) ;
34+
2935describe ( "mime detection" , ( ) => {
3036 async function expectDetectedMime ( params : {
3137 input : Parameters < typeof detectMime > [ 0 ] ;
@@ -221,37 +227,51 @@ describe("mime detection", () => {
221227 } ) ;
222228 } ) ;
223229
224- it ( "preserves audio metadata when an MP4 extension cannot identify track kind" , async ( ) => {
225- await expectDetectedMime ( {
226- input : { filePath : "voice.mp4" , headerMime : "audio/mp4" } ,
230+ it . each ( [
231+ {
232+ name : "audio/mp4 header" ,
233+ filePath : "voice.mp4" ,
234+ headerMime : "audio/mp4" ,
227235 expected : "audio/mp4" ,
228- } ) ;
229- } ) ;
230-
231- it ( "preserves audio/x-m4a header for isom-brand ISO-BMFF bytes (Android/ffmpeg m4a)" , async ( ) => {
232- // Non-Apple m4a files use "isom" as the ftyp major brand;
233- // file-type sniffs those as video/mp4 regardless of audio content.
234- const isomBrand = Buffer . from (
235- // 28-byte ftyp box: size=28, "ftyp", major="isom", minor=0, compat=["isom"]
236- "0000001c6674797069736f6d0000000069736f6d0000000000000000" ,
237- "hex" ,
238- ) ;
239-
240- await expectDetectedMime ( {
241- input : { buffer : isomBrand , filePath : "voice.m4a" , headerMime : "audio/x-m4a" } ,
236+ } ,
237+ {
238+ name : "audio/x-m4a header" ,
239+ filePath : "voice.m4a" ,
240+ headerMime : "audio/x-m4a" ,
242241 expected : "audio/x-m4a" ,
243- } ) ;
244- } ) ;
245-
246- it ( "uses the file extension to rescue isom-brand bytes when no header exists" , async ( ) => {
247- const isomBrand = Buffer . from (
248- "0000001c6674797069736f6d0000000069736f6d0000000000000000 ",
249- "hex" ,
250- ) ;
251-
252- await expectDetectedMime ( {
253- input : { buffer : isomBrand , filePath : "voice.m4a" } ,
242+ } ,
243+ {
244+ name : "audio/m4a header" ,
245+ filePath : "voice.m4a" ,
246+ headerMime : "audio/m4a" ,
247+ expected : "audio/m4a ",
248+ } ,
249+ {
250+ name : "m4a extension" ,
251+ filePath : "voice.m4a" ,
252+ headerMime : undefined ,
254253 expected : "audio/x-m4a" ,
254+ } ,
255+ {
256+ name : "mp4 extension without an audio hint" ,
257+ filePath : "clip.mp4" ,
258+ headerMime : undefined ,
259+ expected : "video/mp4" ,
260+ } ,
261+ {
262+ name : "audio/aac elementary-stream metadata" ,
263+ filePath : "voice.aac" ,
264+ headerMime : "audio/aac" ,
265+ expected : "video/mp4" ,
266+ } ,
267+ ] as const ) ( "resolves ambiguous isom-brand bytes from $name" , async ( testCase ) => {
268+ await expectDetectedMime ( {
269+ input : {
270+ buffer : ISOM_BRAND_BUFFER ,
271+ filePath : testCase . filePath ,
272+ headerMime : testCase . headerMime ,
273+ } ,
274+ expected : testCase . expected ,
255275 } ) ;
256276 } ) ;
257277
@@ -408,6 +428,7 @@ describe("extensionForMime", () => {
408428 { mime : "audio/x-wav" , expected : ".wav" } ,
409429 { mime : "audio/webm" , expected : ".webm" } ,
410430 { mime : "audio/x-m4a" , expected : ".m4a" } ,
431+ { mime : "audio/m4a" , expected : ".m4a" } ,
411432 { mime : "audio/mp4" , expected : ".m4a" } ,
412433 { mime : "video/x-msvideo" , expected : ".avi" } ,
413434 { mime : "video/mp4" , expected : ".mp4" } ,
0 commit comments