diff --git a/index.html b/index.html index 91309b0..6883790 100644 --- a/index.html +++ b/index.html @@ -724,7 +724,62 @@ async function readId3v1(file) { return tags; } catch { return null; } } +async function readVorbisComments(file) { + try { + const maxRead = Math.min(file.size, 256 * 1024); + const data = await readBytes(file, 0, maxRead); + if (data[0] !== 0x66 || data[1] !== 0x4C || data[2] !== 0x61 || data[3] !== 0x43) return null; + let pos = 4; + let commentData = null; + while (pos + 4 <= data.length) { + const isLast = data[pos] & 0x80; + const blockType = data[pos] & 0x7f; + const blockLen = (data[pos + 1] << 16) | (data[pos + 2] << 8) | data[pos + 3]; + pos += 4; + if (pos + blockLen > data.length) break; + if (blockType === 4) { commentData = data.slice(pos, pos + blockLen); break; } + pos += blockLen; + if (isLast) break; + } + if (!commentData) return null; + let off = 0; + if (off + 4 > commentData.length) return null; + const vendorLen = commentData[off] | (commentData[off + 1] << 8) | (commentData[off + 2] << 16) | (commentData[off + 3] << 24); + off += 4 + vendorLen; + if (off + 4 > commentData.length) return null; + const numTags = commentData[off] | (commentData[off + 1] << 8) | (commentData[off + 2] << 16) | (commentData[off + 3] << 24); + off += 4; + const tags = {}; + for (let i = 0; i < numTags; i++) { + if (off + 4 > commentData.length) break; + const tagLen = commentData[off] | (commentData[off + 1] << 8) | (commentData[off + 2] << 16) | (commentData[off + 3] << 24); + off += 4; + if (off + tagLen > commentData.length) break; + const tagStr = new TextDecoder('utf-8').decode(commentData.slice(off, off + tagLen)); + off += tagLen; + const eqIdx = tagStr.indexOf('='); + if (eqIdx === -1) continue; + const key = tagStr.substring(0, eqIdx).toUpperCase(); + const value = tagStr.substring(eqIdx + 1); + switch (key) { + case 'TITLE': tags.title = value; break; + case 'ARTIST': tags.artist = value; break; + case 'ALBUM': tags.album = value; break; + case 'GENRE': tags.genre = value; break; + case 'DATE': + case 'YEAR': tags.year = value; break; + case 'TRACKNUMBER': tags.track = value; break; + } + } + return Object.keys(tags).length > 0 ? tags : null; + } catch { return null; } +} async function readTags(file) { + const ext = file.name.toLowerCase().slice(file.name.lastIndexOf('.')); + if (ext === '.flac') { + const v = await readVorbisComments(file); + if (v) return v; + } let tags = await readId3v2(file); if (tags && (tags.title || tags.artist)) return tags; const v1 = await readId3v1(file);