audio-metadata
Version:
Extract metadata from audio files
124 lines (107 loc) • 2.69 kB
JavaScript
var utils = require('./utils');
function checkMagicId3(view, offset) {
var id3Magic = utils.readBytes(view, offset, 3);
//"ID3"
return id3Magic[0] === 73 && id3Magic[1] === 68 && id3Magic[2] === 51;
}
function getUint28(view, offset) {
var sizeBytes = utils.readBytes(view, offset, 4);
var mask = 0xfffffff;
return ((sizeBytes[0] & mask) << 21) |
((sizeBytes[1] & mask) << 14) |
((sizeBytes[2] & mask) << 7) |
(sizeBytes[3] & mask);
}
//http://id3.org/id3v2.3.0
//http://id3.org/id3v2.4.0-structure
//http://id3.org/id3v2.4.0-frames
module.exports = function(buffer) {
var view = utils.createView(buffer);
if (!checkMagicId3(view, 0)) {
return null;
}
var offset = 3;
//var majorVersion = view.getUint8(offset);
offset += 2;
var flags = view.getUint8(offset);
offset++;
var size = getUint28(view, offset);
offset += 4;
var extendedHeader = (flags & 128) > 0;
if (extendedHeader) {
offset += getUint28(view, offset);
}
function readFrame(offset) {
try {
var id = utils.readAscii(view, offset, 4);
var size = getUint28(view, offset + 4);
offset += 10; //+2 more for flags we don't care about
if (id[0] !== 'T') {
return {
id: id,
size: size + 10
};
}
var encoding = view.getUint8(offset),
data = '';
if (encoding <= 3) {
offset++;
if (encoding === 3) {
//UTF8 - null terminated
data = utils.readUtf8(view, offset, size - 1);
} else {
//ISO-8859-1, UTF-16, UTF-16BE
//UTF-16 and UTF-16BE are $FF $00 terminated
//ISO is null terminated
//screw these encodings, read it as ascii
data = utils.readAscii(view, offset, size - 1);
}
} else {
//no encoding info, read it as ascii
data = utils.readAscii(view, offset, size);
}
//id3v2.4 is supposed to have encoding terminations, but sometimes
//they don't? meh.
data = utils.trimNull(data);
return {
id: id,
size: size + 10,
content: data
};
} catch (e) {
return null;
}
}
var idMap = {
TALB: 'album',
TCOM: 'composer',
TIT1: 'title',
TIT2: 'title',
TPE1: 'artist',
TRCK: 'track',
TSSE: 'encoder',
TDRC: 'year',
TCON: 'genre'
};
var endOfTags = offset + size,
frames = {};
while (offset < endOfTags) {
var frame = readFrame(offset);
if (!frame) {
break;
}
offset += frame.size;
if (!frame.content) {
continue;
}
var id = idMap[frame.id] || frame.id;
if (id === 'TXXX') {
var nullByte = frame.content.indexOf('\u0000');
id = frame.content.substring(0, nullByte);
frames[id] = frame.content.substring(nullByte + 1);
} else {
frames[id] = frames[frame.id] = frame.content;
}
}
return frames;
};