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

music-metadata

Package Overview
Dependencies
Maintainers
1
Versions
256
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

music-metadata - npm Package Compare versions

Comparing version 0.1.0 to 0.1.1

1

lib/id3v2.js

@@ -1,2 +0,1 @@

/* jshint maxlen: 120 */
'use strict'

@@ -3,0 +2,0 @@ var strtok = require('strtok2')

262

lib/monkeysaudio.js
'use strict'
var common = require('./common')
var strtok = require('strtok2')
var type = 'APEv2'
var type = 'APEv2' // ToDo: version should be made dynamic, APE may also contain ID3
var ape = {}
/**
* APETag version history / supported formats
*
* 1.0 (1000) - Original APE tag spec. Fully supported by this code.
* 2.0 (2000) - Refined APE tag spec (better streaming support, UTF encoding). Fully supported by this code.
*
* Notes:
* - also supports reading of ID3v1.1 tags
* - all saving done in the APE Tag format using CURRENT_APE_TAG_VERSION
*
* APE File Format Overview: (pieces in order -- only valid for the latest version APE files)
*
* JUNK - any amount of "junk" before the APE_DESCRIPTOR (so people that put ID3v2 tags on the files aren't hosed)
* APE_DESCRIPTOR - defines the sizes (and offsets) of all the pieces, as well as the MD5 checksum
* APE_HEADER - describes all of the necessary information about the APE file
* SEEK TABLE - the table that represents seek offsets [optional]
* HEADER DATA - the pre-audio data from the original file [optional]
* APE FRAMES - the actual compressed audio (broken into frames for seekability)
* TERMINATING DATA - the post-audio data from the original file [optional]
* TAG - describes all the properties of the file [optional]
*/
module.exports = function (stream, callback, done) {
var ApeDescriptor = {
len: 44,
strtok.parse(stream, function (v, cb) {
if (v === undefined) {
cb.state = 'descriptor'
return Ape.descriptor
}
switch (cb.state) {
case 'descriptor':
if (v.ID !== 'MAC ') {
throw new Error('Expected MAC on beginning of file') // ToDo: strip/parse JUNK
}
ape.descriptor = v
var lenExp = v.descriptorBytes - ape.descriptor.len
if (lenExp > 0) {
cb.state = 'descriptorExpansion'
return new strtok.IgnoreType(lenExp)
} else {
cb.state = 'header'
return Ape.header
}
cb.state = 'descriptorExpansion'
return new strtok.IgnoreType(lenExp)
case 'descriptorExpansion':
cb.state = 'header'
return Ape.header
case 'header':
ape.header = v
callback('format', 'tagType', type)
callback('format', 'bitsPerSample', v.bitsPerSample)
callback('format', 'sampleRate', v.sampleRate)
callback('format', 'numberOfChannels', v.channel)
callback('format', 'duration', calculateDuration(v))
var forwardBytes = ape.descriptor.seekTableBytes + ape.descriptor.headerDataBytes +
ape.descriptor.apeFrameDataBytes + ape.descriptor.terminatingDataBytes
cb.state = 'skipData'
return new strtok.IgnoreType(forwardBytes)
case 'skipData':
cb.state = 'tagFooter'
return Ape.tagFooter
case 'tagFooter':
if (v.ID !== 'APETAGEX') {
done(new Error('Expected footer to start with APETAGEX '))
}
ape.footer = v
cb.state = 'tagField'
return Ape.tagField(v)
case 'tagField':
parseTags(ape.footer, v, callback)
done()
break
default:
done(new Error('Illegal state: ' + cb.state))
}
return 0
})
}
var Ape = {
/**
* APE_DESCRIPTOR: defines the sizes (and offsets) of all the pieces, as well as the MD5 checksum
*/
descriptor: {
len: 52,
get: function (buf, off) {
return {
// should equal 'MAC '
ID: new strtok.StringType(4, 'ascii').get(buf, off),
// version number * 1000 (3.81 = 3810) (remember that 4-byte alignment causes this to take 4-bytes)
version: strtok.UINT32_LE.get(buf, off + 4) / 1000,
// the number of descriptor bytes (allows later expansion of this header)
descriptorBytes: strtok.UINT32_LE.get(buf, off + 8),
headerDataBytes: strtok.UINT32_LE.get(buf, off + 12),
APEFrameDataBytes: strtok.UINT32_LE.get(buf, off + 16),
APEFrameDataBytesHigh: strtok.UINT32_LE.get(buf, off + 20),
terminatingDataBytes: strtok.UINT32_LE.get(buf, off + 24),
fileMD5: new strtok.BufferType(16).get(buf, 28)
// the number of header APE_HEADER bytes
headerBytes: strtok.UINT32_LE.get(buf, off + 12),
// the number of header APE_HEADER bytes
seekTableBytes: strtok.UINT32_LE.get(buf, off + 16),
// the number of header data bytes (from original file)
headerDataBytes: strtok.UINT32_LE.get(buf, off + 20),
// the number of bytes of APE frame data
apeFrameDataBytes: strtok.UINT32_LE.get(buf, off + 24),
// the high order number of APE frame data bytes
apeFrameDataBytesHigh: strtok.UINT32_LE.get(buf, off + 28),
// the terminating data of the file (not including tag data)
terminatingDataBytes: strtok.UINT32_LE.get(buf, off + 32),
// the MD5 hash of the file (see notes for usage... it's a littly tricky)
fileMD5: new strtok.BufferType(16).get(buf, off + 36)
}
}
}
},
// headerDataBytes = 24
var ApeHeader = {
/**
* APE_HEADER: describes all of the necessary information about the APE file
*/
header: {
len: 24,

@@ -31,43 +137,47 @@

return {
// the compression level (see defines I.E. COMPRESSION_LEVEL_FAST)
compressionLevel: strtok.UINT16_LE.get(buf, off),
// any format flags (for future use)
formatFlags: strtok.UINT16_LE.get(buf, off + 2),
// the number of audio blocks in one frame
blocksPerFrame: strtok.UINT32_LE.get(buf, off + 4),
// the number of audio blocks in the final frame
finalFrameBlocks: strtok.UINT32_LE.get(buf, off + 8),
// the total number of frames
totalFrames: strtok.UINT32_LE.get(buf, off + 12),
// the bits per sample (typically 16)
bitsPerSample: strtok.UINT16_LE.get(buf, off + 16),
// the number of channels (1 or 2)
channel: strtok.UINT16_LE.get(buf, off + 18),
// the sample rate (typically 44100)
sampleRate: strtok.UINT32_LE.get(buf, off + 20)
}
}
}
},
strtok.parse(stream, function (v, cb) {
if (v === undefined) {
cb.state = 0
return ApeDescriptor
}
/**
* TAG: describes all the properties of the file [optional]
*/
tagFooter: {
len: 32,
switch (cb.state) {
case 0:
if (v.ID !== 'MAC ') {
throw new Error('Expected MAC on beginning of file')
}
cb.state = 1
return new strtok.BufferType(v.descriptorBytes - 44)
case 1:
cb.state = 2
return ApeHeader
case 2:
callback('format', 'tagType', type)
callback('format', 'bitsPerSample', v.bitsPerSample)
callback('format', 'sampleRate', v.sampleRate)
callback('format', 'numberOfChannels', v.channel)
callback('format', 'duration', calculateDuration(v))
return -1
get: function (buf, off) {
return {
// should equal 'APETAGEX'
ID: new strtok.StringType(8, 'ascii').get(buf, off),
// equals CURRENT_APE_TAG_VERSION
version: strtok.UINT32_LE.get(buf, off + 8),
// the complete size of the tag, including this footer (excludes header)
size: strtok.UINT32_LE.get(buf, off + 12),
// the number of fields in the tag
fields: strtok.UINT32_LE.get(buf, off + 16),
// reserved for later use (must be zero)
reserved: new strtok.BufferType(12).get(buf, off + 20) // ToDo: what is this???
}
}
})
},
return readMetadata(stream, callback, done)
tagField: function (footer) {
return new strtok.BufferType(footer.size - Ape.tagFooter.len)
}
}

@@ -86,37 +196,15 @@

function readMetadata (stream, callback, done) {
var bufs = []
function parseTags (footer, buffer, callback) {
var offset = 0
// TODO: need to be able to parse the tag if its at the start of the file
stream.on('data', function (data) {
bufs.push(data)
})
for (var i = 0; i < footer.fields; i++) {
var size = strtok.UINT32_LE.get(buffer, offset, offset += 4)
var flags = parseTagFlags(strtok.UINT32_LE.get(buffer, offset, offset += 4))
common.streamOnRealEnd(stream, function () {
var buffer = Buffer.concat(bufs)
var offset = buffer.length - 32
var zero = common.findZero(buffer, offset, buffer.length)
var key = buffer.toString('ascii', offset, zero)
offset = zero + 1
if (buffer.toString('utf8', offset, offset += 8) !== 'APETAGEX') {
done(new Error("expected APE header but wasn't found"))
}
var footer = {
version: strtok.UINT32_LE.get(buffer, offset, offset + 4),
size: strtok.UINT32_LE.get(buffer, offset + 4, offset + 8),
count: strtok.UINT32_LE.get(buffer, offset + 8, offset + 12)
}
// go 'back' to where the 'tags' start
offset = buffer.length - footer.size
for (var i = 0; i < footer.count; i++) {
var size = strtok.UINT32_LE.get(buffer, offset, offset += 4)
var flags = strtok.UINT32_LE.get(buffer, offset, offset += 4)
var kind = (flags & 6) >> 1
var zero = common.findZero(buffer, offset, buffer.length)
var key = buffer.toString('ascii', offset, zero)
offset = zero + 1
if (kind === 0) { // utf-8 textstring
switch (flags.dataType) {
case 'text_utf8': { // utf-8 textstring
var value = buffer.toString('utf8', offset, offset += size)

@@ -129,3 +217,5 @@ var values = value.split(/\x00/g)

})
} else if (kind === 1) { // binary (probably artwork)
}
break
case 'binary': { // binary (probably artwork)
if (key === 'Cover Art (Front)' || key === 'Cover Art (Back)') {

@@ -148,5 +238,29 @@ var picData = buffer.slice(offset, offset + size)

}
break
}
return done()
})
}
}
function parseTagFlags (flags) {
return {
containsHeader: isBitSet(flags, 31),
containsFooter: isBitSet(flags, 30),
isHeader: isBitSet(flags, 31),
readOnly: isBitSet(flags, 0),
dataType: getDataType((flags & 6) >> 1)
}
}
function getDataType (type) {
var types = ['text_utf8', 'binary', 'external_info', 'reserved']
return types[type]
}
/**
* @param num {number}
* @param bit 0 is least significant bit (LSB)
* @return {boolean} true if bit is 1; otherwise false
*/
function isBitSet (num, bit) {
return (num & 1 << bit) !== 0
}
{
"name": "music-metadata",
"description": "Streaming music metadata parser for node and the browser.",
"version": "0.1.0",
"version": "0.1.1",
"author": "Borewit",

@@ -70,3 +70,3 @@ "dependencies": {

"node": true,
"maxlen": 100,
"maxlen": 120,
"indent": 2

@@ -73,0 +73,0 @@ },

@@ -23,5 +23,4 @@ [![Build Status][travis-image]][travis-url] [![NPM version][npm-image]][npm-url] [![npm downloads][npm-downloads-image]][npm-url]

* asf (wma, wmv)
* MonkeyAudio, APEv2 (ape)
* ape (MonkeyAudio)
API

@@ -44,98 +43,98 @@ -----------------

{
"common": {
"title": "Lungs",
"artist": ["I Have A Tribe"],
"albumartist": ["I Have A Tribe"],
"album": "No Countries",
"year": "2015",
"track": {"no": 4, "of": 5},
"genre": ["Pop Rock"],
"disk": {"no": 1, "of": 1},
"picture": [
common: {
title: 'Lungs',
artist: ['I Have A Tribe'],
albumartist: ['I Have A Tribe'],
album: 'No Countries',
year: '2015',
track: {no: 4, of: 5},
genre: ['Pop Rock'],
disk: {'no: 1, 'of: 1},
picture: [
{
"format": "jpg",
"data": {
"type": "Buffer",
"data": ["..."]
format: 'jpg',
data: {
type: 'Buffer',
data: ['...']
}
}
],
"grouping": "Rock",
"copyright": "2015 Grönland Records",
"releasecountry": "DE",
"label": "Grönland Records",
"musicbrainz_albumartistid": ["d8e73ae6-9884-4061-a056-c686b3375c9d"],
"date": "2015-10-16",
"musicbrainz_trackid": "ed040a93-1f95-4f91-8c41-359f5a6e7770",
"albumartistsort": ["I Have a Tribe"],
"originaldate": "2015-10-16",
"script": "Latn",
"musicbrainz_albumid": "4f54e938-89b4-4ee8-b282-74964f1e23bb",
"releasestatus": "official",
"acoustid_id": "5c94b20e-be79-4f6d-9800-d4caf8bc2a76",
"catalognumber": "DAGRON153",
"musicbrainz_artistid": ["d8e73ae6-9884-4061-a056-c686b3375c9d"],
"media": "Digital Media",
"releasetype": ["ep"],
"originalyear": "2015",
"musicbrainz_releasegroupid": "9c288627-be99-490e-9d3e-e6b135e9b8dd",
"musicbrainz_recordingid": "a1a9ede1-219b-464c-9520-d9fd1debf933",
"artistsort": ["I Have a Tribe"]
grouping: 'Rock',
copyright: '2015 Grönland Records',
releasecountry: 'DE',
label: 'Grönland Records',
musicbrainz_albumartistid: ['d8e73ae6-9884-4061-a056-c686b3375c9d'],
date: '2015-10-16',
musicbrainz_trackid: 'ed040a93-1f95-4f91-8c41-359f5a6e7770',
albumartistsort: ['I Have a Tribe'],
originaldate: '2015-10-16',
script: 'Latn',
musicbrainz_albumid: '4f54e938-89b4-4ee8-b282-74964f1e23bb',
releasestatus: 'official',
acoustid_id: '5c94b20e-be79-4f6d-9800-d4caf8bc2a76',
catalognumber: 'DAGRON153',
musicbrainz_artistid: ['d8e73ae6-9884-4061-a056-c686b3375c9d'],
media': 'Digital Media',
releasetype: ['ep'],
originalyear: '2015',
musicbrainz_releasegroupid: '9c288627-be99-490e-9d3e-e6b135e9b8dd',
musicbrainz_recordingid: 'a1a9ede1-219b-464c-9520-d9fd1debf933',
artistsort: ['I Have a Tribe']
},
"format": {
"duration": 266.56,
"numberOfChannels": 2,
"bitsPerSample": 16,
"tagType": "vorbis",
"sampleRate": 44100
format: {
duration: 266.56,
numberOfChannels: 2,
bitsPerSample: 16,
tagType: 'vorbis',
sampleRate: 44100
},
"vorbis": {
"GROUPING": "Rock",
"COPYRIGHT": "2015 Grönland Records",
"GENRE": ["Pop Rock"],
"DESCRIPTION": ["Interprètes : I Have A Tribe, Main Artist; Patrick O'Laoghaire, Composer, Lyricist; Copyright Control\r\nLabel : Grönland Records - GoodToGo\r\n"],
"TITLE": "Lungs",
"RELEASECOUNTRY": "DE",
"TOTALDISCS": ["1"],
"LABEL": "Grönland Records",
"TOTALTRACKS": ["5"],
"MUSICBRAINZ_ALBUMARTISTID": ["d8e73ae6-9884-4061-a056-c686b3375c9d"],
"DATE": "2015-10-16",
"DISCNUMBER": "1",
"TRACKTOTAL": "5",
"MUSICBRAINZ_RELEASETRACKID": "ed040a93-1f95-4f91-8c41-359f5a6e7770",
"ALBUMARTISTSORT": ["I Have a Tribe"],
"ORIGINALDATE": "2015-10-16",
"SCRIPT": "Latn",
"MUSICBRAINZ_ALBUMID": "4f54e938-89b4-4ee8-b282-74964f1e23bb",
"RELEASESTATUS": "official",
"ALBUMARTIST": ["I Have A Tribe"],
"ACOUSTID_ID": "5c94b20e-be79-4f6d-9800-d4caf8bc2a76",
"CATALOGNUMBER": "DAGRON153",
"ALBUM": "No Countries",
"MUSICBRAINZ_ARTISTID": ["d8e73ae6-9884-4061-a056-c686b3375c9d"],
"MEDIA": "Digital Media",
"RELEASETYPE": ["ep"],
"ORIGINALYEAR": "2015",
"ARTIST": ["I Have A Tribe"],
"DISCTOTAL": "1",
"MUSICBRAINZ_RELEASEGROUPID": "9c288627-be99-490e-9d3e-e6b135e9b8dd",
"MUSICBRAINZ_TRACKID": "a1a9ede1-219b-464c-9520-d9fd1debf933",
"ARTISTSORT": ["I Have a Tribe"],
"ARTISTS": ["I Have A Tribe"],
"TRACKNUMBER": "4",
"METADATA_BLOCK_PICTURE": [
vorbis: {
GROUPING: 'Rock',
COPYRIGHT: '2015 Grönland Records',
GENRE: ['Pop Rock'],
DESCRIPTION: ['Interprètes : I Have A Tribe, Main Artist; Patrick O'Laoghaire, Composer, Lyricist; Copyright Control\r\nLabel : Grönland Records - GoodToGo\r\n'],
TITLE: 'Lungs',
RELEASECOUNTRY: 'DE',
TOTALDISCS: ['1'],
LABEL: 'Grönland Records',
TOTALTRACKS: ['5'],
MUSICBRAINZ_ALBUMARTISTID: ['d8e73ae6-9884-4061-a056-c686b3375c9d'],
DATE: '2015-10-16',
DISCNUMBER: '1',
TRACKTOTAL: '5',
MUSICBRAINZ_RELEASETRACKID: 'ed040a93-1f95-4f91-8c41-359f5a6e7770',
ALBUMARTISTSORT: ['I Have a Tribe'],
ORIGINALDATE: '2015-10-16',
SCRIPT: 'Latn',
MUSICBRAINZ_ALBUMID: '4f54e938-89b4-4ee8-b282-74964f1e23bb',
RELEASESTATUS: 'official',
ALBUMARTIST: ['I Have A Tribe'],
ACOUSTID_ID: '5c94b20e-be79-4f6d-9800-d4caf8bc2a76',
CATALOGNUMBER: 'DAGRON153',
ALBUM: 'No Countries',
MUSICBRAINZ_ARTISTID: ['d8e73ae6-9884-4061-a056-c686b3375c9d'],
MEDIA: 'Digital Media',
RELEASETYPE: ['ep'],
ORIGINALYEAR: '2015',
ARTIST: ['I Have A Tribe'],
DISCTOTAL: '1',
MUSICBRAINZ_RELEASEGROUPID: '9c288627-be99-490e-9d3e-e6b135e9b8dd',
MUSICBRAINZ_TRACKID: 'a1a9ede1-219b-464c-9520-d9fd1debf933',
ARTISTSORT: ['I Have a Tribe'],
ARTISTS: ['I Have A Tribe'],
TRACKNUMBER: '4',
METADATA_BLOCK_PICTURE: [
{
"type": "Cover (front)",
"format": "image/jpeg",
"description": "Official cover included in digital release",
"width": 0,
"height": 0,
"colour_depth": 0,
"indexed_color": 0,
"data": {
"type": "Buffer",
"data": ["..."]
type: 'Cover (front)',
format: 'image/jpeg',
description: 'Official cover included in digital release',
width: 0,
height: 0,
colour_depth: 0,
indexed_color: 0,
data: {
type: 'Buffer',
data: ['...']
}

@@ -187,3 +186,3 @@ }

Based on [musicmetadata] (https://github.com/leetreveil/musicmetadata/) written by Lee Treveil <leetreveil@gmail.com> and many others.
Based on [musicmetadata](https://github.com/leetreveil/musicmetadata/) written by Lee Treveil <leetreveil@gmail.com> and many others.

@@ -201,2 +200,2 @@ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

[travis-url]: https://travis-ci.org/profile/Borewit/music-metadata
[travis-image]: https://api.travis-ci.org/Borewit/music-metadata.svg?branch=master
[travis-image]: https://travis-ci.org/Borewit/music-metadata.svg?branch=master

Sorry, the diff of this file is too big to display

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