atproto user agency toolkit for individuals and groups
at main 85 lines 1.5 kB view raw
1/** 2 * Detect content type from file magic bytes. 3 * Returns the detected MIME type or null if unknown. 4 */ 5export function detectContentType(bytes: Uint8Array): string | null { 6 // MP4/M4V/MOV - check for ftyp box 7 if (bytes.length >= 12) { 8 const ftyp = String.fromCharCode( 9 bytes[4]!, 10 bytes[5]!, 11 bytes[6]!, 12 bytes[7]!, 13 ); 14 if (ftyp === "ftyp") { 15 const brand = String.fromCharCode( 16 bytes[8]!, 17 bytes[9]!, 18 bytes[10]!, 19 bytes[11]!, 20 ); 21 if ( 22 brand === "isom" || 23 brand === "iso2" || 24 brand === "mp41" || 25 brand === "mp42" || 26 brand === "avc1" 27 ) { 28 return "video/mp4"; 29 } 30 if (brand === "M4V " || brand === "M4VH" || brand === "M4VP") { 31 return "video/x-m4v"; 32 } 33 if (brand === "qt ") { 34 return "video/quicktime"; 35 } 36 return "video/mp4"; 37 } 38 } 39 40 // JPEG 41 if (bytes[0] === 0xff && bytes[1] === 0xd8 && bytes[2] === 0xff) { 42 return "image/jpeg"; 43 } 44 45 // PNG 46 if ( 47 bytes[0] === 0x89 && 48 bytes[1] === 0x50 && 49 bytes[2] === 0x4e && 50 bytes[3] === 0x47 51 ) { 52 return "image/png"; 53 } 54 55 // GIF 56 if (bytes[0] === 0x47 && bytes[1] === 0x49 && bytes[2] === 0x46) { 57 return "image/gif"; 58 } 59 60 // WebP 61 if ( 62 bytes[0] === 0x52 && 63 bytes[1] === 0x49 && 64 bytes[2] === 0x46 && 65 bytes[3] === 0x46 && 66 bytes[8] === 0x57 && 67 bytes[9] === 0x45 && 68 bytes[10] === 0x42 && 69 bytes[11] === 0x50 70 ) { 71 return "image/webp"; 72 } 73 74 // WebM 75 if ( 76 bytes[0] === 0x1a && 77 bytes[1] === 0x45 && 78 bytes[2] === 0xdf && 79 bytes[3] === 0xa3 80 ) { 81 return "video/webm"; 82 } 83 84 return null; 85}