musicmetadata
Advanced tools
Comparing version 0.2.7 to 0.3.0
@@ -5,3 +5,3 @@ var drop = require('drag-and-drop-files'); | ||
drop(document, function (files) { | ||
var mm = new musicmetadata(files[0]) | ||
var mm = musicmetadata(files[0]) | ||
@@ -8,0 +8,0 @@ mm.on('metadata', function (result) { |
var strtok = require('strtok2'); | ||
var bufferEqual = require('buffer-equal'); | ||
var equal = require('deep-equal'); | ||
@@ -232,1 +233,105 @@ var asfGuidBuf = new Buffer([ | ||
] | ||
exports.id3BitrateCalculator = function (bits, mpegVersion, layer) { | ||
if (equal(bits, [0, 0, 0, 0])) { | ||
return 'free'; | ||
} | ||
if (equal(bits, [1, 1, 1, 1])) { | ||
return 'reserved'; | ||
} | ||
if (mpegVersion === 1 && layer === 1) { | ||
if (equal(bits, [0, 0, 0, 1])) return 32; | ||
if (equal(bits, [0, 0, 1, 0])) return 64; | ||
if (equal(bits, [0, 0, 1, 1])) return 96; | ||
if (equal(bits, [0, 1, 0, 0])) return 128; | ||
if (equal(bits, [0, 1, 0, 1])) return 160; | ||
if (equal(bits, [0, 1, 1, 0])) return 192; | ||
if (equal(bits, [0, 1, 1, 1])) return 224; | ||
if (equal(bits, [1, 0, 0, 0])) return 256; | ||
if (equal(bits, [1, 0, 0, 1])) return 288; | ||
if (equal(bits, [1, 0, 1, 0])) return 320; | ||
if (equal(bits, [1, 0, 1, 1])) return 352; | ||
if (equal(bits, [1, 1, 0, 0])) return 384; | ||
if (equal(bits, [1, 1, 0, 1])) return 416; | ||
if (equal(bits, [1, 1, 1, 0])) return 448; | ||
} else if (mpegVersion === 1 && layer === 2) { | ||
if (equal(bits, [0, 0, 0, 1])) return 32; | ||
if (equal(bits, [0, 0, 1, 0])) return 48; | ||
if (equal(bits, [0, 0, 1, 1])) return 56; | ||
if (equal(bits, [0, 1, 0, 0])) return 64; | ||
if (equal(bits, [0, 1, 0, 1])) return 80; | ||
if (equal(bits, [0, 1, 1, 0])) return 96; | ||
if (equal(bits, [0, 1, 1, 1])) return 112; | ||
if (equal(bits, [1, 0, 0, 0])) return 128; | ||
if (equal(bits, [1, 0, 0, 1])) return 160; | ||
if (equal(bits, [1, 0, 1, 0])) return 192; | ||
if (equal(bits, [1, 0, 1, 1])) return 224; | ||
if (equal(bits, [1, 1, 0, 0])) return 256; | ||
if (equal(bits, [1, 1, 0, 1])) return 320; | ||
if (equal(bits, [1, 1, 1, 0])) return 384; | ||
} else if (mpegVersion === 1 && layer === 3) { | ||
if (equal(bits, [0, 0, 0, 1])) return 32; | ||
if (equal(bits, [0, 0, 1, 0])) return 40; | ||
if (equal(bits, [0, 0, 1, 1])) return 48; | ||
if (equal(bits, [0, 1, 0, 0])) return 56; | ||
if (equal(bits, [0, 1, 0, 1])) return 64; | ||
if (equal(bits, [0, 1, 1, 0])) return 80; | ||
if (equal(bits, [0, 1, 1, 1])) return 96; | ||
if (equal(bits, [1, 0, 0, 0])) return 112; | ||
if (equal(bits, [1, 0, 0, 1])) return 128; | ||
if (equal(bits, [1, 0, 1, 0])) return 160; | ||
if (equal(bits, [1, 0, 1, 1])) return 192; | ||
if (equal(bits, [1, 1, 0, 0])) return 224; | ||
if (equal(bits, [1, 1, 0, 1])) return 256; | ||
if (equal(bits, [1, 1, 1, 0])) return 320; | ||
} else if (mpegVersion === 2 && layer === 1) { | ||
if (equal(bits, [0, 0, 0, 1])) return 32; | ||
if (equal(bits, [0, 0, 1, 0])) return 48; | ||
if (equal(bits, [0, 0, 1, 1])) return 56; | ||
if (equal(bits, [0, 1, 0, 0])) return 64; | ||
if (equal(bits, [0, 1, 0, 1])) return 80; | ||
if (equal(bits, [0, 1, 1, 0])) return 96; | ||
if (equal(bits, [0, 1, 1, 1])) return 112; | ||
if (equal(bits, [1, 0, 0, 0])) return 128; | ||
if (equal(bits, [1, 0, 0, 1])) return 144; | ||
if (equal(bits, [1, 0, 1, 0])) return 160; | ||
if (equal(bits, [1, 0, 1, 1])) return 176; | ||
if (equal(bits, [1, 1, 0, 0])) return 192; | ||
if (equal(bits, [1, 1, 0, 1])) return 224; | ||
if (equal(bits, [1, 1, 1, 0])) return 256; | ||
} else if (mpegVersion === 2 && (layer === 2 || layer === 3)) { | ||
if (equal(bits, [0, 0, 0, 1])) return 8; | ||
if (equal(bits, [0, 0, 1, 0])) return 16; | ||
if (equal(bits, [0, 0, 1, 1])) return 24; | ||
if (equal(bits, [0, 1, 0, 0])) return 32; | ||
if (equal(bits, [0, 1, 0, 1])) return 40; | ||
if (equal(bits, [0, 1, 1, 0])) return 48; | ||
if (equal(bits, [0, 1, 1, 1])) return 56; | ||
if (equal(bits, [1, 0, 0, 0])) return 64; | ||
if (equal(bits, [1, 0, 0, 1])) return 80; | ||
if (equal(bits, [1, 0, 1, 0])) return 96; | ||
if (equal(bits, [1, 0, 1, 1])) return 112; | ||
if (equal(bits, [1, 1, 0, 0])) return 128; | ||
if (equal(bits, [1, 1, 0, 1])) return 144; | ||
if (equal(bits, [1, 1, 1, 0])) return 160; | ||
} | ||
} | ||
exports.samplingRateCalculator = function (bits, mpegVersion) { | ||
if (equal(bits, [1, 1])) { | ||
return 'reserved'; | ||
} | ||
if (mpegVersion === 1) { | ||
if (equal(bits, [0, 0])) return 44100; | ||
if (equal(bits, [0, 1])) return 48000; | ||
if (equal(bits, [1, 0])) return 32000; | ||
} else if (mpegVersion === 2) { | ||
if (equal(bits, [0, 0])) return 22050; | ||
if (equal(bits, [0, 1])) return 24000; | ||
if (equal(bits, [1, 0])) return 16000; | ||
} else if (mpegVersion === 2.5) { | ||
if (equal(bits, [0, 0])) return 11025; | ||
if (equal(bits, [0, 1])) return 12000; | ||
if (equal(bits, [1, 0])) return 8000; | ||
} | ||
} |
256
lib/id3v2.js
var strtok = require('strtok2'); | ||
var parser = require('./id3v2_frames'); | ||
var common = require('./common'); | ||
var BitArray = require('node-bitarray'); | ||
var equal = require('deep-equal'); | ||
@@ -13,5 +15,2 @@ module.exports = function (stream, callback, done) { | ||
switch (cb.state) { | ||
case -1: // skip | ||
cb.state = 2; | ||
return readFrameHeader(cb.header.major); | ||
case 0: // header | ||
@@ -21,4 +20,3 @@ if (v.toString('ascii', 0, 3) !== 'ID3') { | ||
} | ||
cb.header = { | ||
cb.id3Header = { | ||
version: '2.' + v[3] + '.' + v[4], | ||
@@ -31,80 +29,138 @@ major: v[3], | ||
size: common.strtokINT32SYNCSAFE.get(v, 6) | ||
}; | ||
if (cb.header.xheader) { | ||
cb.state = 1; | ||
return strtok.UINT32_BE; | ||
} | ||
cb.state = 1; | ||
return new strtok.BufferType(cb.id3Header.size); | ||
// expect the first frames header next | ||
case 1: // id3 data | ||
parseMetadata(v, cb.id3Header, callback); | ||
cb.state = 2; | ||
return readFrameHeader(cb.header.major); | ||
return new strtok.BufferType(4); | ||
case 2: // audio frame header | ||
var bts = BitArray.fromBuffer(v); | ||
case 1: // xheader | ||
cb.state = -1; | ||
return new strtok.BufferType(v - 4); | ||
var syncWordBits = bts.slice(0, 11); | ||
if (sum(syncWordBits) != 11) { | ||
return done(new Error('expected frame header but was not found')); | ||
} | ||
case 2: // frameheader | ||
var header = cb.frameHeader = {}; | ||
switch (cb.header.major) { | ||
case 2: | ||
header.id = v.toString('ascii', 0, 3); | ||
header.length = common.strtokUINT24_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 = common.strtokINT32SYNCSAFE.get(v, 4, 8); | ||
header.flags = readFrameFlags(v.slice(8, 10)); | ||
break; | ||
var header = { | ||
'version': readMpegVersion(bts.slice(11, 13)), | ||
'layer': readLayer(bts.slice(13, 15)), | ||
'protection': !bts.__bits[15], | ||
'padding': !!bts.__bits[22], | ||
'mode': readMode(bts.slice(22, 24)) | ||
} | ||
// 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) { | ||
header.samples_per_frame = calcSamplesPerFrame( | ||
header.version, header.layer); | ||
header.bitrate = common.id3BitrateCalculator( | ||
bts.slice(16, 20), header.version, header.layer); | ||
header.sample_rate = common.samplingRateCalculator( | ||
bts.slice(20, 22), header.version); | ||
var sideInfoLength = calculateSideInfoLength( | ||
header.layer, header.mode, header.version); | ||
cb.audioFrameHeader = header; | ||
cb.state = 3; | ||
return new strtok.BufferType(sideInfoLength); | ||
case 3: // side information | ||
cb.state = 4; | ||
return new strtok.BufferType(12); | ||
case 4: // xtra / info header | ||
var id = v.toString('ascii', 0, 4); | ||
if (id !== 'Xtra' && id !== 'Info') return done(); | ||
var bits = BitArray.fromBuffer(v.slice(4, 8)); | ||
// frames field is not present | ||
if (bits.__bits[bits.__bits.length-1] !== 1) { | ||
return done(); | ||
} | ||
cb.state++; | ||
return new strtok.BufferType(header.length); | ||
case 3: // framedata | ||
cb.state = 2; // frameheader up next | ||
var frame, encoding; | ||
switch (cb.header.major) { | ||
case 2: | ||
frame = parser.readData(v, cb.frameHeader.id, null, cb.header.major); | ||
callback(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); | ||
} | ||
frame = parser.readData(v, cb.frameHeader.id, cb.frameHeader.flags, cb.header.major); | ||
callback(cb.frameHeader.id, frame); | ||
return new strtok.BufferType(10); | ||
} | ||
var numFrames = v.readUInt32BE(8); | ||
callback('duration', Math.round(numFrames * | ||
(cb.audioFrameHeader.samples_per_frame / cb.audioFrameHeader.sample_rate))); | ||
return done(); | ||
} | ||
}) | ||
} | ||
function readFrameHeader (majorVer) { | ||
switch (majorVer) { | ||
case 2: | ||
return new strtok.BufferType(6); | ||
case 3: | ||
case 4: | ||
return new strtok.BufferType(10); | ||
default: | ||
return done(new Error('header version is incorrect')); | ||
function parseMetadata (data, header, callback) { | ||
var offset = 0; | ||
if (header.xheader) { | ||
offset += data.readUInt32BE(0); | ||
} | ||
while (true) { | ||
if (offset === data.length) break; | ||
var frameHeaderBytes = data.slice(offset, offset += getFrameHeaderLength(header.major)); | ||
var frameHeader = readFrameHeader(frameHeaderBytes, header.major); | ||
// Last frame. Check first char is a letter, bit of defensive programming | ||
if (frameHeader.id === '' || frameHeader.id === '\u0000\u0000\u0000\u0000' | ||
|| 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.search(frameHeader.id[0]) === -1) { | ||
break; | ||
} | ||
var frameDataBytes = data.slice(offset, offset += frameHeader.length); | ||
var frameData = readFrameData(frameDataBytes, frameHeader, header.major); | ||
callback(frameHeader.id, frameData); | ||
} | ||
} | ||
function readFrameData (v, frameHeader, majorVer) { | ||
switch (majorVer) { | ||
case 2: | ||
return parser.readData(v, frameHeader.id, null, majorVer); | ||
case 3: | ||
case 4: | ||
if (frameHeader.flags.format.unsync) { | ||
v = common.removeUnsyncBytes(v); | ||
} | ||
if (frameHeader.flags.format.data_length_indicator) { | ||
v = v.slice(4, v.length); | ||
} | ||
return parser.readData(v, frameHeader.id, frameHeader.flags, majorVer); | ||
} | ||
} | ||
function readFrameHeader (v, majorVer) { | ||
var header = {}; | ||
switch (majorVer) { | ||
case 2: | ||
header.id = v.toString('ascii', 0, 3); | ||
header.length = common.strtokUINT24_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 = common.strtokINT32SYNCSAFE.get(v, 4, 8); | ||
header.flags = readFrameFlags(v.slice(8, 10)); | ||
break; | ||
} | ||
return header; | ||
} | ||
function getFrameHeaderLength (majorVer) { | ||
switch (majorVer) { | ||
case 2: | ||
return 6; | ||
case 3: | ||
case 4: | ||
return 10; | ||
default: | ||
return done(new Error('header version is incorrect')); | ||
} | ||
} | ||
function readFrameFlags (b) { | ||
@@ -126,1 +182,69 @@ return { | ||
} | ||
function sum (array) { | ||
var result = 0; | ||
for (var i = 0; i < array.length; i++) { | ||
result += array[i] | ||
}; | ||
return result; | ||
} | ||
function readMpegVersion (bits) { | ||
if (equal(bits, [0, 0])) { | ||
return 2.5; | ||
} else if (equal(bits, [0, 1])) { | ||
return 'reserved'; | ||
} else if (equal(bits, [1, 0])) { | ||
return 2; | ||
} else if (equal(bits, [1, 1])) { | ||
return 1; | ||
} | ||
} | ||
function readLayer (bits) { | ||
if (equal(bits, [0, 0])) { | ||
return 'reserved'; | ||
} else if (equal(bits, [0, 1])) { | ||
return 3; | ||
} else if (equal(bits, [1, 0])) { | ||
return 2; | ||
} else if (equal(bits, [1, 1])) { | ||
return 1; | ||
} | ||
} | ||
function readMode (bits) { | ||
if (equal(bits, [0, 0])) { | ||
return 'stereo'; | ||
} else if (equal(bits, [0, 1])) { | ||
return 'joint_stereo'; | ||
} else if (equal(bits, [1, 0])) { | ||
return 'dual_channel'; | ||
} else if (equal(bits, [1, 1])) { | ||
return 'mono'; | ||
} | ||
} | ||
function calcSamplesPerFrame (version, layer) { | ||
if (layer === 1) return 384; | ||
if (layer === 2) return 1152; | ||
if (layer === 3 && version === 1) return 1152; | ||
if (layer === 3 && (version === 2 || version === 2.5)) return 576; | ||
} | ||
function calculateSideInfoLength (layer, mode, version) { | ||
if (layer !== 3) return 2; | ||
if (['stereo', 'joint_stereo', 'dual_channel'].indexOf(mode) >= 0) { | ||
if (version === 1) { | ||
return 32; | ||
} else if (version === 2 || version === 2.5) { | ||
return 17; | ||
} | ||
} else if (mode === 'mono') { | ||
if (version === 1) { | ||
return 17; | ||
} else if (version === 2 || version === 2.5) { | ||
return 9; | ||
} | ||
} | ||
} |
@@ -37,2 +37,7 @@ var strtok = require('strtok2'); | ||
if (v === 'mdhd') { | ||
cb.state = 3; | ||
return new strtok.BufferType(cb.atomLength - 8); | ||
} | ||
if (!~CONTAINER_ATOMS.indexOf(v)) { | ||
@@ -60,2 +65,10 @@ // whats the num for ilst? | ||
return strtok.UINT32_BE; | ||
case 3: // mdhd atom | ||
// TODO: support version 1 | ||
var sampleRate = v.readUInt32BE(12); | ||
var duration = v.readUInt32BE(16); | ||
callback('duration', Math.floor(duration / sampleRate)); | ||
cb.state = 0; | ||
return strtok.UINT32_BE; | ||
} | ||
@@ -121,2 +134,2 @@ | ||
var CONTAINER_ATOMS = ['moov', 'udta', 'meta', 'ilst']; | ||
var CONTAINER_ATOMS = ['moov', 'udta', 'meta', 'ilst', 'trak', 'mdia']; |
@@ -19,2 +19,3 @@ var util = require('util'); | ||
var MusicMetadata = module.exports = function (stream) { | ||
if (!(this instanceof MusicMetadata)) return new MusicMetadata(stream); | ||
if (process.browser) { | ||
@@ -41,3 +42,4 @@ this.stream = wrapFileWithStream(stream); | ||
disk: { no: 0, of: 0 }, | ||
picture: {} | ||
picture: {}, | ||
duration: 0 | ||
} | ||
@@ -77,3 +79,3 @@ | ||
var val; | ||
if (alias === 'title' || alias === 'album' || alias === 'year') { | ||
if (alias === 'title' || alias === 'album' || alias === 'year' || alias === 'duration') { | ||
val = this.aliased[alias][0]; | ||
@@ -250,3 +252,4 @@ } else { | ||
'Cover Art (Front)', 'Cover Art (Back)'], | ||
['composer', 'TCOM', 'TCM', '©wrt', 'COMPOSER'] | ||
['composer', 'TCOM', 'TCM', '©wrt', 'COMPOSER'], | ||
['duration'] | ||
]; |
@@ -10,2 +10,11 @@ var fs = require('fs'); | ||
var pageLength = 0; | ||
var sampleRate = 0; | ||
var header; | ||
stream.on('end', function () { | ||
callback('duration', Math.floor(header.pcm_sample_pos / sampleRate)); | ||
done(); | ||
}) | ||
// top level parser that handles the parsing of pages | ||
@@ -20,7 +29,7 @@ strtok.parse(stream, function (v, cb) { | ||
case 0: // header | ||
var header = { | ||
type: v.toString(0, 4), | ||
header = { | ||
type: v.toString('ascii', 0, 4), | ||
version: v[4], | ||
packet_flag: v[5], | ||
pcm_sample_pos: 'not_implemented', | ||
pcm_sample_pos: (v.readUInt32LE(10) << 32) + v.readUInt32LE(6), | ||
stream_serial_num: strtok.UINT32_LE.get(v, 14), | ||
@@ -31,2 +40,5 @@ page_number: strtok.UINT32_LE.get(v, 18), | ||
} | ||
if (header.type !== 'OggS') { | ||
return done(new Error('expected ogg header but was not found')); | ||
} | ||
cb.pageNumber = header.page_number; | ||
@@ -42,8 +54,7 @@ cb.state++; | ||
cb.state++; | ||
pageLength = pageLen; | ||
return new strtok.BufferType(pageLen); | ||
case 2: // page data | ||
if (cb.pageNumber >= 1) { | ||
innerStream.emit('data', new Buffer(v)); | ||
} | ||
innerStream.emit('data', new Buffer(v)); | ||
cb.state = 0; | ||
@@ -66,4 +77,11 @@ return new strtok.BufferType(27); | ||
case 0: // type | ||
cb.state++; | ||
return strtok.UINT32_LE; | ||
if (v.toString() === '\x01vorbis') { | ||
cb.state = 6; | ||
return new strtok.BufferType(23); | ||
} else if (v.toString() === '\x03vorbis') { | ||
cb.state++; | ||
return strtok.UINT32_LE; | ||
} else { | ||
return done(new Error('expected vorbis header but found something else')); | ||
} | ||
@@ -76,6 +94,8 @@ case 1: // vendor length | ||
cb.state++; | ||
return strtok.UINT32_LE; | ||
return new strtok.BufferType(4); | ||
case 3: // user comment list length | ||
cb.commentsLength = v; | ||
cb.commentsLength = v.readUInt32LE(0); | ||
// no metadata, stop parsing | ||
if (cb.commentsLength === 0) return strtok.DONE; | ||
cb.state++; | ||
@@ -101,8 +121,21 @@ return strtok.UINT32_LE; | ||
if (cb.commentsRead === cb.commentsLength) return done(); | ||
if (cb.commentsRead === cb.commentsLength) { | ||
return strtok.DONE; | ||
} | ||
cb.state--; // back to comment length | ||
return strtok.UINT32_LE; | ||
case 6: // vorbis info | ||
var info = { | ||
'version': v.readUInt32LE(0), | ||
'channel_mode': v.readUInt8(4), | ||
'sample_rate': v.readUInt32LE(5), | ||
'bitrate_nominal': v.readUInt32LE(13) | ||
} | ||
sampleRate = info.sample_rate; | ||
cb.state = 0; | ||
return new strtok.BufferType(7); | ||
} | ||
}) | ||
} |
{ | ||
"name": "musicmetadata", | ||
"description": "Streaming music metadata parser for node and the browser, written in pure Javascript.", | ||
"version": "0.2.7", | ||
"description": "Streaming music metadata parser for node and the browser.", | ||
"version": "0.3.0", | ||
"author": "Lee Treveil", | ||
@@ -11,3 +11,5 @@ "dependencies": { | ||
"through": "~2.3.4", | ||
"drag-and-drop-files": "0.0.1" | ||
"drag-and-drop-files": "0.0.1", | ||
"node-bitarray": "0.0.2", | ||
"deep-equal": "~0.1.2" | ||
}, | ||
@@ -31,6 +33,7 @@ "keywords": [ | ||
"scripts": { | ||
"test": "[ ! -d 'test/' ] && echo 'The test directory is not included with the project due to the size of the test audio files. If you want to run the tests you can git clone the project.' || tape test/test-*.js" | ||
"test": "[ ! -d 'test/' ] && echo 'The test directory is not included with the project due to the size of the test audio files. If you want to run the tests you can git clone the project.' || tap test/test-*.js" | ||
}, | ||
"devDependencies": { | ||
"tape": "~2.3.2" | ||
"tape": "~2.3.2", | ||
"tap": "~0.4.8" | ||
}, | ||
@@ -50,3 +53,3 @@ "testling": { | ||
"safari": [ | ||
6 | ||
5.1 | ||
] | ||
@@ -53,0 +56,0 @@ } |
@@ -1,6 +0,6 @@ | ||
[![Build Status](https://secure.travis-ci.org/leetreveil/node-musicmetadata.png)](http://travis-ci.org/leetreveil/node-musicmetadata) | ||
[![Build Status](https://secure.travis-ci.org/leetreveil/musicmetadata.png)](http://travis-ci.org/leetreveil/musicmetadata) | ||
[![browser support](https://ci.testling.com/leetreveil/node-musicmetadata.png)](https://ci.testling.com/leetreveil/node-musicmetadata) | ||
[![browser support](https://ci.testling.com/leetreveil/musicmetadata.png)](https://ci.testling.com/leetreveil/musicmetadata) | ||
Streaming music metadata parser for node and the browser, written in pure Javascript. | ||
Streaming music metadata parser for node and the browser. | ||
@@ -42,3 +42,3 @@ Installation | ||
// create a new parser from a node ReadStream | ||
var parser = new mm(fs.createReadStream('sample.mp3')); | ||
var parser = mm(fs.createReadStream('sample.mp3')); | ||
@@ -62,3 +62,4 @@ // listen for the metadata event | ||
disk : { no : 1, of : 2 }, | ||
picture : [ { format : 'jpg', data : <Buffer> } ] | ||
picture : [ { format : 'jpg', data : <Buffer> } ], | ||
duration : 302 // in seconds | ||
} | ||
@@ -75,3 +76,3 @@ ``` | ||
You can also listen for custom metadata types that are not part of the standard metadata as defined above. For example if you wanted to read the TLEN frame from a id3v2.x file you can do this: | ||
You can also listen for custom metadata types that are not part of the standard metadata as defined above. For example if you wanted to read the `TLEN` frame from a id3v2.x file you can do this: | ||
@@ -78,0 +79,0 @@ ```javascript |
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
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 2 instances in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
5465572
25
8442
104
7
2
11
5
+ Addeddeep-equal@~0.1.2
+ Addednode-bitarray@0.0.2
+ Addeddeep-equal@0.1.2(transitive)
+ Addednode-bitarray@0.0.2(transitive)