| 1 | export async function getId3Tags(url: string) { |
| 2 | const buf = await fetch(url, { headers: { Range: 'bytes=0-2047' } }).then(x => x.arrayBuffer()) |
| 3 | const dv = new DataView(buf) |
| 4 | if (dv.getUint32(0) !== 0x49443303) return // ID3 identifier |
| 5 | const tags: Record<string, string> = {} |
| 6 | let index = 10 |
| 7 | while (index < buf.byteLength) { |
| 8 | const frameId = String.fromCharCode(dv.getUint8(index++), dv.getUint8(index++), dv.getUint8(index++), dv.getUint8(index++)) |
| 9 | if (frameId === '\0\0\0\0') break |
| 10 | const frameSize = dv.getUint32(index) |
| 11 | index += 6 // skip size + flags |
| 12 | const enc = !dv.getUint8(index++) ? 'ISO-8859-1' // 1 is for unicode |
| 13 | : { 239: 'utf-8', 255: 'utf-16le', 254: 'utf-16be' }[dv.getUint8(index)] // decode bom |
| 14 | tags[frameId] = new TextDecoder(enc).decode(buf.slice(index, index += frameSize - 1)) |
| 15 | } |
| 16 | for (const [k, v] of Object.entries({ TALB: 'album', TIT2: 'title', TPE1: 'artist', TYER: 'year', TRCK: 'track' })) // easier access to main fields |
| 17 | tags[v] = tags[k] |
| 18 | return tags |
| 19 | } |