UNPKG

jamp3

Version:

mp3, id3v1, id3v2 - reader & writer

305 lines 11.5 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.MP3Reader = void 0; const fs_extra_1 = __importDefault(require("fs-extra")); const id3v1_reader_1 = require("../id3v1/id3v1.reader"); const id3v2_reader_1 = require("../id3v2/id3v2.reader"); const mp3_mpeg_frame_1 = require("./mp3.mpeg.frame"); const buffer_1 = require("../common/buffer"); const mp3_mpeg_chain_1 = require("./mp3.mpeg.chain"); const stream_reader_1 = require("../common/stream-reader"); class MP3Reader { constructor() { this.options = {}; this.layout = { frameheaders: [], headframes: [], tags: [], size: 0 }; this.id3v2reader = new id3v2_reader_1.ID3v2Reader(); this.id3v1reader = new id3v1_reader_1.ID3v1Reader(); this.mpegFramereader = new mp3_mpeg_frame_1.MPEGFrameReader(); this.stream = new stream_reader_1.ReaderStream(); this.scanMpeg = true; this.scanid3v1 = true; this.scanid3v2 = true; this.scanMPEGFrame = true; this.hasMPEGHeadFrame = false; } readFullMPEGFrame(chunk, pos, header) { if (this.demandData(chunk, pos)) { return true; } header.offset = this.stream.pos - chunk.length + pos; const a = this.mpegFramereader.readFrame(chunk, pos, header); if (a.frame) { if (a.frame.vbri || a.frame.xing) { this.layout.headframes.push(a.frame); } this.layout.frameheaders.push(a.frame.header); if (this.options.mpegQuick) { this.hasMPEGHeadFrame = this.hasMPEGHeadFrame || !!a.frame.mode; if (this.layout.frameheaders.length % 50 === 0) { if (this.hasMPEGHeadFrame) { this.scanMpeg = false; } else { const chain = (0, mp3_mpeg_chain_1.getBestMPEGChain)(this.layout.frameheaders, 20); if (chain && chain.count >= 10) { this.scanMpeg = false; } } } } } return false; } readMPEGFrame(chunk, pos) { if (this.demandData(chunk, pos)) { return true; } const header = this.mpegFramereader.readMPEGFrameHeader(chunk, pos); if (header) { this.scanid3v2 = false; if (!this.scanMPEGFrame) { header.offset = this.stream.pos - chunk.length + pos; this.layout.frameheaders.push((0, mp3_mpeg_frame_1.collapseRawHeader)(header)); } else { return this.readFullMPEGFrame(chunk, pos, header); } } return false; } readID3V1(chunk, pos) { if (this.demandData(chunk, pos)) { return true; } const tag = this.id3v1reader.readTag(chunk.slice(pos, pos + 128)); if (!tag) { return false; } tag.start = this.stream.pos - chunk.length + pos; tag.end = tag.start + 128; this.layout.tags.push(tag); if (!this.stream.end || chunk.length - 128 - pos > 0) { this.stream.unshift(chunk.slice(pos + 1)); } else { this.stream.unshift(chunk.slice(pos + 128)); } return true; } readID3V2(chunk, pos) { return __awaiter(this, void 0, void 0, function* () { if (this.demandData(chunk, pos)) { return true; } const id3Header = this.id3v2reader.headerReader.readID3v2Header(chunk, pos); if (!id3Header || !id3Header.valid) { return false; } const start = this.stream.pos - chunk.length + pos; this.stream.unshift(chunk.slice(pos)); const result = yield this.id3v2reader.readReaderStream(this.stream); if (result) { let rest = result.rest || buffer_1.BufferUtils.zeroBuffer(0); if (result.tag && result.tag.head.valid) { this.layout.tags.push(result.tag); result.tag.start = start; result.tag.end = this.stream.pos - rest.length; if (!this.options.detectDuplicateID3v2) { this.scanid3v2 = false; } if (this.options.id3v1IfNotID3v2) { this.scanid3v1 = false; } } else { rest = rest.slice(1); } this.stream.unshift(rest); return true; } return false; }); } processChunkToEnd(chunk) { return __awaiter(this, void 0, void 0, function* () { if (this.options.streamSize !== undefined) { return false; } yield this.stream.consumeToEnd(); return false; }); } processChunkID3v1(chunk) { return __awaiter(this, void 0, void 0, function* () { let pos = 0; if (!this.stream.end && (this.stream.buffersLength > 200)) { this.stream.skip(this.stream.buffersLength - 200); chunk = this.stream.get(200); pos = 0; } while (chunk.length - pos >= 4) { const c1 = chunk[pos]; const c2 = chunk[pos + 1]; const c3 = chunk[pos + 2]; if ((c1 === 84) && (c2 === 65) && (c3 === 71) && this.readID3V1(chunk, pos)) { return true; } pos++; } return false; }); } processChunkID3v1AndID3v2AndMpeg(chunk) { return __awaiter(this, void 0, void 0, function* () { let pos = 0; while (chunk.length - pos >= 4) { const c1 = chunk[pos]; const c2 = chunk[pos + 1]; const c3 = chunk[pos + 2]; if (this.scanid3v2 && c1 === 73 && c2 === 68 && c3 === 51 && (yield this.readID3V2(chunk, pos))) { return true; } else if (this.scanMpeg && c1 === 255 && this.readMPEGFrame(chunk, pos)) { return true; } else if (this.scanid3v1 && c1 === 84 && c2 === 65 && c3 === 71 && this.readID3V1(chunk, pos)) { return true; } pos++; } return false; }); } processChunkID3v1AndID3v2(chunk) { return __awaiter(this, void 0, void 0, function* () { let pos = 0; while (chunk.length - pos >= 4) { const c1 = chunk[pos]; const c2 = chunk[pos + 1]; const c3 = chunk[pos + 2]; if ((c1 === 73 && c2 === 68 && c3 === 51) && (yield this.readID3V2(chunk, pos))) { return true; } else if ((c1 === 84 && c2 === 65 && c3 === 71) && this.readID3V1(chunk, pos)) { return true; } pos++; } return false; }); } demandData(chunk, pos) { if (!this.stream.end && (chunk.length - pos) < 200) { this.stream.unshift(chunk.slice(pos)); return true; } return false; } processChunk(chunk) { return __awaiter(this, void 0, void 0, function* () { if (this.demandData(chunk, 0)) { return true; } if (!this.scanMpeg && !this.scanid3v2 && !this.scanid3v1) { return this.processChunkToEnd(chunk); } else if (!this.scanMpeg && !this.scanid3v2) { if (yield this.processChunkID3v1(chunk)) { return true; } } else if (!this.scanMpeg) { if (yield this.processChunkID3v1AndID3v2(chunk)) { return true; } } else if (yield this.processChunkID3v1AndID3v2AndMpeg(chunk)) { return true; } if (chunk.length > 3) { this.stream.unshift(chunk.slice(chunk.length - 3)); } return true; }); } scan() { return __awaiter(this, void 0, void 0, function* () { if (this.stream.end) { return; } const requestChunkLength = 20000; let go = true; while (go) { const data = yield this.stream.read(requestChunkLength); if (!data || (data.length === 0)) { go = false; break; } try { go = yield this.processChunk(data); } catch (e) { return Promise.reject(e); } } this.layout.size = (this.options.streamSize !== undefined) ? this.options.streamSize : this.stream.pos; }); } setOptions(options) { this.options = options || {}; this.scanMpeg = options.mpeg || options.mpegQuick || false; this.scanid3v1 = options.id3v1 || options.id3v1IfNotID3v2 || false; this.scanid3v2 = options.id3v2 || options.id3v1IfNotID3v2 || false; this.layout = { headframes: [], frameheaders: [], tags: [], size: 0 }; } read(filename, options) { return __awaiter(this, void 0, void 0, function* () { this.setOptions(options); if (!options.streamSize) { options.streamSize = (yield fs_extra_1.default.stat(filename)).size; } yield this.stream.open(filename); try { yield this.scan(); this.stream.close(); } catch (e) { this.stream.close(); return Promise.reject(e); } return this.layout; }); } readStream(stream, options) { return __awaiter(this, void 0, void 0, function* () { this.setOptions(options); yield this.stream.openStream(stream); yield this.scan(); return this.layout; }); } } exports.MP3Reader = MP3Reader; //# sourceMappingURL=mp3.reader.js.map