musicmetadata
Advanced tools
Comparing version 0.1.1 to 0.1.2
@@ -0,1 +1,38 @@ | ||
var strtok = require('strtok'); | ||
exports.joinBuffers = function(buffers, totalLength) { | ||
var result = new Buffer(totalLength); | ||
var pos = 0 | ||
for (var i=0; i < buffers.length; i++) { | ||
buffers[i].copy(result, pos); | ||
pos += buffers[i].length; | ||
} | ||
return result; | ||
} | ||
exports.readVorbisPicture = function(buffer) { | ||
var picture = {}, | ||
offset = 0; | ||
picture.type = PICTURE_TYPE[strtok.UINT32_BE.get(buffer, 0)]; | ||
var mimeLen = strtok.UINT32_BE.get(buffer, offset += 4); | ||
picture.format = buffer.toString('utf-8', offset += 4, offset + mimeLen); | ||
var descLen = strtok.UINT32_BE.get(buffer, offset += mimeLen); | ||
picture.description = buffer.toString('utf-8', offset += 4, offset + descLen); | ||
picture.width = strtok.UINT32_BE.get(buffer, offset += descLen); | ||
picture.height = strtok.UINT32_BE.get(buffer, offset += 4); | ||
picture.colour_depth = strtok.UINT32_BE.get(buffer, offset += 4); | ||
picture.indexed_color = strtok.UINT32_BE.get(buffer, offset += 4); | ||
var picDataLen = strtok.UINT32_BE.get(buffer, offset += 4); | ||
picture.data = buffer.slice(offset += 4, offset + picDataLen); | ||
return picture; | ||
} | ||
exports.removeUnsyncBytes = function(bytes) { | ||
@@ -25,7 +62,7 @@ var output = []; | ||
if (encoding === 'utf16') { | ||
while(buffer[i] !== 0 || buffer[i+1] !== 0) { | ||
while (buffer[i] !== 0 || buffer[i+1] !== 0) { | ||
body(); | ||
} | ||
} else { | ||
while(buffer[i] !== 0) { | ||
while (buffer[i] !== 0) { | ||
body(); | ||
@@ -84,3 +121,3 @@ } | ||
exports.PICTURE_TYPE = [ | ||
var PICTURE_TYPE = exports.PICTURE_TYPE = [ | ||
"Other", | ||
@@ -87,0 +124,0 @@ "32x32 pixels 'file icon' (PNG only)", |
@@ -11,37 +11,46 @@ var common = require('./common'); | ||
Id3v1.prototype.parse = function() { | ||
var self = this; | ||
var self = this, | ||
bufs = [], | ||
dataLen = 0; | ||
self.stream.on('data', function(data) { | ||
self.stream.on('end', function() { | ||
if (self.stream.bufferSize >= 128) { | ||
var offset = data.length - 128; | ||
var header = data.toString('utf8', offset, offset += 3); | ||
this.stream.on('data', function(data) { | ||
bufs.push(data); | ||
dataLen += data.length; | ||
}); | ||
this.stream.on('close', function(data) { | ||
try { | ||
var data = common.joinBuffers(bufs, dataLen); | ||
var offset = data.length - 128; | ||
var header = data.toString('utf8', offset, offset += 3); | ||
if (header === 'TAG') { | ||
var title = data.toString('ascii', offset, offset += 30); | ||
self.emit('title', title.trim().replace(/\x00/g,'')); | ||
if (header === 'TAG') { | ||
var title = data.toString('ascii', offset, offset += 30); | ||
self.emit('title', title.trim().replace(/\x00/g,'')); | ||
var artist = data.toString('ascii', offset, offset += 30); | ||
self.emit('artist', artist.trim().replace(/\x00/g,'')); | ||
var artist = data.toString('ascii', offset, offset += 30); | ||
self.emit('artist', artist.trim().replace(/\x00/g,'')); | ||
var album = data.toString('ascii', offset, offset += 30); | ||
self.emit('album', album.trim().replace(/\x00/g,'')); | ||
var album = data.toString('ascii', offset, offset += 30); | ||
self.emit('album', album.trim().replace(/\x00/g,'')); | ||
var year = data.toString('ascii', offset, offset += 4); | ||
self.emit('year', year.trim().replace(/\x00/g,'')); | ||
var comment = data.toString('ascii', offset, offset += 28); | ||
self.emit('comment', comment.trim().replace(/\x00/g,'')); | ||
var track = data[data.length - 2]; | ||
self.emit('track', track); | ||
var genre = common.GENRES[data[data.length - 1]]; | ||
self.emit('genre', genre); | ||
self.emit('done'); | ||
} | ||
var year = data.toString('ascii', offset, offset += 4); | ||
self.emit('year', year.trim().replace(/\x00/g,'')); | ||
var comment = data.toString('ascii', offset, offset += 28); | ||
self.emit('comment', comment.trim().replace(/\x00/g,'')); | ||
var track = data[data.length - 2]; | ||
self.emit('track', track); | ||
var genre = common.GENRES[data[data.length - 1]]; | ||
self.emit('genre', genre); | ||
self.emit('done'); | ||
} | ||
}); | ||
}); | ||
} catch (exception) { | ||
self.emit('done', exception); | ||
} | ||
}); | ||
} |
211
lib/id3v2.js
@@ -17,104 +17,103 @@ var strtok = require('strtok'), | ||
try { | ||
if (!v) { | ||
cb.position = 'header'; | ||
return new strtok.BufferType(10); | ||
if (!v) { | ||
cb.position = 'header'; | ||
return new strtok.BufferType(10); | ||
} | ||
if (cb.position === 'header') { | ||
if (v.toString('ascii', 0, 3) !== 'ID3') { | ||
throw new Error('expected id3 header but was not found'); | ||
} | ||
if (cb.position === 'header') { | ||
if (v.toString('ascii',0, 3) !== 'ID3') { | ||
throw new Error('expected id3 header but was not found'); | ||
} | ||
cb.header = { | ||
version: '2.' + v[3] + '.' + v[4], | ||
major: v[3], | ||
unsync: strtok.BITSET.get(v, 5, 7), | ||
xheader: strtok.BITSET.get(v, 5, 6), | ||
xindicator: strtok.BITSET.get(v, 5, 5), | ||
footer: strtok.BITSET.get(v, 5, 4), | ||
size: strtok.INT32SYNCSAFE.get(v, 6) | ||
}; | ||
if (cb.header.xheader) { | ||
cb.position = 'xheader'; | ||
return strtok.UINT32_BE; | ||
} | ||
//expect the first frames header next | ||
cb.position = 'frameheader'; | ||
switch (cb.header.major) { | ||
case 2 : | ||
return new strtok.BufferType(6); | ||
case 3 : case 4 : | ||
return new strtok.BufferType(10); | ||
default : | ||
throw new Error('header version is incorrect'); | ||
} | ||
cb.header = { | ||
version: '2.' + v[3] + '.' + v[4], | ||
major: v[3], | ||
unsync: strtok.BITSET.get(v, 5, 7), | ||
xheader: strtok.BITSET.get(v, 5, 6), | ||
xindicator: strtok.BITSET.get(v, 5, 5), | ||
footer: strtok.BITSET.get(v, 5, 4), | ||
size: strtok.INT32SYNCSAFE.get(v, 6) | ||
}; | ||
if (cb.header.xheader) { | ||
cb.position = 'xheader'; | ||
return strtok.UINT32_BE; | ||
} | ||
if (cb.position === 'xheader') { | ||
cb.position = 'frameheader'; | ||
//TODO: this will not work because we do not detect raw objects | ||
//our code will fail on mp3's with xheaders | ||
return new strtok.BufferType(v); //skip xheader | ||
//expect the first frames header next | ||
cb.position = 'frameheader'; | ||
switch (cb.header.major) { | ||
case 2 : | ||
return new strtok.BufferType(6); | ||
case 3 : case 4 : | ||
return new strtok.BufferType(10); | ||
default : | ||
throw new Error('header version is incorrect'); | ||
} | ||
if (cb.position === 'frameheader') { | ||
cb.position = 'framedata'; | ||
var header = cb.frameHeader = {}; | ||
switch (cb.header.major) { | ||
case 2 : | ||
header.id = v.toString('ascii', 0, 3); | ||
header.length = strtok.UINT24_BE.get(v, 3, 6); | ||
break; | ||
case 3 : | ||
header.id = v.toString('ascii', 0, 4); | ||
header.length = strtok.UINT32_BE.get(v, 4, 8); | ||
header.flags = readFrameFlags(v.slice(8, 10)); | ||
break; | ||
case 4 : | ||
header.id = v.toString('ascii', 0, 4); | ||
header.length = strtok.INT32SYNCSAFE.get(v, 4, 8); | ||
header.flags = readFrameFlags(v.slice(8, 10)); | ||
break; | ||
} | ||
// Last frame. Check first char is a letter, bit of defensive programming | ||
if (header.id === '' || header.id === '\u0000\u0000\u0000\u0000' | ||
|| 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.search(header.id[0]) === -1 ) { | ||
self.emit('done'); | ||
return strtok.DONE; | ||
} | ||
return new strtok.BufferType(header.length); | ||
} | ||
if (cb.position === 'xheader') { | ||
cb.position = 'frameheader'; | ||
//TODO: this will not work because we do not detect raw objects | ||
//our code will fail on mp3's with xheaders | ||
return new strtok.BufferType(v); //skip xheader | ||
} | ||
if (cb.position === 'frameheader') { | ||
cb.position = 'framedata'; | ||
var header = cb.frameHeader = {}; | ||
switch (cb.header.major) { | ||
case 2 : | ||
header.id = v.toString('ascii', 0, 3); | ||
header.length = strtok.UINT24_BE.get(v, 3, 6); | ||
break; | ||
case 3 : | ||
header.id = v.toString('ascii', 0, 4); | ||
header.length = strtok.UINT32_BE.get(v, 4, 8); | ||
header.flags = readFrameFlags(v.slice(8, 10)); | ||
break; | ||
case 4 : | ||
header.id = v.toString('ascii', 0, 4); | ||
header.length = strtok.INT32SYNCSAFE.get(v, 4, 8); | ||
header.flags = readFrameFlags(v.slice(8, 10)); | ||
break; | ||
} | ||
// Last frame. Check first char is a letter, bit of defensive programming | ||
if (header.id === '' || header.id === '\u0000\u0000\u0000\u0000' | ||
|| 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.search(header.id[0]) === -1 ) { | ||
self.emit('done'); | ||
return strtok.DONE; | ||
} | ||
return new strtok.BufferType(header.length); | ||
} | ||
if (cb.position === 'framedata') { | ||
cb.position = 'frameheader'; | ||
if (cb.position === 'framedata') { | ||
cb.position = 'frameheader'; | ||
var frame, encoding; | ||
switch (cb.header.major) { | ||
case 2 : | ||
frame = parser.readData(v, cb.frameHeader.id , null, cb.header.major); | ||
self.emit(cb.frameHeader.id, frame); | ||
return new strtok.BufferType(6); | ||
case 3 : case 4: | ||
if (cb.frameHeader.flags.format.unsync) { | ||
v = common.removeUnsyncBytes(v); | ||
} | ||
var frame, encoding; | ||
switch (cb.header.major) { | ||
case 2 : | ||
frame = parser.readData(v, cb.frameHeader.id , null, cb.header.major); | ||
self.emit(cb.frameHeader.id, frame); | ||
return new strtok.BufferType(6); | ||
case 3 : case 4: | ||
if (cb.frameHeader.flags.format.unsync) { | ||
v = common.removeUnsyncBytes(v); | ||
} | ||
if (cb.frameHeader.flags.format.data_length_indicator) { | ||
v = v.slice(4, v.length); //TODO: do we need to do something with this? | ||
} | ||
frame = parser.readData(v, cb.frameHeader.id, cb.frameHeader.flags, cb.header.major); | ||
self.emit(cb.frameHeader.id, frame); | ||
return new strtok.BufferType(10); | ||
} | ||
if (cb.frameHeader.flags.format.data_length_indicator) { | ||
v = v.slice(4, v.length); //TODO: do we need to do something with this? | ||
} | ||
frame = parser.readData(v, cb.frameHeader.id, cb.frameHeader.flags, cb.header.major); | ||
self.emit(cb.frameHeader.id, frame); | ||
return new strtok.BufferType(10); | ||
} | ||
} catch(exception) { | ||
self.emit('error', exception); | ||
self.emit('done'); | ||
} | ||
} catch (exception) { | ||
self.emit('done', exception); | ||
return strtok.DONE; | ||
@@ -140,26 +139,2 @@ } | ||
}; | ||
}; | ||
strtok.UINT24_BE = { | ||
len : 3, | ||
get : function(buf, off) { | ||
return (((buf[off] << 8) + buf[off + 1]) << 8) + buf[off + 2]; | ||
} | ||
}; | ||
strtok.BITSET = { | ||
len : 1, | ||
get : function(buf, off, bit) { | ||
return (buf[off] & (1 << bit)) !== 0; | ||
} | ||
}; | ||
strtok.INT32SYNCSAFE = { | ||
len : 4, | ||
get : function (buf, off) { | ||
return buf[off + 3] & 0x7f | | ||
((buf[off + 2]) << 7) | | ||
((buf[off + 1]) << 14) | | ||
((buf[off]) << 21); | ||
} | ||
}; |
@@ -25,3 +25,3 @@ var strtok = require('strtok'), | ||
if(cb.position === 'skip') { | ||
if (cb.position === 'skip') { | ||
cb.position = 'atomlength'; | ||
@@ -31,3 +31,3 @@ return strtok.UINT32_BE; | ||
if(cb.position === 'atomlength') { | ||
if (cb.position === 'atomlength') { | ||
cb.position = 'atomname'; | ||
@@ -40,3 +40,3 @@ cb.atomLength = v; | ||
cb.atomName = v; | ||
//meta has 4 bytes padding at the start (skip) | ||
@@ -68,5 +68,11 @@ if (v === 'meta') { | ||
if (cb.position === 'ilstatom') { | ||
var result = processMetaAtom(v, cb.atomName); | ||
cb.metaAtomsTotalLength += cb.atomLength; | ||
self.emit(cb.atomName, result); | ||
var result = processMetaAtom(v, cb.atomName, cb.atomLength -8); | ||
if (result.length > 0) { | ||
for (var i=0; i < result.length; i++) { | ||
self.emit(cb.atomName, result[i]); | ||
} | ||
} | ||
cb.position = 'atomlength'; | ||
@@ -77,7 +83,6 @@ return strtok.UINT32_BE; | ||
//if we ever get this this point something bad has happened | ||
throw new Exception('error parsing'); | ||
throw new Error('error parsing'); | ||
} catch(exception) { | ||
self.emit('error', exception); | ||
self.emit('done'); | ||
} catch (exception) { | ||
self.emit('done', exception); | ||
return strtok.DONE; | ||
@@ -88,28 +93,43 @@ } | ||
function processMetaAtom(data, atomName) { | ||
var length = strtok.UINT32_BE.get(data, 0); | ||
var type = TYPES[strtok.UINT32_BE.get(data, 8)]; | ||
function processMetaAtom(data, atomName, atomLength) { | ||
var result = []; | ||
var offset = 0; | ||
switch (type) { | ||
case 'text' : | ||
return data.toString('utf8', 16, length); | ||
case 'uint8' : | ||
if (atomName === 'gnre') { | ||
var genreInt = strtok.UINT16_BE.get(data, 16); | ||
return common.GENRES[genreInt -1]; | ||
} | ||
//ignore proprietary iTunes atoms (for now) | ||
if (atomName == '----') return result; | ||
if (atomName === 'trkn' || atomName == 'disk') { | ||
return data[19] + '/' + data [21]; // 1/1 | ||
while (offset < atomLength) { | ||
var length = strtok.UINT32_BE.get(data, offset); | ||
var type = TYPES[strtok.UINT32_BE.get(data, offset + 8)]; | ||
var content = (function processMetaDataAtom(data, type, atomName) { | ||
switch (type) { | ||
case 'text' : | ||
return data.toString('utf8', 4); | ||
case 'uint8' : | ||
if (atomName === 'gnre') { | ||
var genreInt = strtok.UINT16_BE.get(data, 4); | ||
return common.GENRES[genreInt -1]; | ||
} | ||
if (atomName === 'trkn' || atomName == 'disk') { | ||
return data[7] + '/' + data [9]; | ||
} | ||
return strtok.UINT16_BE.get(data, 4); | ||
case 'jpeg' : case 'png' : | ||
return { | ||
format : 'image/' + type, | ||
data : data.slice(4) | ||
}; | ||
} | ||
})(data.slice(offset + 12, offset + length), type, atomName); | ||
return strtok.UINT16_BE.get(data, 16); | ||
case 'jpeg' : case 'png' : | ||
return { | ||
format : 'image/' + type, | ||
data : data.slice(16, length) | ||
}; | ||
result.push(content); | ||
offset += length; | ||
} | ||
}; | ||
return result; | ||
} | ||
@@ -116,0 +136,0 @@ var TYPES = { |
@@ -1,2 +0,3 @@ | ||
var common = require('./common'); | ||
var common = require('./common'), | ||
strtok = require('strtok'); | ||
@@ -12,7 +13,7 @@ var MusicMetadata = module.exports = function(stream) { | ||
var self = this; | ||
this.stream.once('data', function(result) { | ||
//default to id3v1.1 if we cannot detect any other tags | ||
var version = 'id3v1'; | ||
var version = 'id3v1'; | ||
if ('ID3' === result.toString('binary', 0, 3)) { | ||
@@ -23,5 +24,9 @@ version = 'id3v2'; | ||
} else if ('OggS' === result.toString('binary', 0, 4)) { | ||
version = 'vorbis'; | ||
version = 'ogg'; | ||
} else if ('fLaC' === result.toString('binary', 0, 4)) { | ||
version = 'flac'; | ||
} else if ('MAC' === result.toString('binary', 0, 3)) { | ||
version = 'mac'; // monkeysaudio | ||
} | ||
var module = require('./' + version); | ||
@@ -32,4 +37,4 @@ var parser = new module(self.stream); | ||
//re-emitting the same data event so the correct id3 processor picks up the stream from the start | ||
//is it possible that the id3 processor could pick up the NEXT event before the first one is re-emitted? | ||
//re-emitting the same event so the processor picks the | ||
//stream up from the start | ||
self.stream.emit('data', result); | ||
@@ -41,12 +46,33 @@ }); | ||
var metadata = { title : '', artist : [], albumartist : [], album : '', | ||
year : 0, track : [0, 0], disk : [0, 1], genre : [], | ||
picture : {} }; | ||
year : 0, track : { no : 0, of : 0 }, genre : [], | ||
disk : { no : 0, of : 0 }, picture : {} }; | ||
var aliased = {}; | ||
function emit() { | ||
var event = arguments[0]; | ||
var value = arguments[1]; | ||
if (event === 'done') { | ||
if (event === 'done') { | ||
for (var alias in aliased) { | ||
if (aliased.hasOwnProperty(alias)) { | ||
var val; | ||
if (alias === 'title' || alias === 'album' || alias === 'year') { | ||
val = aliased[alias][0]; | ||
} else { | ||
val = aliased[alias]; | ||
} | ||
self.emit(alias, val); | ||
if (metadata.hasOwnProperty(alias)) { | ||
metadata[alias] = val; | ||
} | ||
} | ||
} | ||
self.emit('metadata', metadata); | ||
self.emit('done'); | ||
self.emit('done', value); | ||
return; | ||
@@ -64,18 +90,25 @@ } | ||
//emit original event & value | ||
if(event !== alias) { | ||
if (event !== alias) { | ||
self.emit(event, value); | ||
} | ||
//we need to do something special for these events | ||
//TODO: parseInt will return NaN for strings | ||
if (event === 'TRACKTOTAL' || event === 'DISCTOTAL') { | ||
var evt; | ||
if (event === 'TRACKTOTAL') evt = 'track'; | ||
if (event === 'DISCTOTAL') evt = 'disk'; | ||
var cleaned = parseInt(value) | ||
if (!aliased.hasOwnProperty(evt)) { | ||
aliased[evt] = { no : 0, of : cleaned }; | ||
} else { | ||
aliased[evt][1] = cleaned; | ||
} | ||
} | ||
//if the event has been aliased then we need to clean it before | ||
//it is emitted to the user. e.g. genre (20) -> Electronic | ||
if (alias) { | ||
if (alias === 'genre') value = common.parseGenre(value); | ||
self.emit(alias, value); | ||
} | ||
if (metadata.hasOwnProperty(alias)) { | ||
//cleanup the values that are part of the standard metadata | ||
var cleaned = value; | ||
if (alias === 'year') cleaned = cleanupYear(value); | ||
if (alias === 'disk') cleaned = cleanupTrack(value); | ||
if (alias === 'genre') cleaned = common.parseGenre(value); | ||
if (alias === 'picture') cleaned = cleanupPicture(value); | ||
@@ -85,31 +118,26 @@ | ||
cleaned = cleanupTrack(value); | ||
metadata[alias][0] = cleaned[0]; | ||
if (cleaned[1]) { | ||
metadata[alias][1] = cleaned[1]; | ||
if (aliased[alias]) { | ||
aliased[alias].no = cleaned.no; | ||
return; | ||
} else { | ||
aliased[alias] = cleaned; | ||
return; | ||
} | ||
return; | ||
} | ||
//populate the metadata object | ||
if (metadata[alias].constructor === Array) { | ||
if (cleaned.constructor === String) { | ||
cleaned = cleaned.split('/'); | ||
} | ||
if (cleaned.constructor === Array) { | ||
for (var i=0; i < cleaned.length; i++) { | ||
metadata[alias].push(cleaned[i]); | ||
} | ||
} else { | ||
metadata[alias].push(cleaned); | ||
} | ||
if (cleaned.constructor === String) { | ||
cleaned = cleaned.split('/'); | ||
if (cleaned.length === 1) cleaned = cleaned[0]; | ||
} | ||
if (!aliased.hasOwnProperty(alias)) { | ||
aliased[alias] = []; | ||
} | ||
if (cleaned.constructor === Array) { | ||
aliased[alias] = cleaned; | ||
} else { | ||
metadata[alias] = cleaned; | ||
aliased[alias].push(cleaned); | ||
} | ||
} | ||
//we need to do something special for these events | ||
//TODO: parseInt will return NaN for strings | ||
if (event === 'TRACKTOTAL') metadata['track'][1] = parseInt(value); | ||
if (event === 'DISCTOTAL') metadata['disk'][1] = parseInt(value); | ||
}; | ||
@@ -125,36 +153,60 @@ | ||
//TODO: a string of 1of1 would fail to be converted | ||
//converts 1/1 to [1,1] | ||
//or 1 to [1,0] | ||
//converts 1/10 to no : 1, of : 10 | ||
//or 1 to no : 1, of : 0 | ||
function cleanupTrack(origVal) { | ||
var split = origVal.toString().split('/'); | ||
for (var i=0; i< split.length; i++) { | ||
split[i] = parseInt(split[i]); | ||
} | ||
return split; | ||
var number = parseInt(split[0]) || 0; | ||
var total = parseInt(split[1]) || 0; | ||
return { no : number, of : total } | ||
} | ||
function cleanupYear(origVal) { | ||
return parseInt(origVal); | ||
function cleanupPicture(picture) { | ||
var newFormat; | ||
if (picture.format) { | ||
var split = picture.format.toLowerCase().split('/'); | ||
newFormat = (split.length > 1) ? split[1] : split[0]; | ||
if (newFormat === 'jpeg') newFormat = 'jpg'; | ||
} else { | ||
newFormat = 'jpg'; | ||
} | ||
return { format : newFormat, data : picture.data }; | ||
} | ||
function cleanupPicture(origPicture) { | ||
var split = origPicture.format.toLowerCase().split('/'); | ||
var newFormat = (split.length > 1) ? split[1] : split[0]; | ||
if (newFormat === 'jpeg') newFormat = 'jpg'; | ||
return { format : newFormat, data : origPicture.data }; | ||
} | ||
//mappings for common metadata types(id3v2.3, id3v2.2, id4, vorbis) | ||
//mappings for common metadata types(id3v2.3, id3v2.2, id4, vorbis, APEv2) | ||
var MAPPINGS = [ | ||
['title', 'TIT2', 'TT2', '©nam', 'TITLE'], | ||
['artist', 'TPE1', 'TP1', '©ART', 'ARTIST'], | ||
['albumartist', 'TPE2', 'TP2', 'aART', 'ALBUMARTIST'], | ||
['album', 'TALB', 'TAL', '©alb', 'ALBUM'], | ||
['year', 'TDRC', 'TYER', 'TYE', '©day', 'DATE'], | ||
['title', 'TIT2', 'TT2', '©nam', 'TITLE', 'Title'], | ||
['artist', 'TPE1', 'TP1', '©ART', 'ARTIST', 'Artist'], | ||
['albumartist', 'TPE2', 'TP2', 'aART', 'ALBUMARTIST', 'ENSEMBLE', 'Ensemble'], | ||
['album', 'TALB', 'TAL', '©alb', 'ALBUM', 'Album'], | ||
['year', 'TDRC', 'TYER', 'TYE', '©day', 'DATE', 'Year'], | ||
['comment', 'COMM', 'COM', '©cmt', 'COMMENT'], | ||
['track', 'TRCK', 'TRK', 'trkn', 'TRACKNUMBER'], | ||
['disk', 'TPOS', 'TPA', 'disk', 'DISCNUMBER'], | ||
['genre', 'TCON', 'TCO', '©gen', 'gnre', 'GENRE'], | ||
['picture', 'APIC', 'PIC', 'covr', 'METADATA_BLOCK_PICTURE'], | ||
['genre', 'TCON', 'TCO', '©gen', 'gnre', 'GENRE', 'Genre'], | ||
['picture', 'APIC', 'PIC', 'covr', 'METADATA_BLOCK_PICTURE', 'Cover Art (Front)', 'COVER ART (FRONT)', 'Cover Art (Back)', 'COVER ART (Back)'], | ||
['composer', 'TCOM', 'TCM', '©wrt', 'COMPOSER'] | ||
]; | ||
]; | ||
strtok.UINT24_BE = { | ||
len : 3, | ||
get : function(buf, off) { | ||
return (((buf[off] << 8) + buf[off + 1]) << 8) + buf[off + 2]; | ||
} | ||
}; | ||
strtok.BITSET = { | ||
len : 1, | ||
get : function(buf, off, bit) { | ||
return (buf[off] & (1 << bit)) !== 0; | ||
} | ||
}; | ||
strtok.INT32SYNCSAFE = { | ||
len : 4, | ||
get : function (buf, off) { | ||
return buf[off + 3] & 0x7f | | ||
((buf[off + 2]) << 7) | | ||
((buf[off + 1]) << 14) | | ||
((buf[off]) << 21); | ||
} | ||
}; |
{ | ||
"name": "musicmetadata", | ||
"description": "Music metadata library for node, using pure Javascript.", | ||
"version": "0.1.1", | ||
"version": "0.1.2", | ||
"author": "Lee Treveil", | ||
"dependencies": { | ||
"strtok" : "*" | ||
"strtok": "*", | ||
"testy": "*" | ||
}, | ||
"main": "lib", | ||
"scripts": | ||
{ "test": "node test/alltests.js" } | ||
} | ||
"scripts": { | ||
"test": "node test/alltests.js" | ||
}, | ||
"devDependencies": {}, | ||
"engines": { | ||
"node": "*" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "git://github.com/leetreveil/node-musicmetadata.git" | ||
} | ||
} |
@@ -10,3 +10,3 @@ Installation | ||
----------------- | ||
mp3 (1.1, 2.2, 2.3, 2.4), m4a(mp4) and ogg(vorbis) | ||
mp3 (1.1, 2.2, 2.3, 2.4), m4a(mp4), vorbis (ogg, flac) | ||
@@ -16,31 +16,37 @@ | ||
----------------- | ||
var fs = require('fs'), | ||
musicmetadata = require('musicmetadata'); | ||
//create a new parser from a node ReadStream | ||
var parser = new musicmetadata(fs.createReadStream('sample.mp3')); | ||
//listen for the metadata event | ||
parser.on('metadata', function(result) { | ||
console.log(result); | ||
}); | ||
```javascript | ||
var fs = require('fs'), | ||
musicmetadata = require('musicmetadata'); | ||
//create a new parser from a node ReadStream | ||
var parser = new musicmetadata(fs.createReadStream('sample.mp3')); | ||
//listen for the metadata event | ||
parser.on('metadata', function(result) { | ||
console.log(result); | ||
}); | ||
``` | ||
This will output the standard music metadata: | ||
{ artist : 'Spor', | ||
album : 'Nightlife, Vol 5.', | ||
albumartist : ['Andy C', 'Spor'], | ||
title : 'Stronger', | ||
year : 2010, | ||
track : [1, 44], | ||
disk : [1, 2], | ||
picture: { format : 'jpg', data : <Buffer> } | ||
} | ||
```javascript | ||
{ artist : 'Spor', | ||
album : 'Nightlife, Vol 5.', | ||
albumartist : ['Andy C', 'Spor'], | ||
title : 'Stronger', | ||
year : '2010', | ||
track : { no : 1, of : 44 }, | ||
disk : { no : 1, of : 2 }, | ||
picture : { format : 'jpg', data : <Buffer> } | ||
} | ||
``` | ||
If you just want the artist - listen for the artist event: | ||
parser.on('artist', function(result) { | ||
console.log(result); | ||
}); | ||
```javascript | ||
parser.on('artist', function(result) { | ||
console.log(result); | ||
}); | ||
``` | ||
@@ -50,14 +56,20 @@ You can also listen for the 'done' event, this will be raised when parsing has finished or an error has occurred. This could be | ||
parser.on('done', function() { | ||
stream.destroy(); | ||
}); | ||
```javascript | ||
parser.on('done', function(err) { | ||
if (err) throw err; | ||
stream.destroy(); | ||
}); | ||
``` | ||
Use the 'error' event to listen for errors that occurred while parsing. | ||
parser.on('error', function(error) { | ||
console.log(error.message); | ||
}); | ||
## Changelog | ||
### v0.1.2 | ||
* Fixed id3v1.1 implementation | ||
* Implemented multiple artwork support (id4) | ||
* Added flac support | ||
[Commits](https://github.com/leetreveil/node-musicmetadata/compare/0.1.1...0.1.2) | ||
### v0.1.1 | ||
@@ -64,0 +76,0 @@ |
@@ -7,4 +7,5 @@ if (module == require.main) { | ||
'test-id3v1.1.js', 'test-id3v2.2.js', 'test-id3v2.3.js', | ||
'test-id3v2.4.js', 'test-id4.js', 'test-vorbis.js', | ||
'test-unknownencoding.js', 'test-nonasciichars.js']; | ||
'test-id3v2.4.js', 'test-id4.js', 'test-ogg.js', | ||
'test-unknownencoding.js', 'test-nonasciichars.js', | ||
'test-flac.js']; | ||
@@ -14,5 +15,11 @@ for (var i=0; i < tests.length; i++) { | ||
var test = spawn(process.execPath, [fullPath]); | ||
test.stdout.pipe(process.stdout); | ||
test.stderr.pipe(process.stderr); | ||
test.stdout.on('data', function(data) { | ||
process.stdout.write(data.toString()); | ||
}); | ||
test.stderr.on('data', function(data) { | ||
process.stderr.write(data.toString()); | ||
}); | ||
} | ||
} |
var common = require('../lib/common'), | ||
assert = require('assert'), | ||
testHelper = require('./testHelper'); | ||
testy = require('testy'), | ||
assert = testy.assert; | ||
var testHelper = new testHelper(2, __filename); | ||
testy.expected = 2; | ||
@@ -15,2 +15,3 @@ //testing that we can deunsync data correctly | ||
assert.deepEqual(expected, new Buffer(common.removeUnsyncBytes(sample))); | ||
testHelper.ranTests(2); | ||
testy.finish(); |
@@ -1,6 +0,6 @@ | ||
var assert = require('assert'), | ||
parseGenre = require('../lib/common').parseGenre, | ||
testHelper = require('./testHelper'); | ||
var parseGenre = require('../lib/common').parseGenre, | ||
testy = require('testy'), | ||
assert = testy.assert; | ||
var testHelper = new testHelper(8, __filename); | ||
testy.expected = 8; | ||
@@ -31,3 +31,3 @@ var simple = 'Electronic'; | ||
testHelper.ranTests(8); | ||
testy.finish(); | ||
@@ -34,0 +34,0 @@ |
var id3 = require('../lib/index'), | ||
fs = require('fs'), | ||
assert = require('assert'), | ||
testHelper = require('./testHelper'); | ||
testy = require('testy'), | ||
assert = testy.assert; | ||
testy.expected = 17; | ||
var sample = require('path').join(__dirname, 'samples/id3v1.mp3'); | ||
var parser = new id3(fs.createReadStream(sample)); | ||
var parser = new id3(fs.createReadStream(sample, { bufferSize: 10 })); | ||
var testHelper = new testHelper(16, __filename); | ||
parser.on('metadata', function(result) { | ||
assert.strictEqual(result.title, 'Blood Sugar'); | ||
assert.deepEqual(result.artist, ['Pendulum']); | ||
//we can't do assert.strictEqual(result.albumartist, []); :( | ||
assert.strictEqual(result.albumartist[0], undefined); | ||
assert.strictEqual(result.artist[0], 'Pendulum'); | ||
assert.strictEqual(result.albumartist.length, 0); | ||
assert.strictEqual(result.album, 'Blood Sugar (Single)'); | ||
assert.strictEqual(result.year, 2007); | ||
assert.strictEqual(result.track[0], 1); | ||
assert.strictEqual(result.track[1], 0); | ||
assert.deepEqual(result.genre, ['Electronic']); | ||
testHelper.ranTests(8); | ||
assert.strictEqual(result.year, '2007'); | ||
assert.strictEqual(result.track.no, 1); | ||
assert.strictEqual(result.track.of, 0); | ||
assert.strictEqual(result.genre[0], 'Electronic'); | ||
}); | ||
@@ -26,8 +24,6 @@ | ||
assert.strictEqual(result, 'Blood Sugar'); | ||
testHelper.ranTests(1); | ||
}); | ||
parser.on('artist', function(result) { | ||
assert.strictEqual(result, 'Pendulum'); | ||
testHelper.ranTests(1); | ||
assert.strictEqual(result[0], 'Pendulum'); | ||
}); | ||
@@ -37,3 +33,2 @@ | ||
assert.strictEqual(result, 'Blood Sugar (Single)'); | ||
testHelper.ranTests(1); | ||
}); | ||
@@ -43,22 +38,21 @@ | ||
assert.strictEqual(result, '2007'); | ||
testHelper.ranTests(1); | ||
}); | ||
parser.on('track', function(result) { | ||
assert.strictEqual(result, 1); | ||
testHelper.ranTests(1); | ||
assert.strictEqual(result.no, 1); | ||
assert.strictEqual(result.of, 0); | ||
}); | ||
parser.on('genre', function(result) { | ||
assert.deepEqual(result, 'Electronic'); | ||
testHelper.ranTests(1); | ||
assert.strictEqual(result[0], 'Electronic'); | ||
}); | ||
parser.on('comment', function(result) { | ||
assert.strictEqual(result, 'abcdefg'); | ||
testHelper.ranTests(1); | ||
assert.strictEqual(result[0], 'abcdefg'); | ||
}); | ||
parser.on('done', function(result) { | ||
testHelper.ranTests(1); | ||
parser.on('done', function(err) { | ||
if (err) throw err; | ||
assert.ok(true); | ||
testy.finish(); | ||
}); |
var id3 = require('../lib/index'), | ||
fs = require('fs'), | ||
assert = require('assert'), | ||
testHelper = require('./testHelper'); | ||
testy = require('testy'), | ||
assert = testy.assert; | ||
testy.expected = 45; | ||
var sample = require('path').join(__dirname, 'samples/id3v2.2.mp3'); | ||
var parser = new id3(fs.createReadStream(sample)); | ||
var testHelper = new testHelper(46, __filename); | ||
parser.on('metadata', function(result) { | ||
assert.strictEqual(result.title, 'You Are The One'); | ||
assert.deepEqual(result.artist, ['Shiny Toy Guns']); | ||
assert.strictEqual(result.artist[0], 'Shiny Toy Guns'); | ||
assert.strictEqual(result.album, 'We Are Pilots'); | ||
assert.strictEqual(result.year, 2006); | ||
assert.strictEqual(result.track[0], 1); | ||
assert.strictEqual(result.track[1], 11); | ||
assert.deepEqual(result.genre, ['Alternative']); | ||
assert.strictEqual(result.picture.format, 'jpg'); | ||
assert.strictEqual(result.picture.data.length, 99738); | ||
testHelper.ranTests(9); | ||
assert.strictEqual(result.year, '2006'); | ||
assert.strictEqual(result.track.no, 1); | ||
assert.strictEqual(result.track.of, 11); | ||
assert.strictEqual(result.genre[0], 'Alternative'); | ||
assert.strictEqual(result.picture[0].format, 'jpg'); | ||
assert.strictEqual(result.picture[0].data.length, 99738); | ||
}); | ||
//Aliased tests | ||
parser.on('title', function(result) { | ||
assert.strictEqual(result, 'You Are The One'); | ||
testHelper.ranTests(1); | ||
}); | ||
parser.on('artist', function(result) { | ||
assert.deepEqual(result, 'Shiny Toy Guns'); | ||
testHelper.ranTests(1); | ||
assert.deepEqual(result[0], 'Shiny Toy Guns'); | ||
}); | ||
@@ -36,3 +34,2 @@ | ||
assert.strictEqual(result, 'We Are Pilots'); | ||
testHelper.ranTests(1); | ||
}); | ||
@@ -42,26 +39,21 @@ | ||
assert.strictEqual(result, '2006'); | ||
testHelper.ranTests(1); | ||
}); | ||
parser.on('track', function(result) { | ||
assert.strictEqual(result, '1/11'); | ||
testHelper.ranTests(1); | ||
assert.strictEqual(result.no, 1); | ||
assert.strictEqual(result.of, 11); | ||
}); | ||
parser.on('genre', function(result) { | ||
assert.strictEqual(result, 'Alternative'); | ||
testHelper.ranTests(1); | ||
assert.strictEqual(result[0], 'Alternative'); | ||
}); | ||
parser.on('picture', function(result) { | ||
assert.strictEqual(result.format, 'JPG'); | ||
assert.strictEqual(result.type, 'Other'); | ||
assert.strictEqual(result.description, ''); | ||
assert.strictEqual(result.data.length, 99738); | ||
testHelper.ranTests(4); | ||
assert.strictEqual(result[0].format, 'jpg'); | ||
assert.strictEqual(result[0].data.length, 99738); | ||
}); | ||
//Raw tests | ||
parser.on('TP1', function(result) { | ||
assert.strictEqual(result, 'Shiny Toy Guns'); | ||
testHelper.ranTests(1); | ||
}); | ||
@@ -71,3 +63,2 @@ | ||
assert.strictEqual(result, '1/11'); | ||
testHelper.ranTests(1); | ||
}); | ||
@@ -77,3 +68,2 @@ | ||
assert.strictEqual(result, '2006'); | ||
testHelper.ranTests(1); | ||
}); | ||
@@ -83,3 +73,2 @@ | ||
assert.strictEqual(result, 'iTunes v7.0.2.16'); | ||
testHelper.ranTests(1); | ||
}); | ||
@@ -89,3 +78,2 @@ | ||
assert.strictEqual(result, '(20)'); //Alternative | ||
testHelper.ranTests(1); | ||
}); | ||
@@ -95,3 +83,2 @@ | ||
assert.strictEqual(result, 'We Are Pilots'); | ||
testHelper.ranTests(1); | ||
}); | ||
@@ -101,3 +88,2 @@ | ||
assert.strictEqual(result, 'You Are The One'); | ||
testHelper.ranTests(1); | ||
}); | ||
@@ -109,4 +95,3 @@ | ||
assert.strictEqual(result.description, ''); | ||
assert.strictEqual(result.data.length, 99738); | ||
testHelper.ranTests(4); | ||
assert.strictEqual(result.data.length, 99738); | ||
}); | ||
@@ -119,3 +104,2 @@ | ||
assert.strictEqual(result.text.length, 832); | ||
testHelper.ranTests(3); | ||
}); | ||
@@ -131,3 +115,2 @@ | ||
assert.strictEqual(result.text, '0'); | ||
testHelper.ranTests(3); | ||
break; | ||
@@ -138,3 +121,2 @@ case 1: | ||
assert.strictEqual(result.text, '0000299C 0000291D 0000DBE0 0000D6BA 0003C378 0003C2C1 0000902A 00008F1B 00012FC6 00015FBC'); | ||
testHelper.ranTests(3); | ||
break; | ||
@@ -145,3 +127,2 @@ case 2: | ||
assert.strictEqual(result.text, '00000000 00000210 00000AD4 0000000000B6499C 00000000 006327AD 00000000 00000000 00000000 00000000 00000000 00000000'); | ||
testHelper.ranTests(3); | ||
break; | ||
@@ -152,3 +133,2 @@ case 3: | ||
assert.strictEqual(result.text, '11+3ABC77F16B8A2F0F1E1A1EBAB868A98F+8210091'); | ||
testHelper.ranTests(3); | ||
break; | ||
@@ -159,4 +139,6 @@ } | ||
parser.on('done', function() { | ||
testHelper.ranTests(1); | ||
parser.on('done', function(err) { | ||
if (err) throw err; | ||
assert.ok(true); | ||
testy.finish(); | ||
}); |
var id3 = require('../lib/index'), | ||
fs = require('fs'), | ||
assert = require('assert'), | ||
testHelper = require('./testHelper'); | ||
testy = require('testy'), | ||
assert = testy.assert; | ||
testy.expected = 42; | ||
var sample = require('path').join(__dirname, 'samples/id3v2.3.mp3'); | ||
var parser = new id3(fs.createReadStream(sample)); | ||
var testHelper = new testHelper(38, __filename); | ||
parser.on('metadata', function(result) { | ||
assert.strictEqual(result.title, 'Home'); | ||
assert.deepEqual(result.artist, ['Explosions In The Sky', 'Another', 'And Another']); | ||
assert.deepEqual(result.albumartist, ['Soundtrack']); | ||
assert.strictEqual(result.artist[0], 'Explosions In The Sky'); | ||
assert.strictEqual(result.artist[1], 'Another'); | ||
assert.strictEqual(result.artist[2], 'And Another'); | ||
assert.strictEqual(result.albumartist[0], 'Soundtrack'); | ||
assert.strictEqual(result.album, 'Friday Night Lights [Original Movie Soundtrack]'); | ||
assert.strictEqual(result.year, 2004); | ||
assert.strictEqual(result.track[0], 5); | ||
assert.strictEqual(result.track[1], 0); | ||
assert.strictEqual(result.disk[0], 1); | ||
assert.strictEqual(result.disk[1], 1); | ||
assert.deepEqual(result.genre, ['Soundtrack']); | ||
assert.strictEqual(result.picture.format, 'jpg'); | ||
assert.strictEqual(result.picture.data.length, 80938); | ||
testHelper.ranTests(12); | ||
assert.strictEqual(result.year, '2004'); | ||
assert.strictEqual(result.track.no, 5); | ||
assert.strictEqual(result.track.of, 0); | ||
assert.strictEqual(result.disk.no, 1); | ||
assert.strictEqual(result.disk.of, 1); | ||
assert.strictEqual(result.genre[0], 'Soundtrack'); | ||
assert.strictEqual(result.picture[0].format, 'jpg'); | ||
assert.strictEqual(result.picture[0].data.length, 80938); | ||
}); | ||
//Aliased tests | ||
parser.on('title', function(result) { | ||
assert.strictEqual(result, 'Home'); | ||
testHelper.ranTests(1) | ||
}); | ||
parser.on('artist', function(result) { | ||
assert.strictEqual(result, 'Explosions In The Sky/Another/And Another'); | ||
testHelper.ranTests(1) | ||
assert.strictEqual(result[0], 'Explosions In The Sky'); | ||
assert.strictEqual(result[1], 'Another'); | ||
assert.strictEqual(result[2], 'And Another'); | ||
}); | ||
parser.on('albumartist', function(result) { | ||
assert.strictEqual(result, 'Soundtrack'); | ||
testHelper.ranTests(1) | ||
assert.strictEqual(result[0], 'Soundtrack'); | ||
}); | ||
@@ -44,3 +44,2 @@ | ||
assert.strictEqual(result, 'Friday Night Lights [Original Movie Soundtrack]'); | ||
testHelper.ranTests(1) | ||
}); | ||
@@ -50,31 +49,26 @@ | ||
assert.strictEqual(result, '2004'); | ||
testHelper.ranTests(1) | ||
}); | ||
parser.on('track', function(result) { | ||
assert.strictEqual(result, '5'); | ||
testHelper.ranTests(1); | ||
assert.strictEqual(result.no, 5); | ||
assert.strictEqual(result.of, 0); | ||
}); | ||
parser.on('disk', function(result) { | ||
assert.strictEqual(result, '1/1'); | ||
testHelper.ranTests(1); | ||
assert.strictEqual(result.no, 1); | ||
assert.strictEqual(result.of, 1); | ||
}); | ||
parser.on('genre', function(result) { | ||
assert.strictEqual(result, 'Soundtrack'); | ||
testHelper.ranTests(1) | ||
assert.strictEqual(result[0], 'Soundtrack'); | ||
}); | ||
parser.on('picture', function(result) { | ||
assert.strictEqual(result.format, 'image/jpg'); | ||
assert.strictEqual(result.type, 'Cover (front)'); | ||
assert.strictEqual(result.description, ''); | ||
assert.strictEqual(result.data.length, 80938); | ||
testHelper.ranTests(4); | ||
assert.strictEqual(result[0].format, 'jpg'); | ||
assert.strictEqual(result[0].data.length, 80938); | ||
}); | ||
//Raw tests | ||
parser.on('TALB', function(result) { | ||
assert.strictEqual(result, 'Friday Night Lights [Original Movie Soundtrack]'); | ||
testHelper.ranTests(1); | ||
}); | ||
@@ -84,3 +78,2 @@ | ||
assert.strictEqual(result, 'Explosions In The Sky/Another/And Another'); | ||
testHelper.ranTests(1); | ||
}); | ||
@@ -90,3 +83,2 @@ | ||
assert.strictEqual(result, 'Soundtrack'); | ||
testHelper.ranTests(1); | ||
}); | ||
@@ -96,3 +88,2 @@ | ||
assert.strictEqual(result, 'Explosions in the Sky'); | ||
testHelper.ranTests(1); | ||
}); | ||
@@ -102,3 +93,2 @@ | ||
assert.strictEqual(result, '1/1'); | ||
testHelper.ranTests(1); | ||
}); | ||
@@ -108,3 +98,2 @@ | ||
assert.strictEqual(result, 'Soundtrack'); | ||
testHelper.ranTests(1); | ||
}); | ||
@@ -114,3 +103,2 @@ | ||
assert.strictEqual(result, 'Home'); | ||
testHelper.ranTests(1); | ||
}); | ||
@@ -120,3 +108,2 @@ | ||
assert.strictEqual(result, '5'); | ||
testHelper.ranTests(1); | ||
}); | ||
@@ -126,3 +113,2 @@ | ||
assert.strictEqual(result, '2004'); | ||
testHelper.ranTests(1); | ||
}); | ||
@@ -135,7 +121,8 @@ | ||
assert.strictEqual(result.data.length, 80938); | ||
testHelper.ranTests(4); | ||
}); | ||
parser.on('done', function(result) { | ||
testHelper.ranTests(1); | ||
parser.on('done', function(err) { | ||
if (err) throw err; | ||
assert.ok(true); | ||
testy.finish(); | ||
}); |
var id3 = require('../lib/index'), | ||
fs = require('fs'), | ||
assert = require('assert'), | ||
testHelper = require('./testHelper'); | ||
testy = require('testy'), | ||
assert = testy.assert; | ||
testy.expected = 44; | ||
var sample = require('path').join(__dirname, 'samples/id3v2.4.mp3'); | ||
var parser = new id3(fs.createReadStream(sample)); | ||
var testHelper = new testHelper(38, __filename); | ||
parser.on('metadata', function(result) { | ||
assert.strictEqual(result.title, 'Home'); | ||
assert.deepEqual(result.artist, ['Explo','ions','nodejsftws']); | ||
assert.deepEqual(result.albumartist, ['Soundtrack']); | ||
assert.strictEqual(result.artist[0], 'Explo'); | ||
assert.strictEqual(result.artist[1], 'ions'); | ||
assert.strictEqual(result.artist[2], 'nodejsftws'); | ||
assert.strictEqual(result.albumartist[0], 'Soundtrack'); | ||
assert.strictEqual(result.album, 'Friday Night Lights [Original Movie Soundtrack]'); | ||
assert.strictEqual(result.year, 2004); | ||
assert.strictEqual(result.track[0], 5); | ||
assert.strictEqual(result.track[1], 0); | ||
assert.strictEqual(result.disk[0], 1); | ||
assert.strictEqual(result.disk[1], 1); | ||
assert.deepEqual(result.genre, ['Soundtrack', 'OST']); | ||
assert.strictEqual(result.picture.format, 'jpg'); | ||
assert.strictEqual(result.picture.data.length, 80938); | ||
testHelper.ranTests(12); | ||
assert.strictEqual(result.year, '2004'); | ||
assert.strictEqual(result.track.no, 5); | ||
assert.strictEqual(result.track.of, 0); | ||
assert.strictEqual(result.disk.no, 1); | ||
assert.strictEqual(result.disk.of, 1); | ||
assert.strictEqual(result.genre[0], 'Soundtrack'); | ||
assert.strictEqual(result.genre[1], 'OST'); | ||
assert.strictEqual(result.picture[0].format, 'jpg'); | ||
assert.strictEqual(result.picture[0].data.length, 80938); | ||
}); | ||
//Aliased tests | ||
parser.on('title', function(result) { | ||
assert.strictEqual(result, 'Home'); | ||
testHelper.ranTests(1); | ||
}); | ||
parser.on('artist', function(result) { | ||
assert.strictEqual(result, 'Explo/ions/nodejsftws'); | ||
testHelper.ranTests(1); | ||
assert.strictEqual(result[0], 'Explo'); | ||
assert.strictEqual(result[1], 'ions'); | ||
assert.strictEqual(result[2], 'nodejsftws'); | ||
}); | ||
parser.on('albumartist', function(result) { | ||
assert.strictEqual(result, 'Soundtrack'); | ||
testHelper.ranTests(1); | ||
assert.strictEqual(result[0], 'Soundtrack'); | ||
}); | ||
@@ -44,3 +46,2 @@ | ||
assert.strictEqual(result, 'Friday Night Lights [Original Movie Soundtrack]'); | ||
testHelper.ranTests(1); | ||
}); | ||
@@ -50,31 +51,27 @@ | ||
assert.strictEqual(result, '2004'); | ||
testHelper.ranTests(1); | ||
}); | ||
parser.on('track', function(result) { | ||
assert.strictEqual(result, '5'); | ||
testHelper.ranTests(1); | ||
assert.strictEqual(result.no, 5); | ||
assert.strictEqual(result.of, 0); | ||
}); | ||
parser.on('disk', function(result) { | ||
assert.strictEqual(result, '1/1'); | ||
testHelper.ranTests(1); | ||
assert.strictEqual(result.no, 1); | ||
assert.strictEqual(result.of, 1); | ||
}); | ||
parser.on('genre', function(result) { | ||
assert.strictEqual(result, 'Soundtrack/OST'); | ||
testHelper.ranTests(1);; | ||
assert.strictEqual(result[0], 'Soundtrack'); | ||
assert.strictEqual(result[1], 'OST'); | ||
}); | ||
parser.on('picture', function(result) { | ||
assert.strictEqual(result.format, 'image/jpg'); | ||
assert.strictEqual(result.type, 'Cover (front)'); | ||
assert.strictEqual(result.description, 'some description'); | ||
assert.strictEqual(result.data.length, 80938); | ||
testHelper.ranTests(4); | ||
assert.strictEqual(result[0].format, 'jpg'); | ||
assert.strictEqual(result[0].data.length, 80938); | ||
}); | ||
//Raw tests | ||
parser.on('TALB', function(result) { | ||
assert.strictEqual(result, 'Friday Night Lights [Original Movie Soundtrack]'); | ||
testHelper.ranTests(1); | ||
}); | ||
@@ -84,3 +81,2 @@ | ||
assert.strictEqual(result, 'Explo/ions/nodejsftws'); | ||
testHelper.ranTests(1); | ||
}); | ||
@@ -90,3 +86,2 @@ | ||
assert.strictEqual(result, 'Soundtrack'); | ||
testHelper.ranTests(1); | ||
}); | ||
@@ -96,3 +91,2 @@ | ||
assert.strictEqual(result, 'Explosions in the Sky'); | ||
testHelper.ranTests(1); | ||
}); | ||
@@ -102,3 +96,2 @@ | ||
assert.strictEqual(result, '1/1'); | ||
testHelper.ranTests(1); | ||
}); | ||
@@ -108,3 +101,2 @@ | ||
assert.strictEqual(result, 'Soundtrack/OST'); | ||
testHelper.ranTests(1); | ||
}); | ||
@@ -114,3 +106,2 @@ | ||
assert.strictEqual(result, 'Home'); | ||
testHelper.ranTests(1); | ||
}); | ||
@@ -120,3 +111,2 @@ | ||
assert.strictEqual(result, '5'); | ||
testHelper.ranTests(1); | ||
}); | ||
@@ -126,3 +116,2 @@ | ||
assert.strictEqual(result, '2004'); | ||
testHelper.ranTests(1); | ||
}); | ||
@@ -135,7 +124,8 @@ | ||
assert.strictEqual(result.data.length, 80938); | ||
testHelper.ranTests(4); | ||
}); | ||
parser.on('done', function(result) { | ||
testHelper.ranTests(1); | ||
parser.on('done', function(err) { | ||
if (err) throw err; | ||
assert.ok(true); | ||
testy.finish(); | ||
}); |
var id3 = require('../lib/index'), | ||
fs = require('fs'), | ||
assert = require('assert'), | ||
testHelper = require('./testHelper'); | ||
testy = require('testy'), | ||
assert = testy.assert; | ||
testy.expected = 47; | ||
var sample = require('path').join(__dirname, 'samples/id4.m4a'); | ||
var parser = new id3(fs.createReadStream(sample)); | ||
var testHelper = new testHelper(37, __filename); | ||
parser.on('metadata', function(result) { | ||
assert.strictEqual(result.title, 'Voodoo People (Pendulum Remix)'); | ||
assert.deepEqual(result.artist, ['The Prodigy']); | ||
assert.deepEqual(result.albumartist, ['Pendulum']); | ||
assert.strictEqual(result.artist[0], 'The Prodigy'); | ||
assert.strictEqual(result.albumartist[0], 'Pendulum'); | ||
assert.strictEqual(result.album, 'Voodoo People'); | ||
assert.strictEqual(result.year, 2005); | ||
assert.strictEqual(result.track[0], 1); | ||
assert.strictEqual(result.track[1], 0); | ||
assert.strictEqual(result.disk[0], 1); | ||
assert.strictEqual(result.disk[1], 1); | ||
assert.deepEqual(result.genre, ['Electronic']); | ||
assert.strictEqual(result.picture.format, 'jpg'); | ||
assert.strictEqual(result.picture.data.length, 196450); | ||
testHelper.ranTests(12); | ||
assert.strictEqual(result.year, '2005'); | ||
assert.strictEqual(result.track.no, 1); | ||
assert.strictEqual(result.track.of, 12); | ||
assert.strictEqual(result.disk.no, 1); | ||
assert.strictEqual(result.disk.of, 1); | ||
assert.strictEqual(result.genre[0], 'Electronic'); | ||
assert.strictEqual(result.picture[0].format, 'jpg'); | ||
assert.strictEqual(result.picture[0].data.length, 196450); | ||
assert.strictEqual(result.picture[1].format, 'jpg'); | ||
assert.strictEqual(result.picture[1].data.length, 196450); | ||
}); | ||
//Aliased tests | ||
parser.on('title', function(result) { | ||
assert.strictEqual(result, 'Voodoo People (Pendulum Remix)'); | ||
testHelper.ranTests(1); | ||
}); | ||
parser.on('artist', function(result) { | ||
assert.strictEqual(result, 'The Prodigy'); | ||
testHelper.ranTests(1); | ||
assert.strictEqual(result[0], 'The Prodigy'); | ||
}); | ||
parser.on('albumartist', function(result) { | ||
assert.strictEqual(result, 'Pendulum'); | ||
testHelper.ranTests(1); | ||
assert.strictEqual(result[0], 'Pendulum'); | ||
}); | ||
@@ -44,3 +43,2 @@ | ||
assert.strictEqual(result, 'Voodoo People'); | ||
testHelper.ranTests(1); | ||
}); | ||
@@ -50,29 +48,36 @@ | ||
assert.strictEqual(result, '2005'); | ||
testHelper.ranTests(1); | ||
}); | ||
parser.on('track', function(result) { | ||
assert.strictEqual(result, '1/0'); | ||
testHelper.ranTests(1); | ||
assert.strictEqual(result.no, 1); | ||
assert.strictEqual(result.of, 12); | ||
}); | ||
parser.on('disk', function(result) { | ||
assert.strictEqual(result, '1/1'); | ||
testHelper.ranTests(1); | ||
assert.strictEqual(result.no, 1); | ||
assert.strictEqual(result.of, 1); | ||
}); | ||
parser.on('genre', function(result) { | ||
assert.strictEqual(result, 'Electronic'); | ||
testHelper.ranTests(1); | ||
assert.strictEqual(result[0], 'Electronic'); | ||
}); | ||
parser.on('picture', function(result) { | ||
assert.strictEqual(result.format, 'image/jpeg'); | ||
assert.strictEqual(result.data.length, 196450); | ||
testHelper.ranTests(2); | ||
assert.strictEqual(result[0].format, 'jpg'); | ||
assert.strictEqual(result[0].data.length, 196450); | ||
assert.strictEqual(result[1].format, 'jpg'); | ||
assert.strictEqual(result[1].data.length, 196450); | ||
}); | ||
parser.on('comment', function(result) { | ||
assert.strictEqual(result[0], '(Pendulum Remix)'); | ||
}); | ||
parser.on('composer', function(result) { | ||
assert.strictEqual(result[0], 'Liam Howlett'); | ||
}); | ||
//Raw tests | ||
parser.on('trkn', function(result) { | ||
assert.strictEqual(result, '1/0'); | ||
testHelper.ranTests(1); | ||
assert.strictEqual(result, '1/12'); | ||
}); | ||
@@ -82,3 +87,2 @@ | ||
assert.strictEqual(result, 0); | ||
testHelper.ranTests(1); | ||
}); | ||
@@ -88,3 +92,2 @@ | ||
assert.strictEqual(result, 'Electronic'); | ||
testHelper.ranTests(1); | ||
}); | ||
@@ -94,3 +97,2 @@ | ||
assert.strictEqual(result, 256); | ||
testHelper.ranTests(1); | ||
}); | ||
@@ -100,3 +102,2 @@ | ||
assert.strictEqual(result, 'Voodoo People'); | ||
testHelper.ranTests(1); | ||
}); | ||
@@ -106,3 +107,2 @@ | ||
assert.strictEqual(result, 'The Prodigy'); | ||
testHelper.ranTests(1); | ||
}); | ||
@@ -112,3 +112,2 @@ | ||
assert.strictEqual(result, 'Pendulum'); | ||
testHelper.ranTests(1); | ||
}); | ||
@@ -118,3 +117,2 @@ | ||
assert.strictEqual(result, '(Pendulum Remix)'); | ||
testHelper.ranTests(1); | ||
}); | ||
@@ -124,3 +122,2 @@ | ||
assert.strictEqual(result, 'Liam Howlett'); | ||
testHelper.ranTests(1); | ||
}); | ||
@@ -130,3 +127,2 @@ | ||
assert.strictEqual(result, 'Voodoo People (Pendulum Remix)'); | ||
testHelper.ranTests(1); | ||
}); | ||
@@ -136,3 +132,2 @@ | ||
assert.strictEqual(result, 'Lavf52.36.0'); | ||
testHelper.ranTests(1); | ||
}); | ||
@@ -142,13 +137,14 @@ | ||
assert.strictEqual(result, '2005'); | ||
testHelper.ranTests(1); | ||
}); | ||
//raised twice (exact same content) | ||
parser.on('covr', function(result) { | ||
assert.strictEqual(result.format, 'image/jpeg'); | ||
assert.strictEqual(result.data.length, 196450); | ||
testHelper.ranTests(2); | ||
}); | ||
parser.on('done', function() { | ||
testHelper.ranTests(1); | ||
parser.on('done', function(err) { | ||
if (err) throw err; | ||
assert.ok(true); | ||
testy.finish(); | ||
}); |
var fs = require('fs'), | ||
mm = require('../lib/index'), | ||
assert = require('assert'), | ||
testHelper = require('./testHelper'); | ||
testy = require('testy'), | ||
assert = testy.assert; | ||
var testHelper = new testHelper(3, __filename); | ||
testy.expected = 3; | ||
@@ -14,5 +14,4 @@ for (var i=0; i < 3; i++) { | ||
//we are testing that the metadata object is not being shared across parsers | ||
assert.deepEqual(result.artist, ['Pendulum']); | ||
testHelper.ranTests(1); | ||
assert.strictEqual(result.artist[0], 'Pendulum'); | ||
}); | ||
} |
var fs = require('fs'), | ||
mm = require('../lib/index'), | ||
assert = require('assert'), | ||
testHelper = require('./testHelper'); | ||
testy = require('testy'), | ||
assert = testy.assert; | ||
testy.expected = 1; | ||
var sample = require('path').join(__dirname, 'samples/bug-non ascii chars.mp3'); | ||
var parser = new mm(fs.createReadStream(sample)); | ||
var testHelper = new testHelper(1, __filename); | ||
parser.on('metadata', function(result) { | ||
assert.strictEqual(result.artist[0], 'Janelle Monáe'); | ||
testHelper.ranTests(1); | ||
testy.finish(); | ||
}); |
var fs = require('fs'), | ||
mm = require('../lib/index'), | ||
assert = require('assert'), | ||
testHelper = require('./testHelper'); | ||
testy = require('testy'), | ||
assert = testy.assert; | ||
testy.expected = 10; | ||
var sample = require('path').join(__dirname, 'samples/bug-unkown encoding.mp3'); | ||
var parser = new mm(fs.createReadStream(sample)); | ||
var testHelper = new testHelper(10, __filename); | ||
parser.on('metadata', function(result) { | ||
assert.strictEqual(result.title, '808'); | ||
assert.deepEqual(result.artist, ['Benga']); | ||
assert.deepEqual(result.albumartist, ['Benga']); | ||
assert.strictEqual(result.artist[0], 'Benga'); | ||
assert.strictEqual(result.albumartist[0], 'Benga'); | ||
assert.strictEqual(result.album, 'Phaze One'); | ||
assert.strictEqual(result.year, 2010); | ||
assert.strictEqual(result.track[0], 4); | ||
assert.strictEqual(result.track[1], 8); | ||
assert.deepEqual(result.genre, ['Dubstep']); | ||
assert.strictEqual(result.picture.format, 'jpg'); | ||
assert.strictEqual(result.picture.data.length, 6761); | ||
testHelper.ranTests(10); | ||
assert.strictEqual(result.year, '2010'); | ||
assert.strictEqual(result.track.no, 4); | ||
assert.strictEqual(result.track.of, 8); | ||
assert.strictEqual(result.genre[0], 'Dubstep'); | ||
assert.strictEqual(result.picture[0].format, 'jpg'); | ||
assert.strictEqual(result.picture[0].data.length, 6761); | ||
testy.finish(); | ||
}); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Wildcard dependency
QualityPackage has a dependency with a floating version range. This can cause issues if the dependency publishes a new major version.
Found 1 instance in 1 package
Native code
Supply chain riskContains native code (e.g., compiled binaries or shared libraries). Including native code can obscure malicious behavior.
Found 4 instances in 1 package
Native code
Supply chain riskContains native code (e.g., compiled binaries or shared libraries). Including native code can obscure malicious behavior.
Found 1 instance in 1 package
No repository
Supply chain riskPackage does not have a linked source code repository. Without this field, a package will have no reference to the location of the source code use to generate the package.
Found 1 instance in 1 package
1589379
40
1738
78
2
3
16
12
+ Addedtesty@*
+ Addedtesty@0.0.8(transitive)