Socket
Socket
Sign inDemoInstall

jsmediatags

Package Overview
Dependencies
Maintainers
1
Versions
33
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

jsmediatags - npm Package Compare versions

Comparing version 3.7.0 to 3.8.0

build2/FLACTagContents.js

65

build2/ArrayFileReader.js
'use strict';
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
var MediaFileReader = require('./MediaFileReader');
class ArrayFileReader extends MediaFileReader {
var ArrayFileReader = function (_MediaFileReader) {
_inherits(ArrayFileReader, _MediaFileReader);
constructor(array) {
super();
this._array = array;
this._size = array.length;
this._isInitialized = true;
}
function ArrayFileReader(array) {
_classCallCheck(this, ArrayFileReader);
static canReadFile(file) {
return Array.isArray(file) || typeof Buffer === 'function' && Buffer.isBuffer(file);
}
var _this = _possibleConstructorReturn(this, (ArrayFileReader.__proto__ || Object.getPrototypeOf(ArrayFileReader)).call(this));
init(callbacks) {
setTimeout(callbacks.onSuccess, 0);
_this._array = array;
_this._size = array.length;
_this._isInitialized = true;
return _this;
}
loadRange(range, callbacks) {
setTimeout(callbacks.onSuccess, 0);
}
getByteAt(offset) {
if (offset >= this._array.length) {
throw new Error("Offset " + offset + " hasn't been loaded yet.");
_createClass(ArrayFileReader, [{
key: 'init',
value: function init(callbacks) {
setTimeout(callbacks.onSuccess, 0);
}
return this._array[offset];
}
}
}, {
key: 'loadRange',
value: function loadRange(range, callbacks) {
setTimeout(callbacks.onSuccess, 0);
}
}, {
key: 'getByteAt',
value: function getByteAt(offset) {
if (offset >= this._array.length) {
throw new Error("Offset " + offset + " hasn't been loaded yet.");
}
return this._array[offset];
}
}], [{
key: 'canReadFile',
value: function canReadFile(file) {
return Array.isArray(file) || typeof Buffer === 'function' && Buffer.isBuffer(file);
}
}]);
return ArrayFileReader;
}(MediaFileReader);
module.exports = ArrayFileReader;
'use strict';
const ChunkedFileData = require('./ChunkedFileData');
const MediaFileReader = require('./MediaFileReader');
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
class BlobFileReader extends MediaFileReader {
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
constructor(blob) {
super();
this._blob = blob;
this._fileData = new ChunkedFileData();
}
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
static canReadFile(file) {
return typeof Blob !== "undefined" && file instanceof Blob ||
// File extends Blob but it seems that File instanceof Blob doesn't
// quite work as expected in Cordova/PhoneGap.
typeof File !== "undefined" && file instanceof File;
}
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
_init(callbacks) {
this._size = this._blob.size;
setTimeout(callbacks.onSuccess, 1);
}
var ChunkedFileData = require('./ChunkedFileData');
var MediaFileReader = require('./MediaFileReader');
loadRange(range, callbacks) {
var self = this;
// $FlowIssue - flow isn't aware of mozSlice or webkitSlice
var blobSlice = this._blob.slice || this._blob.mozSlice || this._blob.webkitSlice;
var blob = blobSlice.call(this._blob, range[0], range[1] + 1);
var browserFileReader = new FileReader();
var BlobFileReader = function (_MediaFileReader) {
_inherits(BlobFileReader, _MediaFileReader);
browserFileReader.onloadend = function (event) {
var intArray = new Uint8Array(browserFileReader.result);
self._fileData.addData(range[0], intArray);
callbacks.onSuccess();
};
browserFileReader.onerror = browserFileReader.onabort = function (event) {
if (callbacks.onError) {
callbacks.onError({ "type": "blob", "info": browserFileReader.error });
}
};
function BlobFileReader(blob) {
_classCallCheck(this, BlobFileReader);
browserFileReader.readAsArrayBuffer(blob);
}
var _this = _possibleConstructorReturn(this, (BlobFileReader.__proto__ || Object.getPrototypeOf(BlobFileReader)).call(this));
getByteAt(offset) {
return this._fileData.getByteAt(offset);
_this._blob = blob;
_this._fileData = new ChunkedFileData();
return _this;
}
}
_createClass(BlobFileReader, [{
key: '_init',
value: function _init(callbacks) {
this._size = this._blob.size;
setTimeout(callbacks.onSuccess, 1);
}
}, {
key: 'loadRange',
value: function loadRange(range, callbacks) {
var self = this;
// $FlowIssue - flow isn't aware of mozSlice or webkitSlice
var blobSlice = this._blob.slice || this._blob.mozSlice || this._blob.webkitSlice;
var blob = blobSlice.call(this._blob, range[0], range[1] + 1);
var browserFileReader = new FileReader();
browserFileReader.onloadend = function (event) {
var intArray = new Uint8Array(Number(browserFileReader.result));
self._fileData.addData(range[0], intArray);
callbacks.onSuccess();
};
browserFileReader.onerror = browserFileReader.onabort = function (event) {
if (callbacks.onError) {
callbacks.onError({ "type": "blob", "info": browserFileReader.error });
}
};
browserFileReader.readAsArrayBuffer(blob);
}
}, {
key: 'getByteAt',
value: function getByteAt(offset) {
return this._fileData.getByteAt(offset);
}
}], [{
key: 'canReadFile',
value: function canReadFile(file) {
return typeof Blob !== "undefined" && file instanceof Blob ||
// File extends Blob but it seems that File instanceof Blob doesn't
// quite work as expected in Cordova/PhoneGap.
typeof File !== "undefined" && file instanceof File;
}
}]);
return BlobFileReader;
}(MediaFileReader);
module.exports = BlobFileReader;

@@ -6,3 +6,3 @@ 'use strict';

*/
const bin = function (string) {
var bin = function (string) {
var binaryArray = new Array(string.length);

@@ -18,3 +18,3 @@ for (var i = 0; i < string.length; i++) {

*/
const pad = function (array, size) {
var pad = function (array, size) {
for (var i = array.length; i < size; i++) {

@@ -30,3 +30,3 @@ array.push(0);

// as $00 00 02 01.
const getSynchsafeInteger32 = function (number) {
var getSynchsafeInteger32 = function (number) {
// 0x7f = 0b01111111

@@ -36,7 +36,7 @@ return [number >> 21 & 0x7f, number >> 14 & 0x7f, number >> 7 & 0x7f, number & 0x7f];

const getInteger32 = function (number) {
var getInteger32 = function (number) {
return [number >> 24 & 0xff, number >> 16 & 0xff, number >> 8 & 0xff, number & 0xff];
};
const getInteger24 = function (number) {
var getInteger24 = function (number) {
return [number >> 16 & 0xff, number >> 8 & 0xff, number & 0xff];

@@ -43,0 +43,0 @@ };

@@ -14,12 +14,21 @@ /**

const NOT_FOUND = -1;
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
class ChunkedFileData {
// $FlowIssue - get/set properties not yet supported
static get NOT_FOUND() {
return NOT_FOUND;
}
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var NOT_FOUND = -1;
constructor() {
var ChunkedFileData = function () {
_createClass(ChunkedFileData, null, [{
key: 'NOT_FOUND',
// $FlowIssue - get/set properties not yet supported
get: function () {
return NOT_FOUND;
}
}]);
function ChunkedFileData() {
_classCallCheck(this, ChunkedFileData);
this._fileData = [];

@@ -31,166 +40,180 @@ }

*/
addData(offset, data) {
var offsetEnd = offset + data.length - 1;
var chunkRange = this._getChunkRange(offset, offsetEnd);
if (chunkRange.startIx === NOT_FOUND) {
this._fileData.splice(chunkRange.insertIx || 0, 0, {
offset: offset,
data: data
});
} else {
// If the data to add collides with existing chunks we prepend and
// append data from the half colliding chunks to make the collision at
// 100%. The new data can then replace all the colliding chunkes.
var firstChunk = this._fileData[chunkRange.startIx];
var lastChunk = this._fileData[chunkRange.endIx];
var needsPrepend = offset > firstChunk.offset;
var needsAppend = offsetEnd < lastChunk.offset + lastChunk.data.length - 1;
var chunk = {
offset: Math.min(offset, firstChunk.offset),
data: data
};
_createClass(ChunkedFileData, [{
key: 'addData',
value: function addData(offset, data) {
var offsetEnd = offset + data.length - 1;
var chunkRange = this._getChunkRange(offset, offsetEnd);
if (needsPrepend) {
var slicedData = this._sliceData(firstChunk.data, 0, offset - firstChunk.offset);
chunk.data = this._concatData(slicedData, data);
}
if (chunkRange.startIx === NOT_FOUND) {
this._fileData.splice(chunkRange.insertIx || 0, 0, {
offset: offset,
data: data
});
} else {
// If the data to add collides with existing chunks we prepend and
// append data from the half colliding chunks to make the collision at
// 100%. The new data can then replace all the colliding chunkes.
var firstChunk = this._fileData[chunkRange.startIx];
var lastChunk = this._fileData[chunkRange.endIx];
var needsPrepend = offset > firstChunk.offset;
var needsAppend = offsetEnd < lastChunk.offset + lastChunk.data.length - 1;
if (needsAppend) {
// Use the lastChunk because the slice logic is easier to handle.
var slicedData = this._sliceData(chunk.data, 0, lastChunk.offset - chunk.offset);
chunk.data = this._concatData(slicedData, lastChunk.data);
}
var chunk = {
offset: Math.min(offset, firstChunk.offset),
data: data
};
this._fileData.splice(chunkRange.startIx, chunkRange.endIx - chunkRange.startIx + 1, chunk);
}
}
if (needsPrepend) {
var slicedData = this._sliceData(firstChunk.data, 0, offset - firstChunk.offset);
chunk.data = this._concatData(slicedData, data);
}
_concatData(dataA, dataB) {
// TypedArrays don't support concat.
if (typeof ArrayBuffer !== "undefined" && ArrayBuffer.isView && ArrayBuffer.isView(dataA)) {
// $FlowIssue - flow thinks dataAandB is a string but it's not
var dataAandB = new dataA.constructor(dataA.length + dataB.length);
// $FlowIssue - flow thinks dataAandB is a string but it's not
dataAandB.set(dataA, 0);
// $FlowIssue - flow thinks dataAandB is a string but it's not
dataAandB.set(dataB, dataA.length);
return dataAandB;
} else {
// $FlowIssue - flow thinks dataAandB is a TypedArray but it's not
return dataA.concat(dataB);
}
}
if (needsAppend) {
// Use the lastChunk because the slice logic is easier to handle.
var slicedData = this._sliceData(chunk.data, 0, lastChunk.offset - chunk.offset);
chunk.data = this._concatData(slicedData, lastChunk.data);
}
_sliceData(data, begin, end) {
// Some TypeArray implementations do not support slice yet.
if (data.slice) {
return data.slice(begin, end);
} else {
// $FlowIssue - flow thinks data is a string but it's not
return data.subarray(begin, end);
this._fileData.splice(chunkRange.startIx, chunkRange.endIx - chunkRange.startIx + 1, chunk);
}
}
}
/**
* Finds the chunk range that overlaps the [offsetStart-1,offsetEnd+1] range.
* When a chunk is adjacent to the offset we still consider it part of the
* range (this is the situation of offsetStart-1 or offsetEnd+1).
* When no chunks are found `insertIx` denotes the index where the data
* should be inserted in the data list (startIx == NOT_FOUND and endIX ==
* NOT_FOUND).
*/
_getChunkRange(offsetStart, offsetEnd) {
var startChunkIx = NOT_FOUND;
var endChunkIx = NOT_FOUND;
var insertIx = 0;
// Could use binary search but not expecting that many blocks to exist.
for (var i = 0; i < this._fileData.length; i++, insertIx = i) {
var chunkOffsetStart = this._fileData[i].offset;
var chunkOffsetEnd = chunkOffsetStart + this._fileData[i].data.length;
if (offsetEnd < chunkOffsetStart - 1) {
// This offset range doesn't overlap with any chunks.
break;
}, {
key: '_concatData',
value: function _concatData(dataA, dataB) {
// TypedArrays don't support concat.
if (typeof ArrayBuffer !== "undefined" && ArrayBuffer.isView && ArrayBuffer.isView(dataA)) {
// $FlowIssue - flow thinks dataAandB is a string but it's not
var dataAandB = new dataA.constructor(dataA.length + dataB.length);
// $FlowIssue - flow thinks dataAandB is a string but it's not
dataAandB.set(dataA, 0);
// $FlowIssue - flow thinks dataAandB is a string but it's not
dataAandB.set(dataB, dataA.length);
return dataAandB;
} else {
// $FlowIssue - flow thinks dataAandB is a TypedArray but it's not
return dataA.concat(dataB);
}
// If it is adjacent we still consider it part of the range because
// we're going end up with a single block with all contiguous data.
if (offsetStart <= chunkOffsetEnd + 1 && offsetEnd >= chunkOffsetStart - 1) {
startChunkIx = i;
break;
}
}, {
key: '_sliceData',
value: function _sliceData(data, begin, end) {
// Some TypeArray implementations do not support slice yet.
if (data.slice) {
return data.slice(begin, end);
} else {
// $FlowIssue - flow thinks data is a string but it's not
return data.subarray(begin, end);
}
}
// No starting chunk was found, meaning that the offset is either before
// or after the current stored chunks.
if (startChunkIx === NOT_FOUND) {
return {
startIx: NOT_FOUND,
endIx: NOT_FOUND,
insertIx: insertIx
};
}
/**
* Finds the chunk range that overlaps the [offsetStart-1,offsetEnd+1] range.
* When a chunk is adjacent to the offset we still consider it part of the
* range (this is the situation of offsetStart-1 or offsetEnd+1).
* When no chunks are found `insertIx` denotes the index where the data
* should be inserted in the data list (startIx == NOT_FOUND and endIX ==
* NOT_FOUND).
*/
// Find the ending chunk.
for (var i = startChunkIx; i < this._fileData.length; i++) {
var chunkOffsetStart = this._fileData[i].offset;
var chunkOffsetEnd = chunkOffsetStart + this._fileData[i].data.length;
}, {
key: '_getChunkRange',
value: function _getChunkRange(offsetStart, offsetEnd) {
var startChunkIx = NOT_FOUND;
var endChunkIx = NOT_FOUND;
var insertIx = 0;
if (offsetEnd >= chunkOffsetStart - 1) {
// Candidate for the end chunk, it doesn't mean it is yet.
endChunkIx = i;
// Could use binary search but not expecting that many blocks to exist.
for (var i = 0; i < this._fileData.length; i++, insertIx = i) {
var chunkOffsetStart = this._fileData[i].offset;
var chunkOffsetEnd = chunkOffsetStart + this._fileData[i].data.length;
if (offsetEnd < chunkOffsetStart - 1) {
// This offset range doesn't overlap with any chunks.
break;
}
// If it is adjacent we still consider it part of the range because
// we're going end up with a single block with all contiguous data.
if (offsetStart <= chunkOffsetEnd + 1 && offsetEnd >= chunkOffsetStart - 1) {
startChunkIx = i;
break;
}
}
if (offsetEnd <= chunkOffsetEnd + 1) {
break;
// No starting chunk was found, meaning that the offset is either before
// or after the current stored chunks.
if (startChunkIx === NOT_FOUND) {
return {
startIx: NOT_FOUND,
endIx: NOT_FOUND,
insertIx: insertIx
};
}
}
if (endChunkIx === NOT_FOUND) {
endChunkIx = startChunkIx;
}
// Find the ending chunk.
for (var i = startChunkIx; i < this._fileData.length; i++) {
var chunkOffsetStart = this._fileData[i].offset;
var chunkOffsetEnd = chunkOffsetStart + this._fileData[i].data.length;
return {
startIx: startChunkIx,
endIx: endChunkIx
};
}
if (offsetEnd >= chunkOffsetStart - 1) {
// Candidate for the end chunk, it doesn't mean it is yet.
endChunkIx = i;
}
if (offsetEnd <= chunkOffsetEnd + 1) {
break;
}
}
hasDataRange(offsetStart, offsetEnd) {
for (var i = 0; i < this._fileData.length; i++) {
var chunk = this._fileData[i];
if (offsetEnd < chunk.offset) {
return false;
if (endChunkIx === NOT_FOUND) {
endChunkIx = startChunkIx;
}
if (offsetStart >= chunk.offset && offsetEnd < chunk.offset + chunk.data.length) {
return true;
}
return {
startIx: startChunkIx,
endIx: endChunkIx
};
}
}, {
key: 'hasDataRange',
value: function hasDataRange(offsetStart, offsetEnd) {
for (var i = 0; i < this._fileData.length; i++) {
var chunk = this._fileData[i];
if (offsetEnd < chunk.offset) {
return false;
}
return false;
}
if (offsetStart >= chunk.offset && offsetEnd < chunk.offset + chunk.data.length) {
return true;
}
}
getByteAt(offset) {
var dataChunk;
return false;
}
}, {
key: 'getByteAt',
value: function getByteAt(offset) {
var dataChunk;
for (var i = 0; i < this._fileData.length; i++) {
var dataChunkStart = this._fileData[i].offset;
var dataChunkEnd = dataChunkStart + this._fileData[i].data.length - 1;
for (var i = 0; i < this._fileData.length; i++) {
var dataChunkStart = this._fileData[i].offset;
var dataChunkEnd = dataChunkStart + this._fileData[i].data.length - 1;
if (offset >= dataChunkStart && offset <= dataChunkEnd) {
dataChunk = this._fileData[i];
break;
if (offset >= dataChunkStart && offset <= dataChunkEnd) {
dataChunk = this._fileData[i];
break;
}
}
}
if (dataChunk) {
return dataChunk.data[offset - dataChunk.offset];
if (dataChunk) {
return dataChunk.data[offset - dataChunk.offset];
}
throw new Error("Offset " + offset + " hasn't been loaded yet.");
}
}]);
throw new Error("Offset " + offset + " hasn't been loaded yet.");
}
}
return ChunkedFileData;
}();
module.exports = ChunkedFileData;
'use strict';
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
var MediaTagReader = require('./MediaTagReader');
var MediaFileReader = require('./MediaFileReader');
class ID3v1TagReader extends MediaTagReader {
static getTagIdentifierByteRange() {
// The identifier is TAG and is at offset: -128. However, to avoid a
// fetch for the tag identifier and another for the data, we load the
// entire data since it's so small.
return {
offset: -128,
length: 128
};
}
var ID3v1TagReader = function (_MediaTagReader) {
_inherits(ID3v1TagReader, _MediaTagReader);
static canReadTagFormat(tagIdentifier) {
var id = String.fromCharCode.apply(String, tagIdentifier.slice(0, 3));
return id === "TAG";
}
function ID3v1TagReader() {
_classCallCheck(this, ID3v1TagReader);
_loadData(mediaFileReader, callbacks) {
var fileSize = mediaFileReader.getSize();
mediaFileReader.loadRange([fileSize - 128, fileSize - 1], callbacks);
return _possibleConstructorReturn(this, (ID3v1TagReader.__proto__ || Object.getPrototypeOf(ID3v1TagReader)).apply(this, arguments));
}
_parseData(data, tags) {
var offset = data.getSize() - 128;
_createClass(ID3v1TagReader, [{
key: '_loadData',
value: function _loadData(mediaFileReader, callbacks) {
var fileSize = mediaFileReader.getSize();
mediaFileReader.loadRange([fileSize - 128, fileSize - 1], callbacks);
}
}, {
key: '_parseData',
value: function _parseData(data, tags) {
var offset = data.getSize() - 128;
var title = data.getStringWithCharsetAt(offset + 3, 30).toString();
var artist = data.getStringWithCharsetAt(offset + 33, 30).toString();
var album = data.getStringWithCharsetAt(offset + 63, 30).toString();
var year = data.getStringWithCharsetAt(offset + 93, 4).toString();
var title = data.getStringWithCharsetAt(offset + 3, 30).toString();
var artist = data.getStringWithCharsetAt(offset + 33, 30).toString();
var album = data.getStringWithCharsetAt(offset + 63, 30).toString();
var year = data.getStringWithCharsetAt(offset + 93, 4).toString();
var trackFlag = data.getByteAt(offset + 97 + 28);
var track = data.getByteAt(offset + 97 + 29);
if (trackFlag == 0 && track != 0) {
var version = "1.1";
var comment = data.getStringWithCharsetAt(offset + 97, 28).toString();
} else {
var version = "1.0";
var comment = data.getStringWithCharsetAt(offset + 97, 30).toString();
track = 0;
}
var trackFlag = data.getByteAt(offset + 97 + 28);
var track = data.getByteAt(offset + 97 + 29);
if (trackFlag == 0 && track != 0) {
var version = "1.1";
var comment = data.getStringWithCharsetAt(offset + 97, 28).toString();
} else {
var version = "1.0";
var comment = data.getStringWithCharsetAt(offset + 97, 30).toString();
track = 0;
}
var genreIdx = data.getByteAt(offset + 97 + 30);
if (genreIdx < 255) {
var genre = GENRES[genreIdx];
} else {
var genre = "";
}
var genreIdx = data.getByteAt(offset + 97 + 30);
if (genreIdx < 255) {
var genre = GENRES[genreIdx];
} else {
var genre = "";
}
var tag = {
"type": "ID3",
"version": version,
"tags": {
"title": title,
"artist": artist,
"album": album,
"year": year,
"comment": comment,
"genre": genre
var tag = {
"type": "ID3",
"version": version,
"tags": {
"title": title,
"artist": artist,
"album": album,
"year": year,
"comment": comment,
"genre": genre
}
};
if (track) {
// $FlowIssue - flow is not happy with adding properties
tag.tags.track = track;
}
};
if (track) {
// $FlowIssue - flow is not happy with adding properties
tag.tags.track = track;
return tag;
}
}], [{
key: 'getTagIdentifierByteRange',
value: function getTagIdentifierByteRange() {
// The identifier is TAG and is at offset: -128. However, to avoid a
// fetch for the tag identifier and another for the data, we load the
// entire data since it's so small.
return {
offset: -128,
length: 128
};
}
}, {
key: 'canReadTagFormat',
value: function canReadTagFormat(tagIdentifier) {
var id = String.fromCharCode.apply(String, tagIdentifier.slice(0, 3));
return id === "TAG";
}
}]);
return tag;
}
}
return ID3v1TagReader;
}(MediaTagReader);

@@ -75,0 +99,0 @@ var GENRES = ["Blues", "Classic Rock", "Country", "Dance", "Disco", "Funk", "Grunge", "Hip-Hop", "Jazz", "Metal", "New Age", "Oldies", "Other", "Pop", "R&B", "Rap", "Reggae", "Rock", "Techno", "Industrial", "Alternative", "Ska", "Death Metal", "Pranks", "Soundtrack", "Euro-Techno", "Ambient", "Trip-Hop", "Vocal", "Jazz+Funk", "Fusion", "Trance", "Classical", "Instrumental", "Acid", "House", "Game", "Sound Clip", "Gospel", "Noise", "AlternRock", "Bass", "Soul", "Punk", "Space", "Meditative", "Instrumental Pop", "Instrumental Rock", "Ethnic", "Gothic", "Darkwave", "Techno-Industrial", "Electronic", "Pop-Folk", "Eurodance", "Dream", "Southern Rock", "Comedy", "Cult", "Gangsta", "Top 40", "Christian Rap", "Pop/Funk", "Jungle", "Native American", "Cabaret", "New Wave", "Psychadelic", "Rave", "Showtunes", "Trailer", "Lo-Fi", "Tribal", "Acid Punk", "Acid Jazz", "Polka", "Retro", "Musical", "Rock & Roll", "Hard Rock", "Folk", "Folk-Rock", "National Folk", "Swing", "Fast Fusion", "Bebob", "Latin", "Revival", "Celtic", "Bluegrass", "Avantgarde", "Gothic Rock", "Progressive Rock", "Psychedelic Rock", "Symphonic Rock", "Slow Rock", "Big Band", "Chorus", "Easy Listening", "Acoustic", "Humour", "Speech", "Chanson", "Opera", "Chamber Music", "Sonata", "Symphony", "Booty Bass", "Primus", "Porn Groove", "Satire", "Slow Jam", "Club", "Tango", "Samba", "Folklore", "Ballad", "Power Ballad", "Rhythmic Soul", "Freestyle", "Duet", "Punk Rock", "Drum Solo", "Acapella", "Euro-House", "Dance Hall"];

'use strict';
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var MediaFileReader = require('./MediaFileReader');
const StringUtils = require('./StringUtils');
var StringUtils = require('./StringUtils');
var ArrayFileReader = require('./ArrayFileReader');
const FRAME_DESCRIPTIONS = {
var FRAME_DESCRIPTIONS = {
// v2.2

@@ -166,194 +170,213 @@ "BUF": "Recommended buffer size",

class ID3v2FrameReader {
static getFrameReaderFunction(frameId) {
if (frameId in frameReaderFunctions) {
return frameReaderFunctions[frameId];
} else if (frameId[0] === "T") {
// All frame ids starting with T are text tags.
return frameReaderFunctions["T*"];
} else if (frameId[0] === "W") {
// All frame ids starting with W are url tags.
return frameReaderFunctions["W*"];
} else {
return null;
}
var ID3v2FrameReader = function () {
function ID3v2FrameReader() {
_classCallCheck(this, ID3v2FrameReader);
}
/**
* All the frames consists of a frame header followed by one or more fields
* containing the actual information.
* The frame ID made out of the characters capital A-Z and 0-9. Identifiers
* beginning with "X", "Y" and "Z" are for experimental use and free for
* everyone to use, without the need to set the experimental bit in the tag
* header. Have in mind that someone else might have used the same identifier
* as you. All other identifiers are either used or reserved for future use.
* The frame ID is followed by a size descriptor, making a total header size
* of ten bytes in every frame. The size is calculated as frame size excluding
* frame header (frame size - 10).
*/
static readFrames(offset, end, data, id3header, tags) {
var frames = {};
var frameHeaderSize = this._getFrameHeaderSize(id3header);
// console.log('header', id3header);
while (
// we should be able to read at least the frame header
offset < end - frameHeaderSize) {
var header = this._readFrameHeader(data, offset, id3header);
var frameId = header.id;
// No frame ID sometimes means it's the last frame (GTFO).
if (!frameId) {
break;
_createClass(ID3v2FrameReader, null, [{
key: 'getFrameReaderFunction',
value: function getFrameReaderFunction(frameId) {
if (frameId in frameReaderFunctions) {
return frameReaderFunctions[frameId];
} else if (frameId[0] === "T") {
// All frame ids starting with T are text tags.
return frameReaderFunctions["T*"];
} else if (frameId[0] === "W") {
// All frame ids starting with W are url tags.
return frameReaderFunctions["W*"];
} else {
return null;
}
}
/**
* All the frames consists of a frame header followed by one or more fields
* containing the actual information.
* The frame ID made out of the characters capital A-Z and 0-9. Identifiers
* beginning with "X", "Y" and "Z" are for experimental use and free for
* everyone to use, without the need to set the experimental bit in the tag
* header. Have in mind that someone else might have used the same identifier
* as you. All other identifiers are either used or reserved for future use.
* The frame ID is followed by a size descriptor, making a total header size
* of ten bytes in every frame. The size is calculated as frame size excluding
* frame header (frame size - 10).
*/
var flags = header.flags;
var frameSize = header.size;
var frameDataOffset = offset + header.headerSize;
var frameData = data;
}, {
key: 'readFrames',
value: function readFrames(offset, end, data, id3header, tags) {
var frames = {};
var frameHeaderSize = this._getFrameHeaderSize(id3header);
// console.log('header', id3header);
while (
// we should be able to read at least the frame header
offset < end - frameHeaderSize) {
var header = this._readFrameHeader(data, offset, id3header);
var frameId = header.id;
// console.log(offset, frameId, header.size + header.headerSize, flags && flags.format.unsynchronisation);
// advance data offset to the next frame data
offset += header.headerSize + header.size;
// No frame ID sometimes means it's the last frame (GTFO).
if (!frameId) {
break;
}
// skip unwanted tags
if (tags && tags.indexOf(frameId) === -1) {
continue;
}
// Workaround: MP3ext V3.3.17 places a non-compliant padding string at
// the end of the ID3v2 header. A string like "MP3ext V3.3.19(ansi)"
// is added multiple times at the end of the ID3 tag. More information
// about this issue can be found at
// https://github.com/aadsm/jsmediatags/issues/58#issuecomment-313865336
if (frameId === 'MP3e' || frameId === '\x00MP3' || frameId === '\x00\x00MP' || frameId === ' MP3') {
break;
}
var flags = header.flags;
var frameSize = header.size;
var frameDataOffset = offset + header.headerSize;
var frameData = data;
var unsyncData;
if (flags && flags.format.unsynchronisation) {
frameData = this.getUnsyncFileReader(frameData, frameDataOffset, frameSize);
frameDataOffset = 0;
frameSize = frameData.getSize();
}
// console.log(offset, frameId, header.size + header.headerSize, flags && flags.format.unsynchronisation);
// advance data offset to the next frame data
offset += header.headerSize + header.size;
// the first 4 bytes are the real data size
// (after unsynchronisation && encryption)
if (flags && flags.format.data_length_indicator) {
// var frameDataSize = frameData.getSynchsafeInteger32At(frameDataOffset);
frameDataOffset += 4;
frameSize -= 4;
}
// skip unwanted tags
if (tags && tags.indexOf(frameId) === -1) {
continue;
}
// Workaround: MP3ext V3.3.17 places a non-compliant padding string at
// the end of the ID3v2 header. A string like "MP3ext V3.3.19(ansi)"
// is added multiple times at the end of the ID3 tag. More information
// about this issue can be found at
// https://github.com/aadsm/jsmediatags/issues/58#issuecomment-313865336
if (frameId === 'MP3e' || frameId === '\x00MP3' || frameId === '\x00\x00MP' || frameId === ' MP3') {
break;
}
var readFrameFunc = ID3v2FrameReader.getFrameReaderFunction(frameId);
var parsedData = readFrameFunc ? readFrameFunc.apply(this, [frameDataOffset, frameSize, frameData, flags, id3header]) : null;
var desc = this._getFrameDescription(frameId);
var unsyncData;
if (flags && flags.format.unsynchronisation) {
frameData = this.getUnsyncFileReader(frameData, frameDataOffset, frameSize);
frameDataOffset = 0;
frameSize = frameData.getSize();
}
var frame = {
id: frameId,
size: frameSize,
description: desc,
data: parsedData
};
// the first 4 bytes are the real data size
// (after unsynchronisation && encryption)
if (flags && flags.format.data_length_indicator) {
// var frameDataSize = frameData.getSynchsafeInteger32At(frameDataOffset);
frameDataOffset += 4;
frameSize -= 4;
}
if (frameId in frames) {
if (frames[frameId].id) {
frames[frameId] = [frames[frameId]];
var readFrameFunc = ID3v2FrameReader.getFrameReaderFunction(frameId);
var parsedData = readFrameFunc ? readFrameFunc.apply(this, [frameDataOffset, frameSize, frameData, flags, id3header]) : null;
var desc = this._getFrameDescription(frameId);
var frame = {
id: frameId,
size: frameSize,
description: desc,
data: parsedData
};
if (frameId in frames) {
if (frames[frameId].id) {
frames[frameId] = [frames[frameId]];
}
frames[frameId].push(frame);
} else {
frames[frameId] = frame;
}
frames[frameId].push(frame);
}
return frames;
}
}, {
key: '_getFrameHeaderSize',
value: function _getFrameHeaderSize(id3header) {
var major = id3header.major;
if (major == 2) {
return 6;
} else if (major == 3 || major == 4) {
return 10;
} else {
frames[frameId] = frame;
return 0;
}
}
}, {
key: '_readFrameHeader',
value: function _readFrameHeader(data, offset, id3header) {
var major = id3header.major;
var flags = null;
var frameHeaderSize = this._getFrameHeaderSize(id3header);
return frames;
}
switch (major) {
case 2:
var frameId = data.getStringAt(offset, 3);
var frameSize = data.getInteger24At(offset + 3, true);
break;
static _getFrameHeaderSize(id3header) {
var major = id3header.major;
case 3:
var frameId = data.getStringAt(offset, 4);
var frameSize = data.getLongAt(offset + 4, true);
break;
if (major == 2) {
return 6;
} else if (major == 3 || major == 4) {
return 10;
} else {
return 0;
}
}
case 4:
var frameId = data.getStringAt(offset, 4);
var frameSize = data.getSynchsafeInteger32At(offset + 4);
break;
}
static _readFrameHeader(data, offset, id3header) {
var major = id3header.major;
var flags = null;
var frameHeaderSize = this._getFrameHeaderSize(id3header);
if (frameId == String.fromCharCode(0, 0, 0) || frameId == String.fromCharCode(0, 0, 0, 0)) {
frameId = "";
}
switch (major) {
case 2:
var frameId = data.getStringAt(offset, 3);
var frameSize = data.getInteger24At(offset + 3, true);
break;
// if frameId is empty then it's the last frame
if (frameId) {
// read frame message and format flags
if (major > 2) {
flags = this._readFrameFlags(data, offset + 8);
}
}
case 3:
var frameId = data.getStringAt(offset, 4);
var frameSize = data.getLongAt(offset + 4, true);
break;
case 4:
var frameId = data.getStringAt(offset, 4);
var frameSize = data.getSynchsafeInteger32At(offset + 4);
break;
return {
"id": frameId || "",
"size": frameSize || 0,
"headerSize": frameHeaderSize || 0,
"flags": flags
};
}
if (frameId == String.fromCharCode(0, 0, 0) || frameId == String.fromCharCode(0, 0, 0, 0)) {
frameId = "";
}, {
key: '_readFrameFlags',
value: function _readFrameFlags(data, offset) {
return {
message: {
tag_alter_preservation: data.isBitSetAt(offset, 6),
file_alter_preservation: data.isBitSetAt(offset, 5),
read_only: data.isBitSetAt(offset, 4)
},
format: {
grouping_identity: data.isBitSetAt(offset + 1, 7),
compression: data.isBitSetAt(offset + 1, 3),
encryption: data.isBitSetAt(offset + 1, 2),
unsynchronisation: data.isBitSetAt(offset + 1, 1),
data_length_indicator: data.isBitSetAt(offset + 1, 0)
}
};
}
// if frameId is empty then it's the last frame
if (frameId) {
// read frame message and format flags
if (major > 2) {
flags = this._readFrameFlags(data, offset + 8);
}, {
key: '_getFrameDescription',
value: function _getFrameDescription(frameId) {
if (frameId in FRAME_DESCRIPTIONS) {
return FRAME_DESCRIPTIONS[frameId];
} else {
return 'Unknown';
}
}
return {
"id": frameId || "",
"size": frameSize || 0,
"headerSize": frameHeaderSize || 0,
"flags": flags
};
}
static _readFrameFlags(data, offset) {
return {
message: {
tag_alter_preservation: data.isBitSetAt(offset, 6),
file_alter_preservation: data.isBitSetAt(offset, 5),
read_only: data.isBitSetAt(offset, 4)
},
format: {
grouping_identity: data.isBitSetAt(offset + 1, 7),
compression: data.isBitSetAt(offset + 1, 3),
encryption: data.isBitSetAt(offset + 1, 2),
unsynchronisation: data.isBitSetAt(offset + 1, 1),
data_length_indicator: data.isBitSetAt(offset + 1, 0)
}, {
key: 'getUnsyncFileReader',
value: function getUnsyncFileReader(data, offset, size) {
var frameData = data.getBytesAt(offset, size);
for (var i = 0; i < frameData.length - 1; i++) {
if (frameData[i] === 0xff && frameData[i + 1] === 0x00) {
frameData.splice(i + 1, 1);
}
}
};
}
static _getFrameDescription(frameId) {
if (frameId in FRAME_DESCRIPTIONS) {
return FRAME_DESCRIPTIONS[frameId];
} else {
return 'Unknown';
return new ArrayFileReader(frameData);
}
}
}]);
static getUnsyncFileReader(data, offset, size) {
var frameData = data.getBytesAt(offset, size);
for (var i = 0; i < frameData.length - 1; i++) {
if (frameData[i] === 0xff && frameData[i + 1] === 0x00) {
frameData.splice(i + 1, 1);
}
}
return ID3v2FrameReader;
}();
return new ArrayFileReader(frameData);
}
};
;

@@ -380,3 +403,3 @@ var frameReaderFunctions = {};

}
var bite = data.getByteAt(offset, 1);
var bite = data.getByteAt(offset);
var type = PICTURE_TYPE[bite];

@@ -419,3 +442,3 @@ var desc = data.getStringWithCharsetAt(offset + 1, length - (offset - start) - 1, charset);

var originalOffset = offset;
var result = { childElementIds: [] };
var result = { childElementIds: [], id: undefined, topLevel: undefined, ordered: undefined, entryCount: undefined, subFrames: undefined };
var id = StringUtils.readNullTerminatedString(data.getBytesAt(offset, length));

@@ -548,2 +571,5 @@ result.id = id.toString();

break;
default:
charset = 'iso-8859-1';
}

@@ -550,0 +576,0 @@

@@ -14,22 +14,27 @@ /**

const ByteArrayUtils = require('./ByteArrayUtils');
const bin = ByteArrayUtils.bin;
const getSynchsafeInteger32 = ByteArrayUtils.getSynchsafeInteger32;
const getInteger32 = ByteArrayUtils.getInteger32;
const getInteger24 = ByteArrayUtils.getInteger24;
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var ByteArrayUtils = require('./ByteArrayUtils');
var bin = ByteArrayUtils.bin;
var getSynchsafeInteger32 = ByteArrayUtils.getSynchsafeInteger32;
var getInteger32 = ByteArrayUtils.getInteger32;
var getInteger24 = ByteArrayUtils.getInteger24;
// Offsets
const FLAGS = 5;
const SIZE = 6;
const EXTENDED_HEADER = 10;
const EXTENDED_FLAGS_V3 = 14;
const EXTENDED_FLAGS_V4 = 15;
const START_EXTENDED_DATA_V3 = 20;
const START_EXTENDED_DATA_V4 = 16;
var FLAGS = 5;
var SIZE = 6;
var EXTENDED_HEADER = 10;
var EXTENDED_FLAGS_V3 = 14;
var EXTENDED_FLAGS_V4 = 15;
var START_EXTENDED_DATA_V3 = 20;
var START_EXTENDED_DATA_V4 = 16;
// Sizes
const HEADER_SIZE = 10;
var HEADER_SIZE = 10;
class ID3v2TagContents {
var ID3v2TagContents = function () {
function ID3v2TagContents(major, revision) {
_classCallCheck(this, ID3v2TagContents);
constructor(major, revision) {
if (major < 2 || major > 4) {

@@ -54,280 +59,301 @@ throw new Error('Major version not supported');

toArray() {
return this._contents.slice(0);
}
_createClass(ID3v2TagContents, [{
key: 'toArray',
value: function toArray() {
return this._contents.slice(0);
}
}, {
key: 'setFlags',
value: function setFlags(flags) {
var binaryFlags = 0;
setFlags(flags) {
var binaryFlags = 0;
if (flags.unsynchronisation) {
binaryFlags |= 1 << 7;
}
if (flags.extended_header) {
binaryFlags |= 1 << 6;
}
if (flags.experimental_indicator) {
binaryFlags |= 1 << 5;
}
if (flags.footer_present) {
binaryFlags |= 1 << 4;
}
if (flags.unsynchronisation) {
binaryFlags |= 1 << 7;
this._contents[FLAGS] = binaryFlags;
return this;
}
if (flags.extended_header) {
binaryFlags |= 1 << 6;
}
if (flags.experimental_indicator) {
binaryFlags |= 1 << 5;
}
if (flags.footer_present) {
binaryFlags |= 1 << 4;
}
}, {
key: 'setCrc',
value: function setCrc(crc) {
if (!this._hasExtendedHeader) {
this._initExtendedHeader();
}
this._contents[FLAGS] = binaryFlags;
return this;
}
if (this._major === 3) {
this._setBitAtOffset(EXTENDED_FLAGS_V3, 7);
this._setData(START_EXTENDED_DATA_V3, crc);
this._extendedHeader['CRC'] = crc.length;
// Update extended header size.
this._setData(EXTENDED_HEADER, getInteger32(10));
} else if (this._major === 4) {
this._setBitAtOffset(EXTENDED_FLAGS_V4, 5);
this._addExtendedHeaderData('CRC', crc);
}
setCrc(crc) {
if (!this._hasExtendedHeader) {
this._initExtendedHeader();
this._updateSize();
return this;
}
}, {
key: 'setTagIsUpdate',
value: function setTagIsUpdate() {
if (!this._hasExtendedHeader) {
this._initExtendedHeader();
}
if (this._major === 3) {
this._setBitAtOffset(EXTENDED_FLAGS_V3, 7);
this._setData(START_EXTENDED_DATA_V3, crc);
this._extendedHeader['CRC'] = crc.length;
// Update extended header size.
this._setData(EXTENDED_HEADER, getInteger32(10));
} else if (this._major === 4) {
this._setBitAtOffset(EXTENDED_FLAGS_V4, 5);
this._addExtendedHeaderData('CRC', crc);
}
if (this._major === 4) {
this._setBitAtOffset(EXTENDED_FLAGS_V4, 6);
this._addExtendedHeaderData('UPDATE', []);
}
this._updateSize();
return this;
}
setTagIsUpdate() {
if (!this._hasExtendedHeader) {
this._initExtendedHeader();
return this;
}
if (this._major === 4) {
this._setBitAtOffset(EXTENDED_FLAGS_V4, 6);
this._addExtendedHeaderData('UPDATE', []);
}
/**
* For some applications it might be desired to restrict a tag in more
* ways than imposed by the ID3v2 specification. Note that the
* presence of these restrictions does not affect how the tag is
* decoded, merely how it was restricted before encoding. If this flag
* is set the tag is restricted as follows:
*
* Flag data length $01
* Restrictions %ppqrrstt
*
* p - Tag size restrictions
* 00 No more than 128 frames and 1 MB total tag size.
* 01 No more than 64 frames and 128 KB total tag size.
* 10 No more than 32 frames and 40 KB total tag size.
* 11 No more than 32 frames and 4 KB total tag size.
*
* q - Text encoding restrictions
*
* 0 No restrictions
* 1 Strings are only encoded with ISO-8859-1 [ISO-8859-1] or
* UTF-8 [UTF-8].
*
* r - Text fields size restrictions
*
* 00 No restrictions
* 01 No string is longer than 1024 characters.
* 10 No string is longer than 128 characters.
* 11 No string is longer than 30 characters.
*
* Note that nothing is said about how many bytes is used to
* represent those characters, since it is encoding dependent. If a
* text frame consists of more than one string, the sum of the
* strungs is restricted as stated.
*
* s - Image encoding restrictions
*
* 0 No restrictions
* 1 Images are encoded only with PNG [PNG] or JPEG [JFIF].
*
* t - Image size restrictions
*
* 00 No restrictions
* 01 All images are 256x256 pixels or smaller.
* 10 All images are 64x64 pixels or smaller.
* 11 All images are exactly 64x64 pixels, unless required
* otherwise.
*/
return this;
}
}, {
key: 'setTagRestrictions',
value: function setTagRestrictions(size, textEncoding, textSize, imageEncoding, imageSize) {
if (!this._hasExtendedHeader) {
this._initExtendedHeader();
}
/**
* For some applications it might be desired to restrict a tag in more
* ways than imposed by the ID3v2 specification. Note that the
* presence of these restrictions does not affect how the tag is
* decoded, merely how it was restricted before encoding. If this flag
* is set the tag is restricted as follows:
*
* Flag data length $01
* Restrictions %ppqrrstt
*
* p - Tag size restrictions
* 00 No more than 128 frames and 1 MB total tag size.
* 01 No more than 64 frames and 128 KB total tag size.
* 10 No more than 32 frames and 40 KB total tag size.
* 11 No more than 32 frames and 4 KB total tag size.
*
* q - Text encoding restrictions
*
* 0 No restrictions
* 1 Strings are only encoded with ISO-8859-1 [ISO-8859-1] or
* UTF-8 [UTF-8].
*
* r - Text fields size restrictions
*
* 00 No restrictions
* 01 No string is longer than 1024 characters.
* 10 No string is longer than 128 characters.
* 11 No string is longer than 30 characters.
*
* Note that nothing is said about how many bytes is used to
* represent those characters, since it is encoding dependent. If a
* text frame consists of more than one string, the sum of the
* strungs is restricted as stated.
*
* s - Image encoding restrictions
*
* 0 No restrictions
* 1 Images are encoded only with PNG [PNG] or JPEG [JFIF].
*
* t - Image size restrictions
*
* 00 No restrictions
* 01 All images are 256x256 pixels or smaller.
* 10 All images are 64x64 pixels or smaller.
* 11 All images are exactly 64x64 pixels, unless required
* otherwise.
*/
setTagRestrictions(size, textEncoding, textSize, imageEncoding, imageSize) {
if (!this._hasExtendedHeader) {
this._initExtendedHeader();
}
if (this._major === 4) {
this._setBitAtOffset(EXTENDED_FLAGS_V4, 4);
// 0x03 = 0b11
this._addExtendedHeaderData('RESTRICTIONS', [(size & 0x3) << 6 | (textEncoding & 0x1) << 5 | (textSize & 0x3) << 3 | (imageEncoding & 0x1) << 2 | (imageSize & 0x3) << 1]);
}
if (this._major === 4) {
this._setBitAtOffset(EXTENDED_FLAGS_V4, 4);
// 0x03 = 0b11
this._addExtendedHeaderData('RESTRICTIONS', [(size & 0x3) << 6 | (textEncoding & 0x1) << 5 | (textSize & 0x3) << 3 | (imageEncoding & 0x1) << 2 | (imageSize & 0x3) << 1]);
return this;
}
return this;
}
/**
* noFlagsDataLength - The data length if all flags were set to 0,
* for instance, the length before compression and unsynchronisation.
* This field is only needed when data_length_indicator flag is set.
*/
/**
* noFlagsDataLength - The data length if all flags were set to 0,
* for instance, the length before compression and unsynchronisation.
* This field is only needed when data_length_indicator flag is set.
*/
addFrame(id, data, flags, noFlagsDataLength) {
var size = 0;
var frameFlags = [0, 0];
if (flags) {
flags.message = flags.message || {};
flags.format = flags.format || {};
}
data = data || [];
}, {
key: 'addFrame',
value: function addFrame(id, data, flags, noFlagsDataLength) {
var size = 0;
var frameFlags = [0, 0];
if (flags) {
flags.message = flags.message || {};
flags.format = flags.format || {};
}
data = data || [];
var dataLength = data.length;
var isTagUnsynchronised = this._contents[FLAGS] & 1 << 7;
if (isTagUnsynchronised) {
var unsynchronisedByteCount = 0;
var dataLength = data.length;
var isTagUnsynchronised = this._contents[FLAGS] & 1 << 7;
if (isTagUnsynchronised) {
var unsynchronisedByteCount = 0;
for (var i = 0; i < data.length - 1; i++) {
if (data[i] === 0xff && data[i + 1] === 0x00) {
unsynchronisedByteCount++;
for (var i = 0; i < data.length - 1; i++) {
if (data[i] === 0xff && data[i + 1] === 0x00) {
unsynchronisedByteCount++;
}
}
dataLength -= unsynchronisedByteCount;
}
dataLength -= unsynchronisedByteCount;
}
if (this._major === 2) {
size = getInteger24(dataLength);
} else if (this._major === 3) {
size = getInteger32(dataLength);
if (flags) {
frameFlags[0] |= (flags.message.tag_alter_preservation ? 1 : 0) << 7;
frameFlags[0] |= (flags.message.file_alter_preservation ? 1 : 0) << 6;
frameFlags[0] |= (flags.message.read_only ? 1 : 0) << 5;
frameFlags[1] |= (flags.format.compression ? 1 : 0) << 7;
frameFlags[1] |= (flags.format.encryption ? 1 : 0) << 6;
frameFlags[1] |= (flags.format.grouping_identify ? 1 : 0) << 5;
}
} else if (this._major === 4) {
if (flags) {
frameFlags[0] |= (flags.message.tag_alter_preservation ? 1 : 0) << 6;
frameFlags[0] |= (flags.message.file_alter_preservation ? 1 : 0) << 5;
frameFlags[0] |= (flags.message.read_only ? 1 : 0) << 4;
frameFlags[1] |= (flags.format.grouping_identify ? 1 : 0) << 6;
frameFlags[1] |= (flags.format.compression ? 1 : 0) << 3;
frameFlags[1] |= (flags.format.encryption ? 1 : 0) << 2;
frameFlags[1] |= (flags.format.unsynchronisation ? 1 : 0) << 1;
frameFlags[1] |= flags.format.data_length_indicator ? 1 : 0;
if (flags.format.data_length_indicator) {
dataLength += 4;
if (this._major === 2) {
size = getInteger24(dataLength);
} else if (this._major === 3) {
size = getInteger32(dataLength);
if (flags) {
frameFlags[0] |= (flags.message.tag_alter_preservation ? 1 : 0) << 7;
frameFlags[0] |= (flags.message.file_alter_preservation ? 1 : 0) << 6;
frameFlags[0] |= (flags.message.read_only ? 1 : 0) << 5;
frameFlags[1] |= (flags.format.compression ? 1 : 0) << 7;
frameFlags[1] |= (flags.format.encryption ? 1 : 0) << 6;
frameFlags[1] |= (flags.format.grouping_identify ? 1 : 0) << 5;
}
} else if (this._major === 4) {
if (flags) {
frameFlags[0] |= (flags.message.tag_alter_preservation ? 1 : 0) << 6;
frameFlags[0] |= (flags.message.file_alter_preservation ? 1 : 0) << 5;
frameFlags[0] |= (flags.message.read_only ? 1 : 0) << 4;
frameFlags[1] |= (flags.format.grouping_identify ? 1 : 0) << 6;
frameFlags[1] |= (flags.format.compression ? 1 : 0) << 3;
frameFlags[1] |= (flags.format.encryption ? 1 : 0) << 2;
frameFlags[1] |= (flags.format.unsynchronisation ? 1 : 0) << 1;
frameFlags[1] |= flags.format.data_length_indicator ? 1 : 0;
if (flags.format.data_length_indicator) {
dataLength += 4;
}
}
size = getSynchsafeInteger32(dataLength);
} else {
throw Error("Major version not supported");
}
size = getSynchsafeInteger32(dataLength);
} else {
throw Error("Major version not supported");
}
var frame = [].concat(bin(id), size, frameFlags, flags && flags.format.data_length_indicator && noFlagsDataLength ? getSynchsafeInteger32(noFlagsDataLength) : [], data);
if (!this._frames[id]) {
this._frames[id] = [];
}
this._frames[id].push(frame);
this._addData(this._nextFrameOffset, frame);
this._updateSize();
return this;
}
_addExtendedHeaderData(tagKey, tagData) {
var offset = START_EXTENDED_DATA_V4;
// Each flag that is set in the extended header has data attached, which
// comes in the order in which the flags are encountered (i.e. the data
// for flag 'b' comes before the data for flag 'c').
// _extendedHeader keeps track of which tag data we have by storing the
// size of the data. To know where to add a particular tag data we just need
// to sum all the data lengths of the tags that come before this tagKey
// because the keys in the map are in order.
for (var key in this._extendedHeader) {
if (this._extendedHeader.hasOwnProperty(key)) {
if (key === tagKey) {
break;
} else {
offset += this._extendedHeader[key];
}
var frame = [].concat(bin(id), size, frameFlags, flags && flags.format.data_length_indicator && noFlagsDataLength ? getSynchsafeInteger32(noFlagsDataLength) : [], data);
if (!this._frames[id]) {
this._frames[id] = [];
}
}
this._frames[id].push(frame);
this._addData(this._nextFrameOffset, frame);
var data = [tagData.length].concat(tagData);
this._extendedHeader[tagKey] = data.length;
this._addData(offset, data);
}
_initExtendedHeader() {
this._hasExtendedHeader = true;
if (this._major === 3) {
this._addData(EXTENDED_HEADER, [0, 0, 0, 6, // size
0, 0, // flags
0, 0, 0, 0 // padding
]);
} else if (this._major === 4) {
this._addData(EXTENDED_HEADER, [].concat(getSynchsafeInteger32(6), // size
[1], // number of flag bytes
[0] // extended flags
));
} else {
throw new Error("Version doesn't support extended header.");
this._updateSize();
return this;
}
}
}, {
key: '_addExtendedHeaderData',
value: function _addExtendedHeaderData(tagKey, tagData) {
var offset = START_EXTENDED_DATA_V4;
_updateSize() {
// Header (10 bytes) is not included in the size.
var size = 0;
if (this._hasExtendedHeader) {
// Extended header size
size += this._major === 4 ? 6 : 10;
// Extended header data size
// Each flag that is set in the extended header has data attached, which
// comes in the order in which the flags are encountered (i.e. the data
// for flag 'b' comes before the data for flag 'c').
// _extendedHeader keeps track of which tag data we have by storing the
// size of the data. To know where to add a particular tag data we just need
// to sum all the data lengths of the tags that come before this tagKey
// because the keys in the map are in order.
for (var key in this._extendedHeader) {
if (this._extendedHeader.hasOwnProperty(key)) {
size += this._extendedHeader[key];
if (key === tagKey) {
break;
} else {
offset += this._extendedHeader[key];
}
}
}
var data = [tagData.length].concat(tagData);
this._extendedHeader[tagKey] = data.length;
this._addData(offset, data);
}
}, {
key: '_initExtendedHeader',
value: function _initExtendedHeader() {
this._hasExtendedHeader = true;
for (var frameId in this._frames) {
if (this._frames.hasOwnProperty(frameId)) {
for (var i = 0, frame; frame = this._frames[frameId][i]; i++) {
size += frame.length;
}
if (this._major === 3) {
this._addData(EXTENDED_HEADER, [0, 0, 0, 6, // size
0, 0, // flags
0, 0, 0, 0 // padding
]);
} else if (this._major === 4) {
this._addData(EXTENDED_HEADER, [].concat(getSynchsafeInteger32(6), // size
[1], // number of flag bytes
[0] // extended flags
));
} else {
throw new Error("Version doesn't support extended header.");
}
}
}, {
key: '_updateSize',
value: function _updateSize() {
// Header (10 bytes) is not included in the size.
var size = 0;
this._nextFrameOffset = size + HEADER_SIZE;
if (this._hasExtendedHeader) {
// Extended header size
size += this._major === 4 ? 6 : 10;
// Extended header data size
for (var key in this._extendedHeader) {
if (this._extendedHeader.hasOwnProperty(key)) {
size += this._extendedHeader[key];
}
}
}
this._size = size;
this._setData(SIZE, getSynchsafeInteger32(size));
}
for (var frameId in this._frames) {
if (this._frames.hasOwnProperty(frameId)) {
for (var i = 0, frame; frame = this._frames[frameId][i]; i++) {
size += frame.length;
}
}
}
_setBitAtOffset(offset, bit) {
var data = this._getData(offset, 1);
data[0] |= 1 << bit;
this._setData(offset, data);
}
this._nextFrameOffset = size + HEADER_SIZE;
_getData(offset, length) {
return this._contents.slice(offset, offset + length);
}
this._size = size;
this._setData(SIZE, getSynchsafeInteger32(size));
}
}, {
key: '_setBitAtOffset',
value: function _setBitAtOffset(offset, bit) {
var data = this._getData(offset, 1);
data[0] |= 1 << bit;
this._setData(offset, data);
}
}, {
key: '_getData',
value: function _getData(offset, length) {
return this._contents.slice(offset, offset + length);
}
}, {
key: '_setData',
value: function _setData(offset, data) {
this._contents.splice.apply(this._contents, [offset, data.length].concat(data));
}
}, {
key: '_addData',
value: function _addData(offset, data) {
this._contents.splice.apply(this._contents, [offset, 0].concat(data));
}
}]);
_setData(offset, data) {
this._contents.splice.apply(this._contents, [offset, data.length].concat(data));
}
return ID3v2TagContents;
}();
_addData(offset, data) {
this._contents.splice.apply(this._contents, [offset, 0].concat(data));
}
}
module.exports = ID3v2TagContents;
'use strict';
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
var MediaTagReader = require('./MediaTagReader');

@@ -7,116 +15,134 @@ var MediaFileReader = require('./MediaFileReader');

const ID3_HEADER_SIZE = 10;
var ID3_HEADER_SIZE = 10;
class ID3v2TagReader extends MediaTagReader {
static getTagIdentifierByteRange() {
// ID3 header
return {
offset: 0,
length: ID3_HEADER_SIZE
};
}
var ID3v2TagReader = function (_MediaTagReader) {
_inherits(ID3v2TagReader, _MediaTagReader);
static canReadTagFormat(tagIdentifier) {
var id = String.fromCharCode.apply(String, tagIdentifier.slice(0, 3));
return id === 'ID3';
}
function ID3v2TagReader() {
_classCallCheck(this, ID3v2TagReader);
_loadData(mediaFileReader, callbacks) {
mediaFileReader.loadRange([6, 9], {
onSuccess: function () {
mediaFileReader.loadRange(
// The tag size does not include the header size.
[0, ID3_HEADER_SIZE + mediaFileReader.getSynchsafeInteger32At(6) - 1], callbacks);
},
onError: callbacks.onError
});
return _possibleConstructorReturn(this, (ID3v2TagReader.__proto__ || Object.getPrototypeOf(ID3v2TagReader)).apply(this, arguments));
}
_parseData(data, tags) {
var offset = 0;
var major = data.getByteAt(offset + 3);
if (major > 4) {
return { "type": "ID3", "version": ">2.4", "tags": {} };
_createClass(ID3v2TagReader, [{
key: '_loadData',
value: function _loadData(mediaFileReader, callbacks) {
mediaFileReader.loadRange([6, 9], {
onSuccess: function () {
mediaFileReader.loadRange(
// The tag size does not include the header size.
[0, ID3_HEADER_SIZE + mediaFileReader.getSynchsafeInteger32At(6) - 1], callbacks);
},
onError: callbacks.onError
});
}
var revision = data.getByteAt(offset + 4);
var unsynch = data.isBitSetAt(offset + 5, 7);
var xheader = data.isBitSetAt(offset + 5, 6);
var xindicator = data.isBitSetAt(offset + 5, 5);
var size = data.getSynchsafeInteger32At(offset + 6);
offset += 10;
}, {
key: '_parseData',
value: function _parseData(data, tags) {
var offset = 0;
var major = data.getByteAt(offset + 3);
if (major > 4) {
return { "type": "ID3", "version": ">2.4", "tags": {} };
}
var revision = data.getByteAt(offset + 4);
var unsynch = data.isBitSetAt(offset + 5, 7);
var xheader = data.isBitSetAt(offset + 5, 6);
var xindicator = data.isBitSetAt(offset + 5, 5);
var size = data.getSynchsafeInteger32At(offset + 6);
offset += 10;
if (xheader) {
// TODO: support 2.4
var xheadersize = data.getLongAt(offset, true);
// The 'Extended header size', currently 6 or 10 bytes, excludes itself.
offset += xheadersize + 4;
}
if (xheader) {
// TODO: support 2.4
var xheadersize = data.getLongAt(offset, true);
// The 'Extended header size', currently 6 or 10 bytes, excludes itself.
offset += xheadersize + 4;
}
var id3 = {
"type": "ID3",
"version": '2.' + major + '.' + revision,
"major": major,
"revision": revision,
"flags": {
"unsynchronisation": unsynch,
"extended_header": xheader,
"experimental_indicator": xindicator,
// TODO: footer_present
"footer_present": false
},
"size": size,
"tags": {}
};
var id3 = {
"type": "ID3",
"version": '2.' + major + '.' + revision,
"major": major,
"revision": revision,
"flags": {
"unsynchronisation": unsynch,
"extended_header": xheader,
"experimental_indicator": xindicator,
// TODO: footer_present
"footer_present": false
},
"size": size,
"tags": {}
};
if (tags) {
var expandedTags = this._expandShortcutTags(tags);
}
if (tags) {
var expandedTags = this._expandShortcutTags(tags);
}
var offsetEnd = size + 10 /*header size*/;
// When this flag is set the entire tag needs to be un-unsynchronised
// before parsing each individual frame. Individual frame sizes might not
// take unsynchronisation into consideration when it's set on the tag
// header.
if (id3.flags.unsynchronisation) {
data = ID3v2FrameReader.getUnsyncFileReader(data, offset, size);
offset = 0;
offsetEnd = data.getSize();
}
var frames = ID3v2FrameReader.readFrames(offset, offsetEnd, data, id3, expandedTags);
// create shortcuts for most common data.
for (var name in SHORTCUTS) if (SHORTCUTS.hasOwnProperty(name)) {
var frameData = this._getFrameData(frames, SHORTCUTS[name]);
if (frameData) {
id3.tags[name] = frameData;
var offsetEnd = size + 10 /*header size*/;
// When this flag is set the entire tag needs to be un-unsynchronised
// before parsing each individual frame. Individual frame sizes might not
// take unsynchronisation into consideration when it's set on the tag
// header.
if (id3.flags.unsynchronisation) {
data = ID3v2FrameReader.getUnsyncFileReader(data, offset, size);
offset = 0;
offsetEnd = data.getSize();
}
}
for (var frame in frames) if (frames.hasOwnProperty(frame)) {
id3.tags[frame] = frames[frame];
var frames = ID3v2FrameReader.readFrames(offset, offsetEnd, data, id3, expandedTags);
// create shortcuts for most common data.
for (var name in SHORTCUTS) {
if (SHORTCUTS.hasOwnProperty(name)) {
var frameData = this._getFrameData(frames, SHORTCUTS[name]);
if (frameData) {
id3.tags[name] = frameData;
}
}
}for (var frame in frames) {
if (frames.hasOwnProperty(frame)) {
id3.tags[frame] = frames[frame];
}
}return id3;
}
return id3;
}
_getFrameData(frames, ids) {
var frame;
for (var i = 0, id; id = ids[i]; i++) {
if (id in frames) {
if (frames[id] instanceof Array) {
frame = frames[id][0];
} else {
frame = frames[id];
}, {
key: '_getFrameData',
value: function _getFrameData(frames, ids) {
var frame;
for (var i = 0, id; id = ids[i]; i++) {
if (id in frames) {
if (frames[id] instanceof Array) {
frame = frames[id][0];
} else {
frame = frames[id];
}
return frame.data;
}
return frame.data;
}
}
}
}, {
key: 'getShortcuts',
value: function getShortcuts() {
return SHORTCUTS;
}
}], [{
key: 'getTagIdentifierByteRange',
value: function getTagIdentifierByteRange() {
// ID3 header
return {
offset: 0,
length: ID3_HEADER_SIZE
};
}
}, {
key: 'canReadTagFormat',
value: function canReadTagFormat(tagIdentifier) {
var id = String.fromCharCode.apply(String, tagIdentifier.slice(0, 3));
return id === 'ID3';
}
}]);
getShortcuts() {
return SHORTCUTS;
}
}
return ID3v2TagReader;
}(MediaTagReader);
const SHORTCUTS = {
var SHORTCUTS = {
"title": ["TIT2", "TT2"],

@@ -123,0 +149,0 @@ "artist": ["TPE1", "TP1"],

'use strict';
const MediaFileReader = require("./MediaFileReader");
const NodeFileReader = require("./NodeFileReader");
const XhrFileReader = require("./XhrFileReader");
const BlobFileReader = require("./BlobFileReader");
const ArrayFileReader = require("./ArrayFileReader");
const MediaTagReader = require("./MediaTagReader");
const ID3v1TagReader = require("./ID3v1TagReader");
const ID3v2TagReader = require("./ID3v2TagReader");
const MP4TagReader = require("./MP4TagReader");
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var MediaFileReader = require("./MediaFileReader");
var NodeFileReader = require("./NodeFileReader");
var XhrFileReader = require("./XhrFileReader");
var BlobFileReader = require("./BlobFileReader");
var ArrayFileReader = require("./ArrayFileReader");
var MediaTagReader = require("./MediaTagReader");
var ID3v1TagReader = require("./ID3v1TagReader");
var ID3v2TagReader = require("./ID3v2TagReader");
var MP4TagReader = require("./MP4TagReader");
var FLACTagReader = require("./FLACTagReader");
var mediaFileReaders = [];

@@ -21,5 +26,5 @@ var mediaTagReaders = [];

function isRangeValid(range, fileSize) {
const invalidPositiveRange = range.offset >= 0 && range.offset + range.length >= fileSize;
var invalidPositiveRange = range.offset >= 0 && range.offset + range.length >= fileSize;
const invalidNegativeRange = range.offset < 0 && (-range.offset > fileSize || range.offset + range.length > 0);
var invalidNegativeRange = range.offset < 0 && (-range.offset > fileSize || range.offset + range.length > 0);

@@ -29,214 +34,242 @@ return !(invalidPositiveRange || invalidNegativeRange);

class Reader {
var Reader = function () {
function Reader(file) {
_classCallCheck(this, Reader);
constructor(file) {
this._file = file;
}
setTagsToRead(tagsToRead) {
this._tagsToRead = tagsToRead;
return this;
}
_createClass(Reader, [{
key: "setTagsToRead",
value: function setTagsToRead(tagsToRead) {
this._tagsToRead = tagsToRead;
return this;
}
}, {
key: "setFileReader",
value: function setFileReader(fileReader) {
this._fileReader = fileReader;
return this;
}
}, {
key: "setTagReader",
value: function setTagReader(tagReader) {
this._tagReader = tagReader;
return this;
}
}, {
key: "read",
value: function read(callbacks) {
var FileReader = this._getFileReader();
var fileReader = new FileReader(this._file);
var self = this;
setFileReader(fileReader) {
this._fileReader = fileReader;
return this;
}
setTagReader(tagReader) {
this._tagReader = tagReader;
return this;
}
read(callbacks) {
var FileReader = this._getFileReader();
var fileReader = new FileReader(this._file);
var self = this;
fileReader.init({
onSuccess: function () {
self._getTagReader(fileReader, {
onSuccess: function (TagReader) {
new TagReader(fileReader).setTagsToRead(self._tagsToRead).read(callbacks);
},
onError: callbacks.onError
});
},
onError: callbacks.onError
});
}
_getFileReader() {
if (this._fileReader) {
return this._fileReader;
} else {
return this._findFileReader();
fileReader.init({
onSuccess: function () {
self._getTagReader(fileReader, {
onSuccess: function (TagReader) {
new TagReader(fileReader).setTagsToRead(self._tagsToRead).read(callbacks);
},
onError: callbacks.onError
});
},
onError: callbacks.onError
});
}
}
_findFileReader() {
for (var i = 0; i < mediaFileReaders.length; i++) {
if (mediaFileReaders[i].canReadFile(this._file)) {
return mediaFileReaders[i];
}, {
key: "_getFileReader",
value: function _getFileReader() {
if (this._fileReader) {
return this._fileReader;
} else {
return this._findFileReader();
}
}
}, {
key: "_findFileReader",
value: function _findFileReader() {
for (var i = 0; i < mediaFileReaders.length; i++) {
if (mediaFileReaders[i].canReadFile(this._file)) {
return mediaFileReaders[i];
}
}
throw new Error("No suitable file reader found for ", this._file);
}
_getTagReader(fileReader, callbacks) {
if (this._tagReader) {
var tagReader = this._tagReader;
setTimeout(function () {
callbacks.onSuccess(tagReader);
}, 1);
} else {
this._findTagReader(fileReader, callbacks);
throw new Error("No suitable file reader found for " + this._file);
}
}
_findTagReader(fileReader, callbacks) {
// We don't want to make multiple fetches per tag reader to get the tag
// identifier. The strategy here is to combine all the tag identifier
// ranges into one and make a single fetch. This is particularly important
// in file readers that have expensive loads like the XHR one.
// However, with this strategy we run into the problem of loading the
// entire file because tag identifiers might be at the start or end of
// the file.
// To get around this we divide the tag readers into two categories, the
// ones that read their tag identifiers from the start of the file and the
// ones that read from the end of the file.
var tagReadersAtFileStart = [];
var tagReadersAtFileEnd = [];
var fileSize = fileReader.getSize();
for (var i = 0; i < mediaTagReaders.length; i++) {
var range = mediaTagReaders[i].getTagIdentifierByteRange();
if (!isRangeValid(range, fileSize)) {
continue;
}
if (range.offset >= 0 && range.offset < fileSize / 2 || range.offset < 0 && range.offset < -fileSize / 2) {
tagReadersAtFileStart.push(mediaTagReaders[i]);
}, {
key: "_getTagReader",
value: function _getTagReader(fileReader, callbacks) {
if (this._tagReader) {
var tagReader = this._tagReader;
setTimeout(function () {
callbacks.onSuccess(tagReader);
}, 1);
} else {
tagReadersAtFileEnd.push(mediaTagReaders[i]);
this._findTagReader(fileReader, callbacks);
}
}
}, {
key: "_findTagReader",
value: function _findTagReader(fileReader, callbacks) {
// We don't want to make multiple fetches per tag reader to get the tag
// identifier. The strategy here is to combine all the tag identifier
// ranges into one and make a single fetch. This is particularly important
// in file readers that have expensive loads like the XHR one.
// However, with this strategy we run into the problem of loading the
// entire file because tag identifiers might be at the start or end of
// the file.
// To get around this we divide the tag readers into two categories, the
// ones that read their tag identifiers from the start of the file and the
// ones that read from the end of the file.
var tagReadersAtFileStart = [];
var tagReadersAtFileEnd = [];
var fileSize = fileReader.getSize();
var tagsLoaded = false;
var loadTagIdentifiersCallbacks = {
onSuccess: function () {
if (!tagsLoaded) {
// We're expecting to load two sets of tag identifiers. This flag
// indicates when the first one has been loaded.
tagsLoaded = true;
return;
for (var i = 0; i < mediaTagReaders.length; i++) {
var range = mediaTagReaders[i].getTagIdentifierByteRange();
if (!isRangeValid(range, fileSize)) {
continue;
}
for (var i = 0; i < mediaTagReaders.length; i++) {
var range = mediaTagReaders[i].getTagIdentifierByteRange();
if (!isRangeValid(range, fileSize)) {
continue;
if (range.offset >= 0 && range.offset < fileSize / 2 || range.offset < 0 && range.offset < -fileSize / 2) {
tagReadersAtFileStart.push(mediaTagReaders[i]);
} else {
tagReadersAtFileEnd.push(mediaTagReaders[i]);
}
}
var tagsLoaded = false;
var loadTagIdentifiersCallbacks = {
onSuccess: function () {
if (!tagsLoaded) {
// We're expecting to load two sets of tag identifiers. This flag
// indicates when the first one has been loaded.
tagsLoaded = true;
return;
}
try {
var tagIndentifier = fileReader.getBytesAt(range.offset >= 0 ? range.offset : range.offset + fileSize, range.length);
} catch (ex) {
if (callbacks.onError) {
callbacks.onError({
"type": "fileReader",
"info": ex.message
});
for (var i = 0; i < mediaTagReaders.length; i++) {
var range = mediaTagReaders[i].getTagIdentifierByteRange();
if (!isRangeValid(range, fileSize)) {
continue;
}
try {
var tagIndentifier = fileReader.getBytesAt(range.offset >= 0 ? range.offset : range.offset + fileSize, range.length);
} catch (ex) {
if (callbacks.onError) {
callbacks.onError({
"type": "fileReader",
"info": ex.message
});
}
return;
}
if (mediaTagReaders[i].canReadTagFormat(tagIndentifier)) {
callbacks.onSuccess(mediaTagReaders[i]);
return;
}
}
if (mediaTagReaders[i].canReadTagFormat(tagIndentifier)) {
callbacks.onSuccess(mediaTagReaders[i]);
return;
if (callbacks.onError) {
callbacks.onError({
"type": "tagFormat",
"info": "No suitable tag reader found"
});
}
}
},
onError: callbacks.onError
};
if (callbacks.onError) {
callbacks.onError({
"type": "tagFormat",
"info": "No suitable tag reader found"
});
}
},
onError: callbacks.onError
};
this._loadTagIdentifierRanges(fileReader, tagReadersAtFileStart, loadTagIdentifiersCallbacks);
this._loadTagIdentifierRanges(fileReader, tagReadersAtFileEnd, loadTagIdentifiersCallbacks);
}
}, {
key: "_loadTagIdentifierRanges",
value: function _loadTagIdentifierRanges(fileReader, tagReaders, callbacks) {
if (tagReaders.length === 0) {
// Force async
setTimeout(callbacks.onSuccess, 1);
return;
}
this._loadTagIdentifierRanges(fileReader, tagReadersAtFileStart, loadTagIdentifiersCallbacks);
this._loadTagIdentifierRanges(fileReader, tagReadersAtFileEnd, loadTagIdentifiersCallbacks);
}
var tagIdentifierRange = [Number.MAX_VALUE, 0];
var fileSize = fileReader.getSize();
_loadTagIdentifierRanges(fileReader, tagReaders, callbacks) {
if (tagReaders.length === 0) {
// Force async
setTimeout(callbacks.onSuccess, 1);
return;
}
// Create a super set of all ranges so we can load them all at once.
// Might need to rethink this approach if there are tag ranges too far
// a part from each other. We're good for now though.
for (var i = 0; i < tagReaders.length; i++) {
var range = tagReaders[i].getTagIdentifierByteRange();
var start = range.offset >= 0 ? range.offset : range.offset + fileSize;
var end = start + range.length - 1;
var tagIdentifierRange = [Number.MAX_VALUE, 0];
var fileSize = fileReader.getSize();
tagIdentifierRange[0] = Math.min(start, tagIdentifierRange[0]);
tagIdentifierRange[1] = Math.max(end, tagIdentifierRange[1]);
}
// Create a super set of all ranges so we can load them all at once.
// Might need to rethink this approach if there are tag ranges too far
// a part from each other. We're good for now though.
for (var i = 0; i < tagReaders.length; i++) {
var range = tagReaders[i].getTagIdentifierByteRange();
var start = range.offset >= 0 ? range.offset : range.offset + fileSize;
var end = start + range.length - 1;
tagIdentifierRange[0] = Math.min(start, tagIdentifierRange[0]);
tagIdentifierRange[1] = Math.max(end, tagIdentifierRange[1]);
fileReader.loadRange(tagIdentifierRange, callbacks);
}
}]);
fileReader.loadRange(tagIdentifierRange, callbacks);
}
}
return Reader;
}();
class Config {
static addFileReader(fileReader) {
mediaFileReaders.push(fileReader);
return Config;
var Config = function () {
function Config() {
_classCallCheck(this, Config);
}
static addTagReader(tagReader) {
mediaTagReaders.push(tagReader);
return Config;
}
_createClass(Config, null, [{
key: "addFileReader",
value: function addFileReader(fileReader) {
mediaFileReaders.push(fileReader);
return Config;
}
}, {
key: "addTagReader",
value: function addTagReader(tagReader) {
mediaTagReaders.push(tagReader);
return Config;
}
}, {
key: "removeTagReader",
value: function removeTagReader(tagReader) {
var tagReaderIx = mediaTagReaders.indexOf(tagReader);
static removeTagReader(tagReader) {
var tagReaderIx = mediaTagReaders.indexOf(tagReader);
if (tagReaderIx >= 0) {
mediaTagReaders.splice(tagReaderIx, 1);
}
if (tagReaderIx >= 0) {
mediaTagReaders.splice(tagReaderIx, 1);
return Config;
}
}, {
key: "EXPERIMENTAL_avoidHeadRequests",
value: function EXPERIMENTAL_avoidHeadRequests() {
XhrFileReader.setConfig({
avoidHeadRequests: true
});
}
}, {
key: "setDisallowedXhrHeaders",
value: function setDisallowedXhrHeaders(disallowedXhrHeaders) {
XhrFileReader.setConfig({
disallowedXhrHeaders: disallowedXhrHeaders
});
}
}, {
key: "setXhrTimeoutInSec",
value: function setXhrTimeoutInSec(timeoutInSec) {
XhrFileReader.setConfig({
timeoutInSec: timeoutInSec
});
}
}]);
return Config;
}
return Config;
}();
static EXPERIMENTAL_avoidHeadRequests() {
XhrFileReader.setConfig({
avoidHeadRequests: true
});
}
Config.addFileReader(XhrFileReader).addFileReader(BlobFileReader).addFileReader(ArrayFileReader).addTagReader(ID3v2TagReader).addTagReader(ID3v1TagReader).addTagReader(MP4TagReader).addTagReader(FLACTagReader);
static setDisallowedXhrHeaders(disallowedXhrHeaders) {
XhrFileReader.setConfig({
disallowedXhrHeaders: disallowedXhrHeaders
});
}
static setXhrTimeoutInSec(timeoutInSec) {
XhrFileReader.setConfig({
timeoutInSec: timeoutInSec
});
}
}
Config.addFileReader(XhrFileReader).addFileReader(BlobFileReader).addFileReader(ArrayFileReader).addTagReader(ID3v2TagReader).addTagReader(ID3v1TagReader).addTagReader(MP4TagReader);
if (typeof process !== "undefined" && !process.browser) {

@@ -243,0 +276,0 @@ Config.addFileReader(NodeFileReader);

'use strict';
const StringUtils = require('./StringUtils');
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
class MediaFileReader {
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
constructor() {
var StringUtils = require('./StringUtils');
var MediaFileReader = function () {
function MediaFileReader(path) {
_classCallCheck(this, MediaFileReader);
this._isInitialized = false;

@@ -15,184 +20,216 @@ this._size = 0;

*/
static canReadFile(file) {
throw new Error("Must implement canReadFile function");
}
/**
* This function needs to be called before any other function.
* Loads the necessary initial information from the file.
*/
init(callbacks) {
var self = this;
if (this._isInitialized) {
setTimeout(callbacks.onSuccess, 1);
} else {
return this._init({
onSuccess: function () {
self._isInitialized = true;
callbacks.onSuccess();
},
onError: callbacks.onError
});
}
}
_createClass(MediaFileReader, [{
key: 'init',
_init(callbacks) {
throw new Error("Must implement init function");
}
/**
* @param range The start and end indexes of the range to load.
* Ex: [0, 7] load bytes 0 to 7 inclusive.
*/
loadRange(range, callbacks) {
throw new Error("Must implement loadRange function");
}
/**
* This function needs to be called before any other function.
* Loads the necessary initial information from the file.
*/
value: function init(callbacks) {
var self = this;
/**
* @return The size of the file in bytes.
*/
getSize() {
if (!this._isInitialized) {
throw new Error("init() must be called first.");
if (this._isInitialized) {
setTimeout(callbacks.onSuccess, 1);
} else {
return this._init({
onSuccess: function () {
self._isInitialized = true;
callbacks.onSuccess();
},
onError: callbacks.onError
});
}
}
}, {
key: '_init',
value: function _init(callbacks) {
throw new Error("Must implement init function");
}
return this._size;
}
/**
* @param range The start and end indexes of the range to load.
* Ex: [0, 7] load bytes 0 to 7 inclusive.
*/
getByteAt(offset) {
throw new Error("Must implement getByteAt function");
}
getBytesAt(offset, length) {
var bytes = new Array(length);
for (var i = 0; i < length; i++) {
bytes[i] = this.getByteAt(offset + i);
}, {
key: 'loadRange',
value: function loadRange(range, callbacks) {
throw new Error("Must implement loadRange function");
}
return bytes;
}
isBitSetAt(offset, bit) {
var iByte = this.getByteAt(offset);
return (iByte & 1 << bit) != 0;
}
/**
* @return The size of the file in bytes.
*/
getSByteAt(offset) {
var iByte = this.getByteAt(offset);
if (iByte > 127) {
return iByte - 256;
} else {
return iByte;
}
}
}, {
key: 'getSize',
value: function getSize() {
if (!this._isInitialized) {
throw new Error("init() must be called first.");
}
getShortAt(offset, isBigEndian) {
var iShort = isBigEndian ? (this.getByteAt(offset) << 8) + this.getByteAt(offset + 1) : (this.getByteAt(offset + 1) << 8) + this.getByteAt(offset);
if (iShort < 0) {
iShort += 65536;
return this._size;
}
return iShort;
}
getSShortAt(offset, isBigEndian) {
var iUShort = this.getShortAt(offset, isBigEndian);
if (iUShort > 32767) {
return iUShort - 65536;
} else {
return iUShort;
}, {
key: 'getByteAt',
value: function getByteAt(offset) {
throw new Error("Must implement getByteAt function");
}
}
}, {
key: 'getBytesAt',
value: function getBytesAt(offset, length) {
var bytes = new Array(length);
for (var i = 0; i < length; i++) {
bytes[i] = this.getByteAt(offset + i);
}
return bytes;
}
}, {
key: 'isBitSetAt',
value: function isBitSetAt(offset, bit) {
var iByte = this.getByteAt(offset);
return (iByte & 1 << bit) != 0;
}
}, {
key: 'getSByteAt',
value: function getSByteAt(offset) {
var iByte = this.getByteAt(offset);
if (iByte > 127) {
return iByte - 256;
} else {
return iByte;
}
}
}, {
key: 'getShortAt',
value: function getShortAt(offset, isBigEndian) {
var iShort = isBigEndian ? (this.getByteAt(offset) << 8) + this.getByteAt(offset + 1) : (this.getByteAt(offset + 1) << 8) + this.getByteAt(offset);
if (iShort < 0) {
iShort += 65536;
}
return iShort;
}
}, {
key: 'getSShortAt',
value: function getSShortAt(offset, isBigEndian) {
var iUShort = this.getShortAt(offset, isBigEndian);
if (iUShort > 32767) {
return iUShort - 65536;
} else {
return iUShort;
}
}
}, {
key: 'getLongAt',
value: function getLongAt(offset, isBigEndian) {
var iByte1 = this.getByteAt(offset),
iByte2 = this.getByteAt(offset + 1),
iByte3 = this.getByteAt(offset + 2),
iByte4 = this.getByteAt(offset + 3);
getLongAt(offset, isBigEndian) {
var iByte1 = this.getByteAt(offset),
iByte2 = this.getByteAt(offset + 1),
iByte3 = this.getByteAt(offset + 2),
iByte4 = this.getByteAt(offset + 3);
var iLong = isBigEndian ? (((iByte1 << 8) + iByte2 << 8) + iByte3 << 8) + iByte4 : (((iByte4 << 8) + iByte3 << 8) + iByte2 << 8) + iByte1;
var iLong = isBigEndian ? (((iByte1 << 8) + iByte2 << 8) + iByte3 << 8) + iByte4 : (((iByte4 << 8) + iByte3 << 8) + iByte2 << 8) + iByte1;
if (iLong < 0) {
iLong += 4294967296;
}
if (iLong < 0) {
iLong += 4294967296;
return iLong;
}
}, {
key: 'getSLongAt',
value: function getSLongAt(offset, isBigEndian) {
var iULong = this.getLongAt(offset, isBigEndian);
return iLong;
}
getSLongAt(offset, isBigEndian) {
var iULong = this.getLongAt(offset, isBigEndian);
if (iULong > 2147483647) {
return iULong - 4294967296;
} else {
return iULong;
if (iULong > 2147483647) {
return iULong - 4294967296;
} else {
return iULong;
}
}
}
}, {
key: 'getInteger24At',
value: function getInteger24At(offset, isBigEndian) {
var iByte1 = this.getByteAt(offset),
iByte2 = this.getByteAt(offset + 1),
iByte3 = this.getByteAt(offset + 2);
getInteger24At(offset, isBigEndian) {
var iByte1 = this.getByteAt(offset),
iByte2 = this.getByteAt(offset + 1),
iByte3 = this.getByteAt(offset + 2);
var iInteger = isBigEndian ? ((iByte1 << 8) + iByte2 << 8) + iByte3 : ((iByte3 << 8) + iByte2 << 8) + iByte1;
var iInteger = isBigEndian ? ((iByte1 << 8) + iByte2 << 8) + iByte3 : ((iByte3 << 8) + iByte2 << 8) + iByte1;
if (iInteger < 0) {
iInteger += 16777216;
}
if (iInteger < 0) {
iInteger += 16777216;
return iInteger;
}
return iInteger;
}
getStringAt(offset, length) {
var string = [];
for (var i = offset, j = 0; i < offset + length; i++, j++) {
string[j] = String.fromCharCode(this.getByteAt(i));
}, {
key: 'getStringAt',
value: function getStringAt(offset, length) {
var string = [];
for (var i = offset, j = 0; i < offset + length; i++, j++) {
string[j] = String.fromCharCode(this.getByteAt(i));
}
return string.join("");
}
return string.join("");
}
}, {
key: 'getStringWithCharsetAt',
value: function getStringWithCharsetAt(offset, length, charset) {
var bytes = this.getBytesAt(offset, length);
var string;
getStringWithCharsetAt(offset, length, charset) {
var bytes = this.getBytesAt(offset, length);
var string;
switch ((charset || '').toLowerCase()) {
case "utf-16":
case "utf-16le":
case "utf-16be":
string = StringUtils.readUTF16String(bytes, charset === "utf-16be");
break;
switch ((charset || '').toLowerCase()) {
case "utf-16":
case "utf-16le":
case "utf-16be":
string = StringUtils.readUTF16String(bytes, charset === "utf-16be");
break;
case "utf-8":
string = StringUtils.readUTF8String(bytes);
break;
case "utf-8":
string = StringUtils.readUTF8String(bytes);
break;
default:
string = StringUtils.readNullTerminatedString(bytes);
break;
}
default:
string = StringUtils.readNullTerminatedString(bytes);
break;
return string;
}
}, {
key: 'getCharAt',
value: function getCharAt(offset) {
return String.fromCharCode(this.getByteAt(offset));
}
return string;
}
/**
* The ID3v2 tag/frame size is encoded with four bytes where the most
* significant bit (bit 7) is set to zero in every byte, making a total of 28
* bits. The zeroed bits are ignored, so a 257 bytes long tag is represented
* as $00 00 02 01.
*/
getCharAt(offset) {
return String.fromCharCode(this.getByteAt(offset));
}
}, {
key: 'getSynchsafeInteger32At',
value: function getSynchsafeInteger32At(offset) {
var size1 = this.getByteAt(offset);
var size2 = this.getByteAt(offset + 1);
var size3 = this.getByteAt(offset + 2);
var size4 = this.getByteAt(offset + 3);
// 0x7f = 0b01111111
var size = size4 & 0x7f | (size3 & 0x7f) << 7 | (size2 & 0x7f) << 14 | (size1 & 0x7f) << 21;
/**
* The ID3v2 tag/frame size is encoded with four bytes where the most
* significant bit (bit 7) is set to zero in every byte, making a total of 28
* bits. The zeroed bits are ignored, so a 257 bytes long tag is represented
* as $00 00 02 01.
*/
getSynchsafeInteger32At(offset) {
var size1 = this.getByteAt(offset);
var size2 = this.getByteAt(offset + 1);
var size3 = this.getByteAt(offset + 2);
var size4 = this.getByteAt(offset + 3);
// 0x7f = 0b01111111
var size = size4 & 0x7f | (size3 & 0x7f) << 7 | (size2 & 0x7f) << 14 | (size1 & 0x7f) << 21;
return size;
}
}], [{
key: 'canReadFile',
value: function canReadFile(file) {
throw new Error("Must implement canReadFile function");
}
}]);
return size;
}
}
return MediaFileReader;
}();
module.exports = MediaFileReader;
'use strict';
const MediaFileReader = require('./MediaFileReader');
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
class MediaTagReader {
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
constructor(mediaFileReader) {
var MediaFileReader = require('./MediaFileReader');
var MediaTagReader = function () {
function MediaTagReader(mediaFileReader) {
_classCallCheck(this, MediaTagReader);
this._mediaFileReader = mediaFileReader;

@@ -17,82 +22,102 @@ this._tags = null;

*/
static getTagIdentifierByteRange() {
throw new Error("Must implement");
}
/**
* Given a tag identifier (read from the file byte positions speficied by
* getTagIdentifierByteRange) this function checks if it can read the tag
* format or not.
*/
static canReadTagFormat(tagIdentifier) {
throw new Error("Must implement");
}
setTagsToRead(tags) {
this._tags = tags;
return this;
}
_createClass(MediaTagReader, [{
key: 'setTagsToRead',
value: function setTagsToRead(tags) {
this._tags = tags;
return this;
}
}, {
key: 'read',
value: function read(callbacks) {
var self = this;
read(callbacks) {
var self = this;
this._mediaFileReader.init({
onSuccess: function () {
self._loadData(self._mediaFileReader, {
onSuccess: function () {
try {
var tags = self._parseData(self._mediaFileReader, self._tags);
} catch (ex) {
if (callbacks.onError) {
callbacks.onError({
"type": "parseData",
"info": ex.message
});
return;
this._mediaFileReader.init({
onSuccess: function () {
self._loadData(self._mediaFileReader, {
onSuccess: function () {
try {
var tags = self._parseData(self._mediaFileReader, self._tags);
} catch (ex) {
if (callbacks.onError) {
callbacks.onError({
"type": "parseData",
"info": ex.message
});
return;
}
}
}
// TODO: destroy mediaFileReader
callbacks.onSuccess(tags);
},
onError: callbacks.onError
});
},
onError: callbacks.onError
});
}
// TODO: destroy mediaFileReader
callbacks.onSuccess(tags);
},
onError: callbacks.onError
});
},
onError: callbacks.onError
});
}
}, {
key: 'getShortcuts',
value: function getShortcuts() {
return {};
}
getShortcuts() {
return {};
}
/**
* Load the necessary bytes from the media file.
*/
/**
* Load the necessary bytes from the media file.
*/
_loadData(mediaFileReader, callbacks) {
throw new Error("Must implement _loadData function");
}
}, {
key: '_loadData',
value: function _loadData(mediaFileReader, callbacks) {
throw new Error("Must implement _loadData function");
}
/**
* Parse the loaded data to read the media tags.
*/
_parseData(mediaFileReader, tags) {
throw new Error("Must implement _parseData function");
}
/**
* Parse the loaded data to read the media tags.
*/
_expandShortcutTags(tagsWithShortcuts) {
if (!tagsWithShortcuts) {
return null;
}, {
key: '_parseData',
value: function _parseData(mediaFileReader, tags) {
throw new Error("Must implement _parseData function");
}
}, {
key: '_expandShortcutTags',
value: function _expandShortcutTags(tagsWithShortcuts) {
if (!tagsWithShortcuts) {
return null;
}
var tags = [];
var shortcuts = this.getShortcuts();
for (var i = 0, tagOrShortcut; tagOrShortcut = tagsWithShortcuts[i]; i++) {
tags = tags.concat(shortcuts[tagOrShortcut] || [tagOrShortcut]);
var tags = [];
var shortcuts = this.getShortcuts();
for (var i = 0, tagOrShortcut; tagOrShortcut = tagsWithShortcuts[i]; i++) {
tags = tags.concat(shortcuts[tagOrShortcut] || [tagOrShortcut]);
}
return tags;
}
}], [{
key: 'getTagIdentifierByteRange',
value: function getTagIdentifierByteRange() {
throw new Error("Must implement");
}
return tags;
}
}
/**
* Given a tag identifier (read from the file byte positions speficied by
* getTagIdentifierByteRange) this function checks if it can read the tag
* format or not.
*/
}, {
key: 'canReadTagFormat',
value: function canReadTagFormat(tagIdentifier) {
throw new Error("Must implement");
}
}]);
return MediaTagReader;
}();
module.exports = MediaTagReader;

@@ -11,45 +11,59 @@ /**

const ByteArrayUtils = require('./ByteArrayUtils');
const bin = ByteArrayUtils.bin;
const pad = ByteArrayUtils.pad;
const getInteger32 = ByteArrayUtils.getInteger32;
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
class MP4TagContents {
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
constructor(ftyp, atoms) {
this._atoms = [new Atom("ftyp", pad(bin(ftyp), 24))].concat(atoms || []);
}
var ByteArrayUtils = require('./ByteArrayUtils');
var bin = ByteArrayUtils.bin;
var pad = ByteArrayUtils.pad;
var getInteger32 = ByteArrayUtils.getInteger32;
toArray() {
return this._atoms.reduce(function (array, atom) {
return array.concat(atom.toArray());
}, []);
}
var MP4TagContents = function () {
function MP4TagContents(ftyp, atoms) {
_classCallCheck(this, MP4TagContents);
static createAtom(atomName) {
return new Atom(atomName);
this._atoms = [new Atom("ftyp", pad(bin(ftyp), 24))].concat(atoms || []);
}
static createContainerAtom(atomName, atoms, data) {
return new Atom(atomName, data, atoms);
}
_createClass(MP4TagContents, [{
key: 'toArray',
value: function toArray() {
return this._atoms.reduce(function (array, atom) {
return array.concat(atom.toArray());
}, []);
}
}], [{
key: 'createAtom',
value: function createAtom(atomName) {
return new Atom(atomName);
}
}, {
key: 'createContainerAtom',
value: function createContainerAtom(atomName, atoms, data) {
return new Atom(atomName, data, atoms);
}
}, {
key: 'createMetadataAtom',
value: function createMetadataAtom(atomName, type, data) {
var klass = {
"uint8": 0,
"uint8b": 21, // Apple changed from 21 to 0 in latest versions
"text": 1,
"jpeg": 13,
"png": 14
}[type];
static createMetadataAtom(atomName, type, data) {
var klass = {
"uint8": 0,
"uint8b": 21, // Apple changed from 21 to 0 in latest versions
"text": 1,
"jpeg": 13,
"png": 14
}[type];
return this.createContainerAtom(atomName, [new Atom("data", [].concat([0x00, 0x00, 0x00, klass], // 1 byte atom version + 3 byte atom flags
[0x00, 0x00, 0x00, 0x00], // NULL space
data))]);
}
}]);
return this.createContainerAtom(atomName, [new Atom("data", [].concat([0x00, 0x00, 0x00, klass], // 1 byte atom version + 3 byte atom flags
[0x00, 0x00, 0x00, 0x00], // NULL space
data))]);
}
}
return MP4TagContents;
}();
class Atom {
var Atom = function () {
function Atom(name, data, atoms) {
_classCallCheck(this, Atom);
constructor(name, data, atoms) {
this._name = name;

@@ -60,12 +74,17 @@ this._data = data || [];

toArray() {
var atomsArray = this._atoms.reduce(function (array, atom) {
return array.concat(atom.toArray());
}, []);
var length = 4 + this._name.length + this._data.length + atomsArray.length;
_createClass(Atom, [{
key: 'toArray',
value: function toArray() {
var atomsArray = this._atoms.reduce(function (array, atom) {
return array.concat(atom.toArray());
}, []);
var length = 4 + this._name.length + this._data.length + atomsArray.length;
return [].concat(getInteger32(length), bin(this._name), this._data, atomsArray);
}
}
return [].concat(getInteger32(length), bin(this._name), this._data, atomsArray);
}
}]);
return Atom;
}();
module.exports = MP4TagContents;

@@ -11,74 +11,86 @@ /**

var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
var MediaTagReader = require('./MediaTagReader');
var MediaFileReader = require('./MediaFileReader');
class MP4TagReader extends MediaTagReader {
static getTagIdentifierByteRange() {
// The tag identifier is located in [4, 8] but since we'll need to reader
// the header of the first block anyway, we load it instead to avoid
// making two requests.
return {
offset: 0,
length: 16
};
}
var MP4TagReader = function (_MediaTagReader) {
_inherits(MP4TagReader, _MediaTagReader);
static canReadTagFormat(tagIdentifier) {
var id = String.fromCharCode.apply(String, tagIdentifier.slice(4, 8));
return id === "ftyp";
}
function MP4TagReader() {
_classCallCheck(this, MP4TagReader);
_loadData(mediaFileReader, callbacks) {
// MP4 metadata isn't located in a specific location of the file. Roughly
// speaking, it's composed of blocks chained together like a linked list.
// These blocks are called atoms (or boxes).
// Each atom of the list can have its own child linked list. Atoms in this
// situation do not possess any data and are called "container" as they only
// contain other atoms.
// Other atoms represent a particular set of data, like audio, video or
// metadata. In order to find and load all the interesting atoms we need
// to traverse the entire linked list of atoms and only load the ones
// associated with metadata.
// The metadata atoms can be find under the "moov.udta.meta.ilst" hierarchy.
var self = this;
// Load the header of the first atom
mediaFileReader.loadRange([0, 16], {
onSuccess: function () {
self._loadAtom(mediaFileReader, 0, "", callbacks);
},
onError: callbacks.onError
});
return _possibleConstructorReturn(this, (MP4TagReader.__proto__ || Object.getPrototypeOf(MP4TagReader)).apply(this, arguments));
}
_loadAtom(mediaFileReader, offset, parentAtomFullName, callbacks) {
if (offset >= mediaFileReader.getSize()) {
callbacks.onSuccess();
return;
}
_createClass(MP4TagReader, [{
key: '_loadData',
value: function _loadData(mediaFileReader, callbacks) {
// MP4 metadata isn't located in a specific location of the file. Roughly
// speaking, it's composed of blocks chained together like a linked list.
// These blocks are called atoms (or boxes).
// Each atom of the list can have its own child linked list. Atoms in this
// situation do not possess any data and are called "container" as they only
// contain other atoms.
// Other atoms represent a particular set of data, like audio, video or
// metadata. In order to find and load all the interesting atoms we need
// to traverse the entire linked list of atoms and only load the ones
// associated with metadata.
// The metadata atoms can be find under the "moov.udta.meta.ilst" hierarchy.
var self = this;
// 8 is the size of the atomSize and atomName fields.
// When reading the current block we always read 8 more bytes in order
// to also read the header of the next block.
var atomSize = mediaFileReader.getLongAt(offset, true);
if (atomSize == 0 || isNaN(atomSize)) {
callbacks.onSuccess();
return;
var self = this;
// Load the header of the first atom
mediaFileReader.loadRange([0, 16], {
onSuccess: function () {
self._loadAtom(mediaFileReader, 0, "", callbacks);
},
onError: callbacks.onError
});
}
var atomName = mediaFileReader.getStringAt(offset + 4, 4);
// console.log(parentAtomFullName, atomName, atomSize);
// Container atoms (no actual data)
if (this._isContainerAtom(atomName)) {
if (atomName == "meta") {
// The "meta" atom breaks convention and is a container with data.
offset += 4; // next_item_id (uint32)
}, {
key: '_loadAtom',
value: function _loadAtom(mediaFileReader, offset, parentAtomFullName, callbacks) {
if (offset >= mediaFileReader.getSize()) {
callbacks.onSuccess();
return;
}
var atomFullName = (parentAtomFullName ? parentAtomFullName + "." : "") + atomName;
if (atomFullName === "moov.udta.meta.ilst") {
mediaFileReader.loadRange([offset, offset + atomSize], callbacks);
var self = this;
// 8 is the size of the atomSize and atomName fields.
// When reading the current block we always read 8 more bytes in order
// to also read the header of the next block.
var atomSize = mediaFileReader.getLongAt(offset, true);
if (atomSize == 0 || isNaN(atomSize)) {
callbacks.onSuccess();
return;
}
var atomName = mediaFileReader.getStringAt(offset + 4, 4);
// console.log(parentAtomFullName, atomName, atomSize);
// Container atoms (no actual data)
if (this._isContainerAtom(atomName)) {
if (atomName == "meta") {
// The "meta" atom breaks convention and is a container with data.
offset += 4; // next_item_id (uint32)
}
var atomFullName = (parentAtomFullName ? parentAtomFullName + "." : "") + atomName;
if (atomFullName === "moov.udta.meta.ilst") {
mediaFileReader.loadRange([offset, offset + atomSize], callbacks);
} else {
mediaFileReader.loadRange([offset + 8, offset + 8 + 8], {
onSuccess: function () {
self._loadAtom(mediaFileReader, offset + 8, atomFullName, callbacks);
},
onError: callbacks.onError
});
}
} else {
mediaFileReader.loadRange([offset + 8, offset + 8 + 8], {
mediaFileReader.loadRange([offset + atomSize, offset + atomSize + 8], {
onSuccess: function () {
self._loadAtom(mediaFileReader, offset + 8, atomFullName, callbacks);
self._loadAtom(mediaFileReader, offset + atomSize, parentAtomFullName, callbacks);
},

@@ -88,160 +100,180 @@ onError: callbacks.onError

}
} else {
mediaFileReader.loadRange([offset + atomSize, offset + atomSize + 8], {
onSuccess: function () {
self._loadAtom(mediaFileReader, offset + atomSize, parentAtomFullName, callbacks);
},
onError: callbacks.onError
});
}
}
}, {
key: '_isContainerAtom',
value: function _isContainerAtom(atomName) {
return ["moov", "udta", "meta", "ilst"].indexOf(atomName) >= 0;
}
}, {
key: '_canReadAtom',
value: function _canReadAtom(atomName) {
return atomName !== "----";
}
}, {
key: '_parseData',
value: function _parseData(data, tagsToRead) {
var tags = {};
_isContainerAtom(atomName) {
return ["moov", "udta", "meta", "ilst"].indexOf(atomName) >= 0;
}
tagsToRead = this._expandShortcutTags(tagsToRead);
this._readAtom(tags, data, 0, data.getSize(), tagsToRead);
_canReadAtom(atomName) {
return atomName !== "----";
}
_parseData(data, tagsToRead) {
var tags = {};
tagsToRead = this._expandShortcutTags(tagsToRead);
this._readAtom(tags, data, 0, data.getSize(), tagsToRead);
// create shortcuts for most common data.
for (var name in SHORTCUTS) if (SHORTCUTS.hasOwnProperty(name)) {
var tag = tags[SHORTCUTS[name]];
if (tag) {
if (name === "track") {
tags[name] = tag.data.track;
} else {
tags[name] = tag.data;
// create shortcuts for most common data.
for (var name in SHORTCUTS) {
if (SHORTCUTS.hasOwnProperty(name)) {
var tag = tags[SHORTCUTS[name]];
if (tag) {
if (name === "track") {
tags[name] = tag.data.track;
} else {
tags[name] = tag.data;
}
}
}
}
}return {
"type": "MP4",
"ftyp": data.getStringAt(8, 4),
"version": data.getLongAt(12, true),
"tags": tags
};
}
}, {
key: '_readAtom',
value: function _readAtom(tags, data, offset, length, tagsToRead, parentAtomFullName, indent) {
indent = indent === undefined ? "" : indent + " ";
return {
"type": "MP4",
"ftyp": data.getStringAt(8, 4),
"version": data.getLongAt(12, true),
"tags": tags
};
}
var seek = offset;
while (seek < offset + length) {
var atomSize = data.getLongAt(seek, true);
if (atomSize == 0) {
return;
}
var atomName = data.getStringAt(seek + 4, 4);
_readAtom(tags, data, offset, length, tagsToRead, parentAtomFullName, indent) {
indent = indent === undefined ? "" : indent + " ";
// console.log(seek, parentAtomFullName, atomName, atomSize);
if (this._isContainerAtom(atomName)) {
if (atomName == "meta") {
seek += 4; // next_item_id (uint32)
}
var atomFullName = (parentAtomFullName ? parentAtomFullName + "." : "") + atomName;
this._readAtom(tags, data, seek + 8, atomSize - 8, tagsToRead, atomFullName, indent);
return;
}
var seek = offset;
while (seek < offset + length) {
var atomSize = data.getLongAt(seek, true);
if (atomSize == 0) {
return;
}
var atomName = data.getStringAt(seek + 4, 4);
// console.log(seek, parentAtomFullName, atomName, atomSize);
if (this._isContainerAtom(atomName)) {
if (atomName == "meta") {
seek += 4; // next_item_id (uint32)
// Value atoms
if ((!tagsToRead || tagsToRead.indexOf(atomName) >= 0) && parentAtomFullName === "moov.udta.meta.ilst" && this._canReadAtom(atomName)) {
tags[atomName] = this._readMetadataAtom(data, seek);
}
var atomFullName = (parentAtomFullName ? parentAtomFullName + "." : "") + atomName;
this._readAtom(tags, data, seek + 8, atomSize - 8, tagsToRead, atomFullName, indent);
return;
}
// Value atoms
if ((!tagsToRead || tagsToRead.indexOf(atomName) >= 0) && parentAtomFullName === "moov.udta.meta.ilst" && this._canReadAtom(atomName)) {
tags[atomName] = this._readMetadataAtom(data, seek);
seek += atomSize;
}
seek += atomSize;
}
}
}, {
key: '_readMetadataAtom',
value: function _readMetadataAtom(data, offset) {
// 16: name + size + "data" + size (4 bytes each)
var METADATA_HEADER = 16;
_readMetadataAtom(data, offset) {
// 16: name + size + "data" + size (4 bytes each)
const METADATA_HEADER = 16;
var atomSize = data.getLongAt(offset, true);
var atomName = data.getStringAt(offset + 4, 4);
var atomSize = data.getLongAt(offset, true);
var atomName = data.getStringAt(offset + 4, 4);
var klass = data.getInteger24At(offset + METADATA_HEADER + 1, true);
var type = TYPES[klass];
var atomData;
if (atomName == "trkn") {
atomData = {
"track": data.getByteAt(offset + METADATA_HEADER + 11),
"total": data.getByteAt(offset + METADATA_HEADER + 13)
};
} else if (atomName == "disk") {
atomData = {
"disk": data.getByteAt(offset + METADATA_HEADER + 11),
"total": data.getByteAt(offset + METADATA_HEADER + 13)
};
} else {
// 4: atom version (1 byte) + atom flags (3 bytes)
// 4: NULL (usually locale indicator)
var atomHeader = METADATA_HEADER + 4 + 4;
var dataStart = offset + atomHeader;
var dataLength = atomSize - atomHeader;
var klass = data.getInteger24At(offset + METADATA_HEADER + 1, true);
var type = TYPES[klass];
var atomData;
// Workaround for covers being parsed as 'uint8' type despite being an 'covr' atom
if (atomName === 'covr' && type === 'uint8') {
type = 'jpeg';
}
if (atomName == "trkn") {
atomData = {
"track": data.getByteAt(offset + METADATA_HEADER + 11),
"total": data.getByteAt(offset + METADATA_HEADER + 13)
};
} else if (atomName == "disk") {
atomData = {
"disk": data.getByteAt(offset + METADATA_HEADER + 11),
"total": data.getByteAt(offset + METADATA_HEADER + 13)
};
} else {
// 4: atom version (1 byte) + atom flags (3 bytes)
// 4: NULL (usually locale indicator)
var atomHeader = METADATA_HEADER + 4 + 4;
var dataStart = offset + atomHeader;
var dataLength = atomSize - atomHeader;
var atomData;
switch (type) {
case "text":
atomData = data.getStringWithCharsetAt(dataStart, dataLength, "utf-8").toString();
break;
// Workaround for covers being parsed as 'uint8' type despite being an 'covr' atom
if (atomName === 'covr' && type === 'uint8') {
type = 'jpeg';
}
case "uint8":
atomData = data.getShortAt(dataStart, false);
break;
switch (type) {
case "text":
atomData = data.getStringWithCharsetAt(dataStart, dataLength, "utf-8").toString();
break;
case "int":
case "uint":
// Though the QuickTime spec doesn't state it, there are 64-bit values
// such as plID (Playlist/Collection ID). With its single 64-bit floating
// point number type, these are hard to parse and pass in JavaScript.
// The high word of plID seems to always be zero, so, as this is the
// only current 64-bit atom handled, it is parsed from its 32-bit
// low word as an unsigned long.
//
var intReader = type == 'int' ? dataLength == 1 ? data.getSByteAt : dataLength == 2 ? data.getSShortAt : dataLength == 4 ? data.getSLongAt : data.getLongAt : dataLength == 1 ? data.getByteAt : dataLength == 2 ? data.getShortAt : data.getLongAt;
case "uint8":
atomData = data.getShortAt(dataStart, false);
break;
atomData = intReader.call(data, dataStart + (dataLength == 8 ? 4 : 0), true);
break;
case "int":
case "uint":
// Though the QuickTime spec doesn't state it, there are 64-bit values
// such as plID (Playlist/Collection ID). With its single 64-bit floating
// point number type, these are hard to parse and pass in JavaScript.
// The high word of plID seems to always be zero, so, as this is the
// only current 64-bit atom handled, it is parsed from its 32-bit
// low word as an unsigned long.
//
var intReader = type == 'int' ? dataLength == 1 ? data.getSByteAt : dataLength == 2 ? data.getSShortAt : dataLength == 4 ? data.getSLongAt : data.getLongAt : dataLength == 1 ? data.getByteAt : dataLength == 2 ? data.getShortAt : data.getLongAt;
// $FlowFixMe - getByteAt doesn't receive a second argument
atomData = intReader.call(data, dataStart + (dataLength == 8 ? 4 : 0), true);
break;
case "jpeg":
case "png":
atomData = {
"format": "image/" + type,
"data": data.getBytesAt(dataStart, dataLength)
};
break;
case "jpeg":
case "png":
atomData = {
"format": "image/" + type,
"data": data.getBytesAt(dataStart, dataLength)
};
break;
}
}
return {
id: atomName,
size: atomSize,
description: ATOM_DESCRIPTIONS[atomName] || "Unknown",
data: atomData
};
}
}, {
key: 'getShortcuts',
value: function getShortcuts() {
return SHORTCUTS;
}
}], [{
key: 'getTagIdentifierByteRange',
value: function getTagIdentifierByteRange() {
// The tag identifier is located in [4, 8] but since we'll need to reader
// the header of the first block anyway, we load it instead to avoid
// making two requests.
return {
offset: 0,
length: 16
};
}
}, {
key: 'canReadTagFormat',
value: function canReadTagFormat(tagIdentifier) {
var id = String.fromCharCode.apply(String, tagIdentifier.slice(4, 8));
return id === "ftyp";
}
}]);
return {
id: atomName,
size: atomSize,
description: ATOM_DESCRIPTIONS[atomName] || "Unknown",
data: atomData
};
}
return MP4TagReader;
}(MediaTagReader);
getShortcuts() {
return SHORTCUTS;
}
}
/*
* https://developer.apple.com/library/content/documentation/QuickTime/QTFF/Metadata/Metadata.html#//apple_ref/doc/uid/TP40000939-CH1-SW35
*/
const TYPES = {
var TYPES = {
"0": "uint8",

@@ -255,3 +287,3 @@ "1": "text",

const ATOM_DESCRIPTIONS = {
var ATOM_DESCRIPTIONS = {
"©alb": "Album",

@@ -307,7 +339,7 @@ "©ART": "Artist",

const UNSUPPORTED_ATOMS = {
var UNSUPPORTED_ATOMS = {
"----": 1
};
const SHORTCUTS = {
var SHORTCUTS = {
"title": "©nam",

@@ -314,0 +346,0 @@ "artist": "©ART",

'use strict';
const fs = require('fs');
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
const ChunkedFileData = require('./ChunkedFileData');
const MediaFileReader = require('./MediaFileReader');
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
class NodeFileReader extends MediaFileReader {
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
constructor(path) {
super();
this._path = path;
this._fileData = new ChunkedFileData();
}
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
static canReadFile(file) {
return typeof file === 'string' && !/^[a-z]+:\/\//i.test(file);
}
var fs = require('fs');
getByteAt(offset) {
return this._fileData.getByteAt(offset);
}
var ChunkedFileData = require('./ChunkedFileData');
var MediaFileReader = require('./MediaFileReader');
_init(callbacks) {
var self = this;
var NodeFileReader = function (_MediaFileReader) {
_inherits(NodeFileReader, _MediaFileReader);
fs.stat(self._path, function (err, stats) {
if (err) {
if (callbacks.onError) {
callbacks.onError({ "type": "fs", "info": err });
}
} else {
self._size = stats.size;
callbacks.onSuccess();
}
});
}
function NodeFileReader(path) {
_classCallCheck(this, NodeFileReader);
loadRange(range, callbacks) {
var fd = -1;
var self = this;
var fileData = this._fileData;
var _this = _possibleConstructorReturn(this, (NodeFileReader.__proto__ || Object.getPrototypeOf(NodeFileReader)).call(this));
var length = range[1] - range[0] + 1;
var onSuccess = callbacks.onSuccess;
var onError = callbacks.onError || function () {};
_this._path = path;
_this._fileData = new ChunkedFileData();
return _this;
}
if (fileData.hasDataRange(range[0], range[1])) {
process.nextTick(onSuccess);
return;
_createClass(NodeFileReader, [{
key: 'getByteAt',
value: function getByteAt(offset) {
return this._fileData.getByteAt(offset);
}
}, {
key: '_init',
value: function _init(callbacks) {
var self = this;
var readData = function (err, _fd) {
if (err) {
onError({ "type": "fs", "info": err });
fs.stat(self._path, function (err, stats) {
if (err) {
if (callbacks.onError) {
callbacks.onError({ "type": "fs", "info": err });
}
} else {
self._size = stats.size;
callbacks.onSuccess();
}
});
}
}, {
key: 'loadRange',
value: function loadRange(range, callbacks) {
var fd = -1;
var self = this;
var fileData = this._fileData;
var length = range[1] - range[0] + 1;
var onSuccess = callbacks.onSuccess;
var onError = callbacks.onError || function (object) {};
if (fileData.hasDataRange(range[0], range[1])) {
process.nextTick(onSuccess);
return;
}
fd = _fd;
// TODO: Should create a pool of Buffer objects across all instances of
// NodeFileReader. This is fine for now.
var buffer = new Buffer(length);
fs.read(_fd, buffer, 0, length, range[0], processData);
};
var readData = function (err, _fd) {
if (err) {
onError({ "type": "fs", "info": err });
return;
}
var processData = function (err, bytesRead, buffer) {
fs.close(fd, function (err) {
fd = _fd;
// TODO: Should create a pool of Buffer objects across all instances of
// NodeFileReader. This is fine for now.
var buffer = new Buffer(length);
fs.read(_fd, buffer, 0, length, range[0], processData);
};
var processData = function (err, bytesRead, buffer) {
fs.close(fd, function (err) {
if (err) {
console.error(err);
}
});
if (err) {
console.error(err);
onError({ "type": "fs", "info": err });
return;
}
});
if (err) {
onError({ "type": "fs", "info": err });
return;
}
storeBuffer(buffer);
onSuccess();
};
storeBuffer(buffer);
onSuccess();
};
var storeBuffer = function (buffer) {
var data = Array.prototype.slice.call(buffer, 0, length);
fileData.addData(range[0], data);
};
var storeBuffer = function (buffer) {
var data = Array.prototype.slice.call(buffer, 0, length);
fileData.addData(range[0], data);
};
fs.open(this._path, "r", undefined, readData);
}
}], [{
key: 'canReadFile',
value: function canReadFile(file) {
return typeof file === 'string' && !/^[a-z]+:\/\//i.test(file);
}
}]);
fs.open(this._path, "r", undefined, readData);
}
}
return NodeFileReader;
}(MediaFileReader);
module.exports = NodeFileReader;
'use strict';
class InternalDecodedString {
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
constructor(value, bytesReadCount) {
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var InternalDecodedString = function () {
function InternalDecodedString(value, bytesReadCount) {
_classCallCheck(this, InternalDecodedString);
this._value = value;

@@ -11,7 +16,12 @@ this.bytesReadCount = bytesReadCount;

toString() {
return this._value;
}
}
_createClass(InternalDecodedString, [{
key: "toString",
value: function toString() {
return this._value;
}
}]);
return InternalDecodedString;
}();
var StringUtils = {

@@ -18,0 +28,0 @@ readUTF16String: function (bytes, bigEndian, maxBytes) {

'use strict';
const ChunkedFileData = require('./ChunkedFileData');
const MediaFileReader = require('./MediaFileReader');
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
const CHUNK_SIZE = 1024;
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
class XhrFileReader extends MediaFileReader {
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
constructor(url) {
super();
this._url = url;
this._fileData = new ChunkedFileData();
}
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
static canReadFile(file) {
return typeof file === 'string' && /^[a-z]+:\/\//i.test(file);
}
var ChunkedFileData = require('./ChunkedFileData');
var MediaFileReader = require('./MediaFileReader');
static setConfig(config) {
for (var key in config) if (config.hasOwnProperty(key)) {
this._config[key] = config[key];
}
var CHUNK_SIZE = 1024;
var disallowedXhrHeaders = this._config.disallowedXhrHeaders;
for (var i = 0; i < disallowedXhrHeaders.length; i++) {
disallowedXhrHeaders[i] = disallowedXhrHeaders[i].toLowerCase();
}
}
var XhrFileReader = function (_MediaFileReader) {
_inherits(XhrFileReader, _MediaFileReader);
_init(callbacks) {
if (XhrFileReader._config.avoidHeadRequests) {
this._fetchSizeWithGetRequest(callbacks);
} else {
this._fetchSizeWithHeadRequest(callbacks);
}
}
function XhrFileReader(url) {
_classCallCheck(this, XhrFileReader);
_fetchSizeWithHeadRequest(callbacks) {
var self = this;
var _this = _possibleConstructorReturn(this, (XhrFileReader.__proto__ || Object.getPrototypeOf(XhrFileReader)).call(this));
this._makeXHRRequest("HEAD", null, {
onSuccess: function (xhr) {
var contentLength = self._parseContentLength(xhr);
if (contentLength) {
self._size = contentLength;
callbacks.onSuccess();
} else {
// Content-Length not provided by the server, fallback to
// GET requests.
self._fetchSizeWithGetRequest(callbacks);
}
},
onError: callbacks.onError
});
_this._url = url;
_this._fileData = new ChunkedFileData();
return _this;
}
_fetchSizeWithGetRequest(callbacks) {
var self = this;
var range = this._roundRangeToChunkMultiple([0, 0]);
_createClass(XhrFileReader, [{
key: '_init',
value: function _init(callbacks) {
if (XhrFileReader._config.avoidHeadRequests) {
this._fetchSizeWithGetRequest(callbacks);
} else {
this._fetchSizeWithHeadRequest(callbacks);
}
}
}, {
key: '_fetchSizeWithHeadRequest',
value: function _fetchSizeWithHeadRequest(callbacks) {
var self = this;
this._makeXHRRequest("GET", range, {
onSuccess: function (xhr) {
var contentRange = self._parseContentRange(xhr);
var data = self._getXhrResponseContent(xhr);
if (contentRange) {
if (contentRange.instanceLength == null) {
// Last resort, server is not able to tell us the content length,
// need to fetch entire file then.
self._fetchEntireFile(callbacks);
return;
this._makeXHRRequest("HEAD", null, {
onSuccess: function (xhr) {
var contentLength = self._parseContentLength(xhr);
if (contentLength) {
self._size = contentLength;
callbacks.onSuccess();
} else {
// Content-Length not provided by the server, fallback to
// GET requests.
self._fetchSizeWithGetRequest(callbacks);
}
self._size = contentRange.instanceLength;
} else {
// Range request not supported, we got the entire file
self._size = data.length;
}
},
onError: callbacks.onError
});
}
}, {
key: '_fetchSizeWithGetRequest',
value: function _fetchSizeWithGetRequest(callbacks) {
var self = this;
var range = this._roundRangeToChunkMultiple([0, 0]);
self._fileData.addData(0, data);
callbacks.onSuccess();
},
onError: callbacks.onError
});
}
this._makeXHRRequest("GET", range, {
onSuccess: function (xhr) {
var contentRange = self._parseContentRange(xhr);
var data = self._getXhrResponseContent(xhr);
_fetchEntireFile(callbacks) {
var self = this;
this._makeXHRRequest("GET", null, {
onSuccess: function (xhr) {
var data = self._getXhrResponseContent(xhr);
self._size = data.length;
self._fileData.addData(0, data);
callbacks.onSuccess();
},
onError: callbacks.onError
});
}
if (contentRange) {
if (contentRange.instanceLength == null) {
// Last resort, server is not able to tell us the content length,
// need to fetch entire file then.
self._fetchEntireFile(callbacks);
return;
}
self._size = contentRange.instanceLength;
} else {
// Range request not supported, we got the entire file
self._size = data.length;
}
_getXhrResponseContent(xhr) {
return xhr.responseBody || xhr.responseText || "";
}
_parseContentLength(xhr) {
var contentLength = this._getResponseHeader(xhr, "Content-Length");
if (contentLength == null) {
return contentLength;
} else {
return parseInt(contentLength, 10);
self._fileData.addData(0, data);
callbacks.onSuccess();
},
onError: callbacks.onError
});
}
}
}, {
key: '_fetchEntireFile',
value: function _fetchEntireFile(callbacks) {
var self = this;
this._makeXHRRequest("GET", null, {
onSuccess: function (xhr) {
var data = self._getXhrResponseContent(xhr);
self._size = data.length;
self._fileData.addData(0, data);
callbacks.onSuccess();
},
onError: callbacks.onError
});
}
}, {
key: '_getXhrResponseContent',
value: function _getXhrResponseContent(xhr) {
return xhr.responseBody || xhr.responseText || "";
}
}, {
key: '_parseContentLength',
value: function _parseContentLength(xhr) {
var contentLength = this._getResponseHeader(xhr, "Content-Length");
_parseContentRange(xhr) {
var contentRange = this._getResponseHeader(xhr, "Content-Range");
if (contentRange) {
var parsedContentRange = contentRange.match(/bytes (\d+)-(\d+)\/(?:(\d+)|\*)/i);
if (!parsedContentRange) {
throw new Error("FIXME: Unknown Content-Range syntax: ", contentRange);
if (contentLength == null) {
return contentLength;
} else {
return parseInt(contentLength, 10);
}
return {
firstBytePosition: parseInt(parsedContentRange[1], 10),
lastBytePosition: parseInt(parsedContentRange[2], 10),
instanceLength: parsedContentRange[3] ? parseInt(parsedContentRange[3], 10) : null
};
} else {
return null;
}
}
}, {
key: '_parseContentRange',
value: function _parseContentRange(xhr) {
var contentRange = this._getResponseHeader(xhr, "Content-Range");
loadRange(range, callbacks) {
var self = this;
if (contentRange) {
var parsedContentRange = contentRange.match(/bytes (\d+)-(\d+)\/(?:(\d+)|\*)/i);
if (!parsedContentRange) {
throw new Error("FIXME: Unknown Content-Range syntax: " + contentRange);
}
if (self._fileData.hasDataRange(range[0], Math.min(self._size, range[1]))) {
setTimeout(callbacks.onSuccess, 1);
return;
return {
firstBytePosition: parseInt(parsedContentRange[1], 10),
lastBytePosition: parseInt(parsedContentRange[2], 10),
instanceLength: parsedContentRange[3] ? parseInt(parsedContentRange[3], 10) : null
};
} else {
return null;
}
}
}, {
key: 'loadRange',
value: function loadRange(range, callbacks) {
var self = this;
// Always download in multiples of CHUNK_SIZE. If we're going to make a
// request might as well get a chunk that makes sense. The big cost is
// establishing the connection so getting 10bytes or 1K doesn't really
// make a difference.
range = this._roundRangeToChunkMultiple(range);
if (self._fileData.hasDataRange(range[0], Math.min(self._size, range[1]))) {
setTimeout(callbacks.onSuccess, 1);
return;
}
// Upper range should not be greater than max file size
range[1] = Math.min(self._size, range[1]);
// Always download in multiples of CHUNK_SIZE. If we're going to make a
// request might as well get a chunk that makes sense. The big cost is
// establishing the connection so getting 10bytes or 1K doesn't really
// make a difference.
range = this._roundRangeToChunkMultiple(range);
this._makeXHRRequest("GET", range, {
onSuccess: function (xhr) {
var data = self._getXhrResponseContent(xhr);
self._fileData.addData(range[0], data);
callbacks.onSuccess();
},
onError: callbacks.onError
});
}
// Upper range should not be greater than max file size
range[1] = Math.min(self._size, range[1]);
_roundRangeToChunkMultiple(range) {
var length = range[1] - range[0] + 1;
var newLength = Math.ceil(length / CHUNK_SIZE) * CHUNK_SIZE;
return [range[0], range[0] + newLength - 1];
}
this._makeXHRRequest("GET", range, {
onSuccess: function (xhr) {
var data = self._getXhrResponseContent(xhr);
self._fileData.addData(range[0], data);
callbacks.onSuccess();
},
onError: callbacks.onError
});
}
}, {
key: '_roundRangeToChunkMultiple',
value: function _roundRangeToChunkMultiple(range) {
var length = range[1] - range[0] + 1;
var newLength = Math.ceil(length / CHUNK_SIZE) * CHUNK_SIZE;
return [range[0], range[0] + newLength - 1];
}
}, {
key: '_makeXHRRequest',
value: function _makeXHRRequest(method, range, callbacks) {
var xhr = this._createXHRObject();
xhr.open(method, this._url);
_makeXHRRequest(method, range, callbacks) {
var xhr = this._createXHRObject();
var onXHRLoad = function () {
// 200 - OK
// 206 - Partial Content
// $FlowIssue - xhr will not be null here
if (xhr.status === 200 || xhr.status === 206) {
callbacks.onSuccess(xhr);
} else if (callbacks.onError) {
callbacks.onError({
"type": "xhr",
"info": "Unexpected HTTP status " + xhr.status + ".",
"xhr": xhr
});
}
xhr = null;
};
if (typeof xhr.onload !== 'undefined') {
xhr.onload = onXHRLoad;
xhr.onerror = function () {
if (callbacks.onError) {
callbacks.onError({
"type": "xhr",
"info": "Generic XHR error, check xhr object.",
"xhr": xhr
});
}
};
} else {
xhr.onreadystatechange = function () {
var onXHRLoad = function () {
// 200 - OK
// 206 - Partial Content
// $FlowIssue - xhr will not be null here
if (xhr.readyState === 4) {
onXHRLoad();
}
};
}
if (XhrFileReader._config.timeoutInSec) {
xhr.timeout = XhrFileReader._config.timeoutInSec * 1000;
xhr.ontimeout = function () {
if (callbacks.onError) {
if (xhr.status === 200 || xhr.status === 206) {
callbacks.onSuccess(xhr);
} else if (callbacks.onError) {
callbacks.onError({
"type": "xhr",
// $FlowIssue - xhr.timeout will not be null
"info": "Timeout after " + xhr.timeout / 1000 + "s. Use jsmediatags.Config.setXhrTimeout to override.",
"info": "Unexpected HTTP status " + xhr.status + ".",
"xhr": xhr
});
}
xhr = null;
};
}
xhr.open(method, this._url);
xhr.overrideMimeType("text/plain; charset=x-user-defined");
if (range) {
this._setRequestHeader(xhr, "Range", "bytes=" + range[0] + "-" + range[1]);
}
this._setRequestHeader(xhr, "If-Modified-Since", "Sat, 01 Jan 1970 00:00:00 GMT");
xhr.send(null);
}
if (typeof xhr.onload !== 'undefined') {
xhr.onload = onXHRLoad;
xhr.onerror = function () {
if (callbacks.onError) {
callbacks.onError({
"type": "xhr",
"info": "Generic XHR error, check xhr object.",
"xhr": xhr
});
}
};
} else {
xhr.onreadystatechange = function () {
// $FlowIssue - xhr will not be null here
if (xhr.readyState === 4) {
onXHRLoad();
}
};
}
_setRequestHeader(xhr, headerName, headerValue) {
if (XhrFileReader._config.disallowedXhrHeaders.indexOf(headerName.toLowerCase()) < 0) {
xhr.setRequestHeader(headerName, headerValue);
if (XhrFileReader._config.timeoutInSec) {
xhr.timeout = XhrFileReader._config.timeoutInSec * 1000;
xhr.ontimeout = function () {
if (callbacks.onError) {
callbacks.onError({
"type": "xhr",
// $FlowIssue - xhr.timeout will not be null
"info": "Timeout after " + xhr.timeout / 1000 + "s. Use jsmediatags.Config.setXhrTimeout to override.",
"xhr": xhr
});
}
};
}
xhr.overrideMimeType("text/plain; charset=x-user-defined");
if (range) {
this._setRequestHeader(xhr, "Range", "bytes=" + range[0] + "-" + range[1]);
}
this._setRequestHeader(xhr, "If-Modified-Since", "Sat, 01 Jan 1970 00:00:00 GMT");
xhr.send(null);
}
}
}, {
key: '_setRequestHeader',
value: function _setRequestHeader(xhr, headerName, headerValue) {
if (XhrFileReader._config.disallowedXhrHeaders.indexOf(headerName.toLowerCase()) < 0) {
xhr.setRequestHeader(headerName, headerValue);
}
}
}, {
key: '_hasResponseHeader',
value: function _hasResponseHeader(xhr, headerName) {
var allResponseHeaders = xhr.getAllResponseHeaders();
_hasResponseHeader(xhr, headerName) {
var allResponseHeaders = xhr.getAllResponseHeaders();
if (!allResponseHeaders) {
return false;
}
if (!allResponseHeaders) {
return false;
}
var headers = allResponseHeaders.split("\r\n");
var headerNames = [];
for (var i = 0; i < headers.length; i++) {
headerNames[i] = headers[i].split(":")[0].toLowerCase();
}
var headers = allResponseHeaders.split("\r\n");
var headerNames = [];
for (var i = 0; i < headers.length; i++) {
headerNames[i] = headers[i].split(":")[0].toLowerCase();
return headerNames.indexOf(headerName.toLowerCase()) >= 0;
}
}, {
key: '_getResponseHeader',
value: function _getResponseHeader(xhr, headerName) {
if (!this._hasResponseHeader(xhr, headerName)) {
return null;
}
return headerNames.indexOf(headerName.toLowerCase()) >= 0;
}
_getResponseHeader(xhr, headerName) {
if (!this._hasResponseHeader(xhr, headerName)) {
return null;
return xhr.getResponseHeader(headerName);
}
}, {
key: 'getByteAt',
value: function getByteAt(offset) {
var character = this._fileData.getByteAt(offset);
return character.charCodeAt(0) & 0xff;
}
}, {
key: '_isWebWorker',
value: function _isWebWorker() {
return typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope;
}
}, {
key: '_createXHRObject',
value: function _createXHRObject() {
if (typeof window === "undefined" && !this._isWebWorker()) {
// $FlowIssue - flow is not able to recognize this module.
return new (require("xhr2").XMLHttpRequest)();
}
return xhr.getResponseHeader(headerName);
}
if (typeof XMLHttpRequest !== "undefined") {
return new XMLHttpRequest();
}
getByteAt(offset) {
var character = this._fileData.getByteAt(offset);
return character.charCodeAt(0) & 0xff;
}
_isWebWorker() {
return typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope;
}
_createXHRObject() {
if (typeof window === "undefined" && !this._isWebWorker()) {
// $FlowIssue - flow is not able to recognize this module.
return new (require("xhr2").XMLHttpRequest)();
throw new Error("XMLHttpRequest is not supported");
}
if (typeof XMLHttpRequest !== "undefined") {
return new XMLHttpRequest();
}], [{
key: 'canReadFile',
value: function canReadFile(file) {
return typeof file === 'string' && /^[a-z]+:\/\//i.test(file);
}
}, {
key: 'setConfig',
value: function setConfig(config) {
for (var key in config) {
if (config.hasOwnProperty(key)) {
this._config[key] = config[key];
}
}var disallowedXhrHeaders = this._config.disallowedXhrHeaders;
for (var i = 0; i < disallowedXhrHeaders.length; i++) {
disallowedXhrHeaders[i] = disallowedXhrHeaders[i].toLowerCase();
}
}
}]);
throw new Error("XMLHttpRequest is not supported");
}
}
return XhrFileReader;
}(MediaFileReader);

@@ -281,0 +316,0 @@ XhrFileReader._config = {

@@ -10,2 +10,5 @@ # JS MediaTags Contributors

* **[Michael Nguyen](https://github.com/TehTotalPwnage)**
* Added FLAC support.
* **[Mark R. James](https://github.com/mrj)**

@@ -23,2 +26,5 @@ * Added support for atID, cnID, plID, geID, xid, and flvr iTunes/Quicktime tags.

* **[Gilles Piou](https://github.com/pioug)**
* Updated and tidied up jest and babel.
* **[Guilherme Dellagustin](https://github.com/dellagustin)**

@@ -25,0 +31,0 @@ * **[Jon Hester](https://github.com/jonhester)**

@@ -1,20 +0,26 @@

(function(v){"object"===typeof exports&&"undefined"!==typeof module?module.exports=v():"function"===typeof define&&define.amd?define([],v):("undefined"!==typeof window?window:"undefined"!==typeof global?global:"undefined"!==typeof self?self:this).jsmediatags=v()})(function(){return function f(p,q,l){function m(b,a){if(!q[b]){if(!p[b]){var d="function"==typeof require&&require;if(!a&&d)return d(b,!0);if(e)return e(b,!0);a=Error("Cannot find module '"+b+"'");throw a.code="MODULE_NOT_FOUND",a;}a=q[b]=
{exports:{}};p[b][0].call(a.exports,function(a){var d=p[b][1][a];return m(d?d:a)},a,a.exports,f,p,q,l)}return q[b].exports}for(var e="function"==typeof require&&require,c=0;c<l.length;c++)m(l[c]);return m}({1:[function(f,p,q){},{}],2:[function(f,p,q){p.exports=XMLHttpRequest},{}],3:[function(f,p,q){function l(e,c){if("function"!==typeof c&&null!==c)throw new TypeError("Super expression must either be null or a function, not "+typeof c);e.prototype=Object.create(c&&c.prototype,{constructor:{value:e,
enumerable:!1,writable:!0,configurable:!0}});c&&(Object.setPrototypeOf?Object.setPrototypeOf(e,c):e.__proto__=c)}var m=function(){function e(c,b){for(var a=0;a<b.length;a++){var d=b[a];d.enumerable=d.enumerable||!1;d.configurable=!0;"value"in d&&(d.writable=!0);Object.defineProperty(c,d.key,d)}}return function(c,b,a){b&&e(c.prototype,b);a&&e(c,a);return c}}();f=function(e){function c(b){if(!(this instanceof c))throw new TypeError("Cannot call a class as a function");var a=Object.getPrototypeOf(c).call(this);
if(!this)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");a=!a||"object"!==typeof a&&"function"!==typeof a?this:a;a._array=b;a._size=b.length;a._isInitialized=!0;return a}l(c,e);m(c,[{key:"init",value:function(b){setTimeout(b.onSuccess,0)}},{key:"loadRange",value:function(b,a){setTimeout(a.onSuccess,0)}},{key:"getByteAt",value:function(b){if(b>=this._array.length)throw Error("Offset "+b+" hasn't been loaded yet.");return this._array[b]}}],[{key:"canReadFile",value:function(b){return Array.isArray(b)||
"function"===typeof Buffer&&Buffer.isBuffer(b)}}]);return c}(f("./MediaFileReader"));p.exports=f},{"./MediaFileReader":10}],4:[function(f,p,q){function l(c,b){if("function"!==typeof b&&null!==b)throw new TypeError("Super expression must either be null or a function, not "+typeof b);c.prototype=Object.create(b&&b.prototype,{constructor:{value:c,enumerable:!1,writable:!0,configurable:!0}});b&&(Object.setPrototypeOf?Object.setPrototypeOf(c,b):c.__proto__=b)}var m=function(){function c(b,a){for(var d=
0;d<a.length;d++){var c=a[d];c.enumerable=c.enumerable||!1;c.configurable=!0;"value"in c&&(c.writable=!0);Object.defineProperty(b,c.key,c)}}return function(b,a,d){a&&c(b.prototype,a);d&&c(b,d);return b}}(),e=f("./ChunkedFileData");f=function(c){function b(a){if(!(this instanceof b))throw new TypeError("Cannot call a class as a function");var d=Object.getPrototypeOf(b).call(this);if(!this)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");d=!d||"object"!==typeof d&&
"function"!==typeof d?this:d;d._blob=a;d._fileData=new e;return d}l(b,c);m(b,[{key:"_init",value:function(a){this._size=this._blob.size;setTimeout(a.onSuccess,1)}},{key:"loadRange",value:function(a,d){var c=this,h=(this._blob.slice||this._blob.mozSlice||this._blob.webkitSlice).call(this._blob,a[0],a[1]+1),b=new FileReader;b.onloadend=function(h){h=new Uint8Array(b.result);c._fileData.addData(a[0],h);d.onSuccess()};b.onerror=b.onabort=function(a){if(d.onError)d.onError({type:"blob",info:b.error})};
b.readAsArrayBuffer(h)}},{key:"getByteAt",value:function(a){return this._fileData.getByteAt(a)}}],[{key:"canReadFile",value:function(a){return"undefined"!==typeof Blob&&a instanceof Blob||"undefined"!==typeof File&&a instanceof File}}]);return b}(f("./MediaFileReader"));p.exports=f},{"./ChunkedFileData":5,"./MediaFileReader":10}],5:[function(f,p,q){var l=function(){function f(e,c){for(var b=0;b<c.length;b++){var a=c[b];a.enumerable=a.enumerable||!1;a.configurable=!0;"value"in a&&(a.writable=!0);Object.defineProperty(e,
a.key,a)}}return function(e,c,b){c&&f(e.prototype,c);b&&f(e,b);return e}}();f=function(){function f(){if(!(this instanceof f))throw new TypeError("Cannot call a class as a function");this._fileData=[]}l(f,null,[{key:"NOT_FOUND",get:function(){return-1}}]);l(f,[{key:"addData",value:function(e,c){var b=e+c.length-1,a=this._getChunkRange(e,b);if(-1===a.startIx)this._fileData.splice(a.insertIx||0,0,{offset:e,data:c});else{var d=this._fileData[a.startIx],g=this._fileData[a.endIx];b=b<g.offset+g.data.length-
1;var h={offset:Math.min(e,d.offset),data:c};e>d.offset&&(e=this._sliceData(d.data,0,e-d.offset),h.data=this._concatData(e,c));b&&(e=this._sliceData(h.data,0,g.offset-h.offset),h.data=this._concatData(e,g.data));this._fileData.splice(a.startIx,a.endIx-a.startIx+1,h)}}},{key:"_concatData",value:function(e,c){if("undefined"!==typeof ArrayBuffer&&ArrayBuffer.isView&&ArrayBuffer.isView(e)){var b=new e.constructor(e.length+c.length);b.set(e,0);b.set(c,e.length);return b}return e.concat(c)}},{key:"_sliceData",
value:function(e,c,b){return e.slice?e.slice(c,b):e.subarray(c,b)}},{key:"_getChunkRange",value:function(e,c){for(var b,a,d=-1,g=-1,h=0,k=0;k<this._fileData.length;k++,h=k){a=this._fileData[k].offset;b=a+this._fileData[k].data.length;if(c<a-1)break;if(e<=b+1&&c>=a-1){d=k;break}}if(-1===d)return{startIx:-1,endIx:-1,insertIx:h};for(k=d;k<this._fileData.length&&!(a=this._fileData[k].offset,b=a+this._fileData[k].data.length,c>=a-1&&(g=k),c<=b+1);k++);-1===g&&(g=d);return{startIx:d,endIx:g}}},{key:"hasDataRange",
value:function(e,c){for(var b=0;b<this._fileData.length;b++){var a=this._fileData[b];if(c<a.offset)break;if(e>=a.offset&&c<a.offset+a.data.length)return!0}return!1}},{key:"getByteAt",value:function(e){for(var c,b=0;b<this._fileData.length;b++){var a=this._fileData[b].offset,d=a+this._fileData[b].data.length-1;if(e>=a&&e<=d){c=this._fileData[b];break}}if(c)return c.data[e-c.offset];throw Error("Offset "+e+" hasn't been loaded yet.");}}]);return f}();p.exports=f},{}],6:[function(f,p,q){function l(c,
b){if("function"!==typeof b&&null!==b)throw new TypeError("Super expression must either be null or a function, not "+typeof b);c.prototype=Object.create(b&&b.prototype,{constructor:{value:c,enumerable:!1,writable:!0,configurable:!0}});b&&(Object.setPrototypeOf?Object.setPrototypeOf(c,b):c.__proto__=b)}var m=function(){function c(c,a){for(var d=0;d<a.length;d++){var b=a[d];b.enumerable=b.enumerable||!1;b.configurable=!0;"value"in b&&(b.writable=!0);Object.defineProperty(c,b.key,b)}}return function(b,
a,d){a&&c(b.prototype,a);d&&c(b,d);return b}}();q=f("./MediaTagReader");f("./MediaFileReader");f=function(c){function b(){if(!(this instanceof b))throw new TypeError("Cannot call a class as a function");var a=Object.getPrototypeOf(b).apply(this,arguments);if(!this)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!a||"object"!==typeof a&&"function"!==typeof a?this:a}l(b,c);m(b,[{key:"_loadData",value:function(a,d){var b=a.getSize();a.loadRange([b-128,b-1],
d)}},{key:"_parseData",value:function(a,d){var b=a.getSize()-128,c=a.getStringWithCharsetAt(b+3,30).toString(),k=a.getStringWithCharsetAt(b+33,30).toString(),t=a.getStringWithCharsetAt(b+63,30).toString(),u=a.getStringWithCharsetAt(b+93,4).toString();var r=a.getByteAt(b+97+28);d=a.getByteAt(b+97+29);if(0==r&&0!=d){var n="1.1";r=a.getStringWithCharsetAt(b+97,28).toString()}else n="1.0",r=a.getStringWithCharsetAt(b+97,30).toString(),d=0;a=a.getByteAt(b+97+30);a={type:"ID3",version:n,tags:{title:c,artist:k,
album:t,year:u,comment:r,genre:255>a?e[a]:""}};d&&(a.tags.track=d);return a}}],[{key:"getTagIdentifierByteRange",value:function(){return{offset:-128,length:128}}},{key:"canReadTagFormat",value:function(a){return"TAG"===String.fromCharCode.apply(String,a.slice(0,3))}}]);return b}(q);var e="Blues;Classic Rock;Country;Dance;Disco;Funk;Grunge;Hip-Hop;Jazz;Metal;New Age;Oldies;Other;Pop;R&B;Rap;Reggae;Rock;Techno;Industrial;Alternative;Ska;Death Metal;Pranks;Soundtrack;Euro-Techno;Ambient;Trip-Hop;Vocal;Jazz+Funk;Fusion;Trance;Classical;Instrumental;Acid;House;Game;Sound Clip;Gospel;Noise;AlternRock;Bass;Soul;Punk;Space;Meditative;Instrumental Pop;Instrumental Rock;Ethnic;Gothic;Darkwave;Techno-Industrial;Electronic;Pop-Folk;Eurodance;Dream;Southern Rock;Comedy;Cult;Gangsta;Top 40;Christian Rap;Pop/Funk;Jungle;Native American;Cabaret;New Wave;Psychadelic;Rave;Showtunes;Trailer;Lo-Fi;Tribal;Acid Punk;Acid Jazz;Polka;Retro;Musical;Rock & Roll;Hard Rock;Folk;Folk-Rock;National Folk;Swing;Fast Fusion;Bebob;Latin;Revival;Celtic;Bluegrass;Avantgarde;Gothic Rock;Progressive Rock;Psychedelic Rock;Symphonic Rock;Slow Rock;Big Band;Chorus;Easy Listening;Acoustic;Humour;Speech;Chanson;Opera;Chamber Music;Sonata;Symphony;Booty Bass;Primus;Porn Groove;Satire;Slow Jam;Club;Tango;Samba;Folklore;Ballad;Power Ballad;Rhythmic Soul;Freestyle;Duet;Punk Rock;Drum Solo;Acapella;Euro-House;Dance Hall".split(";");
p.exports=f},{"./MediaFileReader":10,"./MediaTagReader":11}],7:[function(f,p,q){function l(a){switch(a){case 0:var d="iso-8859-1";break;case 1:d="utf-16";break;case 2:d="utf-16be";break;case 3:d="utf-8"}return d}function m(a,d,b,c){c=b.getStringWithCharsetAt(a+1,d-1,c);a=b.getStringWithCharsetAt(a+1+c.bytesReadCount,d-1-c.bytesReadCount);return{user_description:c.toString(),data:a.toString()}}var e=function(){function a(a,d){for(var b=0;b<d.length;b++){var c=d[b];c.enumerable=c.enumerable||!1;c.configurable=
!0;"value"in c&&(c.writable=!0);Object.defineProperty(a,c.key,c)}}return function(d,b,c){b&&a(d.prototype,b);c&&a(d,c);return d}}();f("./MediaFileReader");var c=f("./StringUtils"),b=f("./ArrayFileReader"),a={BUF:"Recommended buffer size",CNT:"Play counter",COM:"Comments",CRA:"Audio encryption",CRM:"Encrypted meta frame",ETC:"Event timing codes",EQU:"Equalization",GEO:"General encapsulated object",IPL:"Involved people list",LNK:"Linked information",MCI:"Music CD Identifier",MLL:"MPEG location lookup table",
PIC:"Attached picture",POP:"Popularimeter",REV:"Reverb",RVA:"Relative volume adjustment",SLT:"Synchronized lyric/text",STC:"Synced tempo codes",TAL:"Album/Movie/Show title",TBP:"BPM (Beats Per Minute)",TCM:"Composer",TCO:"Content type",TCR:"Copyright message",TDA:"Date",TDY:"Playlist delay",TEN:"Encoded by",TFT:"File type",TIM:"Time",TKE:"Initial key",TLA:"Language(s)",TLE:"Length",TMT:"Media type",TOA:"Original artist(s)/performer(s)",TOF:"Original filename",TOL:"Original Lyricist(s)/text writer(s)",
(function(y){"object"===typeof exports&&"undefined"!==typeof module?module.exports=y():"function"===typeof define&&define.amd?define([],y):("undefined"!==typeof window?window:"undefined"!==typeof global?global:"undefined"!==typeof self?self:this).jsmediatags=y()})(function(){return function f(p,q,m){function l(d,a){if(!q[d]){if(!p[d]){var c="function"==typeof require&&require;if(!a&&c)return c(d,!0);if(e)return e(d,!0);a=Error("Cannot find module '"+d+"'");throw a.code="MODULE_NOT_FOUND",a;}a=q[d]=
{exports:{}};p[d][0].call(a.exports,function(a){var c=p[d][1][a];return l(c?c:a)},a,a.exports,f,p,q,m)}return q[d].exports}for(var e="function"==typeof require&&require,b=0;b<m.length;b++)l(m[b]);return l}({1:[function(f,p,q){},{}],2:[function(f,p,q){p.exports=XMLHttpRequest},{}],3:[function(f,p,q){function m(e,b){if("function"!==typeof b&&null!==b)throw new TypeError("Super expression must either be null or a function, not "+typeof b);e.prototype=Object.create(b&&b.prototype,{constructor:{value:e,
enumerable:!1,writable:!0,configurable:!0}});b&&(Object.setPrototypeOf?Object.setPrototypeOf(e,b):e.__proto__=b)}var l=function(){function e(b,d){for(var a=0;a<d.length;a++){var c=d[a];c.enumerable=c.enumerable||!1;c.configurable=!0;"value"in c&&(c.writable=!0);Object.defineProperty(b,c.key,c)}}return function(b,d,a){d&&e(b.prototype,d);a&&e(b,a);return b}}();f=function(e){function b(d){if(!(this instanceof b))throw new TypeError("Cannot call a class as a function");var a=(b.__proto__||Object.getPrototypeOf(b)).call(this);
if(!this)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");a=!a||"object"!==typeof a&&"function"!==typeof a?this:a;a._array=d;a._size=d.length;a._isInitialized=!0;return a}m(b,e);l(b,[{key:"init",value:function(d){setTimeout(d.onSuccess,0)}},{key:"loadRange",value:function(d,a){setTimeout(a.onSuccess,0)}},{key:"getByteAt",value:function(d){if(d>=this._array.length)throw Error("Offset "+d+" hasn't been loaded yet.");return this._array[d]}}],[{key:"canReadFile",value:function(d){return Array.isArray(d)||
"function"===typeof Buffer&&Buffer.isBuffer(d)}}]);return b}(f("./MediaFileReader"));p.exports=f},{"./MediaFileReader":11}],4:[function(f,p,q){function m(b,d){if("function"!==typeof d&&null!==d)throw new TypeError("Super expression must either be null or a function, not "+typeof d);b.prototype=Object.create(d&&d.prototype,{constructor:{value:b,enumerable:!1,writable:!0,configurable:!0}});d&&(Object.setPrototypeOf?Object.setPrototypeOf(b,d):b.__proto__=d)}var l=function(){function b(d,a){for(var c=
0;c<a.length;c++){var b=a[c];b.enumerable=b.enumerable||!1;b.configurable=!0;"value"in b&&(b.writable=!0);Object.defineProperty(d,b.key,b)}}return function(d,a,c){a&&b(d.prototype,a);c&&b(d,c);return d}}(),e=f("./ChunkedFileData");f=function(b){function d(a){if(!(this instanceof d))throw new TypeError("Cannot call a class as a function");var c=(d.__proto__||Object.getPrototypeOf(d)).call(this);if(!this)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");c=!c||"object"!==
typeof c&&"function"!==typeof c?this:c;c._blob=a;c._fileData=new e;return c}m(d,b);l(d,[{key:"_init",value:function(a){this._size=this._blob.size;setTimeout(a.onSuccess,1)}},{key:"loadRange",value:function(a,c){var b=this,h=(this._blob.slice||this._blob.mozSlice||this._blob.webkitSlice).call(this._blob,a[0],a[1]+1),d=new FileReader;d.onloadend=function(h){h=new Uint8Array(Number(d.result));b._fileData.addData(a[0],h);c.onSuccess()};d.onerror=d.onabort=function(a){if(c.onError)c.onError({type:"blob",
info:d.error})};d.readAsArrayBuffer(h)}},{key:"getByteAt",value:function(a){return this._fileData.getByteAt(a)}}],[{key:"canReadFile",value:function(a){return"undefined"!==typeof Blob&&a instanceof Blob||"undefined"!==typeof File&&a instanceof File}}]);return d}(f("./MediaFileReader"));p.exports=f},{"./ChunkedFileData":5,"./MediaFileReader":11}],5:[function(f,p,q){var m=function(){function f(e,b){for(var d=0;d<b.length;d++){var a=b[d];a.enumerable=a.enumerable||!1;a.configurable=!0;"value"in a&&(a.writable=
!0);Object.defineProperty(e,a.key,a)}}return function(e,b,d){b&&f(e.prototype,b);d&&f(e,d);return e}}();f=function(){function f(){if(!(this instanceof f))throw new TypeError("Cannot call a class as a function");this._fileData=[]}m(f,null,[{key:"NOT_FOUND",get:function(){return-1}}]);m(f,[{key:"addData",value:function(e,b){var d=e+b.length-1,a=this._getChunkRange(e,d);if(-1===a.startIx)this._fileData.splice(a.insertIx||0,0,{offset:e,data:b});else{var c=this._fileData[a.startIx],g=this._fileData[a.endIx];
d=d<g.offset+g.data.length-1;var h={offset:Math.min(e,c.offset),data:b};e>c.offset&&(e=this._sliceData(c.data,0,e-c.offset),h.data=this._concatData(e,b));d&&(e=this._sliceData(h.data,0,g.offset-h.offset),h.data=this._concatData(e,g.data));this._fileData.splice(a.startIx,a.endIx-a.startIx+1,h)}}},{key:"_concatData",value:function(e,b){if("undefined"!==typeof ArrayBuffer&&ArrayBuffer.isView&&ArrayBuffer.isView(e)){var d=new e.constructor(e.length+b.length);d.set(e,0);d.set(b,e.length);return d}return e.concat(b)}},
{key:"_sliceData",value:function(e,b,d){return e.slice?e.slice(b,d):e.subarray(b,d)}},{key:"_getChunkRange",value:function(e,b){for(var d,a,c=-1,g=-1,h=0,k=0;k<this._fileData.length;k++,h=k){a=this._fileData[k].offset;d=a+this._fileData[k].data.length;if(b<a-1)break;if(e<=d+1&&b>=a-1){c=k;break}}if(-1===c)return{startIx:-1,endIx:-1,insertIx:h};for(k=c;k<this._fileData.length&&!(a=this._fileData[k].offset,d=a+this._fileData[k].data.length,b>=a-1&&(g=k),b<=d+1);k++);-1===g&&(g=c);return{startIx:c,endIx:g}}},
{key:"hasDataRange",value:function(e,b){for(var d=0;d<this._fileData.length;d++){var a=this._fileData[d];if(b<a.offset)break;if(e>=a.offset&&b<a.offset+a.data.length)return!0}return!1}},{key:"getByteAt",value:function(e){for(var b,d=0;d<this._fileData.length;d++){var a=this._fileData[d].offset,c=a+this._fileData[d].data.length-1;if(e>=a&&e<=c){b=this._fileData[d];break}}if(b)return b.data[e-b.offset];throw Error("Offset "+e+" hasn't been loaded yet.");}}]);return f}();p.exports=f},{}],6:[function(f,
p,q){function m(a,c){if("function"!==typeof c&&null!==c)throw new TypeError("Super expression must either be null or a function, not "+typeof c);a.prototype=Object.create(c&&c.prototype,{constructor:{value:a,enumerable:!1,writable:!0,configurable:!0}});c&&(Object.setPrototypeOf?Object.setPrototypeOf(a,c):a.__proto__=c)}var l=function(){function a(a,d){for(var c=0;c<d.length;c++){var b=d[c];b.enumerable=b.enumerable||!1;b.configurable=!0;"value"in b&&(b.writable=!0);Object.defineProperty(a,b.key,b)}}
return function(c,b,h){b&&a(c.prototype,b);h&&a(c,h);return c}}(),e=[4,132],b=[6,134],d="Other;32x32 pixels 'file icon' (PNG only);Other file icon;Cover (front);Cover (back);Leaflet page;Media (e.g. label side of CD);Lead artist/lead performer/soloist;Artist/performer;Conductor;Band/Orchestra;Composer;Lyricist/text writer;Recording Location;During recording;During performance;Movie/video screen capture;A bright coloured fish;Illustration;Band/artist logotype;Publisher/Studio logotype".split(";");
f=function(a){function c(){if(!(this instanceof c))throw new TypeError("Cannot call a class as a function");var a=(c.__proto__||Object.getPrototypeOf(c)).apply(this,arguments);if(!this)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!a||"object"!==typeof a&&"function"!==typeof a?this:a}m(c,a);l(c,[{key:"_loadData",value:function(a,c){var h=this;a.loadRange([4,7],{onSuccess:function(){h._loadBlock(a,4,c)}})}},{key:"_loadBlock",value:function(a,c,d){var h=
this,g=a.getByteAt(c),k=a.getInteger24At(c+1,!0);if(-1!==e.indexOf(g)){var n=c+4;a.loadRange([n,n+k],{onSuccess:function(){h._commentOffset=n;h._nextBlock(a,c,g,k,d)}})}else-1!==b.indexOf(g)?(n=c+4,a.loadRange([n,n+k],{onSuccess:function(){h._pictureOffset=n;h._nextBlock(a,c,g,k,d)}})):h._nextBlock(a,c,g,k,d)}},{key:"_nextBlock",value:function(a,c,b,d,e){var h=this;if(127<b)if(h._commentOffset)e.onSuccess();else e.onError({type:"loadData",info:"Comment block could not be found."});else a.loadRange([c+
4+d,c+4+4+d],{onSuccess:function(){h._loadBlock(a,c+4+d,e)}})}},{key:"_parseData",value:function(a,c){var b=a.getLongAt(this._commentOffset,!1)+(this._commentOffset+4);c=a.getLongAt(b,!1);b+=4;for(var h,g,e,n,x,f,u=0;u<c;u++){var w=a.getLongAt(b,!1),l=a.getStringWithCharsetAt(b+4,w,"utf-8").toString(),m=l.indexOf("=");l=[l.slice(0,m),l.slice(m+1)];switch(l[0]){case "TITLE":h=l[1];break;case "ARTIST":g=l[1];break;case "ALBUM":e=l[1];break;case "TRACKNUMBER":n=l[1];break;case "GENRE":x=l[1]}b+=4+w}this._pictureOffset&&
(f=a.getLongAt(this._pictureOffset,!0),c=this._pictureOffset+4,b=a.getLongAt(c,!0),u=c+4,c=a.getStringAt(u,b),b=u+b,u=a.getLongAt(b,!0),w=b+4,b=a.getStringWithCharsetAt(w,u,"utf-8").toString(),u=w+u+16,w=a.getLongAt(u,!0),a=a.getBytesAt(u+4,w,!0),f={format:c,type:d[f],description:b,data:a});return{type:"FLAC",version:"1",tags:{title:h,artist:g,album:e,track:n,genre:x,picture:f}}}}],[{key:"getTagIdentifierByteRange",value:function(){return{offset:0,length:4}}},{key:"canReadTagFormat",value:function(a){return"fLaC"===
String.fromCharCode.apply(String,a.slice(0,4))}}]);return c}(f("./MediaTagReader"));p.exports=f},{"./MediaTagReader":12}],7:[function(f,p,q){function m(b,d){if("function"!==typeof d&&null!==d)throw new TypeError("Super expression must either be null or a function, not "+typeof d);b.prototype=Object.create(d&&d.prototype,{constructor:{value:b,enumerable:!1,writable:!0,configurable:!0}});d&&(Object.setPrototypeOf?Object.setPrototypeOf(b,d):b.__proto__=d)}var l=function(){function b(b,a){for(var c=0;c<
a.length;c++){var d=a[c];d.enumerable=d.enumerable||!1;d.configurable=!0;"value"in d&&(d.writable=!0);Object.defineProperty(b,d.key,d)}}return function(d,a,c){a&&b(d.prototype,a);c&&b(d,c);return d}}();q=f("./MediaTagReader");f("./MediaFileReader");f=function(b){function d(){if(!(this instanceof d))throw new TypeError("Cannot call a class as a function");var a=(d.__proto__||Object.getPrototypeOf(d)).apply(this,arguments);if(!this)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
return!a||"object"!==typeof a&&"function"!==typeof a?this:a}m(d,b);l(d,[{key:"_loadData",value:function(a,c){var b=a.getSize();a.loadRange([b-128,b-1],c)}},{key:"_parseData",value:function(a,c){var b=a.getSize()-128,d=a.getStringWithCharsetAt(b+3,30).toString(),k=a.getStringWithCharsetAt(b+33,30).toString(),r=a.getStringWithCharsetAt(b+63,30).toString(),t=a.getStringWithCharsetAt(b+93,4).toString();var f=a.getByteAt(b+97+28);c=a.getByteAt(b+97+29);if(0==f&&0!=c){var n="1.1";f=a.getStringWithCharsetAt(b+
97,28).toString()}else n="1.0",f=a.getStringWithCharsetAt(b+97,30).toString(),c=0;a=a.getByteAt(b+97+30);a={type:"ID3",version:n,tags:{title:d,artist:k,album:r,year:t,comment:f,genre:255>a?e[a]:""}};c&&(a.tags.track=c);return a}}],[{key:"getTagIdentifierByteRange",value:function(){return{offset:-128,length:128}}},{key:"canReadTagFormat",value:function(a){return"TAG"===String.fromCharCode.apply(String,a.slice(0,3))}}]);return d}(q);var e="Blues;Classic Rock;Country;Dance;Disco;Funk;Grunge;Hip-Hop;Jazz;Metal;New Age;Oldies;Other;Pop;R&B;Rap;Reggae;Rock;Techno;Industrial;Alternative;Ska;Death Metal;Pranks;Soundtrack;Euro-Techno;Ambient;Trip-Hop;Vocal;Jazz+Funk;Fusion;Trance;Classical;Instrumental;Acid;House;Game;Sound Clip;Gospel;Noise;AlternRock;Bass;Soul;Punk;Space;Meditative;Instrumental Pop;Instrumental Rock;Ethnic;Gothic;Darkwave;Techno-Industrial;Electronic;Pop-Folk;Eurodance;Dream;Southern Rock;Comedy;Cult;Gangsta;Top 40;Christian Rap;Pop/Funk;Jungle;Native American;Cabaret;New Wave;Psychadelic;Rave;Showtunes;Trailer;Lo-Fi;Tribal;Acid Punk;Acid Jazz;Polka;Retro;Musical;Rock & Roll;Hard Rock;Folk;Folk-Rock;National Folk;Swing;Fast Fusion;Bebob;Latin;Revival;Celtic;Bluegrass;Avantgarde;Gothic Rock;Progressive Rock;Psychedelic Rock;Symphonic Rock;Slow Rock;Big Band;Chorus;Easy Listening;Acoustic;Humour;Speech;Chanson;Opera;Chamber Music;Sonata;Symphony;Booty Bass;Primus;Porn Groove;Satire;Slow Jam;Club;Tango;Samba;Folklore;Ballad;Power Ballad;Rhythmic Soul;Freestyle;Duet;Punk Rock;Drum Solo;Acapella;Euro-House;Dance Hall".split(";");
p.exports=f},{"./MediaFileReader":11,"./MediaTagReader":12}],8:[function(f,p,q){function m(a){switch(a){case 0:a="iso-8859-1";break;case 1:a="utf-16";break;case 2:a="utf-16be";break;case 3:a="utf-8";break;default:a="iso-8859-1"}return a}function l(a,c,b,d){d=b.getStringWithCharsetAt(a+1,c-1,d);a=b.getStringWithCharsetAt(a+1+d.bytesReadCount,c-1-d.bytesReadCount);return{user_description:d.toString(),data:a.toString()}}var e=function(){function a(a,c){for(var b=0;b<c.length;b++){var d=c[b];d.enumerable=
d.enumerable||!1;d.configurable=!0;"value"in d&&(d.writable=!0);Object.defineProperty(a,d.key,d)}}return function(c,b,d){b&&a(c.prototype,b);d&&a(c,d);return c}}();f("./MediaFileReader");var b=f("./StringUtils"),d=f("./ArrayFileReader"),a={BUF:"Recommended buffer size",CNT:"Play counter",COM:"Comments",CRA:"Audio encryption",CRM:"Encrypted meta frame",ETC:"Event timing codes",EQU:"Equalization",GEO:"General encapsulated object",IPL:"Involved people list",LNK:"Linked information",MCI:"Music CD Identifier",
MLL:"MPEG location lookup table",PIC:"Attached picture",POP:"Popularimeter",REV:"Reverb",RVA:"Relative volume adjustment",SLT:"Synchronized lyric/text",STC:"Synced tempo codes",TAL:"Album/Movie/Show title",TBP:"BPM (Beats Per Minute)",TCM:"Composer",TCO:"Content type",TCR:"Copyright message",TDA:"Date",TDY:"Playlist delay",TEN:"Encoded by",TFT:"File type",TIM:"Time",TKE:"Initial key",TLA:"Language(s)",TLE:"Length",TMT:"Media type",TOA:"Original artist(s)/performer(s)",TOF:"Original filename",TOL:"Original Lyricist(s)/text writer(s)",
TOR:"Original release year",TOT:"Original album/Movie/Show title",TP1:"Lead artist(s)/Lead performer(s)/Soloist(s)/Performing group",TP2:"Band/Orchestra/Accompaniment",TP3:"Conductor/Performer refinement",TP4:"Interpreted, remixed, or otherwise modified by",TPA:"Part of a set",TPB:"Publisher",TRC:"ISRC (International Standard Recording Code)",TRD:"Recording dates",TRK:"Track number/Position in set",TSI:"Size",TSS:"Software/hardware and settings used for encoding",TT1:"Content group description",TT2:"Title/Songname/Content description",

@@ -26,54 +32,55 @@ TT3:"Subtitle/Description refinement",TXT:"Lyricist/text writer",TXX:"User defined text information frame",TYE:"Year",UFI:"Unique file identifier",ULT:"Unsychronized lyric/text transcription",WAF:"Official audio file webpage",WAR:"Official artist/performer webpage",WAS:"Official audio source webpage",WCM:"Commercial information",WCP:"Copyright/Legal information",WPB:"Publishers official webpage",WXX:"User defined URL link frame",AENC:"Audio encryption",APIC:"Attached picture",ASPI:"Audio seek point index",

TPE4:"Interpreted, remixed, or otherwise modified by",TPOS:"Part of a set",TPRO:"Produced notice",TPUB:"Publisher",TRCK:"Track number/Position in set",TRDA:"Recording dates",TRSN:"Internet radio station name",TRSO:"Internet radio station owner",TSOA:"Album sort order",TSOP:"Performer sort order",TSOT:"Title sort order",TSIZ:"Size",TSRC:"ISRC (international standard recording code)",TSSE:"Software/Hardware and settings used for encoding",TSST:"Set subtitle",TYER:"Year",TXXX:"User defined text information frame",
UFID:"Unique file identifier",USER:"Terms of use",USLT:"Unsychronized lyric/text transcription",WCOM:"Commercial information",WCOP:"Copyright/Legal information",WOAF:"Official audio file webpage",WOAR:"Official artist/performer webpage",WOAS:"Official audio source webpage",WORS:"Official internet radio station homepage",WPAY:"Payment",WPUB:"Publishers official webpage",WXXX:"User defined URL link frame"};f=function(){function c(){if(!(this instanceof c))throw new TypeError("Cannot call a class as a function");
}e(c,null,[{key:"getFrameReaderFunction",value:function(a){return a in d?d[a]:"T"===a[0]?d["T*"]:"W"===a[0]?d["W*"]:null}},{key:"readFrames",value:function(a,d,b,h,g){for(var k={},e=this._getFrameHeaderSize(h);a<d-e;){var t=this._readFrameHeader(b,a,h),n=t.id;if(!n)break;var r=t.flags,f=t.size,u=a+t.headerSize,m=b;a+=t.headerSize+t.size;if(!g||-1!==g.indexOf(n)){if("MP3e"===n||"\x00MP3"===n||"\x00\x00MP"===n||" MP3"===n)break;r&&r.format.unsynchronisation&&(m=this.getUnsyncFileReader(m,u,f),u=0,f=
m.getSize());r&&r.format.data_length_indicator&&(u+=4,f-=4);r=(t=c.getFrameReaderFunction(n))?t.apply(this,[u,f,m,r,h]):null;u=this._getFrameDescription(n);f={id:n,size:f,description:u,data:r};n in k?(k[n].id&&(k[n]=[k[n]]),k[n].push(f)):k[n]=f}}return k}},{key:"_getFrameHeaderSize",value:function(a){a=a.major;return 2==a?6:3==a||4==a?10:0}},{key:"_readFrameHeader",value:function(a,d,b){var c=b.major,h=null;b=this._getFrameHeaderSize(b);switch(c){case 2:var g=a.getStringAt(d,3);var k=a.getInteger24At(d+
3,!0);break;case 3:g=a.getStringAt(d,4);k=a.getLongAt(d+4,!0);break;case 4:g=a.getStringAt(d,4),k=a.getSynchsafeInteger32At(d+4)}if(g==String.fromCharCode(0,0,0)||g==String.fromCharCode(0,0,0,0))g="";g&&2<c&&(h=this._readFrameFlags(a,d+8));return{id:g||"",size:k||0,headerSize:b||0,flags:h}}},{key:"_readFrameFlags",value:function(a,d){return{message:{tag_alter_preservation:a.isBitSetAt(d,6),file_alter_preservation:a.isBitSetAt(d,5),read_only:a.isBitSetAt(d,4)},format:{grouping_identity:a.isBitSetAt(d+
1,7),compression:a.isBitSetAt(d+1,3),encryption:a.isBitSetAt(d+1,2),unsynchronisation:a.isBitSetAt(d+1,1),data_length_indicator:a.isBitSetAt(d+1,0)}}}},{key:"_getFrameDescription",value:function(d){return d in a?a[d]:"Unknown"}},{key:"getUnsyncFileReader",value:function(a,d,c){a=a.getBytesAt(d,c);for(d=0;d<a.length-1;d++)255===a[d]&&0===a[d+1]&&a.splice(d+1,1);return new b(a)}}]);return c}();var d={APIC:function(a,d,b,c,e){c=a;var h=l(b.getByteAt(a));switch(e&&e.major){case 2:e=b.getStringAt(a+1,
3);a+=4;break;case 3:case 4:e=b.getStringWithCharsetAt(a+1,d-1);a+=1+e.bytesReadCount;break;default:throw Error("Couldn't read ID3v2 major version.");}var k=b.getByteAt(a,1);k=g[k];h=b.getStringWithCharsetAt(a+1,d-(a-c)-1,h);a+=1+h.bytesReadCount;return{format:e.toString(),type:k,description:h.toString(),data:b.getBytesAt(a,c+d-a)}},CHAP:function(a,d,b,g,e){g=a;var h={},k=c.readNullTerminatedString(b.getBytesAt(a,d));h.id=k.toString();a+=k.bytesReadCount;h.startTime=b.getLongAt(a,!0);a+=4;h.endTime=
b.getLongAt(a,!0);a+=4;h.startOffset=b.getLongAt(a,!0);a+=4;h.endOffset=b.getLongAt(a,!0);a+=4;h.subFrames=this.readFrames(a,a+(d-(a-g)),b,e);return h},CTOC:function(a,d,b,g,e){g=a;var h={childElementIds:[]},k=c.readNullTerminatedString(b.getBytesAt(a,d));h.id=k.toString();a+=k.bytesReadCount;h.topLevel=b.isBitSetAt(a,1);h.ordered=b.isBitSetAt(a,0);a++;h.entryCount=b.getByteAt(a);a++;for(k=0;k<h.entryCount;k++){var f=c.readNullTerminatedString(b.getBytesAt(a,d-(a-g)));h.childElementIds.push(f.toString());
a+=f.bytesReadCount}h.subFrames=this.readFrames(a,a+(d-(a-g)),b,e);return h},COMM:function(a,d,b,c,g){var h=a,e=l(b.getByteAt(a));c=b.getStringAt(a+1,3);g=b.getStringWithCharsetAt(a+4,d-4,e);a+=4+g.bytesReadCount;a=b.getStringWithCharsetAt(a,h+d-a,e);return{language:c,short_description:g.toString(),text:a.toString()}}};d.COM=d.COMM;d.PIC=function(a,b,c,g,e){return d.APIC(a,b,c,g,e)};d.PCNT=function(a,d,b,c,g){return b.getLongAt(a,!1)};d.CNT=d.PCNT;d["T*"]=function(a,d,b,c,g){c=l(b.getByteAt(a));return b.getStringWithCharsetAt(a+
1,d-1,c).toString()};d.TXXX=function(a,d,b,c,g){c=l(b.getByteAt(a));return m(a,d,b,c)};d["W*"]=function(a,d,b,c,g){c=l(b.getByteAt(a));return void 0!==c?m(a,d,b,c):b.getStringWithCharsetAt(a,d,c).toString()};d.TCON=function(a,b,c,g){return d["T*"].apply(this,arguments).replace(/^\(\d+\)/,"")};d.TCO=d.TCON;d.USLT=function(a,d,b,c,g){var h=a,e=l(b.getByteAt(a));c=b.getStringAt(a+1,3);g=b.getStringWithCharsetAt(a+4,d-4,e);a+=4+g.bytesReadCount;a=b.getStringWithCharsetAt(a,h+d-a,e);return{language:c,
descriptor:g.toString(),lyrics:a.toString()}};d.ULT=d.USLT;d.UFID=function(a,d,b,g,e){g=c.readNullTerminatedString(b.getBytesAt(a,d));a+=g.bytesReadCount;a=b.getBytesAt(a,d-g.bytesReadCount);return{ownerIdentifier:g.toString(),identifier:a}};var g="Other;32x32 pixels 'file icon' (PNG only);Other file icon;Cover (front);Cover (back);Leaflet page;Media (e.g. label side of CD);Lead artist/lead performer/soloist;Artist/performer;Conductor;Band/Orchestra;Composer;Lyricist/text writer;Recording Location;During recording;During performance;Movie/video screen capture;A bright coloured fish;Illustration;Band/artist logotype;Publisher/Studio logotype".split(";");
p.exports=f},{"./ArrayFileReader":3,"./MediaFileReader":10,"./StringUtils":12}],8:[function(f,p,q){function l(b,a){if("function"!==typeof a&&null!==a)throw new TypeError("Super expression must either be null or a function, not "+typeof a);b.prototype=Object.create(a&&a.prototype,{constructor:{value:b,enumerable:!1,writable:!0,configurable:!0}});a&&(Object.setPrototypeOf?Object.setPrototypeOf(b,a):b.__proto__=a)}var m=function(){function b(a,d){for(var b=0;b<d.length;b++){var c=d[b];c.enumerable=c.enumerable||
!1;c.configurable=!0;"value"in c&&(c.writable=!0);Object.defineProperty(a,c.key,c)}}return function(a,d,c){d&&b(a.prototype,d);c&&b(a,c);return a}}();q=f("./MediaTagReader");f("./MediaFileReader");var e=f("./ID3v2FrameReader");f=function(b){function a(){if(!(this instanceof a))throw new TypeError("Cannot call a class as a function");var d=Object.getPrototypeOf(a).apply(this,arguments);if(!this)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!d||"object"!==
typeof d&&"function"!==typeof d?this:d}l(a,b);m(a,[{key:"_loadData",value:function(a,b){a.loadRange([6,9],{onSuccess:function(){a.loadRange([0,10+a.getSynchsafeInteger32At(6)-1],b)},onError:b.onError})}},{key:"_parseData",value:function(a,b){var d,g=0,f=a.getByteAt(g+3);if(4<f)return{type:"ID3",version:">2.4",tags:{}};var m=a.getByteAt(g+4),r=a.isBitSetAt(g+5,7),n=a.isBitSetAt(g+5,6),x=a.isBitSetAt(g+5,5),l=a.getSynchsafeInteger32At(g+6);g+=10;if(n){var p=a.getLongAt(g,!0);g+=p+4}f={type:"ID3",version:"2."+
f+"."+m,major:f,revision:m,flags:{unsynchronisation:r,extended_header:n,experimental_indicator:x,footer_present:!1},size:l,tags:{}};b&&(d=this._expandShortcutTags(b));b=l+10;f.flags.unsynchronisation&&(a=e.getUnsyncFileReader(a,g,l),g=0,b=a.getSize());a=e.readFrames(g,b,a,f,d);for(var q in c)c.hasOwnProperty(q)&&(d=this._getFrameData(a,c[q]))&&(f.tags[q]=d);for(var w in a)a.hasOwnProperty(w)&&(f.tags[w]=a[w]);return f}},{key:"_getFrameData",value:function(a,b){for(var d=0,c;c=b[d];d++)if(c in a)return a=
a[c]instanceof Array?a[c][0]:a[c],a.data}},{key:"getShortcuts",value:function(){return c}}],[{key:"getTagIdentifierByteRange",value:function(){return{offset:0,length:10}}},{key:"canReadTagFormat",value:function(a){return"ID3"===String.fromCharCode.apply(String,a.slice(0,3))}}]);return a}(q);var c={title:["TIT2","TT2"],artist:["TPE1","TP1"],album:["TALB","TAL"],year:["TYER","TYE"],comment:["COMM","COM"],track:["TRCK","TRK"],genre:["TCON","TCO"],picture:["APIC","PIC"],lyrics:["USLT","ULT"]};p.exports=
f},{"./ID3v2FrameReader":7,"./MediaFileReader":10,"./MediaTagReader":11}],9:[function(f,p,q){function l(a,b){if("function"!==typeof b&&null!==b)throw new TypeError("Super expression must either be null or a function, not "+typeof b);a.prototype=Object.create(b&&b.prototype,{constructor:{value:a,enumerable:!1,writable:!0,configurable:!0}});b&&(Object.setPrototypeOf?Object.setPrototypeOf(a,b):a.__proto__=b)}var m=function(){function a(a,b){for(var c=0;c<b.length;c++){var d=b[c];d.enumerable=d.enumerable||
!1;d.configurable=!0;"value"in d&&(d.writable=!0);Object.defineProperty(a,d.key,d)}}return function(b,c,e){c&&a(b.prototype,c);e&&a(b,e);return b}}();q=f("./MediaTagReader");f("./MediaFileReader");f=function(a){function d(){if(!(this instanceof d))throw new TypeError("Cannot call a class as a function");var a=Object.getPrototypeOf(d).apply(this,arguments);if(!this)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!a||"object"!==typeof a&&"function"!==typeof a?
this:a}l(d,a);m(d,[{key:"_loadData",value:function(a,b){var c=this;a.loadRange([0,16],{onSuccess:function(){c._loadAtom(a,0,"",b)},onError:b.onError})}},{key:"_loadAtom",value:function(a,b,c,d){if(b>=a.getSize())d.onSuccess();else{var g=this,e=a.getLongAt(b,!0);if(0==e||isNaN(e))d.onSuccess();else{var h=a.getStringAt(b+4,4);if(this._isContainerAtom(h)){"meta"==h&&(b+=4);var f=(c?c+".":"")+h;"moov.udta.meta.ilst"===f?a.loadRange([b,b+e],d):a.loadRange([b+8,b+8+8],{onSuccess:function(){g._loadAtom(a,
b+8,f,d)},onError:d.onError})}else a.loadRange([b+e,b+e+8],{onSuccess:function(){g._loadAtom(a,b+e,c,d)},onError:d.onError})}}}},{key:"_isContainerAtom",value:function(a){return 0<=["moov","udta","meta","ilst"].indexOf(a)}},{key:"_canReadAtom",value:function(a){return"----"!==a}},{key:"_parseData",value:function(a,c){var d={};c=this._expandShortcutTags(c);this._readAtom(d,a,0,a.getSize(),c);for(var e in b)b.hasOwnProperty(e)&&(c=d[b[e]])&&(d[e]="track"===e?c.data.track:c.data);return{type:"MP4",ftyp:a.getStringAt(8,
4),version:a.getLongAt(12,!0),tags:d}}},{key:"_readAtom",value:function(a,b,c,d,e,f,n){n=void 0===n?"":n+" ";for(var g=c;g<c+d;){var h=b.getLongAt(g,!0);if(0==h)break;var k=b.getStringAt(g+4,4);if(this._isContainerAtom(k)){"meta"==k&&(g+=4);this._readAtom(a,b,g+8,h-8,e,(f?f+".":"")+k,n);break}(!e||0<=e.indexOf(k))&&"moov.udta.meta.ilst"===f&&this._canReadAtom(k)&&(a[k]=this._readMetadataAtom(b,g));g+=h}}},{key:"_readMetadataAtom",value:function(a,b){var d=a.getLongAt(b,!0),g=a.getStringAt(b+4,4),
h=a.getInteger24At(b+16+1,!0);h=e[h];if("trkn"==g)var f={track:a.getByteAt(b+16+11),total:a.getByteAt(b+16+13)};else if("disk"==g)f={disk:a.getByteAt(b+16+11),total:a.getByteAt(b+16+13)};else{b+=24;var n=d-24;"covr"===g&&"uint8"===h&&(h="jpeg");switch(h){case "text":f=a.getStringWithCharsetAt(b,n,"utf-8").toString();break;case "uint8":f=a.getShortAt(b,!1);break;case "int":case "uint":f=("int"==h?1==n?a.getSByteAt:2==n?a.getSShortAt:4==n?a.getSLongAt:a.getLongAt:1==n?a.getByteAt:2==n?a.getShortAt:
a.getLongAt).call(a,b+(8==n?4:0),!0);break;case "jpeg":case "png":f={format:"image/"+h,data:a.getBytesAt(b,n)}}}return{id:g,size:d,description:c[g]||"Unknown",data:f}}},{key:"getShortcuts",value:function(){return b}}],[{key:"getTagIdentifierByteRange",value:function(){return{offset:0,length:16}}},{key:"canReadTagFormat",value:function(a){return"ftyp"===String.fromCharCode.apply(String,a.slice(4,8))}}]);return d}(q);var e={0:"uint8",1:"text",13:"jpeg",14:"png",21:"int",22:"uint"},c={"\u00a9alb":"Album",
UFID:"Unique file identifier",USER:"Terms of use",USLT:"Unsychronized lyric/text transcription",WCOM:"Commercial information",WCOP:"Copyright/Legal information",WOAF:"Official audio file webpage",WOAR:"Official artist/performer webpage",WOAS:"Official audio source webpage",WORS:"Official internet radio station homepage",WPAY:"Payment",WPUB:"Publishers official webpage",WXXX:"User defined URL link frame"};f=function(){function b(){if(!(this instanceof b))throw new TypeError("Cannot call a class as a function");
}e(b,null,[{key:"getFrameReaderFunction",value:function(a){return a in c?c[a]:"T"===a[0]?c["T*"]:"W"===a[0]?c["W*"]:null}},{key:"readFrames",value:function(a,c,d,h,g){for(var k={},e=this._getFrameHeaderSize(h);a<c-e;){var r=this._readFrameHeader(d,a,h),n=r.id;if(!n)break;var f=r.flags,t=r.size,v=a+r.headerSize,l=d;a+=r.headerSize+r.size;if(!g||-1!==g.indexOf(n)){if("MP3e"===n||"\x00MP3"===n||"\x00\x00MP"===n||" MP3"===n)break;f&&f.format.unsynchronisation&&(l=this.getUnsyncFileReader(l,v,t),v=0,t=
l.getSize());f&&f.format.data_length_indicator&&(v+=4,t-=4);f=(r=b.getFrameReaderFunction(n))?r.apply(this,[v,t,l,f,h]):null;v=this._getFrameDescription(n);t={id:n,size:t,description:v,data:f};n in k?(k[n].id&&(k[n]=[k[n]]),k[n].push(t)):k[n]=t}}return k}},{key:"_getFrameHeaderSize",value:function(a){a=a.major;return 2==a?6:3==a||4==a?10:0}},{key:"_readFrameHeader",value:function(a,c,b){var d=b.major,h=null;b=this._getFrameHeaderSize(b);switch(d){case 2:var g=a.getStringAt(c,3);var k=a.getInteger24At(c+
3,!0);break;case 3:g=a.getStringAt(c,4);k=a.getLongAt(c+4,!0);break;case 4:g=a.getStringAt(c,4),k=a.getSynchsafeInteger32At(c+4)}if(g==String.fromCharCode(0,0,0)||g==String.fromCharCode(0,0,0,0))g="";g&&2<d&&(h=this._readFrameFlags(a,c+8));return{id:g||"",size:k||0,headerSize:b||0,flags:h}}},{key:"_readFrameFlags",value:function(a,c){return{message:{tag_alter_preservation:a.isBitSetAt(c,6),file_alter_preservation:a.isBitSetAt(c,5),read_only:a.isBitSetAt(c,4)},format:{grouping_identity:a.isBitSetAt(c+
1,7),compression:a.isBitSetAt(c+1,3),encryption:a.isBitSetAt(c+1,2),unsynchronisation:a.isBitSetAt(c+1,1),data_length_indicator:a.isBitSetAt(c+1,0)}}}},{key:"_getFrameDescription",value:function(c){return c in a?a[c]:"Unknown"}},{key:"getUnsyncFileReader",value:function(a,c,b){a=a.getBytesAt(c,b);for(c=0;c<a.length-1;c++)255===a[c]&&0===a[c+1]&&a.splice(c+1,1);return new d(a)}}]);return b}();var c={APIC:function(a,c,b,d,e){d=a;var h=m(b.getByteAt(a));switch(e&&e.major){case 2:e=b.getStringAt(a+1,
3);a+=4;break;case 3:case 4:e=b.getStringWithCharsetAt(a+1,c-1);a+=1+e.bytesReadCount;break;default:throw Error("Couldn't read ID3v2 major version.");}var k=b.getByteAt(a);k=g[k];h=b.getStringWithCharsetAt(a+1,c-(a-d)-1,h);a+=1+h.bytesReadCount;return{format:e.toString(),type:k,description:h.toString(),data:b.getBytesAt(a,d+c-a)}},CHAP:function(a,c,d,g,e){g=a;var h={},k=b.readNullTerminatedString(d.getBytesAt(a,c));h.id=k.toString();a+=k.bytesReadCount;h.startTime=d.getLongAt(a,!0);a+=4;h.endTime=
d.getLongAt(a,!0);a+=4;h.startOffset=d.getLongAt(a,!0);a+=4;h.endOffset=d.getLongAt(a,!0);a+=4;h.subFrames=this.readFrames(a,a+(c-(a-g)),d,e);return h},CTOC:function(a,c,d,g,e){g=a;var h={childElementIds:[],id:void 0,topLevel:void 0,ordered:void 0,entryCount:void 0,subFrames:void 0},k=b.readNullTerminatedString(d.getBytesAt(a,c));h.id=k.toString();a+=k.bytesReadCount;h.topLevel=d.isBitSetAt(a,1);h.ordered=d.isBitSetAt(a,0);a++;h.entryCount=d.getByteAt(a);a++;for(k=0;k<h.entryCount;k++){var f=b.readNullTerminatedString(d.getBytesAt(a,
c-(a-g)));h.childElementIds.push(f.toString());a+=f.bytesReadCount}h.subFrames=this.readFrames(a,a+(c-(a-g)),d,e);return h},COMM:function(a,c,b,d,g){var h=a,e=m(b.getByteAt(a));d=b.getStringAt(a+1,3);g=b.getStringWithCharsetAt(a+4,c-4,e);a+=4+g.bytesReadCount;a=b.getStringWithCharsetAt(a,h+c-a,e);return{language:d,short_description:g.toString(),text:a.toString()}}};c.COM=c.COMM;c.PIC=function(a,b,d,g,e){return c.APIC(a,b,d,g,e)};c.PCNT=function(a,c,b,d,g){return b.getLongAt(a,!1)};c.CNT=c.PCNT;c["T*"]=
function(a,c,b,d,g){d=m(b.getByteAt(a));return b.getStringWithCharsetAt(a+1,c-1,d).toString()};c.TXXX=function(a,c,b,d,g){d=m(b.getByteAt(a));return l(a,c,b,d)};c["W*"]=function(a,c,b,d,g){d=m(b.getByteAt(a));return void 0!==d?l(a,c,b,d):b.getStringWithCharsetAt(a,c,d).toString()};c.TCON=function(a,b,d,g){return c["T*"].apply(this,arguments).replace(/^\(\d+\)/,"")};c.TCO=c.TCON;c.USLT=function(a,c,b,d,g){var h=a,e=m(b.getByteAt(a));d=b.getStringAt(a+1,3);g=b.getStringWithCharsetAt(a+4,c-4,e);a+=4+
g.bytesReadCount;a=b.getStringWithCharsetAt(a,h+c-a,e);return{language:d,descriptor:g.toString(),lyrics:a.toString()}};c.ULT=c.USLT;c.UFID=function(a,c,d,g,e){g=b.readNullTerminatedString(d.getBytesAt(a,c));a+=g.bytesReadCount;a=d.getBytesAt(a,c-g.bytesReadCount);return{ownerIdentifier:g.toString(),identifier:a}};var g="Other;32x32 pixels 'file icon' (PNG only);Other file icon;Cover (front);Cover (back);Leaflet page;Media (e.g. label side of CD);Lead artist/lead performer/soloist;Artist/performer;Conductor;Band/Orchestra;Composer;Lyricist/text writer;Recording Location;During recording;During performance;Movie/video screen capture;A bright coloured fish;Illustration;Band/artist logotype;Publisher/Studio logotype".split(";");
p.exports=f},{"./ArrayFileReader":3,"./MediaFileReader":11,"./StringUtils":13}],9:[function(f,p,q){function m(b,a){if("function"!==typeof a&&null!==a)throw new TypeError("Super expression must either be null or a function, not "+typeof a);b.prototype=Object.create(a&&a.prototype,{constructor:{value:b,enumerable:!1,writable:!0,configurable:!0}});a&&(Object.setPrototypeOf?Object.setPrototypeOf(b,a):b.__proto__=a)}var l=function(){function b(a,c){for(var b=0;b<c.length;b++){var d=c[b];d.enumerable=d.enumerable||
!1;d.configurable=!0;"value"in d&&(d.writable=!0);Object.defineProperty(a,d.key,d)}}return function(a,c,d){c&&b(a.prototype,c);d&&b(a,d);return a}}();q=f("./MediaTagReader");f("./MediaFileReader");var e=f("./ID3v2FrameReader");f=function(d){function a(){if(!(this instanceof a))throw new TypeError("Cannot call a class as a function");var c=(a.__proto__||Object.getPrototypeOf(a)).apply(this,arguments);if(!this)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!c||
"object"!==typeof c&&"function"!==typeof c?this:c}m(a,d);l(a,[{key:"_loadData",value:function(a,b){a.loadRange([6,9],{onSuccess:function(){a.loadRange([0,10+a.getSynchsafeInteger32At(6)-1],b)},onError:b.onError})}},{key:"_parseData",value:function(a,d){var c,g=0,f=a.getByteAt(g+3);if(4<f)return{type:"ID3",version:">2.4",tags:{}};var l=a.getByteAt(g+4),m=a.isBitSetAt(g+5,7),n=a.isBitSetAt(g+5,6),x=a.isBitSetAt(g+5,5),p=a.getSynchsafeInteger32At(g+6);g+=10;if(n){var u=a.getLongAt(g,!0);g+=u+4}f={type:"ID3",
version:"2."+f+"."+l,major:f,revision:l,flags:{unsynchronisation:m,extended_header:n,experimental_indicator:x,footer_present:!1},size:p,tags:{}};d&&(c=this._expandShortcutTags(d));d=p+10;f.flags.unsynchronisation&&(a=e.getUnsyncFileReader(a,g,p),g=0,d=a.getSize());a=e.readFrames(g,d,a,f,c);for(var q in b)b.hasOwnProperty(q)&&(c=this._getFrameData(a,b[q]))&&(f.tags[q]=c);for(var z in a)a.hasOwnProperty(z)&&(f.tags[z]=a[z]);return f}},{key:"_getFrameData",value:function(a,b){for(var c=0,d;d=b[c];c++)if(d in
a)return a=a[d]instanceof Array?a[d][0]:a[d],a.data}},{key:"getShortcuts",value:function(){return b}}],[{key:"getTagIdentifierByteRange",value:function(){return{offset:0,length:10}}},{key:"canReadTagFormat",value:function(a){return"ID3"===String.fromCharCode.apply(String,a.slice(0,3))}}]);return a}(q);var b={title:["TIT2","TT2"],artist:["TPE1","TP1"],album:["TALB","TAL"],year:["TYER","TYE"],comment:["COMM","COM"],track:["TRCK","TRK"],genre:["TCON","TCO"],picture:["APIC","PIC"],lyrics:["USLT","ULT"]};
p.exports=f},{"./ID3v2FrameReader":8,"./MediaFileReader":11,"./MediaTagReader":12}],10:[function(f,p,q){function m(a,c){if("function"!==typeof c&&null!==c)throw new TypeError("Super expression must either be null or a function, not "+typeof c);a.prototype=Object.create(c&&c.prototype,{constructor:{value:a,enumerable:!1,writable:!0,configurable:!0}});c&&(Object.setPrototypeOf?Object.setPrototypeOf(a,c):a.__proto__=c)}var l=function(){function a(a,b){for(var c=0;c<b.length;c++){var d=b[c];d.enumerable=
d.enumerable||!1;d.configurable=!0;"value"in d&&(d.writable=!0);Object.defineProperty(a,d.key,d)}}return function(c,b,d){b&&a(c.prototype,b);d&&a(c,d);return c}}();q=f("./MediaTagReader");f("./MediaFileReader");f=function(a){function c(){if(!(this instanceof c))throw new TypeError("Cannot call a class as a function");var a=(c.__proto__||Object.getPrototypeOf(c)).apply(this,arguments);if(!this)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!a||"object"!==
typeof a&&"function"!==typeof a?this:a}m(c,a);l(c,[{key:"_loadData",value:function(a,c){var b=this;a.loadRange([0,16],{onSuccess:function(){b._loadAtom(a,0,"",c)},onError:c.onError})}},{key:"_loadAtom",value:function(a,c,b,d){if(c>=a.getSize())d.onSuccess();else{var g=this,e=a.getLongAt(c,!0);if(0==e||isNaN(e))d.onSuccess();else{var h=a.getStringAt(c+4,4);if(this._isContainerAtom(h)){"meta"==h&&(c+=4);var f=(b?b+".":"")+h;"moov.udta.meta.ilst"===f?a.loadRange([c,c+e],d):a.loadRange([c+8,c+8+8],{onSuccess:function(){g._loadAtom(a,
c+8,f,d)},onError:d.onError})}else a.loadRange([c+e,c+e+8],{onSuccess:function(){g._loadAtom(a,c+e,b,d)},onError:d.onError})}}}},{key:"_isContainerAtom",value:function(a){return 0<=["moov","udta","meta","ilst"].indexOf(a)}},{key:"_canReadAtom",value:function(a){return"----"!==a}},{key:"_parseData",value:function(a,c){var b={};c=this._expandShortcutTags(c);this._readAtom(b,a,0,a.getSize(),c);for(var e in d)d.hasOwnProperty(e)&&(c=b[d[e]])&&(b[e]="track"===e?c.data.track:c.data);return{type:"MP4",ftyp:a.getStringAt(8,
4),version:a.getLongAt(12,!0),tags:b}}},{key:"_readAtom",value:function(a,c,b,d,e,f,n){n=void 0===n?"":n+" ";for(var g=b;g<b+d;){var h=c.getLongAt(g,!0);if(0==h)break;var k=c.getStringAt(g+4,4);if(this._isContainerAtom(k)){"meta"==k&&(g+=4);this._readAtom(a,c,g+8,h-8,e,(f?f+".":"")+k,n);break}(!e||0<=e.indexOf(k))&&"moov.udta.meta.ilst"===f&&this._canReadAtom(k)&&(a[k]=this._readMetadataAtom(c,g));g+=h}}},{key:"_readMetadataAtom",value:function(a,c){var d=a.getLongAt(c,!0),g=a.getStringAt(c+4,4),
h=a.getInteger24At(c+16+1,!0);h=e[h];if("trkn"==g)var f={track:a.getByteAt(c+16+11),total:a.getByteAt(c+16+13)};else if("disk"==g)f={disk:a.getByteAt(c+16+11),total:a.getByteAt(c+16+13)};else{c+=24;var n=d-24;"covr"===g&&"uint8"===h&&(h="jpeg");switch(h){case "text":f=a.getStringWithCharsetAt(c,n,"utf-8").toString();break;case "uint8":f=a.getShortAt(c,!1);break;case "int":case "uint":f=("int"==h?1==n?a.getSByteAt:2==n?a.getSShortAt:4==n?a.getSLongAt:a.getLongAt:1==n?a.getByteAt:2==n?a.getShortAt:
a.getLongAt).call(a,c+(8==n?4:0),!0);break;case "jpeg":case "png":f={format:"image/"+h,data:a.getBytesAt(c,n)}}}return{id:g,size:d,description:b[g]||"Unknown",data:f}}},{key:"getShortcuts",value:function(){return d}}],[{key:"getTagIdentifierByteRange",value:function(){return{offset:0,length:16}}},{key:"canReadTagFormat",value:function(a){return"ftyp"===String.fromCharCode.apply(String,a.slice(4,8))}}]);return c}(q);var e={0:"uint8",1:"text",13:"jpeg",14:"png",21:"int",22:"uint"},b={"\u00a9alb":"Album",
"\u00a9ART":"Artist",aART:"Album Artist","\u00a9day":"Release Date","\u00a9nam":"Title","\u00a9gen":"Genre",gnre:"Genre",trkn:"Track Number","\u00a9wrt":"Composer","\u00a9too":"Encoding Tool","\u00a9enc":"Encoded By",cprt:"Copyright",covr:"Cover Art","\u00a9grp":"Grouping",keyw:"Keywords","\u00a9lyr":"Lyrics","\u00a9cmt":"Comment",tmpo:"Tempo",cpil:"Compilation",disk:"Disc Number",tvsh:"TV Show Name",tven:"TV Episode ID",tvsn:"TV Season",tves:"TV Episode",tvnn:"TV Network",desc:"Description",ldes:"Long Description",
sonm:"Sort Name",soar:"Sort Artist",soaa:"Sort Album",soco:"Sort Composer",sosn:"Sort Show",purd:"Purchase Date",pcst:"Podcast",purl:"Podcast URL",catg:"Category",hdvd:"HD Video",stik:"Media Type",rtng:"Content Rating",pgap:"Gapless Playback",apID:"Purchase Account",sfID:"Country Code",atID:"Artist ID",cnID:"Catalog ID",plID:"Collection ID",geID:"Genre ID","xid ":"Vendor Information",flvr:"Codec Flavor"},b={title:"\u00a9nam",artist:"\u00a9ART",album:"\u00a9alb",year:"\u00a9day",comment:"\u00a9cmt",
track:"trkn",genre:"\u00a9gen",picture:"covr",lyrics:"\u00a9lyr"};p.exports=f},{"./MediaFileReader":10,"./MediaTagReader":11}],10:[function(f,p,q){var l=function(){function e(c,b){for(var a=0;a<b.length;a++){var d=b[a];d.enumerable=d.enumerable||!1;d.configurable=!0;"value"in d&&(d.writable=!0);Object.defineProperty(c,d.key,d)}}return function(c,b,a){b&&e(c.prototype,b);a&&e(c,a);return c}}(),m=f("./StringUtils");f=function(){function e(){if(!(this instanceof e))throw new TypeError("Cannot call a class as a function");
this._isInitialized=!1;this._size=0}l(e,[{key:"init",value:function(c){var b=this;if(this._isInitialized)setTimeout(c.onSuccess,1);else return this._init({onSuccess:function(){b._isInitialized=!0;c.onSuccess()},onError:c.onError})}},{key:"_init",value:function(c){throw Error("Must implement init function");}},{key:"loadRange",value:function(c,b){throw Error("Must implement loadRange function");}},{key:"getSize",value:function(){if(!this._isInitialized)throw Error("init() must be called first.");return this._size}},
{key:"getByteAt",value:function(c){throw Error("Must implement getByteAt function");}},{key:"getBytesAt",value:function(c,b){for(var a=Array(b),d=0;d<b;d++)a[d]=this.getByteAt(c+d);return a}},{key:"isBitSetAt",value:function(c,b){return 0!=(this.getByteAt(c)&1<<b)}},{key:"getSByteAt",value:function(c){c=this.getByteAt(c);return 127<c?c-256:c}},{key:"getShortAt",value:function(c,b){c=b?(this.getByteAt(c)<<8)+this.getByteAt(c+1):(this.getByteAt(c+1)<<8)+this.getByteAt(c);0>c&&(c+=65536);return c}},
{key:"getSShortAt",value:function(c,b){c=this.getShortAt(c,b);return 32767<c?c-65536:c}},{key:"getLongAt",value:function(c,b){var a=this.getByteAt(c),d=this.getByteAt(c+1),e=this.getByteAt(c+2);c=this.getByteAt(c+3);b=b?(((a<<8)+d<<8)+e<<8)+c:(((c<<8)+e<<8)+d<<8)+a;0>b&&(b+=4294967296);return b}},{key:"getSLongAt",value:function(c,b){c=this.getLongAt(c,b);return 2147483647<c?c-4294967296:c}},{key:"getInteger24At",value:function(c,b){var a=this.getByteAt(c),d=this.getByteAt(c+1);c=this.getByteAt(c+
2);b=b?((a<<8)+d<<8)+c:((c<<8)+d<<8)+a;0>b&&(b+=16777216);return b}},{key:"getStringAt",value:function(c,b){for(var a=[],d=c,e=0;d<c+b;d++,e++)a[e]=String.fromCharCode(this.getByteAt(d));return a.join("")}},{key:"getStringWithCharsetAt",value:function(c,b,a){c=this.getBytesAt(c,b);switch((a||"").toLowerCase()){case "utf-16":case "utf-16le":case "utf-16be":a=m.readUTF16String(c,"utf-16be"===a);break;case "utf-8":a=m.readUTF8String(c);break;default:a=m.readNullTerminatedString(c)}return a}},{key:"getCharAt",
value:function(c){return String.fromCharCode(this.getByteAt(c))}},{key:"getSynchsafeInteger32At",value:function(c){var b=this.getByteAt(c),a=this.getByteAt(c+1),d=this.getByteAt(c+2);return this.getByteAt(c+3)&127|(d&127)<<7|(a&127)<<14|(b&127)<<21}}],[{key:"canReadFile",value:function(c){throw Error("Must implement canReadFile function");}}]);return e}();p.exports=f},{"./StringUtils":12}],11:[function(f,p,q){var l=function(){function f(e,c){for(var b=0;b<c.length;b++){var a=c[b];a.enumerable=a.enumerable||
!1;a.configurable=!0;"value"in a&&(a.writable=!0);Object.defineProperty(e,a.key,a)}}return function(e,c,b){c&&f(e.prototype,c);b&&f(e,b);return e}}();f("./MediaFileReader");f=function(){function f(e){if(!(this instanceof f))throw new TypeError("Cannot call a class as a function");this._mediaFileReader=e;this._tags=null}l(f,[{key:"setTagsToRead",value:function(e){this._tags=e;return this}},{key:"read",value:function(e){var c=this;this._mediaFileReader.init({onSuccess:function(){c._loadData(c._mediaFileReader,
{onSuccess:function(){try{var b=c._parseData(c._mediaFileReader,c._tags)}catch(a){if(e.onError){e.onError({type:"parseData",info:a.message});return}}e.onSuccess(b)},onError:e.onError})},onError:e.onError})}},{key:"getShortcuts",value:function(){return{}}},{key:"_loadData",value:function(e,c){throw Error("Must implement _loadData function");}},{key:"_parseData",value:function(e,c){throw Error("Must implement _parseData function");}},{key:"_expandShortcutTags",value:function(e){if(!e)return null;for(var c=
[],b=this.getShortcuts(),a=0,d;d=e[a];a++)c=c.concat(b[d]||[d]);return c}}],[{key:"getTagIdentifierByteRange",value:function(){throw Error("Must implement");}},{key:"canReadTagFormat",value:function(e){throw Error("Must implement");}}]);return f}();p.exports=f},{"./MediaFileReader":10}],12:[function(f,p,q){var l=function(){function e(c,b){for(var a=0;a<b.length;a++){var d=b[a];d.enumerable=d.enumerable||!1;d.configurable=!0;"value"in d&&(d.writable=!0);Object.defineProperty(c,d.key,d)}}return function(c,
b,a){b&&e(c.prototype,b);a&&e(c,a);return c}}(),m=function(){function e(c,b){if(!(this instanceof e))throw new TypeError("Cannot call a class as a function");this._value=c;this.bytesReadCount=b;this.length=c.length}l(e,[{key:"toString",value:function(){return this._value}}]);return e}();p.exports={readUTF16String:function(e,c,b){var a=0,d=1,g=0;b=Math.min(b||e.length,e.length);254==e[0]&&255==e[1]?(c=!0,a=2):255==e[0]&&254==e[1]&&(c=!1,a=2);c&&(d=0,g=1);c=[];for(var h=0;a<b;h++){var f=e[a+d],l=(f<<
8)+e[a+g];a+=2;if(0==l)break;else 216>f||224<=f?c[h]=String.fromCharCode(l):(f=(e[a+d]<<8)+e[a+g],a+=2,c[h]=String.fromCharCode(l,f))}return new m(c.join(""),a)},readUTF8String:function(e,c){var b=0;c=Math.min(c||e.length,e.length);239==e[0]&&187==e[1]&&191==e[2]&&(b=3);for(var a=[],d=0;b<c;d++){var g=e[b++];if(0==g)break;else if(128>g)a[d]=String.fromCharCode(g);else if(194<=g&&224>g){var f=e[b++];a[d]=String.fromCharCode(((g&31)<<6)+(f&63))}else if(224<=g&&240>g){f=e[b++];var k=e[b++];a[d]=String.fromCharCode(((g&
255)<<12)+((f&63)<<6)+(k&63))}else if(240<=g&&245>g){f=e[b++];k=e[b++];var l=e[b++];k=((g&7)<<18)+((f&63)<<12)+((k&63)<<6)+(l&63)-65536;a[d]=String.fromCharCode((k>>10)+55296,(k&1023)+56320)}}return new m(a.join(""),b)},readNullTerminatedString:function(e,c){var b=[];c=c||e.length;for(var a=0;a<c;){var d=e[a++];if(0==d)break;b[a-1]=String.fromCharCode(d)}return new m(b.join(""),a)}}},{}],13:[function(f,p,q){function l(c,b){if("function"!==typeof b&&null!==b)throw new TypeError("Super expression must either be null or a function, not "+
typeof b);c.prototype=Object.create(b&&b.prototype,{constructor:{value:c,enumerable:!1,writable:!0,configurable:!0}});b&&(Object.setPrototypeOf?Object.setPrototypeOf(c,b):c.__proto__=b)}var m=function(){function c(b,a){for(var c=0;c<a.length;c++){var e=a[c];e.enumerable=e.enumerable||!1;e.configurable=!0;"value"in e&&(e.writable=!0);Object.defineProperty(b,e.key,e)}}return function(b,a,d){a&&c(b.prototype,a);d&&c(b,d);return b}}(),e=f("./ChunkedFileData");q=function(c){function b(a){if(!(this instanceof
b))throw new TypeError("Cannot call a class as a function");var c=Object.getPrototypeOf(b).call(this);if(!this)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");c=!c||"object"!==typeof c&&"function"!==typeof c?this:c;c._url=a;c._fileData=new e;return c}l(b,c);m(b,[{key:"_init",value:function(a){b._config.avoidHeadRequests?this._fetchSizeWithGetRequest(a):this._fetchSizeWithHeadRequest(a)}},{key:"_fetchSizeWithHeadRequest",value:function(a){var b=this;this._makeXHRRequest("HEAD",
null,{onSuccess:function(c){(c=b._parseContentLength(c))?(b._size=c,a.onSuccess()):b._fetchSizeWithGetRequest(a)},onError:a.onError})}},{key:"_fetchSizeWithGetRequest",value:function(a){var b=this,c=this._roundRangeToChunkMultiple([0,0]);this._makeXHRRequest("GET",c,{onSuccess:function(c){var d=b._parseContentRange(c);c=b._getXhrResponseContent(c);if(d){if(null==d.instanceLength){b._fetchEntireFile(a);return}b._size=d.instanceLength}else b._size=c.length;b._fileData.addData(0,c);a.onSuccess()},onError:a.onError})}},
{key:"_fetchEntireFile",value:function(a){var b=this;this._makeXHRRequest("GET",null,{onSuccess:function(c){c=b._getXhrResponseContent(c);b._size=c.length;b._fileData.addData(0,c);a.onSuccess()},onError:a.onError})}},{key:"_getXhrResponseContent",value:function(a){return a.responseBody||a.responseText||""}},{key:"_parseContentLength",value:function(a){a=this._getResponseHeader(a,"Content-Length");return null==a?a:parseInt(a,10)}},{key:"_parseContentRange",value:function(a){if(a=this._getResponseHeader(a,
"Content-Range")){var b=a.match(/bytes (\d+)-(\d+)\/(?:(\d+)|\*)/i);if(!b)throw Error("FIXME: Unknown Content-Range syntax: ",a);return{firstBytePosition:parseInt(b[1],10),lastBytePosition:parseInt(b[2],10),instanceLength:b[3]?parseInt(b[3],10):null}}return null}},{key:"loadRange",value:function(a,b){var c=this;c._fileData.hasDataRange(a[0],Math.min(c._size,a[1]))?setTimeout(b.onSuccess,1):(a=this._roundRangeToChunkMultiple(a),a[1]=Math.min(c._size,a[1]),this._makeXHRRequest("GET",a,{onSuccess:function(d){d=
c._getXhrResponseContent(d);c._fileData.addData(a[0],d);b.onSuccess()},onError:b.onError}))}},{key:"_roundRangeToChunkMultiple",value:function(a){return[a[0],a[0]+1024*Math.ceil((a[1]-a[0]+1)/1024)-1]}},{key:"_makeXHRRequest",value:function(a,c,e){var d=this._createXHRObject(),f=function(){if(200===d.status||206===d.status)e.onSuccess(d);else if(e.onError)e.onError({type:"xhr",info:"Unexpected HTTP status "+d.status+".",xhr:d});d=null};"undefined"!==typeof d.onload?(d.onload=f,d.onerror=function(){if(e.onError)e.onError({type:"xhr",
info:"Generic XHR error, check xhr object.",xhr:d})}):d.onreadystatechange=function(){4===d.readyState&&f()};b._config.timeoutInSec&&(d.timeout=1E3*b._config.timeoutInSec,d.ontimeout=function(){if(e.onError)e.onError({type:"xhr",info:"Timeout after "+d.timeout/1E3+"s. Use jsmediatags.Config.setXhrTimeout to override.",xhr:d})});d.open(a,this._url);d.overrideMimeType("text/plain; charset=x-user-defined");c&&this._setRequestHeader(d,"Range","bytes="+c[0]+"-"+c[1]);this._setRequestHeader(d,"If-Modified-Since",
"Sat, 01 Jan 1970 00:00:00 GMT");d.send(null)}},{key:"_setRequestHeader",value:function(a,c,e){0>b._config.disallowedXhrHeaders.indexOf(c.toLowerCase())&&a.setRequestHeader(c,e)}},{key:"_hasResponseHeader",value:function(a,b){a=a.getAllResponseHeaders();if(!a)return!1;a=a.split("\r\n");for(var c=[],d=0;d<a.length;d++)c[d]=a[d].split(":")[0].toLowerCase();return 0<=c.indexOf(b.toLowerCase())}},{key:"_getResponseHeader",value:function(a,b){return this._hasResponseHeader(a,b)?a.getResponseHeader(b):
null}},{key:"getByteAt",value:function(a){return this._fileData.getByteAt(a).charCodeAt(0)&255}},{key:"_isWebWorker",value:function(){return"undefined"!==typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope}},{key:"_createXHRObject",value:function(){if("undefined"===typeof window&&!this._isWebWorker())return new (f("xhr2").XMLHttpRequest);if("undefined"!==typeof XMLHttpRequest)return new XMLHttpRequest;throw Error("XMLHttpRequest is not supported");}}],[{key:"canReadFile",value:function(a){return"string"===
typeof a&&/^[a-z]+:\/\//i.test(a)}},{key:"setConfig",value:function(a){for(var b in a)a.hasOwnProperty(b)&&(this._config[b]=a[b]);a=this._config.disallowedXhrHeaders;for(b=0;b<a.length;b++)a[b]=a[b].toLowerCase()}}]);return b}(f("./MediaFileReader"));q._config={avoidHeadRequests:!1,disallowedXhrHeaders:[],timeoutInSec:30};p.exports=q},{"./ChunkedFileData":5,"./MediaFileReader":10,xhr2:2}],14:[function(f,p,q){function l(a,b){if(!(a instanceof b))throw new TypeError("Cannot call a class as a function");
}function m(a,b){var c=0>a.offset&&(-a.offset>b||0<a.offset+a.length);return!(0<=a.offset&&a.offset+a.length>=b||c)}var e=function(){function a(a,b){for(var c=0;c<b.length;c++){var d=b[c];d.enumerable=d.enumerable||!1;d.configurable=!0;"value"in d&&(d.writable=!0);Object.defineProperty(a,d.key,d)}}return function(b,c,d){c&&a(b.prototype,c);d&&a(b,d);return b}}();f("./MediaFileReader");q=f("./NodeFileReader");var c=f("./XhrFileReader"),b=f("./BlobFileReader"),a=f("./ArrayFileReader");f("./MediaTagReader");
var d=f("./ID3v1TagReader"),g=f("./ID3v2TagReader");f=f("./MP4TagReader");var h=[],k=[],t=function(){function a(b){l(this,a);this._file=b}e(a,[{key:"setTagsToRead",value:function(a){this._tagsToRead=a;return this}},{key:"setFileReader",value:function(a){this._fileReader=a;return this}},{key:"setTagReader",value:function(a){this._tagReader=a;return this}},{key:"read",value:function(a){var b=new (this._getFileReader())(this._file),c=this;b.init({onSuccess:function(){c._getTagReader(b,{onSuccess:function(d){(new d(b)).setTagsToRead(c._tagsToRead).read(a)},
onError:a.onError})},onError:a.onError})}},{key:"_getFileReader",value:function(){return this._fileReader?this._fileReader:this._findFileReader()}},{key:"_findFileReader",value:function(){for(var a=0;a<h.length;a++)if(h[a].canReadFile(this._file))return h[a];throw Error("No suitable file reader found for ",this._file);}},{key:"_getTagReader",value:function(a,b){if(this._tagReader){var c=this._tagReader;setTimeout(function(){b.onSuccess(c)},1)}else this._findTagReader(a,b)}},{key:"_findTagReader",
value:function(a,b){for(var c=[],d=[],e=a.getSize(),f=0;f<k.length;f++){var g=k[f].getTagIdentifierByteRange();m(g,e)&&(0<=g.offset&&g.offset<e/2||0>g.offset&&g.offset<-e/2?c.push(k[f]):d.push(k[f]))}var h=!1;f={onSuccess:function(){if(h){for(var c=0;c<k.length;c++){var d=k[c].getTagIdentifierByteRange();if(m(d,e)){try{var f=a.getBytesAt(0<=d.offset?d.offset:d.offset+e,d.length)}catch(y){if(b.onError){b.onError({type:"fileReader",info:y.message});return}}if(k[c].canReadTagFormat(f)){b.onSuccess(k[c]);
return}}}if(b.onError)b.onError({type:"tagFormat",info:"No suitable tag reader found"})}else h=!0},onError:b.onError};this._loadTagIdentifierRanges(a,c,f);this._loadTagIdentifierRanges(a,d,f)}},{key:"_loadTagIdentifierRanges",value:function(a,b,c){if(0===b.length)setTimeout(c.onSuccess,1);else{for(var d=[Number.MAX_VALUE,0],e=a.getSize(),f=0;f<b.length;f++){var g=b[f].getTagIdentifierByteRange(),h=0<=g.offset?g.offset:g.offset+e;g=h+g.length-1;d[0]=Math.min(h,d[0]);d[1]=Math.max(g,d[1])}a.loadRange(d,
c)}}}]);return a}(),u=function(){function a(){l(this,a)}e(a,null,[{key:"addFileReader",value:function(b){h.push(b);return a}},{key:"addTagReader",value:function(b){k.push(b);return a}},{key:"removeTagReader",value:function(b){b=k.indexOf(b);0<=b&&k.splice(b,1);return a}},{key:"EXPERIMENTAL_avoidHeadRequests",value:function(){c.setConfig({avoidHeadRequests:!0})}},{key:"setDisallowedXhrHeaders",value:function(a){c.setConfig({disallowedXhrHeaders:a})}},{key:"setXhrTimeoutInSec",value:function(a){c.setConfig({timeoutInSec:a})}}]);
return a}();u.addFileReader(c).addFileReader(b).addFileReader(a).addTagReader(g).addTagReader(d).addTagReader(f);"undefined"===typeof process||process.browser||u.addFileReader(q);p.exports={read:function(a,b){(new t(a)).read(b)},Reader:t,Config:u}},{"./ArrayFileReader":3,"./BlobFileReader":4,"./ID3v1TagReader":6,"./ID3v2TagReader":8,"./MP4TagReader":9,"./MediaFileReader":10,"./MediaTagReader":11,"./NodeFileReader":1,"./XhrFileReader":13}]},{},[14])(14)});
sonm:"Sort Name",soar:"Sort Artist",soaa:"Sort Album",soco:"Sort Composer",sosn:"Sort Show",purd:"Purchase Date",pcst:"Podcast",purl:"Podcast URL",catg:"Category",hdvd:"HD Video",stik:"Media Type",rtng:"Content Rating",pgap:"Gapless Playback",apID:"Purchase Account",sfID:"Country Code",atID:"Artist ID",cnID:"Catalog ID",plID:"Collection ID",geID:"Genre ID","xid ":"Vendor Information",flvr:"Codec Flavor"},d={title:"\u00a9nam",artist:"\u00a9ART",album:"\u00a9alb",year:"\u00a9day",comment:"\u00a9cmt",
track:"trkn",genre:"\u00a9gen",picture:"covr",lyrics:"\u00a9lyr"};p.exports=f},{"./MediaFileReader":11,"./MediaTagReader":12}],11:[function(f,p,q){var m=function(){function e(b,d){for(var a=0;a<d.length;a++){var c=d[a];c.enumerable=c.enumerable||!1;c.configurable=!0;"value"in c&&(c.writable=!0);Object.defineProperty(b,c.key,c)}}return function(b,d,a){d&&e(b.prototype,d);a&&e(b,a);return b}}(),l=f("./StringUtils");f=function(){function e(b){if(!(this instanceof e))throw new TypeError("Cannot call a class as a function");
this._isInitialized=!1;this._size=0}m(e,[{key:"init",value:function(b){var d=this;if(this._isInitialized)setTimeout(b.onSuccess,1);else return this._init({onSuccess:function(){d._isInitialized=!0;b.onSuccess()},onError:b.onError})}},{key:"_init",value:function(b){throw Error("Must implement init function");}},{key:"loadRange",value:function(b,d){throw Error("Must implement loadRange function");}},{key:"getSize",value:function(){if(!this._isInitialized)throw Error("init() must be called first.");return this._size}},
{key:"getByteAt",value:function(b){throw Error("Must implement getByteAt function");}},{key:"getBytesAt",value:function(b,d){for(var a=Array(d),c=0;c<d;c++)a[c]=this.getByteAt(b+c);return a}},{key:"isBitSetAt",value:function(b,d){return 0!=(this.getByteAt(b)&1<<d)}},{key:"getSByteAt",value:function(b){b=this.getByteAt(b);return 127<b?b-256:b}},{key:"getShortAt",value:function(b,d){b=d?(this.getByteAt(b)<<8)+this.getByteAt(b+1):(this.getByteAt(b+1)<<8)+this.getByteAt(b);0>b&&(b+=65536);return b}},
{key:"getSShortAt",value:function(b,d){b=this.getShortAt(b,d);return 32767<b?b-65536:b}},{key:"getLongAt",value:function(b,d){var a=this.getByteAt(b),c=this.getByteAt(b+1),e=this.getByteAt(b+2);b=this.getByteAt(b+3);d=d?(((a<<8)+c<<8)+e<<8)+b:(((b<<8)+e<<8)+c<<8)+a;0>d&&(d+=4294967296);return d}},{key:"getSLongAt",value:function(b,d){b=this.getLongAt(b,d);return 2147483647<b?b-4294967296:b}},{key:"getInteger24At",value:function(b,d){var a=this.getByteAt(b),c=this.getByteAt(b+1);b=this.getByteAt(b+
2);d=d?((a<<8)+c<<8)+b:((b<<8)+c<<8)+a;0>d&&(d+=16777216);return d}},{key:"getStringAt",value:function(b,d){for(var a=[],c=b,e=0;c<b+d;c++,e++)a[e]=String.fromCharCode(this.getByteAt(c));return a.join("")}},{key:"getStringWithCharsetAt",value:function(b,d,a){b=this.getBytesAt(b,d);switch((a||"").toLowerCase()){case "utf-16":case "utf-16le":case "utf-16be":a=l.readUTF16String(b,"utf-16be"===a);break;case "utf-8":a=l.readUTF8String(b);break;default:a=l.readNullTerminatedString(b)}return a}},{key:"getCharAt",
value:function(b){return String.fromCharCode(this.getByteAt(b))}},{key:"getSynchsafeInteger32At",value:function(b){var d=this.getByteAt(b),a=this.getByteAt(b+1),c=this.getByteAt(b+2);return this.getByteAt(b+3)&127|(c&127)<<7|(a&127)<<14|(d&127)<<21}}],[{key:"canReadFile",value:function(b){throw Error("Must implement canReadFile function");}}]);return e}();p.exports=f},{"./StringUtils":13}],12:[function(f,p,q){var m=function(){function f(e,b){for(var d=0;d<b.length;d++){var a=b[d];a.enumerable=a.enumerable||
!1;a.configurable=!0;"value"in a&&(a.writable=!0);Object.defineProperty(e,a.key,a)}}return function(e,b,d){b&&f(e.prototype,b);d&&f(e,d);return e}}();f("./MediaFileReader");f=function(){function f(e){if(!(this instanceof f))throw new TypeError("Cannot call a class as a function");this._mediaFileReader=e;this._tags=null}m(f,[{key:"setTagsToRead",value:function(e){this._tags=e;return this}},{key:"read",value:function(e){var b=this;this._mediaFileReader.init({onSuccess:function(){b._loadData(b._mediaFileReader,
{onSuccess:function(){try{var d=b._parseData(b._mediaFileReader,b._tags)}catch(a){if(e.onError){e.onError({type:"parseData",info:a.message});return}}e.onSuccess(d)},onError:e.onError})},onError:e.onError})}},{key:"getShortcuts",value:function(){return{}}},{key:"_loadData",value:function(e,b){throw Error("Must implement _loadData function");}},{key:"_parseData",value:function(e,b){throw Error("Must implement _parseData function");}},{key:"_expandShortcutTags",value:function(e){if(!e)return null;for(var b=
[],d=this.getShortcuts(),a=0,c;c=e[a];a++)b=b.concat(d[c]||[c]);return b}}],[{key:"getTagIdentifierByteRange",value:function(){throw Error("Must implement");}},{key:"canReadTagFormat",value:function(e){throw Error("Must implement");}}]);return f}();p.exports=f},{"./MediaFileReader":11}],13:[function(f,p,q){var m=function(){function e(b,d){for(var a=0;a<d.length;a++){var c=d[a];c.enumerable=c.enumerable||!1;c.configurable=!0;"value"in c&&(c.writable=!0);Object.defineProperty(b,c.key,c)}}return function(b,
d,a){d&&e(b.prototype,d);a&&e(b,a);return b}}(),l=function(){function e(b,d){if(!(this instanceof e))throw new TypeError("Cannot call a class as a function");this._value=b;this.bytesReadCount=d;this.length=b.length}m(e,[{key:"toString",value:function(){return this._value}}]);return e}();p.exports={readUTF16String:function(e,b,d){var a=0,c=1,g=0;d=Math.min(d||e.length,e.length);254==e[0]&&255==e[1]?(b=!0,a=2):255==e[0]&&254==e[1]&&(b=!1,a=2);b&&(c=0,g=1);b=[];for(var h=0;a<d;h++){var f=e[a+c],m=(f<<
8)+e[a+g];a+=2;if(0==m)break;else 216>f||224<=f?b[h]=String.fromCharCode(m):(f=(e[a+c]<<8)+e[a+g],a+=2,b[h]=String.fromCharCode(m,f))}return new l(b.join(""),a)},readUTF8String:function(e,b){var d=0;b=Math.min(b||e.length,e.length);239==e[0]&&187==e[1]&&191==e[2]&&(d=3);for(var a=[],c=0;d<b;c++){var g=e[d++];if(0==g)break;else if(128>g)a[c]=String.fromCharCode(g);else if(194<=g&&224>g){var f=e[d++];a[c]=String.fromCharCode(((g&31)<<6)+(f&63))}else if(224<=g&&240>g){f=e[d++];var k=e[d++];a[c]=String.fromCharCode(((g&
255)<<12)+((f&63)<<6)+(k&63))}else if(240<=g&&245>g){f=e[d++];k=e[d++];var m=e[d++];k=((g&7)<<18)+((f&63)<<12)+((k&63)<<6)+(m&63)-65536;a[c]=String.fromCharCode((k>>10)+55296,(k&1023)+56320)}}return new l(a.join(""),d)},readNullTerminatedString:function(e,b){var d=[];b=b||e.length;for(var a=0;a<b;){var c=e[a++];if(0==c)break;d[a-1]=String.fromCharCode(c)}return new l(d.join(""),a)}}},{}],14:[function(f,p,q){function m(b,d){if("function"!==typeof d&&null!==d)throw new TypeError("Super expression must either be null or a function, not "+
typeof d);b.prototype=Object.create(d&&d.prototype,{constructor:{value:b,enumerable:!1,writable:!0,configurable:!0}});d&&(Object.setPrototypeOf?Object.setPrototypeOf(b,d):b.__proto__=d)}var l=function(){function b(b,a){for(var c=0;c<a.length;c++){var d=a[c];d.enumerable=d.enumerable||!1;d.configurable=!0;"value"in d&&(d.writable=!0);Object.defineProperty(b,d.key,d)}}return function(d,a,c){a&&b(d.prototype,a);c&&b(d,c);return d}}(),e=f("./ChunkedFileData");q=function(b){function d(a){if(!(this instanceof
d))throw new TypeError("Cannot call a class as a function");var c=(d.__proto__||Object.getPrototypeOf(d)).call(this);if(!this)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");c=!c||"object"!==typeof c&&"function"!==typeof c?this:c;c._url=a;c._fileData=new e;return c}m(d,b);l(d,[{key:"_init",value:function(a){d._config.avoidHeadRequests?this._fetchSizeWithGetRequest(a):this._fetchSizeWithHeadRequest(a)}},{key:"_fetchSizeWithHeadRequest",value:function(a){var c=
this;this._makeXHRRequest("HEAD",null,{onSuccess:function(b){(b=c._parseContentLength(b))?(c._size=b,a.onSuccess()):c._fetchSizeWithGetRequest(a)},onError:a.onError})}},{key:"_fetchSizeWithGetRequest",value:function(a){var c=this,b=this._roundRangeToChunkMultiple([0,0]);this._makeXHRRequest("GET",b,{onSuccess:function(b){var d=c._parseContentRange(b);b=c._getXhrResponseContent(b);if(d){if(null==d.instanceLength){c._fetchEntireFile(a);return}c._size=d.instanceLength}else c._size=b.length;c._fileData.addData(0,
b);a.onSuccess()},onError:a.onError})}},{key:"_fetchEntireFile",value:function(a){var c=this;this._makeXHRRequest("GET",null,{onSuccess:function(b){b=c._getXhrResponseContent(b);c._size=b.length;c._fileData.addData(0,b);a.onSuccess()},onError:a.onError})}},{key:"_getXhrResponseContent",value:function(a){return a.responseBody||a.responseText||""}},{key:"_parseContentLength",value:function(a){a=this._getResponseHeader(a,"Content-Length");return null==a?a:parseInt(a,10)}},{key:"_parseContentRange",value:function(a){if(a=
this._getResponseHeader(a,"Content-Range")){var c=a.match(/bytes (\d+)-(\d+)\/(?:(\d+)|\*)/i);if(!c)throw Error("FIXME: Unknown Content-Range syntax: "+a);return{firstBytePosition:parseInt(c[1],10),lastBytePosition:parseInt(c[2],10),instanceLength:c[3]?parseInt(c[3],10):null}}return null}},{key:"loadRange",value:function(a,c){var b=this;b._fileData.hasDataRange(a[0],Math.min(b._size,a[1]))?setTimeout(c.onSuccess,1):(a=this._roundRangeToChunkMultiple(a),a[1]=Math.min(b._size,a[1]),this._makeXHRRequest("GET",
a,{onSuccess:function(d){d=b._getXhrResponseContent(d);b._fileData.addData(a[0],d);c.onSuccess()},onError:c.onError}))}},{key:"_roundRangeToChunkMultiple",value:function(a){return[a[0],a[0]+1024*Math.ceil((a[1]-a[0]+1)/1024)-1]}},{key:"_makeXHRRequest",value:function(a,c,b){var e=this._createXHRObject();e.open(a,this._url);var f=function(){if(200===e.status||206===e.status)b.onSuccess(e);else if(b.onError)b.onError({type:"xhr",info:"Unexpected HTTP status "+e.status+".",xhr:e});e=null};"undefined"!==
typeof e.onload?(e.onload=f,e.onerror=function(){if(b.onError)b.onError({type:"xhr",info:"Generic XHR error, check xhr object.",xhr:e})}):e.onreadystatechange=function(){4===e.readyState&&f()};d._config.timeoutInSec&&(e.timeout=1E3*d._config.timeoutInSec,e.ontimeout=function(){if(b.onError)b.onError({type:"xhr",info:"Timeout after "+e.timeout/1E3+"s. Use jsmediatags.Config.setXhrTimeout to override.",xhr:e})});e.overrideMimeType("text/plain; charset=x-user-defined");c&&this._setRequestHeader(e,"Range",
"bytes="+c[0]+"-"+c[1]);this._setRequestHeader(e,"If-Modified-Since","Sat, 01 Jan 1970 00:00:00 GMT");e.send(null)}},{key:"_setRequestHeader",value:function(a,b,e){0>d._config.disallowedXhrHeaders.indexOf(b.toLowerCase())&&a.setRequestHeader(b,e)}},{key:"_hasResponseHeader",value:function(a,b){a=a.getAllResponseHeaders();if(!a)return!1;a=a.split("\r\n");for(var c=[],d=0;d<a.length;d++)c[d]=a[d].split(":")[0].toLowerCase();return 0<=c.indexOf(b.toLowerCase())}},{key:"_getResponseHeader",value:function(a,
b){return this._hasResponseHeader(a,b)?a.getResponseHeader(b):null}},{key:"getByteAt",value:function(a){return this._fileData.getByteAt(a).charCodeAt(0)&255}},{key:"_isWebWorker",value:function(){return"undefined"!==typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope}},{key:"_createXHRObject",value:function(){if("undefined"===typeof window&&!this._isWebWorker())return new (f("xhr2").XMLHttpRequest);if("undefined"!==typeof XMLHttpRequest)return new XMLHttpRequest;throw Error("XMLHttpRequest is not supported");
}}],[{key:"canReadFile",value:function(a){return"string"===typeof a&&/^[a-z]+:\/\//i.test(a)}},{key:"setConfig",value:function(a){for(var b in a)a.hasOwnProperty(b)&&(this._config[b]=a[b]);a=this._config.disallowedXhrHeaders;for(b=0;b<a.length;b++)a[b]=a[b].toLowerCase()}}]);return d}(f("./MediaFileReader"));q._config={avoidHeadRequests:!1,disallowedXhrHeaders:[],timeoutInSec:30};p.exports=q},{"./ChunkedFileData":5,"./MediaFileReader":11,xhr2:2}],15:[function(f,p,q){function m(a,b){if(!(a instanceof
b))throw new TypeError("Cannot call a class as a function");}function l(a,b){var c=0>a.offset&&(-a.offset>b||0<a.offset+a.length);return!(0<=a.offset&&a.offset+a.length>=b||c)}var e=function(){function a(a,b){for(var c=0;c<b.length;c++){var d=b[c];d.enumerable=d.enumerable||!1;d.configurable=!0;"value"in d&&(d.writable=!0);Object.defineProperty(a,d.key,d)}}return function(b,c,d){c&&a(b.prototype,c);d&&a(b,d);return b}}();f("./MediaFileReader");q=f("./NodeFileReader");var b=f("./XhrFileReader"),d=
f("./BlobFileReader"),a=f("./ArrayFileReader");f("./MediaTagReader");var c=f("./ID3v1TagReader"),g=f("./ID3v2TagReader"),h=f("./MP4TagReader");f=f("./FLACTagReader");var k=[],r=[],t=function(){function a(b){m(this,a);this._file=b}e(a,[{key:"setTagsToRead",value:function(a){this._tagsToRead=a;return this}},{key:"setFileReader",value:function(a){this._fileReader=a;return this}},{key:"setTagReader",value:function(a){this._tagReader=a;return this}},{key:"read",value:function(a){var b=new (this._getFileReader())(this._file),
c=this;b.init({onSuccess:function(){c._getTagReader(b,{onSuccess:function(d){(new d(b)).setTagsToRead(c._tagsToRead).read(a)},onError:a.onError})},onError:a.onError})}},{key:"_getFileReader",value:function(){return this._fileReader?this._fileReader:this._findFileReader()}},{key:"_findFileReader",value:function(){for(var a=0;a<k.length;a++)if(k[a].canReadFile(this._file))return k[a];throw Error("No suitable file reader found for "+this._file);}},{key:"_getTagReader",value:function(a,b){if(this._tagReader){var c=
this._tagReader;setTimeout(function(){b.onSuccess(c)},1)}else this._findTagReader(a,b)}},{key:"_findTagReader",value:function(a,b){for(var c=[],d=[],e=a.getSize(),f=0;f<r.length;f++){var g=r[f].getTagIdentifierByteRange();l(g,e)&&(0<=g.offset&&g.offset<e/2||0>g.offset&&g.offset<-e/2?c.push(r[f]):d.push(r[f]))}var h=!1;f={onSuccess:function(){if(h){for(var c=0;c<r.length;c++){var d=r[c].getTagIdentifierByteRange();if(l(d,e)){try{var f=a.getBytesAt(0<=d.offset?d.offset:d.offset+e,d.length)}catch(A){if(b.onError)b.onError({type:"fileReader",
info:A.message});return}if(r[c].canReadTagFormat(f)){b.onSuccess(r[c]);return}}}if(b.onError)b.onError({type:"tagFormat",info:"No suitable tag reader found"})}else h=!0},onError:b.onError};this._loadTagIdentifierRanges(a,c,f);this._loadTagIdentifierRanges(a,d,f)}},{key:"_loadTagIdentifierRanges",value:function(a,b,c){if(0===b.length)setTimeout(c.onSuccess,1);else{for(var d=[Number.MAX_VALUE,0],e=a.getSize(),f=0;f<b.length;f++){var g=b[f].getTagIdentifierByteRange(),h=0<=g.offset?g.offset:g.offset+
e;g=h+g.length-1;d[0]=Math.min(h,d[0]);d[1]=Math.max(g,d[1])}a.loadRange(d,c)}}}]);return a}(),v=function(){function a(){m(this,a)}e(a,null,[{key:"addFileReader",value:function(b){k.push(b);return a}},{key:"addTagReader",value:function(b){r.push(b);return a}},{key:"removeTagReader",value:function(b){b=r.indexOf(b);0<=b&&r.splice(b,1);return a}},{key:"EXPERIMENTAL_avoidHeadRequests",value:function(){b.setConfig({avoidHeadRequests:!0})}},{key:"setDisallowedXhrHeaders",value:function(a){b.setConfig({disallowedXhrHeaders:a})}},
{key:"setXhrTimeoutInSec",value:function(a){b.setConfig({timeoutInSec:a})}}]);return a}();v.addFileReader(b).addFileReader(d).addFileReader(a).addTagReader(g).addTagReader(c).addTagReader(h).addTagReader(f);"undefined"===typeof process||process.browser||v.addFileReader(q);p.exports={read:function(a,b){(new t(a)).read(b)},Reader:t,Config:v}},{"./ArrayFileReader":3,"./BlobFileReader":4,"./FLACTagReader":6,"./ID3v1TagReader":7,"./ID3v2TagReader":9,"./MP4TagReader":10,"./MediaFileReader":11,"./MediaTagReader":12,
"./NodeFileReader":1,"./XhrFileReader":14}]},{},[15])(15)});
{
"name": "jsmediatags",
"version": "3.7.0",
"version": "3.8.0",
"description": "Media Tags Reader (ID3, MP4)",

@@ -42,15 +42,16 @@ "author": {

"devDependencies": {
"babel-plugin-transform-flow-strip-types": "~6.7.0",
"babel-plugin-transform-es2015-modules-commonjs": "~6.7.4",
"babel-plugin-transform-es2015-classes": "~6.6.5",
"babel-plugin-transform-es2015-block-scoping": "~6.7.1",
"babel-plugin-transform-class-properties": "~6.6.0",
"babel-jest": "~12.0.2",
"babel-core": "~6.7.7",
"babel-cli": "~6.7.7",
"jest-cli": "~12.0.2",
"babel-cli": "~6.26.0",
"babel-core": "~6.26.0",
"babel-jest": "~20.0.3",
"babel-plugin-transform-class-properties": "~6.24.1",
"babel-plugin-transform-es2015-block-scoping": "~6.26.0",
"babel-plugin-transform-es2015-classes": "~6.24.1",
"babel-plugin-transform-es2015-modules-commonjs": "~6.26.0",
"babel-plugin-transform-flow-strip-types": "~6.22.0",
"babelify": "~7.3.0",
"browserify": "~14.4.0",
"google-closure-compiler": "20170626.0.0",
"jest-cli": "~20.0.4",
"watchify": "~3.7.0",
"babelify": "~7.3.0",
"google-closure-compiler": "20170626.0.0"
"flow-bin": "~0.53.1"
},

@@ -61,4 +62,4 @@ "scripts": {

"watch": "babel --ignore __tests__,__mocks,FlowTypes.js --watch src --out-dir build2",
"dist-dev": "mkdir -p dist && browserify src/jsmediatags.js --detect-globals false -i ./src/NodeFileReader.js -o dist/jsmediatags.js -s jsmediatags -t [ babelify --plugins [ transform-flow-strip-types transform-es2015-modules-commonjs transform-class-properties transform-es2015-classes transform-es2015-block-scoping ] ]",
"dist-watch": "mkdir -p dist && watchify src/jsmediatags.js -v --detect-globals false -i ./src/NodeFileReader.js -o dist/jsmediatags.js -s jsmediatags -t [ babelify --plugins [ transform-flow-strip-types transform-es2015-modules-commonjs transform-class-properties transform-es2015-classes transform-es2015-block-scoping ] ]",
"dist-dev": "mkdir -p dist && browserify src/jsmediatags.js --detect-globals false -i ./src/NodeFileReader.js -o dist/jsmediatags.js -s jsmediatags -t babelify",
"dist-watch": "mkdir -p dist && watchify src/jsmediatags.js -v --detect-globals false -i ./src/NodeFileReader.js -o dist/jsmediatags.js -s jsmediatags -t babelify",
"dist": "npm run dist-dev && java -jar node_modules/google-closure-compiler/compiler.jar --warning_level QUIET --compilation_level SIMPLE_OPTIMIZATIONS --js dist/jsmediatags.js > dist/jsmediatags.min.js"

@@ -68,3 +69,6 @@ },

"rootDir": "./src",
"scriptPreprocessor": "../node_modules/babel-jest"
"timers": "fake",
"transform": {
".js$": "babel-jest"
}
},

@@ -71,0 +75,0 @@ "engines": {

@@ -24,2 +24,3 @@ # JS MediaTags

* MP4
* FLAC

@@ -103,3 +104,3 @@ ## How to use

### Articles
* [Cordova : lire les metadatas des mp3s avec jsmediatags](http://blog.luce.pro/2016/02/28/Phonegap-lire-les-metadatas-des-mp3s-avec-jsmediatags/)

@@ -169,2 +170,17 @@

#### FLAC
```javascript
{
type: "FLAC",
version: "1",
tags: {
title: "16/12/95",
artist: "Sam, The Kid",
album: "Pratica(mente)",
track: "12",
picture: ...
}
}
```
The `tags` property includes all tags that were found or specified to be read.

@@ -171,0 +187,0 @@ Since each tag type (e.g.: ID3, MP4) uses different tag names for the same type of data (e.g.: the artist name) the most common tags are also available under human readable names (aka shortcuts). In this example, `artist` will point to `TPE1.data`, `album` to `TALB.data` and so forth.

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