Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

musicmetadata

Package Overview
Dependencies
Maintainers
1
Versions
60
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

musicmetadata - npm Package Compare versions

Comparing version 0.2.7 to 0.3.0

abc.jpg

2

example/main.js

@@ -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;
}
}
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

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc