vendor: 4,111 bytes, lines 1-60
1/** 2 3Downloaded from: https://github.com/aadsm/jsmediatags/blob/master/dist/jsmediatags.js 4 5[BSD License](http://opensource.org/licenses/BSD-3-Clause) 6 7Copyright (c) 2009 Opera Software ASA 8 9Copyright (c) 2015 António Afonso 10 11Copyright (c) 2008 Jacob Seidelin, http://blog.nihilogic.dk/ 12 13Copyright (c) 2010 Joshua Kifer 14 15All rights reserved. 16 17Redistribution and use in source and binary forms, with or without modification, 18are permitted provided that the following conditions are met: 19 20 * Redistributions of source code must retain the above copyright notice, this 21 list of conditions and the following disclaimer. 22 23 * Redistributions in binary form must reproduce the above copyright notice, 24 this list of conditions and the following disclaimer in the documentation 25 and/or other materials provided with the distribution. 26 27 * Neither the name Facebook nor the names of its contributors may be used to 28 endorse or promote products derived from this software without specific 29 prior written permission. 30 31THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 32ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 33WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 34DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 35ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 36(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 37LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 38ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 39(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 40SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 41 42**/ 43 44(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.jsmediatags = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){ 45 46},{}],2:[function(require,module,exports){ 47module.exports = XMLHttpRequest; 48 49},{}],3:[function(require,module,exports){ 50'use strict'; 51 52function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } 53 54function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 55 56function _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); } } 57 58function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } 59 60function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { c
60onstructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); } 61 62function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } 63 64function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } 65 66function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); } 67 68function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } 69 70function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } 71 72function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } 73 74function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } 75 76var MediaFileReader = require('./MediaFileReader'); 77 78var ArrayFileReader = /*#__PURE__*/function (_MediaFileReader) { 79 _inherits(ArrayFileReader, _MediaFileReader); 80 81 var _super = _createSuper(ArrayFileReader); 82 83 function ArrayFileReader(array) { 84 var _this; 85 86 _classCallCheck(this, ArrayFileReader); 87 88 _this = _super.call(this); 89 90 _defineProperty(_assertThisInitialized(_this), "_array", void 0); 91 92 _defineProperty(_assertThisInitialized(_this), "_size", void 0); 93 94 _this._array = array; 95 _this._size = array.length; 96 _this._isInitialized = true; 97 return _this; 98 } 99 100 _createClass(ArrayFileReader, [{ 101 key: "init", 102 value: function init(callbacks) { 103 setTimeout(callbacks.onSuccess, 0); 104 } 105 }, { 106 key: "loadRange", 107 value: function loadRange(range, callbacks) { 108 setTimeout(callbacks.onSuccess, 0); 109 } 110 }, { 111 key: "getByteAt", 112 value: function getByteAt(offset) { 113 if (offset >= this._array.length) { 114 throw new Error("Offset " + offset + " hasn't been loaded yet."); 115 } 116 117 return this._array[offset]; 118 } 119 }], [{ 120 key: "canReadFile", 121 value: function canReadFile(file) { 122 return Array.isArray(file) || typeof Buffer === 'function' && Buffer.isBuffer(file); 123 } 124 }]); 125 126 return ArrayFileReader; 127}(MediaFileReader); 128 129module.exports = ArrayFileReader; 130 131},{"./MediaFileReader":11}],4:[function(require,module,exports){ 132'use strict'; 133 134function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } 135 136function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 137 138function _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); } } 139 140function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } 141 142function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { c
142onstructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); } 143 144function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } 145 146function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } 147 148function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); } 149 150function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } 151 152function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } 153 154function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } 155 156function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } 157 158var ChunkedFileData = require('./ChunkedFileData'); 159 160var MediaFileReader = require('./MediaFileReader'); 161 162var BlobFileReader = /*#__PURE__*/function (_MediaFileReader) { 163 _inherits(BlobFileReader, _MediaFileReader); 164 165 var _super = _createSuper(BlobFileReader); 166 167 function BlobFileReader(blob) { 168 var _this; 169 170 _classCallCheck(this, BlobFileReader); 171 172 _this = _super.call(this); 173 174 _defineProperty(_assertThisInitialized(_this), "_blob", void 0); 175 176 _defineProperty(_assertThisInitialized(_this), "_fileData", void 0); 177 178 _this._blob = blob; 179 _this._fileData = new ChunkedFileData(); 180 return _this; 181 } 182 183 _createClass(BlobFileReader, [{ 184 key: "_init", 185 value: function _init(callbacks) { 186 this._size = this._blob.size; 187 setTimeout(callbacks.onSuccess, 1); 188 } 189 }, { 190 key: "loadRange", 191 value: function loadRange(range, callbacks) { 192 var self = this; // $FlowIssue - flow isn't aware of mozSlice or webkitSlice 193 194 var blobSlice = this._blob.slice || this._blob.mozSlice || this._blob.webkitSlice; 195 var blob = blobSlice.call(this._blob, range[0], range[1] + 1); 196 var browserFileReader = new FileReader(); 197 198 browserFileReader.onloadend = function (event) { 199 var intArray = new Uint8Array(browserFileReader.result); 200 201 self._fileData.addData(range[0], intArray); 202 203 callbacks.onSuccess(); 204 }; 205 206 browserFileReader.onerror = browserFileReader.onabort = function (event) { 207 if (callbacks.onError) { 208 callbacks.onError({ 209 "type": "blob", 210 "info": browserFileReader.error 211 }); 212 } 213 }; 214 215 browserFileReader.readAsArrayBuffer(blob); 216 } 217 }, { 218 key: "getByteAt", 219 value: function getByteAt(offset) { 220 return this._fileData.getByteAt(offset); 221 } 222 }], [{ 223 key: "canReadFile", 224 value: function canReadFile(file) { 225 return typeof Blob !== "undefined" && file instanceof Blob || // File extends Blob but it seems that File instanceof Blob doesn't 226 // quite work as expected in Cordova/PhoneGap. 227 typeof File !== "undefined" && file instanceof File; 228 } 229 }]); 230 231 return BlobFileReader; 232}(MediaFileReader); 233 234module.exports = BlobFileReader; 235 236},{"./ChunkedFileData":5,"./MediaFileReader":11}],5:[function(require,module,exports){ 237/** 238 * This class represents a file that might not have all its data loaded yet. 239 * It is used when loading the entire file is not an option because it's too 240 * expensive. Instead, parts of the file are loaded and added only when needed. 241 * From a reading point of view is as if the entire file is loaded. The 242 * exception is when the data is not available yet, an error will be thrown. 243 * This class does not load the data, it just manages it. It provides operations 244 * to add and read data from the file. 245 * 246 * 247 */ 248'use strict'; 249 250function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 251 252function _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); } } 253 254function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } 255 256function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } 257 258var NOT_FOUND = -1; 259 260var ChunkedFileData = /*#__PURE__*/function () { 261 function ChunkedFileData() { 262 _classCallCheck(this, ChunkedFileData); 263 264 _defineProperty(this, "_fileData", void 0); 265 266 this._fileData = []; 267 } 268 /** 269 * Adds data to the file storage at a specific offset. 270 */ 271 272 273 _createClass(ChunkedFileData, [{ 274 key: "addData", 275 value: function addData(offset, data) { 276 var offsetEnd = offset + data.length - 1; 277 278 var chunkRange = this._getChunkRange(offset, offsetEnd); 279 280 if (chunkRange.startIx === NOT_FOUND) { 281 this._fileData.splice(chunkRange.insertIx || 0, 0, { 282 offset: offset, 283 data: data 284 }); 285 } else { 286 // If the data to add collides with existing chunks we prepend and 287 // append data from the half colliding chunks to make the collision at 288 // 100%. The new data can then replace all the colliding chunkes. 289 var firstChunk = this._fileData[chunkRange.startIx]; 290 var lastChunk = this._fileData[chunkRange.endIx]; 291 var needsPrepend = offset > firstChunk.offset; 292 var needsAppend = offsetEnd < lastChunk.offset + lastChunk.data.length - 1; 293 var chunk = { 294 offset: Math.min(offset, firstChunk.offset), 295 data: data 296 }; 297 298 if (needsPrepend) { 299 var slicedData = this._sliceData(firstChunk.data, 0, offset - firstChunk.offset); 300 301 chunk.data = this._concatData(slicedData, data); 302 } 303 304 if (needsAppend) { 305 // Use the lastChunk because the slice logic is easier to handle. 306 var slicedData = this._sliceData(chunk.data, 0, lastChunk.offset - chunk.offset); 307 308 chunk.data = this._concatData(slicedData, lastChunk.data); 309 } 310 311 this._fileData.splice(chunkRange.startIx, chunkRange.endIx - chunkRange.startIx + 1, chunk); 312 } 313 } 314 }, { 315 key: "_concatData", 316 value: function _concatData(dataA, dataB) { 317 // TypedArrays don't support concat. 318 if (typeof ArrayBuffer !== "undefined" && ArrayBuffer.isView && ArrayBuffer.isView(dataA)) { 319 // $FlowIssue - flow thinks dataAandB is a string but it's not 320 var dataAandB = new dataA.constructor(dataA.length + dataB.length); // $FlowIssue - flow thinks dataAandB is a string but it's not 321 322 dataAandB.set(dataA, 0); // $FlowIssue - flow thinks dataAandB is a string but it's not 323 324 dataAandB.set(dataB, dataA.length); 325 return dataAandB; 326 } else { 327 // $FlowIssue - flow thinks dataAandB is a TypedArray but it's not 328 return dataA.concat(dataB); 329 } 330 } 331 }, { 332 key: "_sliceData", 333 value: function _sliceData(data, begin, end) { 334 // Some TypeArray implementations do not support slice yet. 335 if (data.slice) { 336 return data.slice(begin, end); 337 } else { 338 // $FlowIssue - flow thinks data is a string but it's not 339 return data.subarray(begin, end); 340 } 341 } 342 /** 343 * Finds the chunk range that overlaps the [offsetStart-1,offsetEnd+1] range. 344 * When a chunk is adjacent to the offset we still consider it part of the 345 * range (this is the situation of offsetStart-1 or offsetEnd+1). 346 * When no chunks are found `insertIx` denotes the index where the data 347 * should be inserted in the data list (startIx == NOT_FOUND and endIX == 348 * NOT_FOUND). 349 */ 350 351 }, { 352 key: "_getChunkRange", 353 value: function _getChunkRange(offsetStart, offsetEnd) { 354 var startChunkIx = NOT_FOUND; 355 var endChunkIx = NOT_FOUND; 356 var insertIx = 0; // Could use binary search but not expecting that many blocks to exist. 357 358 for (var i = 0; i < this._fileData.length; i++, insertIx = i) { 359 var chunkOffsetStart = this._fileData[i].offset; 360 var chunkOffsetEnd = chunkOffsetStart + this._fileData[i].data.length; 361 362 if (offsetEnd < chunkOffsetStart - 1) { 363 // This offset range doesn't overlap with any chunks. 364 break; 365 } // If it is adjacent we still consider it part of the range because 366 // we're going end up with a single block with all contiguous data. 367 368 369 if (offsetStart <= chunkOffsetEnd + 1 && offsetEnd >= chunkOffsetStart - 1) { 370 startChunkIx = i; 371 break; 372 } 373 } // No starting chunk was found, meaning that the offset is either before 374 // or after the current stored chunks. 375 376 377 if (startChunkIx === NOT_FOUND) { 378 return { 379 startIx: NOT_FOUND, 380 endIx: NOT_FOUND, 381 insertIx: insertIx 382 }; 383 } // Find the ending chunk. 384 385 386 for (var i = startChunkIx; i < this._fileData.length; i++) { 387 var chunkOffsetStart = this._fileData[i].offset; 388 var chunkOffsetEnd = chunkOffsetStart + this._fileData[i].data.length; 389 390 if (offsetEnd >= chunkOffsetStart - 1) { 391 // Candidate for the end chunk, it doesn't mean it is yet. 392 endChunkIx = i; 393 } 394 395 if (offsetEnd <= chunkOffsetEnd + 1) { 396 break; 397 } 398 } 399 400 if (endChunkIx === NOT_FOUND) { 401 endChunkIx = startChunkIx; 402 } 403 404 return { 405 startIx: startChunkIx, 406 endIx: endChunkIx 407 }; 408 } 409 }, { 410 key: "hasDataRange", 411 value: function hasDataRange(offsetStart, offsetEnd) { 412 for (var i = 0; i < this._fileData.length; i++) { 413 var chunk = this._fileData[i]; 414 415 if (offsetEnd < chunk.offset) { 416 return false;
417 } 418 419 if (offsetStart >= chunk.offset && offsetEnd < chunk.offset + chunk.data.length) { 420 return true; 421 } 422 } 423 424 return false; 425 } 426 }, { 427 key: "getByteAt", 428 value: function getByteAt(offset) { 429 var dataChunk; 430 431 for (var i = 0; i < this._fileData.length; i++) { 432 var dataChunkStart = this._fileData[i].offset; 433 var dataChunkEnd = dataChunkStart + this._fileData[i].data.length - 1; 434 435 if (offset >= dataChunkStart && offset <= dataChunkEnd) { 436 dataChunk = this._fileData[i]; 437 break; 438 } 439 } 440 441 if (dataChunk) { 442 return dataChunk.data[offset - dataChunk.offset]; 443 } 444 445 throw new Error("Offset " + offset + " hasn't been loaded yet."); 446 } 447 }], [{ 448 key: "NOT_FOUND", 449 get: // $FlowIssue - get/set properties not yet supported 450 function get() { 451 return NOT_FOUND; 452 } 453 }]); 454 455 return ChunkedFileData; 456}(); 457 458module.exports = ChunkedFileData; 459 460},{}],6:[function(require,module,exports){ 461"use strict"; 462 463function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } 464 465function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 466 467function _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); } } 468 469function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } 470 471function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); } 472 473function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } 474 475function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } 476 477function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); } 478 479function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } 480 481function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } 482 483function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } 484 485function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } 486 487var MediaTagReader = require('./MediaTagReader'); 488/* The first 4 bytes of a FLAC file describes the header for the file. If these 489 * bytes respectively read "fLaC", we can determine it is a FLAC file. 490 */ 491 492 493var FLAC_HEADER_SIZE = 4;
494/* FLAC metadata is stored in blocks containing data ranging from STREAMINFO to 495 * VORBIS_COMMENT, which is what we want to work with. 496 * 497 * Each metadata header is 4 bytes long, with the first byte determining whether 498 * it is the last metadata block before the audio data and what the block type is. 499 * This first byte can further be split into 8 bits, with the first bit being the 500 * last-metadata-block flag, and the last three bits being the block type. 501 * 502 * Since the specification states that the decimal value for a VORBIS_COMMENT block 503 * type is 4, the two possibilities for the comment block header values are: 504 * - 00000100 (Not a last metadata comment block, value of 4) 505 * - 10000100 (A last metadata comment block, value of 132) 506 * 507 * Similarly, the picture block header values are 6 and 128. 508 * 509 * All values for METADATA_BLOCK_HEADER can be found here. 510 * https://xiph.org/flac/format.html#metadata_block_header 511 */ 512 513var COMMENT_HEADERS = [4, 132]; 514var PICTURE_HEADERS = [6, 134]; // These are the possible image types as defined by the FLAC specification. 515 516var IMAGE_TYPES = ["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"]; 517 518/** 519 * Class representing a MediaTagReader that parses FLAC tags. 520 */ 521var FLACTagReader = /*#__PURE__*/function (_MediaTagReader) { 522 _inherits(FLACTagReader, _MediaTagReader); 523 524 var _super = _createSuper(FLACTagReader); 525 526 function FLACTagReader() { 527 var _this; 528 529 _classCallCheck(this, FLACTagReader); 530 531 for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { 532 args[_key] = arguments[_key]; 533 } 534 535 _this = _super.call.apply(_super, [this].concat(args)); 536 537 _defineProperty(_assertThisInitialized(_this), "_commentOffset", void 0); 538 539 _defineProperty(_assertThisInitialized(_this), "_pictureOffset", void 0); 540 541 return _this; 542 } 543 544 _createClass(FLACTagReader, [{ 545 key: "_loadData", 546 value: 547 /** 548 * Function called to load the data from the file. 549 * 550 * To begin processing the blocks, the next 4 bytes after the initial 4 bytes 551 * (bytes 4 through 7) are loaded. From there, the rest of the loading process 552 * is passed on to the _loadBlock function, which will handle the rest of the 553 * parsing for the metadata blocks. 554 * 555 * @param {MediaFileReader} mediaFileReader - The MediaFileReader used to parse the file. 556 * @param {LoadCallbackType} callbacks - The callback to call once _loadData is completed. 557 */ 558 function _loadData(mediaFileReader, callbacks) { 559 var self = this; 560 mediaFileReader.loadRange([4, 7], { 561 onSuccess: function onSuccess() { 562 self._loadBlock(mediaFileReader, 4, callbacks); 563 } 564 }); 565 } 566 /** 567 * Special internal function used to parse the different FLAC blocks. 568 * 569 * The FLAC specification doesn't specify a specific location for metadata to resign, but 570 * dictates that it may be in one of various blocks located throughout the file. To load the 571 * metadata, we must locate the header first. This can be done by reading the first byte of 572 * each block to determine the block type. After the block type comes a 24 bit integer that stores 573 * the length of the block as big endian. Using this, we locate the block and store the offset for 574 * parsing later. 575 * 576 * After each block has been parsed, the _nextBlock function is called in order 577 * to parse the information of the next block. All blocks need to be parsed in order to find 578 * all of the picture and comment blocks. 579 * 580 * More info on the FLAC specification may be found here: 581 * https://xiph.org/flac/format.html 582 * @param {MediaFileReader} mediaFileReader - The MediaFileReader used to parse the file. 583 * @param {number} offset - The offset to start checking the header from. 584 * @param {LoadCallbackType} callbacks - The callback to call once the header has been found. 585 */ 586 587 }, { 588 key: "_loadBlock", 589 value: function _loadBlock(mediaFileReader, offset, callbacks) { 590 var self = this; 591 /* As mentioned above, this first byte is loaded to see what metadata type 592 * this block represents. 593 */ 594 595 var blockHeader = mediaFileReader.getByteAt(offset); 596 /* The last three bytes (integer 24) contain a value representing the length 597 * of the following metadata block. The 1 is added in order to shift the offset 598 * by one to get the last three bytes in the block header. 599 */ 600 601 var blockSize = mediaFileReader.getInteger24At(offset + 1, true); 602 /* This conditional checks if blockHeader (the byte retrieved representing the 603 * type of the header) is one the headers we are looking for. 604 * 605 * If that is not true, the block is skipped over and the next range is loaded: 606 * - offset + 4 + blockSize adds 4 to skip over the initial metadata header and 607 * blockSize to skip over the block overall, placing it at the head of the next 608 * metadata header. 609 * - offset + 4 + 4 + blockSize does the same thing as the previous block with 610 * the exception of adding another 4 bytes to move it to the end of the new metadata 611 * header. 612 */ 613 614 if (COMMENT_HEADERS.indexOf(blockHeader) !== -1) { 615 /* 4 is added to offset to move it to the head of the actual metadata. 616 * The range starting from offsetMatadata (the beginning of the block) 617 * and offsetMetadata + blockSize (the end of the block) is loaded. 618 */ 619 var offsetMetadata = offset + 4; 620 mediaFileReader.loadRange([offsetMetadata, offsetMetadata + blockSize], { 621 onSuccess: function onSuccess() { 622 self._commentOffset = offsetMetadata; 623 624 self._nextBlock(mediaFileReader, offset, blockHeader, blockSize, callbacks); 625 } 626 }); 627 } else if (PICTURE_HEADERS.indexOf(blockHeader) !== -1) { 628 var offsetMetadata = offset + 4; 629 mediaFileReader.loadRange([offsetMetadata, offsetMetadata + blockSize], { 630 onSuccess: function onSuccess() { 631 self._pictureOffset = offsetMetadata; 632 633 self._nextBlock(mediaFileReader, offset, blockHeader, blockSize, callbacks); 634 } 635 }); 636 } else { 637 self._nextBlock(mediaFileReader, offset, blockHeader, blockSize, callbacks); 638 } 639 } 640 /** 641 * Internal function used to load the next range and respective block. 642 * 643 * If the metadata block that was identified is not the last block before the 644 * audio blocks, the function will continue loading the next blocks. If it is 645 * the last block (identified by any values greater than 127, see FLAC spec.), 646 * the function will determine whether a comment block had been identified. 647 * 648 * If the block does not exist, the error callback is called. Otherwise, the function 649 * will call the success callback, allowing data parsing to begin. 650 * @param {MediaFileReader} mediaFileReader - The MediaFileReader used to parse the file. 651 * @param {number} offset - The offset that the existing header was located at. 652 * @param {number} blockHeader - An integer reflecting the header type of the block. 653 * @param {number} blockSize - The size of the previously processed header. 654 * @param {LoadCallbackType} callbacks - The callback functions to be called. 655 */ 656 657 }, { 658 key: "_nextBlock", 659 value: function _nextBlock(mediaFileReader, offset, blockHeader, blockSize, callbacks) { 660 var self = this; 661 662 if (blockHeader > 127) { 663 if (!self._commentOffset) { 664 callbacks.onError({ 665 "type": "loadData", 666 "info": "Comment block could not be found." 667 }); 668 } else { 669 callbacks.onSuccess(); 670 } 671 } else { 672 mediaFileReader.loadRange([offset + 4 + blockSize, offset + 4 + 4 + blockSize], { 673 onSuccess: function onSuccess() { 674 self._loadBlock(mediaFileReader, offset + 4 + blockSize, callbacks); 675 } 676 }); 677 } 678 } 679 /** 680 * Parses the data and returns the tags. 681 * 682 * This is an overview of the VorbisComment format and what this function attempts to 683 * retrieve: 684 * - First 4 bytes: a long that contains the length of the vendor string. 685 * - Next n bytes: the vendor string encoded in UTF-8. 686 * - Next 4 bytes: a long representing how many comments are in this block 687 * For each comment that exists: 688 * - First 4 bytes: a long representing the length of the comment 689 * - Next n bytes: the comment encoded in UTF-8. 690 * The comment string will usually appear in a format similar to: 691 * ARTIST=me 692 * 693 * Note that the longs and integers in this block are encoded in little endian 694 * as opposed to big endian for the rest of the FLAC spec. 695 * @param {MediaFileReader} data - The MediaFileReader to parse the file with. 696 * @param {Array<string>} [tags] - Optional tags to also be retrieved from the file. 697 * @return {TagType}
697 - An object containing the tag information for the file. 698 */ 699 700 }, { 701 key: "_parseData", 702 value: function _parseData(data, tags) { 703 var vendorLength = data.getLongAt(this._commentOffset, false); 704 var offsetVendor = this._commentOffset + 4; 705 /* This line is able to retrieve the vendor string that the VorbisComment block 706 * contains. However, it is not part of the tags that JSMediaTags normally retrieves, 707 * and is therefore commented out. 708 */ 709 // var vendor = data.getStringWithCharsetAt(offsetVendor, vendorLength, "utf-8").toString(); 710 711 var offsetList = vendorLength + offsetVendor; 712 /* To get the metadata from the block, we first get the long that contains the 713 * number of actual comment values that are existent within the block. 714 * 715 * As we loop through all of the comment blocks, we get the data length in order to 716 * get the right size string, and then determine which category that string falls under. 717 * The dataOffset variable is constantly updated so that it is at the beginning of the 718 * comment that is currently being parsed. 719 * 720 * Additions of 4 here are used to move the offset past the first 4 bytes which only contain 721 * the length of the comment. 722 */ 723 724 var numComments = data.getLongAt(offsetList, false); 725 var dataOffset = offsetList + 4; 726 var title, artist, album, track, genre, picture; 727 728 for (var i = 0; i < numComments; i++) { 729 var _dataLength = data.getLongAt(dataOffset, false); 730 731 var s = data.getStringWithCharsetAt(dataOffset + 4, _dataLength, "utf-8").toString(); 732 var d = s.indexOf("="); 733 var split = [s.slice(0, d), s.slice(d + 1)]; 734 735 switch (split[0].toUpperCase()) { 736 case "TITLE": 737 title = split[1]; 738 break; 739 740 case "ARTIST": 741 artist = split[1]; 742 break; 743 744 case "ALBUM": 745 album = split[1]; 746 break; 747 748 case "TRACKNUMBER": 749 track = split[1]; 750 break; 751 752 case "GENRE": 753 genre = split[1]; 754 break; 755 } 756 757 dataOffset += 4 + _dataLength; 758 } 759 /* If a picture offset was found and assigned, then the reader will start processing 760 * the picture block from that point. 761 * 762 * All the lengths for the picture data can be found online here: 763 * https://xiph.org/flac/format.html#metadata_block_picture 764 */ 765 766 767 if (this._pictureOffset) { 768 var imageType = data.getLongAt(this._pictureOffset, true); 769 var offsetMimeLength = this._pictureOffset + 4; 770 var mimeLength = data.getLongAt(offsetMimeLength, true); 771 var offsetMime = offsetMimeLength + 4; 772 var mime = data.getStringAt(offsetMime, mimeLength); 773 var offsetDescriptionLength = offsetMime + mimeLength; 774 var descriptionLength = data.getLongAt(offsetDescriptionLength, true); 775 var offsetDescription = offsetDescriptionLength + 4; 776 var description = data.getStringWithCharsetAt(offsetDescription, descriptionLength, "utf-8").toString(); 777 var offsetDataLength = offsetDescription + descriptionLength + 16; 778 var dataLength = data.getLongAt(offsetDataLength, true); 779 var offsetData = offsetDataLength + 4; 780 var imageData = data.getBytesAt(offsetData, dataLength, true); 781 picture = { 782 format: mime, 783 type: IMAGE_TYPES[imageType], 784 description: description, 785 data: imageData 786 }; 787 } 788 789 var tag = { 790 type: "FLAC", 791 version: "1", 792 tags: { 793 "title": title, 794 "artist": artist, 795 "album": album, 796 "track": track, 797 "genre": genre, 798 "picture": picture 799 } 800 }; 801 return tag; 802 } 803 }], [{ 804 key: "getTagIdentifierByteRange", 805 value: 806 /** 807 * Gets the byte range for the tag identifier. 808 * 809 * Because the Vorbis comment block is not guaranteed to be in a specified 810 * location, we can only load the first 4 bytes of the file to confirm it 811 * is a FLAC first. 812 * 813 * @return {ByteRange} The byte range that identifies the tag for a FLAC. 814 */ 815 function getTagIdentifierByteRange() { 816 return { 817 offset: 0, 818 length: FLAC_HEADER_SIZE 819 }; 820 } 821 /** 822 * Determines whether or not this reader can read a certain tag format. 823 * 824 * This checks that the first 4 characters in the file are fLaC, which
825 * according to the FLAC file specification should be the characters that 826 * indicate a FLAC file. 827 * 828 * @return {boolean} True if the header is fLaC, false otherwise. 829 */ 830 831 }, { 832 key: "canReadTagFormat", 833 value: function canReadTagFormat(tagIdentifier) { 834 var id = String.fromCharCode.apply(String, tagIdentifier.slice(0, 4)); 835 return id === 'fLaC'; 836 } 837 }]); 838 839 return FLACTagReader; 840}(MediaTagReader); 841 842module.exports = FLACTagReader; 843 844},{"./MediaTagReader":12}],7:[function(require,module,exports){ 845'use strict'; 846 847function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } 848 849function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 850 851function _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); } } 852 853function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } 854 855function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); } 856 857function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } 858 859function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } 860 861function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); } 862 863function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } 864 865function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } 866 867function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } 868 869var MediaTagReader = require('./MediaTagReader'); 870 871var MediaFileReader = require('./MediaFileReader'); 872 873var ID3v1TagReader = /*#__PURE__*/function (_MediaTagReader) { 874 _inherits(ID3v1TagReader, _MediaTagReader); 875 876 var _super = _createSuper(ID3v1TagReader); 877 878 function ID3v1TagReader() { 879 _classCallCheck(this, ID3v1TagReader); 880 881 return _super.apply(this, arguments); 882 } 883 884 _createClass(ID3v1TagReader, [{ 885 key: "_loadData", 886 value: function _loadData(mediaFileReader, callbacks) { 887 var fileSize = mediaFileReader.getSize(); 888 mediaFileReader.loadRange([fileSize - 128, fileSize - 1], callbacks); 889 } 890 }, { 891 key: "_parseData", 892 value: function _parseData(data, tags) { 893 var offset = data.getSize() - 128; 894 var title = data.getStringWithCharsetAt(offset + 3, 30).toString(); 895 var artist = data.getStringWithCharsetAt(offset + 33, 30).toString(); 896 var album = data.getStringWithCharsetAt(offset + 63, 30).toString(); 897 var year = data.getStringWithCharsetAt(offset + 93, 4).toString(); 898 var trackFlag = data.getByteAt(offset + 97 + 28); 899 var track = data.getByteAt(offset + 97 + 29); 900 901 if (trackFlag == 0 && track != 0) { 902 var version = "1.1"; 903 var comment = data.getStringWithCharsetAt(offset + 97, 28).toString(); 904 } else { 905 var version = "1.0"; 906 var comment = data.getStringWithCharsetAt(offset + 97, 30).toString(); 907 track = 0; 908 } 909
910 var genreIdx = data.getByteAt(offset + 97 + 30); 911 912 if (genreIdx < 255) { 913 var genre = GENRES[genreIdx]; 914 } else { 915 var genre = ""; 916 } 917 918 var tag = { 919 "type": "ID3", 920 "version": version, 921 "tags": { 922 "title": title, 923 "artist": artist, 924 "album": album, 925 "year": year, 926 "comment": comment, 927 "genre": genre 928 } 929 }; 930 931 if (track) { 932 // $FlowIssue - flow is not happy with adding properties 933 tag.tags.track = track; 934 } 935 936 return tag; 937 } 938 }], [{ 939 key: "getTagIdentifierByteRange", 940 value: function getTagIdentifierByteRange() { 941 // The identifier is TAG and is at offset: -128. However, to avoid a 942 // fetch for the tag identifier and another for the data, we load the 943 // entire data since it's so small. 944 return { 945 offset: -128, 946 length: 128 947 }; 948 } 949 }, { 950 key: "canReadTagFormat", 951 value: function canReadTagFormat(tagIdentifier) { 952 var id = String.fromCharCode.apply(String, tagIdentifier.slice(0, 3)); 953 return id === "TAG"; 954 } 955 }]); 956 957 return ID3v1TagReader; 958}(MediaTagReader); 959 960var 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"]; 961module.exports = ID3v1TagReader; 962 963},{"./MediaFileReader":11,"./MediaTagReader":12}],8:[function(require,module,exports){ 964'use strict'; 965 966function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 967 968function _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); } } 969 970function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } 971 972var MediaFileReader = require('./MediaFileReader'); 973 974var StringUtils = require('./StringUtils'); 975 976var ArrayFileReader = require('./ArrayFileReader'); 977 978var FRAME_DESCRIPTIONS = { 979 // v2.2 980 "BUF": "Recommended buffer size", 981 "CNT": "Play counter", 982 "COM": "Comments", 983 "CRA": "Audio encryption", 984 "CRM": "Encrypted meta frame", 985 "ETC": "Event timing codes", 986 "EQU": "Equalization", 987 "GEO": "General encapsulated object", 988 "IPL": "Involved people list", 989 "LNK": "Linked information", 990 "MCI": "Music CD Identifier", 991 "MLL": "MPEG location lookup table", 992 "PIC": "Attached picture", 993 "POP": "Popularimeter", 994 "REV": "Reverb", 995 "RVA": "Relative volume adjustment", 996 "SLT": "Synchronized lyric/text", 997 "STC": "Synced tempo codes", 998 "TAL": "Album/Movie/Show title", 999 "TBP": "BPM (Beats Per Minute)", 1000 "TCM": "Composer", 1001 "TCO": "Content type", 1002 "TCR": "Copyright message", 1003 "TDA": "Date", 1004 "TDY": "Playlist delay", 1005 "TEN": "Encoded by", 1006 "TFT": "File type", 1007 "TIM": "Time", 1008 "TKE": "Initial key", 1009 "TLA": "Language(s)", 1010 "TLE": "Length", 1011 "TMT": "Media type", 1012 "TOA": "Original artist(s)/performer(s)", 1013 "TOF": "Original filename", 1014 "TOL": "Original Lyricist(s)/text writer(s)", 1015 "TOR": "Original release year", 1016 "TOT": "Original album/Movie/Show title", 1017 "TP1": "Lead artist(s)/Lead performer(s)/Soloist(s)/Performing group", 1018 "TP2": "Band/Orchestra/Accompaniment", 1019 "TP3": "Conductor/Performer refinement", 1020 "TP4": "Interpreted, remixed, or otherwise modified by", 1021 "TPA": "Part of a set", 1022 "TPB": "Publisher", 1023 "TRC": "ISRC (International Standard Recording Code)", 1024 "TRD": "Recording dates", 1025 "TRK": "Track number/Position in set", 1026 "TSI": "Size",
1027 "TSS": "Software/hardware and settings used for encoding", 1028 "TT1": "Content group description", 1029 "TT2": "Title/Songname/Content description", 1030 "TT3": "Subtitle/Description refinement", 1031 "TXT": "Lyricist/text writer", 1032 "TXX": "User defined text information frame", 1033 "TYE": "Year", 1034 "UFI": "Unique file identifier", 1035 "ULT": "Unsychronized lyric/text transcription", 1036 "WAF": "Official audio file webpage", 1037 "WAR": "Official artist/performer webpage", 1038 "WAS": "Official audio source webpage", 1039 "WCM": "Commercial information", 1040 "WCP": "Copyright/Legal information", 1041 "WPB": "Publishers official webpage", 1042 "WXX": "User defined URL link frame", 1043 // v2.3 1044 "AENC": "Audio encryption", 1045 "APIC": "Attached picture", 1046 "ASPI": "Audio seek point index", 1047 "CHAP": "Chapter", 1048 "CTOC": "Table of contents", 1049 "COMM": "Comments", 1050 "COMR": "Commercial frame", 1051 "ENCR": "Encryption method registration", 1052 "EQU2": "Equalisation (2)", 1053 "EQUA": "Equalization", 1054 "ETCO": "Event timing codes", 1055 "GEOB": "General encapsulated object", 1056 "GRID": "Group identification registration", 1057 "IPLS": "Involved people list", 1058 "LINK": "Linked information", 1059 "MCDI": "Music CD identifier", 1060 "MLLT": "MPEG location lookup table", 1061 "OWNE": "Ownership frame", 1062 "PRIV": "Private frame", 1063 "PCNT": "Play counter", 1064 "POPM": "Popularimeter", 1065 "POSS": "Position synchronisation frame", 1066 "RBUF": "Recommended buffer size", 1067 "RVA2": "Relative volume adjustment (2)", 1068 "RVAD": "Relative volume adjustment", 1069 "RVRB": "Reverb", 1070 "SEEK": "Seek frame", 1071 "SYLT": "Synchronized lyric/text", 1072 "SYTC": "Synchronized tempo codes", 1073 "TALB": "Album/Movie/Show title", 1074 "TBPM": "BPM (beats per minute)", 1075 "TCOM": "Composer", 1076 "TCON": "Content type", 1077 "TCOP": "Copyright message", 1078 "TDAT": "Date", 1079 "TDLY": "Playlist delay", 1080 "TDRC": "Recording time", 1081 "TDRL": "Release time", 1082 "TDTG": "Tagging time", 1083 "TENC": "Encoded by", 1084 "TEXT": "Lyricist/Text writer", 1085 "TFLT": "File type", 1086 "TIME": "Time", 1087 "TIPL": "Involved people list", 1088 "TIT1": "Content group description", 1089 "TIT2": "Title/songname/content description", 1090 "TIT3": "Subtitle/Description refinement", 1091 "TKEY": "Initial key", 1092 "TLAN": "Language(s)", 1093 "TLEN": "Length", 1094 "TMCL": "Musician credits list", 1095 "TMED": "Media type", 1096 "TMOO": "Mood", 1097 "TOAL": "Original album/movie/show title", 1098 "TOFN": "Original filename", 1099 "TOLY": "Original lyricist(s)/text writer(s)", 1100 "TOPE": "Original artist(s)/performer(s)", 1101 "TORY": "Original release year", 1102 "TOWN": "File owner/licensee", 1103 "TPE1": "Lead performer(s)/Soloist(s)", 1104 "TPE2": "Band/orchestra/accompaniment", 1105 "TPE3": "Conductor/performer refinement", 1106 "TPE4": "Interpreted, remixed, or otherwise modified by", 1107 "TPOS": "Part of a set", 1108 "TPRO": "Produced notice", 1109 "TPUB": "Publisher", 1110 "TRCK": "Track number/Position in set", 1111 "TRDA": "Recording dates", 1112 "TRSN": "Internet radio station name", 1113 "TRSO": "Internet radio station owner", 1114 "TSOA": "Album sort order", 1115 "TSOP": "Performer sort order", 1116 "TSOT": "Title sort order", 1117 "TSIZ": "Size", 1118 "TSRC": "ISRC (international standard recording code)", 1119 "TSSE": "Software/Hardware and settings used for encoding", 1120 "TSST": "Set subtitle", 1121 "TYER": "Year", 1122 "TXXX": "User defined text information frame", 1123 "UFID": "Unique file identifier", 1124 "USER": "Terms of use", 1125 "USLT": "Unsychronized lyric/text transcription", 1126 "WCOM": "Commercial information", 1127 "WCOP": "Copyright/Legal information", 1128 "WOAF": "Official audio file webpage", 1129 "WOAR": "Official artist/performer webpage", 1130 "WOAS": "Official audio source webpage", 1131 "WORS": "Official internet radio station homepage", 1132 "WPAY": "Payment", 1133 "WPUB": "Publishers official webpage", 1134 "WXXX": "User defined URL link frame" 1135}; 1136 1137var ID3v2FrameReader = /*#__PURE__*/function () { 1138 function ID3v2FrameReader() { 1139 _classCallCheck(this, ID3v2FrameReader); 1140 } 1141 1142 _createClass(ID3v2FrameReader, null, [{ 1143 key: "getFrameReaderFunction", 1144 value: function getFrameReaderFunction(frameId) { 1145 if (frameId in frameReaderFunctions) { 1146 return frameReaderFunctions[frameId]; 1147 } else if (frameId[0] === "T") { 1148 // All frame ids starting with T are text tags. 1149 return frameReaderFunctions["T*"]; 1150 } else if (frameId[0] === "W") { 1151 // All frame ids starting with W are url tags. 1152 return frameReaderFunctions["W*"]; 1153 } else { 1154 return null; 1155 } 1156 } 1157 /** 1158 * All the frames consists of a frame header followed by one or more fields 1159 * containing the actual information. 1160 * The frame ID made out of the characters capital A-Z and 0-9. Identifiers 1161 * beginning with "X", "Y" and "Z" are for experimental use and free for 1162 * everyone to use, without the need to set the experimental bit in the tag 1163 * header. Have in mind that someone else might have used the same identifier 1164 * as you. All other identifiers are either used or reserved for future use. 1165 * The frame ID is followed by a size descriptor, making a total header size 1166 * of ten bytes in every frame. The size is calculated as frame size excluding 1167 * frame header (frame size - 10). 1168 */ 1169 1170 }, { 1171 key: "readFrames", 1172 value: function readFrames(offset, end, data, id3header, tags) { 1173 var frames = {}; 1174 1175 var frameHeaderSize = this._getFrameHeaderSize(id3header); // console.log('header', id3header); 1176 1177 1178 while ( // we should be able to read at least the frame header 1179 offset < end - frameHeaderSize) { 1180 var header = this._readFrameHeader(data, offset, id3header); 1181 1182 var frameId = header.id; // No frame ID sometimes means it's the last frame (GTFO). 1183 1184 if (!frameId) { 1185 break; 1186 } 1187 1188 var flags = header.flags; 1189 var frameSize = header.size; 1190 var frameDataOffset = offset + header.headerSize; 1191 var frameData = data; // console.log(offset, frameId, header.size + header.headerSize, flags && flags.format.unsynchronisation); 1192 // advance data offset to the next frame data 1193 1194 offset += header.headerSize + header.size; // skip unwanted tags 1195 1196 if (tags && tags.indexOf(frameId) === -1) { 1197 continue; 1198 } // Workaround: MP3ext V3.3.17 places a non-compliant padding string at
1199 // the end of the ID3v2 header. A string like "MP3ext V3.3.19(ansi)" 1200 // is added multiple times at the end of the ID3 tag. More information 1201 // about this issue can be found at 1202 // https://github.com/aadsm/jsmediatags/issues/58#issuecomment-313865336 1203 1204 1205 if (frameId === 'MP3e' || frameId === '\x00MP3' || frameId === '\x00\x00MP' || frameId === ' MP3') { 1206 break; 1207 } 1208 1209 var unsyncData; 1210 1211 if (flags && flags.format.unsynchronisation && !id3header.flags.unsynchronisation) { 1212 frameData = this.getUnsyncFileReader(frameData, frameDataOffset, frameSize); 1213 frameDataOffset = 0; 1214 frameSize = frameData.getSize(); 1215 } // the first 4 bytes are the real data size 1216 // (after unsynchronisation && encryption) 1217 1218 1219 if (flags && flags.format.data_length_indicator) { 1220 // var frameDataSize = frameData.getSynchsafeInteger32At(frameDataOffset); 1221 frameDataOffset += 4; 1222 frameSize -= 4; 1223 } 1224 1225 var readFrameFunc = ID3v2FrameReader.getFrameReaderFunction(frameId); 1226 var parsedData = readFrameFunc ? readFrameFunc.apply(this, [frameDataOffset, frameSize, frameData, flags, id3header]) : null; 1227 1228 var desc = this._getFrameDescription(frameId); 1229 1230 var frame = { 1231 id: frameId, 1232 size: frameSize, 1233 description: desc, 1234 data: parsedData 1235 }; 1236 1237 if (frameId in frames) { 1238 if (frames[frameId].id) { 1239 frames[frameId] = [frames[frameId]]; 1240 } 1241 1242 frames[frameId].push(frame); 1243 } else { 1244 frames[frameId] = frame; 1245 } 1246 } 1247 1248 return frames; 1249 } 1250 }, { 1251 key: "_getFrameHeaderSize", 1252 value: function _getFrameHeaderSize(id3header) { 1253 var major = id3header.major; 1254 1255 if (major == 2) { 1256 return 6; 1257 } else if (major == 3 || major == 4) { 1258 return 10; 1259 } else { 1260 return 0; 1261 } 1262 } 1263 }, { 1264 key: "_readFrameHeader", 1265 value: function _readFrameHeader(data, offset, id3header) { 1266 var major = id3header.major; 1267 var flags = null; 1268 1269 var frameHeaderSize = this._getFrameHeaderSize(id3header); 1270 1271 switch (major) { 1272 case 2: 1273 var frameId = data.getStringAt(offset, 3); 1274 var frameSize = data.getInteger24At(offset + 3, true); 1275 break; 1276 1277 case 3: 1278 var frameId = data.getStringAt(offset, 4); 1279 var frameSize = data.getLongAt(offset + 4, true); 1280 break; 1281 1282 case 4: 1283 var frameId = data.getStringAt(offset, 4); 1284 var frameSize = data.getSynchsafeInteger32At(offset + 4); 1285 break; 1286 } 1287 1288 if (frameId == String.fromCharCode(0, 0, 0) || frameId == String.fromCharCode(0, 0, 0, 0)) { 1289 frameId = ""; 1290 } // if frameId is empty then it's the last frame 1291 1292 1293 if (frameId) { 1294 // read frame message and format flags 1295 if (major > 2) { 1296 flags = this._readFrameFlags(data, offset + 8); 1297 } 1298 } 1299 1300 return { 1301 "id": frameId || "", 1302 "size": frameSize || 0, 1303 "headerSize": frameHeaderSize || 0, 1304 "flags": flags 1305 }; 1306 } 1307 }, { 1308 key: "_readFrameFlags", 1309 value: function _readFrameFlags(data, offset) { 1310 return { 1311 message: { 1312 tag_alter_preservation: data.isBitSetAt(offset, 6), 1313 file_alter_preservation: data.isBitSetAt(offset, 5), 1314 read_only: data.isBitSetAt(offset, 4) 1315 }, 1316 format: { 1317 grouping_identity: data.isBitSetAt(offset + 1, 7), 1318 compression: data.isBitSetAt(offset + 1, 3), 1319 encryption: data.isBitSetAt(offset + 1, 2), 1320 unsynchronisation: data.isBitSetAt(offset + 1, 1), 1321 data_length_indicator: data.isBitSetAt(offset + 1, 0) 1322 } 1323 }; 1324 } 1325 }, { 1326 key: "_getFrameDescription", 1327 value: function _getFrameDescription(frameId) { 1328 if (frameId in FRAME_DESCRIPTIONS) { 1329 return FRAME_DESCRIPTIONS[frameId]; 1330 } else { 1331 return 'Unknown'; 1332 } 1333 } 1334 }, { 1335 key: "getUnsyncFileReader", 1336 value: function getUnsyncFileReader(data, offset, size) { 1337 var frameData = data.getBytesAt(offset, size); 1338 1339 for (var i = 0; i < frameData.length - 1; i++) { 1340 if (frameData[i] === 0xff && frameData[i + 1] === 0x00) { 1341 frameData.splice(i + 1, 1); 1342 } 1343 } 1344 1345 return new ArrayFileReader(frameData); 1346 } 1347 }]); 1348 1349 return ID3v2FrameReader; 1350}(); 1351 1352; 1353var frameReaderFunctions = {}; 1354 1355frameReaderFunctions['APIC'] = function readPictureFrame(offset, length, data, flags, id3header) { 1356 var start = offset; 1357 var charset = getTextEncoding(data.getByteAt(offset)); 1358 1359 switch (id3header && id3header.major) { 1360 case 2: 1361 var format = data.getStringAt(offset + 1, 3); 1362 offset += 4; 1363 break; 1364 1365 case 3: 1366 case 4: 1367 var format = data.getStringWithCharsetAt(offset + 1, length - 1); 1368 offset += 1 + format.bytesReadCount; 1369 break; 1370 1371 default: 1372 throw new Error("Couldn't read ID3v2 major version."); 1373 } 1374 1375 var bite = data.getByteAt(offset); 1376 var type = PICTURE_TYPE[bite]; 1377 var desc = data.getStringWithCharsetAt(offset + 1, length - (offset - start) - 1, charset); 1378 offset += 1 + desc.bytesReadCount; 1379 return { 1380 "format": format.toString(), 1381 "type": type, 1382 "description": desc.toString(), 1383 "data": data.getBytesAt(offset, start + length - offset) 1384 }; 1385};
1385 // ID3v2 chapters according to http://id3.org/id3v2-chapters-1.0 1386 1387 1388frameReaderFunctions['CHAP'] = function readChapterFrame(offset, length, data, flags, id3header) { 1389 var originalOffset = offset; 1390 var result = {}; 1391 var id = StringUtils.readNullTerminatedString(data.getBytesAt(offset, length)); 1392 result.id = id.toString(); 1393 offset += id.bytesReadCount; 1394 result.startTime = data.getLongAt(offset, true); 1395 offset += 4; 1396 result.endTime = data.getLongAt(offset, true); 1397 offset += 4; 1398 result.startOffset = data.getLongAt(offset, true); 1399 offset += 4; 1400 result.endOffset = data.getLongAt(offset, true); 1401 offset += 4; 1402 var remainingLength = length - (offset - originalOffset); 1403 result.subFrames = this.readFrames(offset, offset + remainingLength, data, id3header); 1404 return result; 1405}; // ID3v2 table of contents according to http://id3.org/id3v2-chapters-1.0 1406 1407 1408frameReaderFunctions['CTOC'] = function readTableOfContentsFrame(offset, length, data, flags, id3header) { 1409 var originalOffset = offset; 1410 var result = { 1411 childElementIds: [], 1412 id: undefined, 1413 topLevel: undefined, 1414 ordered: undefined, 1415 entryCount: undefined, 1416 subFrames: undefined 1417 }; 1418 var id = StringUtils.readNullTerminatedString(data.getBytesAt(offset, length)); 1419 result.id = id.toString(); 1420 offset += id.bytesReadCount; 1421 result.topLevel = data.isBitSetAt(offset, 1); 1422 result.ordered = data.isBitSetAt(offset, 0); 1423 offset++; 1424 result.entryCount = data.getByteAt(offset); 1425 offset++; 1426 1427 for (var i = 0; i < result.entryCount; i++) { 1428 var childId = StringUtils.readNullTerminatedString(data.getBytesAt(offset, length - (offset - originalOffset))); 1429 result.childElementIds.push(childId.toString()); 1430 offset += childId.bytesReadCount; 1431 } 1432 1433 var remainingLength = length - (offset - originalOffset); 1434 result.subFrames = this.readFrames(offset, offset + remainingLength, data, id3header); 1435 return result; 1436}; 1437 1438frameReaderFunctions['COMM'] = function readCommentsFrame(offset, length, data, flags, id3header) { 1439 var start = offset; 1440 var charset = getTextEncoding(data.getByteAt(offset)); 1441 var language = data.getStringAt(offset + 1, 3); 1442 var shortdesc = data.getStringWithCharsetAt(offset + 4, length - 4, charset); 1443 offset += 4 + shortdesc.bytesReadCount; 1444 var text = data.getStringWithCharsetAt(offset, start + length - offset, charset); 1445 return { 1446 language: language, 1447 short_description: shortdesc.toString(), 1448 text: text.toString() 1449 }; 1450}; 1451 1452frameReaderFunctions['COM'] = frameReaderFunctions['COMM']; 1453 1454frameReaderFunctions['PIC'] = function (offset, length, data, flags, id3header) { 1455 return frameReaderFunctions['APIC'](offset, length, data, flags, id3header); 1456}; 1457 1458frameReaderFunctions['PCNT'] = function readCounterFrame(offset, length, data, flags, id3header) { 1459 // FIXME: implement the rest of the spec 1460 return data.getLongAt(offset, false); 1461}; 1462 1463frameReaderFunctions['CNT'] = frameReaderFunctions['PCNT']; 1464 1465frameReaderFunctions['T*'] = function readTextFrame(offset, length, data, flags, id3header) { 1466 var charset = getTextEncoding(data.getByteAt(offset)); 1467 return data.getStringWithCharsetAt(offset + 1, length - 1, charset).toString(); 1468}; 1469 1470frameReaderFunctions['TXXX'] = function readTextFrame(offset, length, data, flags, id3header) { 1471 var charset = getTextEncoding(data.getByteAt(offset)); 1472 return getUserDefinedFields(offset, length, data, charset); 1473}; 1474 1475frameReaderFunctions['WXXX'] = function readUrlFrame(offset, length, data, flags, id3header) { 1476 if (length === 0) { 1477 return null; 1478 } 1479 1480 var charset = getTextEncoding(data.getByteAt(offset)); 1481 return getUserDefinedFields(offset, length, data, charset); 1482}; 1483 1484frameReaderFunctions['W*'] = function readUrlFrame(offset, length, data, flags, id3header) { 1485 if (length === 0) { 1486 return null; 1487 } 1488 1489 return data.getStringWithCharsetAt(offset, length, 'iso-8859-1').toString(); 1490}; 1491 1492frameReaderFunctions['TCON'] = function readGenreFrame(offset, length, data, flags) { 1493 var text = frameReaderFunctions['T*'].apply(this, arguments); 1494 return text.replace(/^\(\d+\)/, ''); 1495}; 1496 1497frameReaderFunctions['TCO'] = frameReaderFunctions['TCON']; 1498 1499frameReaderFunctions['USLT'] = function readLyricsFrame(offset, length, data, flags, id3header) { 1500 var start = offset; 1501 var charset = getTextEncoding(data.getByteAt(offset)); 1502 var language = data.getStringAt(offset + 1, 3); 1503 var descriptor = data.getStringWithCharsetAt(offset + 4, length - 4, charset); 1504 offset += 4 + descriptor.bytesReadCount; 1505 var lyrics = data.getStringWithCharsetAt(offset, start + length - offset, charset); 1506 return { 1507 language: language, 1508 descriptor: descriptor.toString(), 1509 lyrics: lyrics.toString() 1510 }; 1511}; 1512 1513frameReaderFunctions['ULT'] = frameReaderFunctions['USLT']; 1514 1515frameReaderFunctions['UFID'] = function readLyricsFrame(offset, length, data, flags, id3header) { 1516 var ownerIdentifier = StringUtils.readNullTerminatedString(data.getBytesAt(offset, length)); 1517 offset += ownerIdentifier.bytesReadCount; 1518 var identifier = data.getBytesAt(offset, length - ownerIdentifier.bytesReadCount); 1519 return { 1520 ownerIdentifier: ownerIdentifier.toString(), 1521 identifier: identifier 1522 }; 1523}; 1524 1525function getTextEncoding(bite) { 1526 var charset; 1527 1528 switch (bite) { 1529 case 0x00: 1530 charset = 'iso-8859-1'; 1531 break; 1532 1533 case 0x01: 1534 charset = 'utf-16'; 1535 break; 1536 1537 case 0x02: 1538 charset = 'utf-16be'; 1539 break; 1540 1541 case 0x03: 1542 charset = 'utf-8'; 1543 break; 1544 1545 default: 1546 charset = 'iso-8859-1'; 1547 } 1548 1549 return charset; 1550}
1550 // Handles reading description/data from either http://id3.org/id3v2.3.0#User_defined_text_information_frame 1551// and http://id3.org/id3v2.3.0#User_defined_URL_link_frame 1552 1553 1554function getUserDefinedFields(offset, length, data, charset) { 1555 var userDesc = data.getStringWithCharsetAt(offset + 1, length - 1, charset); 1556 var userDefinedData = data.getStringWithCharsetAt(offset + 1 + userDesc.bytesReadCount, length - 1 - userDesc.bytesReadCount, charset); 1557 return { 1558 user_description: userDesc.toString(), 1559 data: userDefinedData.toString() 1560 }; 1561} 1562 1563var PICTURE_TYPE = ["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"]; 1564module.exports = ID3v2FrameReader; 1565 1566},{"./ArrayFileReader":3,"./MediaFileReader":11,"./StringUtils":13}],9:[function(require,module,exports){ 1567'use strict'; 1568 1569function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } 1570 1571function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 1572 1573function _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); } } 1574 1575function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } 1576 1577function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); } 1578 1579function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } 1580 1581function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } 1582 1583function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); } 1584 1585function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } 1586 1587function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } 1588 1589function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o
1589); }; return _getPrototypeOf(o); } 1590 1591var MediaTagReader = require('./MediaTagReader'); 1592 1593var MediaFileReader = require('./MediaFileReader'); 1594 1595var ID3v2FrameReader = require('./ID3v2FrameReader'); 1596 1597var ID3_HEADER_SIZE = 10; 1598 1599var ID3v2TagReader = /*#__PURE__*/function (_MediaTagReader) { 1600 _inherits(ID3v2TagReader, _MediaTagReader); 1601 1602 var _super = _createSuper(ID3v2TagReader); 1603 1604 function ID3v2TagReader() { 1605 _classCallCheck(this, ID3v2TagReader); 1606 1607 return _super.apply(this, arguments); 1608 } 1609 1610 _createClass(ID3v2TagReader, [{ 1611 key: "_loadData", 1612 value: function _loadData(mediaFileReader, callbacks) { 1613 mediaFileReader.loadRange([6, 9], { 1614 onSuccess: function onSuccess() { 1615 mediaFileReader.loadRange( // The tag size does not include the header size. 1616 [0, ID3_HEADER_SIZE + mediaFileReader.getSynchsafeInteger32At(6) - 1], callbacks); 1617 }, 1618 onError: callbacks.onError 1619 }); 1620 } 1621 }, { 1622 key: "_parseData", 1623 value: function _parseData(data, tags) { 1624 var offset = 0; 1625 var major = data.getByteAt(offset + 3); 1626 1627 if (major > 4) { 1628 return { 1629 "type": "ID3", 1630 "version": ">2.4", 1631 "tags": {} 1632 }; 1633 } 1634 1635 var revision = data.getByteAt(offset + 4); 1636 var unsynch = data.isBitSetAt(offset + 5, 7); 1637 var xheader = data.isBitSetAt(offset + 5, 6); 1638 var xindicator = data.isBitSetAt(offset + 5, 5); 1639 var size = data.getSynchsafeInteger32At(offset + 6); 1640 offset += 10; 1641 1642 if (xheader) { 1643 // We skip the extended header and don't offer support for it right now. 1644 if (major === 4) { 1645 var xheadersize = data.getSynchsafeInteger32At(offset); 1646 offset += xheadersize; 1647 } else { 1648 var xheadersize = data.getLongAt(offset, true); // The 'Extended header size', currently 6 or 10 bytes, excludes itself. 1649 1650 offset += xheadersize + 4; 1651 } 1652 } 1653 1654 var id3 = { 1655 "type": "ID3", 1656 "version": '2.' + major + '.' + revision, 1657 "major": major, 1658 "revision": revision, 1659 "flags": { 1660 "unsynchronisation": unsynch, 1661 "extended_header": xheader, 1662 "experimental_indicator": xindicator, 1663 // TODO: footer_present 1664 "footer_present": false 1665 }, 1666 "size": size, 1667 "tags": {} 1668 }; 1669 1670 if (tags) { 1671 var expandedTags = this._expandShortcutTags(tags); 1672 } 1673 1674 var offsetEnd = size + 10 1675 /*header size*/ 1676 ; // When this flag is set the entire tag needs to be un-unsynchronised 1677 // before parsing each individual frame. Individual frame sizes might not 1678 // take unsynchronisation into consideration when it's set on the tag 1679 // header. 1680 1681 if (id3.flags.unsynchronisation) { 1682 data = ID3v2FrameReader.getUnsyncFileReader(data, offset, size); 1683 offset = 0; 1684 offsetEnd = data.getSize(); 1685 } 1686 1687 var frames = ID3v2FrameReader.readFrames(offset, offsetEnd, data, id3, expandedTags); // create shortcuts for most common data. 1688 1689 for (var name in SHORTCUTS) { 1690 if (SHORTCUTS.hasOwnProperty(name)) { 1691 var frameData = this._getFrameData(frames, SHORTCUTS[name]); 1692 1693 if (frameData) { 1694 id3.tags[name] = frameData; 1695 } 1696 } 1697 } 1698 1699 for (var frame in frames) { 1700 if (frames.hasOwnProperty(frame)) { 1701 id3.tags[frame] = frames[frame]; 1702 } 1703 } 1704 1705 return id3; 1706 } 1707 }, { 1708 key: "_getFrameData", 1709 value: function _getFrameData(frames, ids) { 1710 var frame; 1711 1712 for (var i = 0, id; id = ids[i]; i++) { 1713 if (id in frames) { 1714 if (frames[id] instanceof Array) { 1715 frame = frames[id][0]; 1716 } else { 1717 frame = frames[id]; 1718 } 1719 1720 return frame.data; 1721 } 1722 } 1723 } 1724 }, { 1725 key: "getShortcuts", 1726 value: function getShortcuts() { 1727 return SHORTCUTS; 1728 } 1729 }], [{ 1730 key: "getTagIdentifierByteRange", 1731 value: function getTagIdentifierByteRange() { 1732 // ID3 header 1733 return { 1734 offset: 0, 1735 length: ID3_HEADER_SIZE 1736 }; 1737 } 1738 }, { 1739 key: "canReadTagFormat", 1740 value: function canReadTagFormat(tagIdentifier) { 1741 var id = String.fromCharCode.apply(String, tagIdentifier.slice(0, 3)); 1742 return id === 'ID3'; 1743 } 1744 }]); 1745 1746 return ID3v2TagReader; 1747}(MediaTagReader); 1748 1749var SHORTCUTS = { 1750 "title": ["TIT2", "TT2"], 1751 "artist": ["TPE1", "TP1"], 1752 "album": ["TALB", "TAL"], 1753 "year": ["TYER", "TYE"], 1754 "comment": ["COMM", "COM"], 1755 "track": ["TRCK", "TRK"], 1756 "genre": ["TCON", "TCO"], 1757 "picture": ["APIC", "PIC"], 1758 "lyrics": ["USLT", "ULT"] 1759}; 1760module.exports = ID3v2TagReader; 1761 1762},{"./ID3v2FrameReader":8,"./MediaFileReader":11,"./MediaTagReader":12}],10:[function(require,module,exports){ 1763/** 1764 * Support for iTunes-style m4a tags 1765 * See: 1766 * http://atomicparsley.sourceforge.net/mpeg-4files.html 1767 * http://developer.apple.com/mac/library/documentation/QuickTime/QTFF/Metadata/Metadata.html 1768 * Authored by Joshua Kifer <joshua.kifer gmail.com> 1769 * 1770 */ 1771'use strict'; 1772 1773function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } 1774 1775function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 1776 1777function _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); } } 1778 1779function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } 1780 1781function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { c
1781onstructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); } 1782 1783function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } 1784 1785function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } 1786 1787function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); } 1788 1789function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } 1790 1791function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } 1792 1793function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } 1794 1795var MediaTagReader = require('./MediaTagReader'); 1796 1797var MediaFileReader = require('./MediaFileReader'); 1798 1799var MP4TagReader = /*#__PURE__*/function (_MediaTagReader) { 1800 _inherits(MP4TagReader, _MediaTagReader); 1801 1802 var _super = _createSuper(MP4TagReader); 1803 1804 function MP4TagReader() { 1805 _classCallCheck(this, MP4TagReader); 1806 1807 return _super.apply(this, arguments); 1808 } 1809 1810 _createClass(MP4TagReader, [{ 1811 key: "_loadData", 1812 value: function _loadData(mediaFileReader, callbacks) { 1813 // MP4 metadata isn't located in a specific location of the file. Roughly 1814 // speaking, it's composed of blocks chained together like a linked list. 1815 // These blocks are called atoms (or boxes). 1816 // Each atom of the list can have its own child linked list. Atoms in this 1817 // situation do not possess any data and are called "container" as they only 1818 // contain other atoms. 1819 // Other atoms represent a particular set of data, like audio, video or 1820 // metadata. In order to find and load all the interesting atoms we need 1821 // to traverse the entire linked list of atoms and only load the ones 1822 // associated with metadata. 1823 // The metadata atoms can be find under the "moov.udta.meta.ilst" hierarchy. 1824 var self = this; // Load the header of the first atom 1825 1826 mediaFileReader.loadRange([0, 16], { 1827 onSuccess: function onSuccess() { 1828 self._loadAtom(mediaFileReader, 0, "", callbacks); 1829 }, 1830 onError: callbacks.onError 1831 }); 1832 } 1833 }, { 1834 key: "_loadAtom", 1835 value: function _loadAtom(mediaFileReader, offset, parentAtomFullName, callbacks) { 1836 if (offset >= mediaFileReader.getSize()) { 1837 callbacks.onSuccess(); 1838 return; 1839 } 1840 1841 var self = this; // 8 is the size of the atomSize and atomName fields. 1842 // When reading the current block we always read 8 more bytes in order 1843 // to also read the header of the next block. 1844 1845 var atomSize = mediaFileReader.getLongAt(offset, true); 1846 1847 if (atomSize == 0 || isNaN(atomSize)) { 1848 callbacks.onSuccess(); 1849 return; 1850 } 1851 1852 var atomName = mediaFileReader.getStringAt(offset + 4, 4); // console.log(parentAtomFullName, atomName, atomSize); 1853 // Container atoms (no actual data) 1854 1855 if (this._isContainerAtom(atomName)) { 1856 if (atomName == "meta") { 1857 // The "meta" atom breaks convention and is a container with data. 1858 offset += 4; // next_item_id (uint32) 1859 } 1860 1861 var atomFullName = (parentAtomFullName ? parentAtomFullName + "." : "") + atomName; 1862 1863 if (atomFullName === "moov.udta.meta.ilst") { 1864 mediaFileReader.loadRange([offset, offset + atomSize], callbacks); 1865 } else { 1866 mediaFileReader.loadRange([offset + 8, offset + 8 + 8], {
1867 onSuccess: function onSuccess() { 1868 self._loadAtom(mediaFileReader, offset + 8, atomFullName, callbacks); 1869 }, 1870 onError: callbacks.onError 1871 }); 1872 } 1873 } else { 1874 mediaFileReader.loadRange([offset + atomSize, offset + atomSize + 8], { 1875 onSuccess: function onSuccess() { 1876 self._loadAtom(mediaFileReader, offset + atomSize, parentAtomFullName, callbacks); 1877 }, 1878 onError: callbacks.onError 1879 }); 1880 } 1881 } 1882 }, { 1883 key: "_isContainerAtom", 1884 value: function _isContainerAtom(atomName) { 1885 return ["moov", "udta", "meta", "ilst"].indexOf(atomName) >= 0; 1886 } 1887 }, { 1888 key: "_canReadAtom", 1889 value: function _canReadAtom(atomName) { 1890 return atomName !== "----"; 1891 } 1892 }, { 1893 key: "_parseData", 1894 value: function _parseData(data, tagsToRead) { 1895 var tags = {}; 1896 tagsToRead = this._expandShortcutTags(tagsToRead); 1897 1898 this._readAtom(tags, data, 0, data.getSize(), tagsToRead); // create shortcuts for most common data. 1899 1900 1901 for (var name in SHORTCUTS) { 1902 if (SHORTCUTS.hasOwnProperty(name)) { 1903 var tag = tags[SHORTCUTS[name]]; 1904 1905 if (tag) { 1906 if (name === "track") { 1907 tags[name] = tag.data.track; 1908 } else { 1909 tags[name] = tag.data; 1910 } 1911 } 1912 } 1913 } 1914 1915 return { 1916 "type": "MP4", 1917 "ftyp": data.getStringAt(8, 4), 1918 "version": data.getLongAt(12, true), 1919 "tags": tags 1920 }; 1921 } 1922 }, { 1923 key: "_readAtom", 1924 value: function _readAtom(tags, data, offset, length, tagsToRead, parentAtomFullName, indent) { 1925 indent = indent === undefined ? "" : indent + " "; 1926 var seek = offset; 1927 1928 while (seek < offset + length) { 1929 var atomSize = data.getLongAt(seek, true); 1930 1931 if (atomSize == 0) { 1932 return; 1933 } 1934 1935 var atomName = data.getStringAt(seek + 4, 4); // console.log(seek, parentAtomFullName, atomName, atomSize); 1936 1937 if (this._isContainerAtom(atomName)) { 1938 if (atomName == "meta") { 1939 seek += 4; // next_item_id (uint32) 1940 } 1941 1942 var atomFullName = (parentAtomFullName ? parentAtomFullName + "." : "") + atomName; 1943 1944 this._readAtom(tags, data, seek + 8, atomSize - 8, tagsToRead, atomFullName, indent); 1945 1946 return; 1947 } // Value atoms 1948 1949 1950 if ((!tagsToRead || tagsToRead.indexOf(atomName) >= 0) && parentAtomFullName === "moov.udta.meta.ilst" && this._canReadAtom(atomName)) { 1951 tags[atomName] = this._readMetadataAtom(data, seek); 1952 } 1953 1954 seek += atomSize; 1955 } 1956 } 1957 }, { 1958 key: "_readMetadataAtom", 1959 value: function _readMetadataAtom(data, offset) { 1960 // 16: size + name + size + "data" (4 bytes each) 1961 // 8: 1 byte atom version & 3 bytes atom flags + 4 bytes NULL space 1962 // 8: 4 bytes track + 4 bytes total 1963 var METADATA_HEADER = 16; 1964 var atomSize = data.getLongAt(offset, true); 1965 var atomName = data.getStringAt(offset + 4, 4); 1966 var klass = data.getInteger24At(offset + METADATA_HEADER + 1, true); 1967 var type = TYPES[klass]; 1968 var atomData; 1969 var bigEndian = true; 1970 1971 if (atomName == "trkn") { 1972 atomData = { 1973 "track": data.getShortAt(offset + METADATA_HEADER + 10, bigEndian), 1974 "total": data.getShortAt(offset + METADATA_HEADER + 14, bigEndian) 1975 }; 1976 } else if (atomName == "disk") { 1977 atomData = { 1978 "disk": data.getShortAt(offset + METADATA_HEADER + 10, bigEndian), 1979 "total": data.getShortAt(offset + METADATA_HEADER + 14, bigEndian) 1980 }; 1981 } else { 1982 // 4: atom version (1 byte) + atom flags (3 bytes) 1983 // 4: NULL (usually locale indicator) 1984 var atomHeader = METADATA_HEADER + 4 + 4; 1985 var dataStart = offset + atomHeader; 1986 var dataLength = atomSize - atomHeader; 1987 var atomData; // Workaround for covers being parsed as 'uint8' type despite being an 'covr' atom 1988 1989 if (atomName === 'covr' && type === 'uint8') { 1990 type = 'jpeg'; 1991 } 1992 1993 switch (type) { 1994 case "text": 1995 atomData = data.getStringWithCharsetAt(dataStart, dataLength, "utf-8").toString(); 1996 break; 1997 1998 case "uint8": 1999 atomData = data.getShortAt(dataStart, false); 2000 break; 2001 2002 case "int": 2003 case "uint": 2004 // Though the QuickTime spec doesn't state it, there are 64-bit values 2005 // such as plID (Playlist/Collection ID). With its single 64-bit floating 2006 // point number type, these are hard to parse and pass in JavaScript. 2007 // The high word of plID seems to always be zero, so, as this is the 2008 // only current 64-bit atom handled, it is parsed from its 32-bit 2009 // low word as an unsigned long. 2010 // 2011 var intReader = type == 'int' ? dataLength == 1 ? data.getSByteAt : dataLength == 2 ? data.getSShortAt : dataLength == 4 ? data.getSLongAt : data.getL
2011ongAt : dataLength == 1 ? data.getByteAt : dataLength == 2 ? data.getShortAt : data.getLongAt; // $FlowFixMe - getByteAt doesn't receive a second argument 2012 2013 atomData = intReader.call(data, dataStart + (dataLength == 8 ? 4 : 0), true); 2014 break; 2015 2016 case "jpeg": 2017 case "png": 2018 atomData = { 2019 "format": "image/" + type, 2020 "data": data.getBytesAt(dataStart, dataLength) 2021 }; 2022 break; 2023 } 2024 } 2025 2026 return { 2027 id: atomName, 2028 size: atomSize, 2029 description: ATOM_DESCRIPTIONS[atomName] || "Unknown", 2030 data: atomData 2031 }; 2032 } 2033 }, { 2034 key: "getShortcuts", 2035 value: function getShortcuts() { 2036 return SHORTCUTS; 2037 } 2038 }], [{ 2039 key: "getTagIdentifierByteRange", 2040 value: function getTagIdentifierByteRange() { 2041 // The tag identifier is located in [4, 8] but since we'll need to reader 2042 // the header of the first block anyway, we load it instead to avoid 2043 // making two requests. 2044 return { 2045 offset: 0, 2046 length: 16 2047 }; 2048 } 2049 }, { 2050 key: "canReadTagFormat", 2051 value: function canReadTagFormat(tagIdentifier) { 2052 var id = String.fromCharCode.apply(String, tagIdentifier.slice(4, 8)); 2053 return id === "ftyp"; 2054 } 2055 }]); 2056 2057 return MP4TagReader; 2058}(MediaTagReader); 2059/* 2060 * https://developer.apple.com/library/content/documentation/QuickTime/QTFF/Metadata/Metadata.html#//apple_ref/doc/uid/TP40000939-CH1-SW35 2061*/ 2062 2063 2064var TYPES = { 2065 "0": "uint8", 2066 "1": "text", 2067 "13": "jpeg", 2068 "14": "png", 2069 "21": "int", 2070 "22": "uint" 2071}; 2072var ATOM_DESCRIPTIONS = { 2073 "©alb": "Album", 2074 "©ART": "Artist", 2075 "aART": "Album Artist", 2076 "©day": "Release Date", 2077 "©nam": "Title", 2078 "©gen": "Genre", 2079 "gnre": "Genre", 2080 "trkn": "Track Number", 2081 "©wrt": "Composer", 2082 "©too": "Encoding Tool", 2083 "©enc": "Encoded By", 2084 "cprt": "Copyright", 2085 "covr": "Cover Art", 2086 "©grp": "Grouping", 2087 "keyw": "Keywords", 2088 "©lyr": "Lyrics", 2089 "©cmt": "Comment", 2090 "tmpo": "Tempo", 2091 "cpil": "Compilation", 2092 "disk": "Disc Number", 2093 "tvsh": "TV Show Name", 2094 "tven": "TV Episode ID", 2095 "tvsn": "TV Season", 2096 "tves": "TV Episode", 2097 "tvnn": "TV Network", 2098 "desc": "Description", 2099 "ldes": "Long Description", 2100 "sonm": "Sort Name", 2101 "soar": "Sort Artist", 2102 "soaa": "Sort Album", 2103 "soco": "Sort Composer", 2104 "sosn": "Sort Show", 2105 "purd": "Purchase Date", 2106 "pcst": "Podcast", 2107 "purl": "Podcast URL", 2108 "catg": "Category", 2109 "hdvd": "HD Video", 2110 "stik": "Media Type", 2111 "rtng": "Content Rating", 2112 "pgap": "Gapless Playback", 2113 "apID": "Purchase Account", 2114 "sfID": "Country Code", 2115 "atID": "Artist ID", 2116 "cnID": "Catalog ID", 2117 "plID": "Collection ID", 2118 "geID": "Genre ID", 2119 "xid ": "Vendor Information", 2120 "flvr": "Codec Flavor" 2121}; 2122var UNSUPPORTED_ATOMS = { 2123 "----": 1 2124}; 2125var SHORTCUTS = { 2126 "title": "©nam", 2127 "artist": "©ART", 2128 "album": "©alb", 2129 "year": "©day", 2130 "comment": "©cmt", 2131 "track": "trkn", 2132 "genre": "©gen", 2133 "picture": "covr", 2134 "lyrics": "©lyr" 2135}; 2136module.exports = MP4TagReader; 2137 2138},{"./MediaFileReader":11,"./MediaTagReader":12}],11:[function(require,module,exports){ 2139'use strict'; 2140 2141function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 2142 2143function _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); } } 2144 2145function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } 2146 2147function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } 2148 2149var StringUtils = require('./StringUtils'); 2150 2151var MediaFileReader = /*#__PURE__*/function () { 2152 function MediaFileReader(path) { 2153 _classCallCheck(this, MediaFileReader); 2154 2155 _defineProperty(this, "_isInitialized", void 0); 2156 2157 _defineProperty(this, "_size", void 0); 2158 2159 this._isInitialized = false;
2160 this._size = 0; 2161 } 2162 /** 2163 * Decides if this media file reader is able to read the given file. 2164 */ 2165 2166 2167 _createClass(MediaFileReader, [{ 2168 key: "init", 2169 value: 2170 /** 2171 * This function needs to be called before any other function. 2172 * Loads the necessary initial information from the file. 2173 */ 2174 function init(callbacks) { 2175 var self = this; 2176 2177 if (this._isInitialized) { 2178 setTimeout(callbacks.onSuccess, 1); 2179 } else { 2180 return this._init({ 2181 onSuccess: function onSuccess() { 2182 self._isInitialized = true; 2183 callbacks.onSuccess(); 2184 }, 2185 onError: callbacks.onError 2186 }); 2187 } 2188 } 2189 }, { 2190 key: "_init", 2191 value: function _init(callbacks) { 2192 throw new Error("Must implement init function"); 2193 } 2194 /** 2195 * @param range The start and end indexes of the range to load. 2196 * Ex: [0, 7] load bytes 0 to 7 inclusive. 2197 */ 2198 2199 }, { 2200 key: "loadRange", 2201 value: function loadRange(range, callbacks) {
2202 throw new Error("Must implement loadRange function"); 2203 } 2204 /** 2205 * @return The size of the file in bytes. 2206 */ 2207 2208 }, { 2209 key: "getSize", 2210 value: function getSize() { 2211 if (!this._isInitialized) { 2212 throw new Error("init() must be called first."); 2213 } 2214 2215 return this._size; 2216 } 2217 }, { 2218 key: "getByteAt", 2219 value: function getByteAt(offset) { 2220 throw new Error("Must implement getByteAt function"); 2221 } 2222 }, { 2223 key: "getBytesAt", 2224 value: function getBytesAt(offset, length) { 2225 var bytes = new Array(length); 2226 2227 for (var i = 0; i < length; i++) { 2228 bytes[i] = this.getByteAt(offset + i); 2229 } 2230 2231 return bytes; 2232 } 2233 }, { 2234 key: "isBitSetAt", 2235 value: function isBitSetAt(offset, bit) { 2236 var iByte = this.getByteAt(offset); 2237 return (iByte & 1 << bit) != 0; 2238 } 2239 }, { 2240 key: "getSByteAt", 2241 value: function getSByteAt(offset) { 2242 var iByte = this.getByteAt(offset); 2243 2244 if (iByte > 127) { 2245 return iByte - 256; 2246 } else { 2247 return iByte; 2248 } 2249 } 2250 }, { 2251 key: "getShortAt", 2252 value: function getShortAt(offset, isBigEndian) { 2253 var iShort = isBigEndian ? (this.getByteAt(offset) << 8) + this.getByteAt(offset + 1) : (this.getByteAt(offset + 1) << 8) + this.getByteAt(offset); 2254 2255 if (iShort < 0) { 2256 iShort += 65536; 2257 } 2258 2259 return iShort; 2260 } 2261 }, { 2262 key: "getSShortAt", 2263 value: function getSShortAt(offset, isBigEndian) { 2264 var iUShort = this.getShortAt(offset, isBigEndian); 2265 2266 if (iUShort > 32767) { 2267 return iUShort - 65536; 2268 } else { 2269 return iUShort; 2270 } 2271 } 2272 }, { 2273 key: "getLongAt", 2274 value: function getLongAt(offset, isBigEndian) { 2275 var iByte1 = this.getByteAt(offset), 2276 iByte2 = this.getByteAt(offset + 1), 2277 iByte3 = this.getByteAt(offset + 2), 2278 iByte4 = this.getByteAt(offset + 3); 2279 var iLong = isBigEndian ? (((iByte1 << 8) + iByte2 << 8) + iByte3 << 8) + iByte4 : (((iByte4 << 8) + iByte3 << 8) + iByte2 << 8) + iByte1; 2280 2281 if (iLong < 0) { 2282 iLong += 4294967296; 2283 } 2284 2285 return iLong; 2286 } 2287 }, { 2288 key: "getSLongAt", 2289 value: function getSLongAt(offset, isBigEndian) { 2290 var iULong = this.getLongAt(offset, isBigEndian); 2291 2292 if (iULong > 2147483647) { 2293 return iULong - 4294967296; 2294 } else { 2295 return iULong; 2296 } 2297 } 2298 }, { 2299 key: "getInteger24At", 2300 value: function getInteger24At(offset, isBigEndian) { 2301 var iByte1 = this.getByteAt(offset), 2302 iByte2 = this.getByteAt(offset + 1), 2303 iByte3 = this.getByteAt(offset + 2); 2304 var iInteger = isBigEndian ? ((iByte1 << 8) + iByte2 << 8) + iByte3 : ((iByte3 << 8) + iByte2 << 8) + iByte1; 2305 2306 if (iInteger < 0) { 2307 iInteger += 16777216; 2308 } 2309 2310 return iInteger; 2311 } 2312 }, { 2313 key: "getStringAt", 2314 value: function getStringAt(offset, length) { 2315 var string = []; 2316 2317 for (var i = offset, j = 0; i < offset + length; i++, j++) { 2318 string[j] = String.fromCharCode(this.getByteAt(i)); 2319 } 2320 2321 return string.join(""); 2322 } 2323 }, { 2324 key: "getStringWithCharsetAt", 2325 value: function getStringWithCharsetAt(offset, length, charset) { 2326 var bytes = this.getBytesAt(offset, length); 2327 var string; 2328 2329 switch ((charset || '').toLowerCase()) { 2330 case "utf-16": 2331 case "utf-16le": 2332 case "utf-16be": 2333 string = StringUtils.readUTF16String(bytes, charset === "utf-16be"); 2334 break; 2335 2336 case "utf-8": 2337 string = StringUtils.readUTF8String(bytes); 2338 break; 2339 2340 default: 2341 string = StringUtils.readNullTerminatedString(bytes); 2342 break; 2343 } 2344 2345 return string; 2346 } 2347 }, { 2348 key: "getCharAt", 2349 value: function getCharAt(offset) { 2350 return String.fromCharCode(this.getByteAt(offset)); 2351 } 2352 /** 2353 * The ID3v2 tag/frame size is encoded with four bytes where the most 2354 * significant bit (bit 7) is set to zero in every byte, making a total of 28 2355 * bits. The zeroed bits are ignored, so a 257 bytes long tag is represented 2356 * as $00 00 02 01. 2357 */ 2358 2359 }, { 2360 key: "getSynchsafeInteger32At", 2361 value: function getSynchsafeInteger32At(offset) { 2362 var size1 = this.getByteAt(offset); 2363 var size2 = this.getByteAt(offset + 1); 2364 var size3 = this.getByteAt(offset + 2); 2365 var size4 = this.getByteAt(offset + 3); // 0x7f = 0b01111111 2366 2367 var size = size4 & 0x7f | (size3 & 0x7f) << 7 | (size2 & 0x7f) << 14 | (size1 & 0x7f) << 21; 2368 return size; 2369 } 2370 }], [{ 2371 key: "canReadFile", 2372 value: function canReadFile(file) {
2373 throw new Error("Must implement canReadFile function"); 2374 } 2375 }]); 2376 2377 return MediaFileReader; 2378}(); 2379 2380module.exports = MediaFileReader; 2381 2382},{"./StringUtils":13}],12:[function(require,module,exports){ 2383'use strict'; 2384 2385function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 2386 2387function _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); } } 2388 2389function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } 2390 2391function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } 2392 2393var MediaFileReader = require('./MediaFileReader'); 2394 2395var MediaTagReader = /*#__PURE__*/function () { 2396 function MediaTagReader(mediaFileReader) { 2397 _classCallCheck(this, MediaTagReader); 2398 2399 _defineProperty(this, "_mediaFileReader", void 0); 2400 2401 _defineProperty(this, "_tags", void 0); 2402 2403 this._mediaFileReader = mediaFileReader; 2404 this._tags = null; 2405 } 2406 /** 2407 * Returns the byte range that needs to be loaded and fed to 2408 * _canReadTagFormat in order to identify if the file contains tag 2409 * information that can be read. 2410 */ 2411 2412 2413 _createClass(MediaTagReader, [{ 2414 key: "setTagsToRead", 2415 value: function setTagsToRead(tags) { 2416 this._tags = tags; 2417 return this; 2418 } 2419 }, { 2420 key: "read", 2421 value: function read(callbacks) { 2422 var self = this; 2423 2424 this._mediaFileReader.init({ 2425 onSuccess: function onSuccess() { 2426 self._loadData(self._mediaFileReader, { 2427 onSuccess: function onSuccess() { 2428 try { 2429 var tags = self._parseData(self._mediaFileReader, self._tags); 2430 } catch (ex) { 2431 if (callbacks.onError) { 2432 callbacks.onError({ 2433 "type": "parseData", 2434 "info": ex.message 2435 }); 2436 return; 2437 } 2438 } // TODO: destroy mediaFileReader 2439 2440 2441 callbacks.onSuccess(tags); 2442 }, 2443 onError: callbacks.onError 2444 }); 2445 }, 2446 onError: callbacks.onError 2447 }); 2448 } 2449 }, { 2450 key: "getShortcuts", 2451 value: function getShortcuts() { 2452 return {}; 2453 } 2454 /** 2455 * Load the necessary bytes from the media file. 2456 */ 2457 2458 }, { 2459 key: "_loadData", 2460 value: function _loadData(mediaFileReader, callbacks) {
2461 throw new Error("Must implement _loadData function"); 2462 } 2463 /** 2464 * Parse the loaded data to read the media tags. 2465 */ 2466 2467 }, { 2468 key: "_parseData", 2469 value: function _parseData(mediaFileReader, tags) { 2470 throw new Error("Must implement _parseData function"); 2471 } 2472 }, { 2473 key: "_expandShortcutTags", 2474 value: function _expandShortcutTags(tagsWithShortcuts) { 2475 if (!tagsWithShortcuts) { 2476 return null; 2477 } 2478 2479 var tags = []; 2480 var shortcuts = this.getShortcuts(); 2481 2482 for (var i = 0, tagOrShortcut; tagOrShortcut = tagsWithShortcuts[i]; i++) { 2483 tags = tags.concat(shortcuts[tagOrShortcut] || [tagOrShortcut]); 2484 } 2485 2486 return tags; 2487 } 2488 }], [{ 2489 key: "getTagIdentifierByteRange", 2490 value: function getTagIdentifierByteRange() { 2491 throw new Error("Must implement"); 2492 } 2493 /** 2494 * Given a tag identifier (read from the file byte positions speficied by 2495 * getTagIdentifierByteRange) this function checks if it can read the tag 2496 * format or not. 2497 */ 2498 2499 }, { 2500 key: "canReadTagFormat", 2501 value: function canReadTagFormat(tagIdentifier) {
2502 throw new Error("Must implement"); 2503 } 2504 }]); 2505 2506 return MediaTagReader; 2507}(); 2508 2509module.exports = MediaTagReader; 2510 2511},{"./MediaFileReader":11}],13:[function(require,module,exports){ 2512'use strict'; 2513 2514function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 2515 2516function _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); } } 2517 2518function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } 2519 2520function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } 2521 2522var InternalDecodedString = /*#__PURE__*/function () { 2523 function InternalDecodedString(value, bytesReadCount) { 2524 _classCallCheck(this, InternalDecodedString); 2525 2526 _defineProperty(this, "_value", void 0); 2527 2528 _defineProperty(this, "bytesReadCount", void 0); 2529 2530 _defineProperty(this, "length", void 0); 2531 2532 this._value = value; 2533 this.bytesReadCount = bytesReadCount; 2534 this.length = value.length; 2535 } 2536 2537 _createClass(InternalDecodedString, [{ 2538 key: "toString", 2539 value: function toString() { 2540 return this._value; 2541 } 2542 }]); 2543 2544 return InternalDecodedString; 2545}(); 2546 2547var StringUtils = { 2548 readUTF16String: function readUTF16String(bytes, bigEndian, maxBytes) { 2549 var ix = 0; 2550 var offset1 = 1, 2551 offset2 = 0; 2552 maxBytes = Math.min(maxBytes || bytes.length, bytes.length); 2553 2554 if (bytes[0] == 0xFE && bytes[1] == 0xFF) { 2555 bigEndian = true; 2556 ix = 2; 2557 } else if (bytes[0] == 0xFF && bytes[1] == 0xFE) { 2558 bigEndian = false; 2559 ix = 2; 2560 } 2561 2562 if (bigEndian) { 2563 offset1 = 0; 2564 offset2 = 1; 2565 } 2566 2567 var arr = []; 2568 2569 for (var j = 0; ix < maxBytes; j++) { 2570 var byte1 = bytes[ix + offset1]; 2571 var byte2 = bytes[ix + offset2]; 2572 var word1 = (byte1 << 8) + byte2; 2573 ix += 2; 2574 2575 if (word1 == 0x0000) { 2576 break; 2577 } else if (byte1 < 0xD8 || byte1 >= 0xE0) { 2578 arr[j] = String.fromCharCode(word1); 2579 } else { 2580 var byte3 = bytes[ix + offset1]; 2581 var byte4 = bytes[ix + offset2]; 2582 var word2 = (byte3 << 8) + byte4; 2583 ix += 2; 2584 arr[j] = String.fromCharCode(word1, word2); 2585 } 2586 } 2587 2588 return new InternalDecodedString(arr.join(""), ix); 2589 }, 2590 readUTF8String: function readUTF8String(bytes, maxBytes) { 2591 var ix = 0; 2592 maxBytes = Math.min(maxBytes || bytes.length, bytes.length); 2593 2594 if (bytes[0] == 0xEF && bytes[1] == 0xBB && bytes[2] == 0xBF) { 2595 ix = 3; 2596 } 2597 2598 var arr = []; 2599 2600 for (var j = 0; ix < maxBytes; j++) { 2601 var byte1 = bytes[ix++]; 2602 2603 if (byte1 == 0x00) { 2604 break; 2605 } else if (byte1 < 0x80) { 2606 arr[j] = String.fromCharCode(byte1); 2607 } else if (byte1 >= 0xC2 && byte1 < 0xE0) { 2608 var byte2 = bytes[ix++]; 2609 arr[j] = String.fromCharCode(((byte1 & 0x1F) << 6) + (byte2 & 0x3F)); 2610 } else if (byte1 >= 0xE0 && byte1 < 0xF0) { 2611 var byte2 = bytes[ix++]; 2612 var byte3 = bytes[ix++]; 2613 arr[j] = String.fromCharCode(((byte1 & 0xFF) << 12) + ((byte2 & 0x3F) << 6) + (byte3 & 0x3F)); 2614 } else if (byte1 >= 0xF0 && byte1 < 0xF5) { 2615 var byte2 = bytes[ix++]; 2616 var byte3 = bytes[ix++]; 2617 var byte4 = bytes[ix++]; 2618 var codepoint = ((byte1 & 0x07) << 18) + ((byte2 & 0x3F) << 12) + ((byte3 & 0x3F) << 6) + (byte4 & 0x3F) - 0x10000; 2619 arr[j] = String.fromCharCode((codepoint >> 10) + 0xD800, (codepoint & 0x3FF) + 0xDC00); 2620 } 2621 } 2622 2623 return new InternalDecodedString(arr.join(""), ix); 2624 }, 2625 readNullTerminatedString: function readNullTerminatedString(bytes, maxBytes) { 2626 var arr = []; 2627 maxBytes = maxBytes || bytes.length; 2628 2629 for (var i = 0; i < maxBytes;) { 2630 var byte1 = bytes[i++]; 2631 2632 if (byte1 == 0x00) { 2633 break; 2634 } 2635 2636 arr[i - 1] = String.fromCharCode(byte1); 2637 } 2638 2639 return new InternalDecodedString(arr.join(""), i); 2640 } 2641}; 2642module.exports = StringUtils; 2643 2644},{}],14:[function(require,module,exports){ 2645'use strict'; 2646 2647function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } 2648 2649function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 2650 2651function _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); } } 2652 2653function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } 2654 2655function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { c
vendor: 2,556 bytes, lines 2655-2700
2655onstructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); } 2656 2657function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } 2658 2659function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } 2660 2661function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); } 2662 2663function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } 2664 2665function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } 2666 2667function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } 2668 2669function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } 2670 2671var ChunkedFileData = require('./ChunkedFileData'); 2672 2673var MediaFileReader = require('./MediaFileReader'); 2674 2675var CHUNK_SIZE = 1024; 2676 2677var XhrFileReader = /*#__PURE__*/function (_MediaFileReader) { 2678 _inherits(XhrFileReader, _MediaFileReader); 2679 2680 var _super = _createSuper(XhrFileReader); 2681 2682 function XhrFileReader(url) { 2683 var _this; 2684 2685 _classCallCheck(this, XhrFileReader); 2686 2687 _this = _super.call(this); 2688 2689 _defineProperty(_assertThisInitialized(_this), "_url", void 0); 2690 2691 _defineProperty(_assertThisInitialized(_this), "_fileData", void 0); 2692 2693 _this._url = url; 2694 _this._fileData = new ChunkedFileData(); 2695 return _this; 2696 } 2697 2698 _createClass(XhrFileReader, [{ 2699 key: "_init", 2700 value: function _init(callbacks) {
2701 if (XhrFileReader._config.avoidHeadRequests) { 2702 this._fetchSizeWithGetRequest(callbacks); 2703 } else { 2704 this._fetchSizeWithHeadRequest(callbacks); 2705 } 2706 } 2707 }, { 2708 key: "_fetchSizeWithHeadRequest", 2709 value: function _fetchSizeWithHeadRequest(callbacks) { 2710 var self = this; 2711 2712 this._makeXHRRequest("HEAD", null, { 2713 onSuccess: function onSuccess(xhr) { 2714 var contentLength = self._parseContentLength(xhr); 2715 2716 if (contentLength) { 2717 self._size = contentLength; 2718 callbacks.onSuccess(); 2719 } else { 2720 // Content-Length not provided by the server, fallback to 2721 // GET requests. 2722 self._fetchSizeWithGetRequest(callbacks); 2723 } 2724 }, 2725 onError: callbacks.onError 2726 }); 2727 } 2728 }, { 2729 key: "_fetchSizeWithGetRequest", 2730 value: function _fetchSizeWithGetRequest(callbacks) { 2731 var self = this; 2732 2733 var range = this._roundRangeToChunkMultiple([0, 0]); 2734 2735 this._makeXHRRequest("GET", range, { 2736 onSuccess: function onSuccess(xhr) { 2737 var contentRange = self._parseContentRange(xhr); 2738 2739 var data = self._getXhrResponseContent(xhr); 2740 2741 if (contentRange) { 2742 if (contentRange.instanceLength == null) { 2743 // Last resort, server is not able to tell us the content length, 2744 // need to fetch entire file then. 2745 self._fetchEntireFile(callbacks); 2746 2747 return; 2748 } 2749 2750 self._size = contentRange.instanceLength; 2751 } else { 2752 // Range request not supported, we got the entire file 2753 self._size = data.length; 2754 } 2755 2756 self._fileData.addData(0, data); 2757 2758 callbacks.onSuccess(); 2759 }, 2760 onError: callbacks.onError 2761 }); 2762 } 2763 }, { 2764 key: "_fetchEntireFile", 2765 value: function _fetchEntireFile(callbacks) { 2766 var self = this; 2767 2768 this._makeXHRRequest("GET", null, { 2769 onSuccess: function onSuccess(xhr) { 2770 var data = self._getXhrResponseContent(xhr); 2771 2772 self._size = data.length; 2773 2774 self._fileData.addData(0, data); 2775 2776 callbacks.onSuccess(); 2777 }, 2778 onError: callbacks.onError 2779 }); 2780 } 2781 }, { 2782 key: "_getXhrResponseContent", 2783 value: function _getXhrResponseContent(xhr) { 2784 return xhr.responseBody || xhr.responseText || ""; 2785 } 2786 }, { 2787 key: "_parseContentLength", 2788 value: function _parseContentLength(xhr) { 2789 var contentLength = this._getResponseHeader(xhr, "Content-Length"); 2790 2791 if (contentLength == null) { 2792 return contentLength; 2793 } else { 2794 return parseInt(contentLength, 10); 2795 } 2796 } 2797 }, { 2798 key: "_parseContentRange", 2799 value: function _parseContentRange(xhr) { 2800 var contentRange = this._getResponseHeader(xhr, "Content-Range"); 2801 2802 if (contentRange) { 2803 var parsedContentRange = contentRange.match(/bytes (\d+)-(\d+)\/(?:(\d+)|\*)/i); 2804 2805 if (!parsedContentRange) { 2806 throw new Error("FIXME: Unknown Content-Range syntax: " + contentRange); 2807 } 2808 2809 return { 2810 firstBytePosition: parseInt(parsedContentRange[1], 10), 2811 lastBytePosition: parseInt(parsedContentRange[2], 10), 2812 instanceLength: parsedContentRange[3] ? parseInt(parsedContentRange[3], 10) : null 2813 }; 2814 } else { 2815 return null; 2816 } 2817 } 2818 }, { 2819 key: "loadRange", 2820 value: function loadRange(range, callbacks) { 2821 var self = this; 2822 2823 if (self._fileData.hasDataRange(range[0], Math.min(self._size, range[1]))) { 2824 setTimeout(callbacks.onSuccess, 1); 2825 return; 2826 } // Always download in multiples of CHUNK_SIZE. If we're going to make a 2827 // request might as well get a chunk that makes sense. The big cost is 2828 // establishing the connection so getting 10bytes or 1K doesn't really 2829 // make a difference. 2830 2831 2832 range = this._roundRangeToChunkMultiple(range); // Upper range should not be greater than max file size 2833 2834 range[1] = Math.min(self._size, range[1]); 2835 2836 this._makeXHRRequest("GET", range, { 2837 onSuccess: function onSuccess(xhr) { 2838 var data = self._getXhrResponseContent(xhr); 2839
2840 self._fileData.addData(range[0], data); 2841 2842 callbacks.onSuccess(); 2843 }, 2844 onError: callbacks.onError 2845 }); 2846 } 2847 }, { 2848 key: "_roundRangeToChunkMultiple", 2849 value: function _roundRangeToChunkMultiple(range) { 2850 var length = range[1] - range[0] + 1; 2851 var newLength = Math.ceil(length / CHUNK_SIZE) * CHUNK_SIZE; 2852 return [range[0], range[0] + newLength - 1]; 2853 } 2854 }, { 2855 key: "_makeXHRRequest", 2856 value: function _makeXHRRequest(method, range, callbacks) { 2857 var xhr = this._createXHRObject(); 2858 2859 xhr.open(method, this._url); 2860 2861 var onXHRLoad = function onXHRLoad() { 2862 // 200 - OK 2863 // 206 - Partial Content 2864 // $FlowIssue - xhr will not be null here 2865 if (xhr.status === 200 || xhr.status === 206) { 2866 callbacks.onSuccess(xhr); 2867 } else if (callbacks.onError) { 2868 callbacks.onError({ 2869 "type": "xhr", 2870 "info": "Unexpected HTTP status " + xhr.status + ".", 2871 "xhr": xhr 2872 }); 2873 } 2874 2875 xhr = null; 2876 }; 2877 2878 if (typeof xhr.onload !== 'undefined') { 2879 xhr.onload = onXHRLoad; 2880 2881 xhr.onerror = function () { 2882 if (callbacks.onError) { 2883 callbacks.onError({ 2884 "type": "xhr", 2885 "info": "Generic XHR error, check xhr object.", 2886 "xhr": xhr 2887 }); 2888 } 2889 }; 2890 } else { 2891 xhr.onreadystatechange = function () { 2892 // $FlowIssue - xhr will not be null here 2893 if (xhr.readyState === 4) { 2894 onXHRLoad(); 2895 } 2896 }; 2897 } 2898
2899 if (XhrFileReader._config.timeoutInSec) { 2900 xhr.timeout = XhrFileReader._config.timeoutInSec * 1000; 2901 2902 xhr.ontimeout = function () { 2903 if (callbacks.onError) { 2904 callbacks.onError({ 2905 "type": "xhr", 2906 // $FlowIssue - xhr.timeout will not be null 2907 "info": "Timeout after " + xhr.timeout / 1000 + "s. Use jsmediatags.Config.setXhrTimeout to override.", 2908 "xhr": xhr 2909 }); 2910 } 2911 }; 2912 } 2913 2914 xhr.overrideMimeType("text/plain; charset=x-user-defined"); 2915 2916 if (range) { 2917 this._setRequestHeader(xhr, "Range", "bytes=" + range[0] + "-" + range[1]); 2918 } 2919 2920 this._setRequestHeader(xhr, "If-Modified-Since", "Sat, 01 Jan 1970 00:00:00 GMT"); 2921 2922 xhr.send(null); 2923 } 2924 }, { 2925 key: "_setRequestHeader", 2926 value: function _setRequestHeader(xhr, headerName, headerValue) { 2927 if (XhrFileReader._config.disallowedXhrHeaders.indexOf(headerName.toLowerCase()) < 0) { 2928 xhr.setRequestHeader(headerName, headerValue); 2929 } 2930 } 2931 }, { 2932 key: "_hasResponseHeader", 2933 value: function _hasResponseHeader(xhr, headerName) { 2934 var allResponseHeaders = xhr.getAllResponseHeaders(); 2935 2936 if (!allResponseHeaders) { 2937 return false; 2938 } 2939 2940 var headers = allResponseHeaders.split("\r\n"); 2941 var headerNames = []; 2942 2943 for (var i = 0; i < headers.length; i++) { 2944 headerNames[i] = headers[i].split(":")[0].toLowerCase(); 2945 } 2946 2947 return headerNames.indexOf(headerName.toLowerCase()) >= 0; 2948 } 2949 }, { 2950 key: "_getResponseHeader", 2951 value: function _getResponseHeader(xhr, headerName) { 2952 if (!this._hasResponseHeader(xhr, headerName)) { 2953 return null; 2954 } 2955 2956 return xhr.getResponseHeader(headerName); 2957 } 2958 }, { 2959 key: "getByteAt", 2960 value: function getByteAt(offset) { 2961 var character = this._fileData.getByteAt(offset); 2962 2963 return character.charCodeAt(0) & 0xff; 2964 } 2965 }, { 2966 key: "_isWebWorker", 2967 value: function _isWebWorker() { 2968 return typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope; 2969 } 2970 }, { 2971 key: "_createXHRObject", 2972 value: function _createXHRObject() { 2973 if (typeof window === "undefined" && !this._isWebWorker()) { 2974 // $FlowIssue - flow is not able to recognize this module. 2975 return new (require("xhr2").XMLHttpRequest)(); 2976 } 2977 2978 if (typeof XMLHttpRequest !== "undefined") { 2979 return new XMLHttpRequest(); 2980 } 2981 2982 throw new Error("XMLHttpRequest is not supported"); 2983 } 2984 }], [{ 2985 key: "canReadFile", 2986 value: function canReadFile(file) { 2987 return typeof file === 'string' && /^[a-z]+:\/\//i.test(file); 2988 } 2989 }, { 2990 key: "setConfig", 2991 value: function setConfig(config) { 2992 for (var key in config) { 2993 if (config.hasOwnProperty(key)) { 2994 this._config[key] = config[key]; 2995 } 2996 } 2997 2998 var disallowedXhrHeaders = this._config.disallowedXhrHeaders; 2999 3000 for (var i = 0; i < disallowedXhrHeaders.length; i++) { 3001 disallowedXhrHeaders[i] = disallowedXhrHeaders[i].toLowerCase(); 3002 } 3003 } 3004 }]); 3005 3006 return XhrFileReader; 3007}(MediaFileReader); 3008 3009_defineProperty(XhrFileReader, "_config", void 0); 3010
3011XhrFileReader._config = { 3012 avoidHeadRequests: false, 3013 disallowedXhrHeaders: [], 3014 timeoutInSec: 30 3015}; 3016module.exports = XhrFileReader; 3017 3018},{"./ChunkedFileData":5,"./MediaFileReader":11,"xhr2":2}],15:[function(require,module,exports){ 3019'use strict'; 3020 3021function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 3022 3023function _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); } } 3024 3025function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } 3026 3027function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } 3028 3029var MediaFileReader = require("./MediaFileReader"); 3030 3031var XhrFileReader = require("./XhrFileReader"); 3032 3033var BlobFileReader = require("./BlobFileReader"); 3034 3035var ArrayFileReader = require("./ArrayFileReader"); 3036 3037var MediaTagReader = require("./MediaTagReader"); 3038 3039var ID3v1TagReader = require("./ID3v1TagReader"); 3040 3041var ID3v2TagReader = require("./ID3v2TagReader"); 3042 3043var MP4TagReader = require("./MP4TagReader"); 3044 3045var FLACTagReader = require("./FLACTagReader"); 3046 3047var mediaFileReaders = []; 3048var mediaTagReaders = []; 3049 3050function read(location, callbacks) { 3051 new Reader(location).read(callbacks); 3052} 3053 3054function isRangeValid(range, fileSize) { 3055 var invalidPositiveRange = range.offset >= 0 && range.offset + range.length >= fileSize; 3056 var invalidNegativeRange = range.offset < 0 && (-range.offset > fileSize || range.offset + range.length > 0); 3057 return !(invalidPositiveRange || invalidNegativeRange); 3058} 3059 3060var Reader = /*#__PURE__*/function () { 3061 function Reader(file) { 3062 _classCallCheck(this, Reader); 3063 3064 _defineProperty(this, "_file", void 0); 3065 3066 _defineProperty(this, "_tagsToRead", void 0); 3067 3068 _defineProperty(this, "_fileReader", void 0); 3069 3070 _defineProperty(this, "_tagReader", void 0); 3071 3072 this._file = file; 3073 } 3074 3075 _createClass(Reader, [{ 3076 key: "setTagsToRead", 3077 value: function setTagsToRead(tagsToRead) { 3078 this._tagsToRead = tagsToRead; 3079 return this; 3080 } 3081 }, { 3082 key: "setFileReader", 3083 value: function setFileReader(fileReader) { 3084 this._fileReader = fileReader; 3085 return this; 3086 } 3087 }, { 3088 key: "setTagReader", 3089 value: function setTagReader(tagReader) { 3090 this._tagReader = tagReader; 3091 return this; 3092 } 3093 }, { 3094 key: "read", 3095 value: function read(callbacks) { 3096 var FileReader = this._getFileReader(); 3097 3098 var fileReader = new FileReader(this._file); 3099 var self = this; 3100 fileReader.init({ 3101 onSuccess: function onSuccess() { 3102 self._getTagReader(fileReader, { 3103 onSuccess: function onSuccess(TagReader) { 3104 new TagReader(fileReader).setTagsToRead(self._tagsToRead).read(callbacks); 3105 }, 3106 onError: callbacks.onError 3107 }); 3108 }, 3109 onError: callbacks.onError 3110 }); 3111 } 3112 }, { 3113 key: "_getFileReader", 3114 value: function _getFileReader() { 3115 if (this._fileReader) { 3116 return this._fileReader; 3117 } else { 3118 return this._findFileReader(); 3119 } 3120 } 3121 }, { 3122 key: "_findFileReader", 3123 value: function _findFileReader() { 3124 for (var i = 0; i < mediaFileReaders.length; i++) { 3125 if (mediaFileReaders[i].canReadFile(this._file)) { 3126 return mediaFileReaders[i]; 3127 } 3128 } 3129 3130 throw new Error("No suitable file reader found for " + this._file); 3131 } 3132 }, { 3133 key: "_getTagReader", 3134 value: function _getTagReader(fileReader, callbacks) { 3135 if (this._tagReader) { 3136 var tagReader = this._tagReader; 3137 setTimeout(function () { 3138 callbacks.onSuccess(tagReader); 3139 }, 1); 3140 } else { 3141 this._findTagReader(fileReader, callbacks); 3142 } 3143 } 3144 }, { 3145 key: "_findTagReader", 3146 value: function _findTagReader(fileReader, callbacks) { 3147 // We don't want to make multiple fetches per tag reader to get the tag 3148 // identifier. The strategy here is to combine all the tag identifier 3149 // ranges into one and make a single fetch. This is particularly important 3150 // in file readers that have expensive loads like the XHR one. 3151 // However, with this strategy we run into the problem of loading the 3152 // entire file because tag identifiers might be at the start or end of 3153 // the file. 3154 // To get around this we divide the tag readers into two categories, the 3155 // ones that read their tag identifiers from the start of the file and the 3156 // ones that read from the end of the file. 3157 var tagReadersAtFileStart = []; 3158 var tagReadersAtFileEnd = []; 3159 var fileSize = fileReader.getSize(); 3160 3161 for (var i = 0; i < mediaTagReaders.length; i++) { 3162 var range = mediaTagReaders[i].getTagIdentifierByteRange(); 3163 3164 if (!isRangeValid(range, fileSize)) { 3165 continue; 3166 } 3167 3168 if (range.offset >= 0 && range.offset < fileSize / 2 || range.offset < 0 && range.offset < -fileSize / 2) { 3169 tagReadersAtFileStart.push(mediaTagReaders[i]); 3170 } else { 3171 tagReadersAtFileEnd.push(mediaTagReaders[i]); 3172 } 3173 } 3174 3175 var tagsLoaded = false;
3176 var loadTagIdentifiersCallbacks = { 3177 onSuccess: function onSuccess() { 3178 if (!tagsLoaded) { 3179 // We're expecting to load two sets of tag identifiers. This flag 3180 // indicates when the first one has been loaded. 3181 tagsLoaded = true; 3182 return; 3183 } 3184 3185 for (var i = 0; i < mediaTagReaders.length; i++) { 3186 var range = mediaTagReaders[i].getTagIdentifierByteRange(); 3187 3188 if (!isRangeValid(range, fileSize)) { 3189 continue; 3190 } 3191 3192 try { 3193 var tagIndentifier = fileReader.getBytesAt(range.offset >= 0 ? range.offset : range.offset + fileSize, range.length); 3194 } catch (ex) { 3195 if (callbacks.onError) { 3196 callbacks.onError({ 3197 "type": "fileReader", 3198 "info": ex.message 3199 }); 3200 } 3201 3202 return; 3203 } 3204 3205 if (mediaTagReaders[i].canReadTagFormat(tagIndentifier)) { 3206 callbacks.onSuccess(mediaTagReaders[i]); 3207 return; 3208 } 3209 } 3210 3211 if (callbacks.onError) { 3212 callbacks.onError({ 3213 "type": "tagFormat", 3214 "info": "No suitable tag reader found" 3215 }); 3216 } 3217 }, 3218 onError: callbacks.onError 3219 }; 3220 3221 this._loadTagIdentifierRanges(fileReader, tagReadersAtFileStart, loadTagIdentifiersCallbacks); 3222 3223 this._loadTagIdentifierRanges(fileReader, tagReadersAtFileEnd, loadTagIdentifiersCallbacks); 3224 } 3225 }, { 3226 key: "_loadTagIdentifierRanges", 3227 value: function _loadTagIdentifierRanges(fileReader, tagReaders, callbacks) { 3228 if (tagReaders.length === 0) { 3229 // Force async 3230 setTimeout(callbacks.onSuccess, 1); 3231 return; 3232 } 3233 3234 var tagIdentifierRange = [Number.MAX_VALUE, 0]; 3235 var fileSize = fileReader.getSize(); // Create a super set of all ranges so we can load them all at once. 3236 // Might need to rethink this approach if there are tag ranges too far 3237 // a part from each other. We're good for now though. 3238 3239 for (var i = 0; i < tagReaders.length; i++) { 3240 var range = tagReaders[i].getTagIdentifierByteRange(); 3241 var start = range.offset >= 0 ? range.offset : range.offset + fileSize; 3242 var end = start + range.length - 1; 3243 tagIdentifierRange[0] = Math.min(start, tagIdentifierRange[0]); 3244 tagIdentifierRange[1] = Math.max(end, tagIdentifierRange[1]); 3245 } 3246 3247 fileReader.loadRange(tagIdentifierRange, callbacks); 3248 } 3249 }]); 3250 3251 return Reader; 3252}(); 3253 3254var Config = /*#__PURE__*/function () { 3255 function Config() { 3256 _classCallCheck(this, Config); 3257 } 3258 3259 _createClass(Config, null, [{ 3260 key: "addFileReader", 3261 value: function addFileReader(fileReader) { 3262 mediaFileReaders.push(fileReader); 3263 return Config; 3264 } 3265 }, { 3266 key: "addTagReader", 3267 value: function addTagReader(tagReader) { 3268 mediaTagReaders.push(tagReader); 3269 return Config; 3270 } 3271 }, { 3272 key: "removeTagReader", 3273 value: function removeTagReader(tagReader) { 3274 var tagReaderIx = mediaTagReaders.indexOf(tagReader); 3275 3276 if (tagReaderIx >= 0) { 3277 mediaTagReaders.splice(tagReaderIx, 1); 3278 } 3279 3280 return Config; 3281 } 3282 }, { 3283 key: "EXPERIMENTAL_avoidHeadRequests", 3284 value: function EXPERIMENTAL_avoidHeadRequests() { 3285 XhrFileReader.setConfig({ 3286 avoidHeadRequests: true 3287 }); 3288 } 3289 }, { 3290 key: "setDisallowedXhrHeaders", 3291 value: function setDisallowedXhrHeaders(disallowedXhrHeaders) { 3292 XhrFileReader.setConfig({ 3293 disallowedXhrHeaders: disallowedXhrHeaders 3294 }); 3295 } 3296 }, { 3297 key: "setXhrTimeoutInSec", 3298 value: function setXhrTimeoutInSec(timeoutInSec) { 3299 XhrFileReader.setConfig({ 3300 timeoutInSec: timeoutInSec 3301 }); 3302 } 3303 }]); 3304 3305 return Config; 3306}(); 3307 3308Config.addFileReader(XhrFileReader).addFileReader(BlobFileReader).addFileReader(ArrayFileReader).addTagReader(ID3v2TagReader).addTagReader(ID3v1TagReader).addTagReader(MP4TagReader).addTagReader(FLACTagReader); 3309 3310if (typeof process !== "undefined" && !process.browser) { 3311 if (typeof navigator !== "undefined" && navigator.product === "ReactNative") { 3312 var ReactNativeFileReader = require('./ReactNativeFileReader'); 3313 3314 Config.addFileReader(ReactNativeFileReader); 3315 } else { 3316 var NodeFileReader = require('./NodeFileReader'); 3317 3318 Config.addFileReader(NodeFileReader); 3319 } 3320} 3321 3322module.exports = { 3323 "read": read, 3324 "Reader": Reader, 3325 "Config": Config 3326}; 3327 3328},{"./ArrayFileReader":3,"./BlobFileReader":4,"./FLACTagReader":6,"./ID3v1TagReader":7,"./ID3v2TagReader":9,"./MP4TagReader":10,"./MediaFileReader":11,"./MediaTagReader":12,"./NodeFileReader":1,"./ReactNativeFileReader":1,"./XhrFileReader":14}]},{},[15])(15) 3329});
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.