UNPKG

jamp3

Version:

mp3, id3v1, id3v2 - reader & writer

154 lines 7.93 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()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.MP3Analyzer = void 0; const mp3_1 = require("./mp3"); const mp3_mpeg_frame_1 = require("./mp3.mpeg.frame"); const __1 = require("../.."); const id3v2_check_1 = require("../id3v2/id3v2.check"); class MP3Analyzer { analyzeID3v2(data) { if (!data.id3v2) { return []; } return (0, id3v2_check_1.checkID3v2)(data.id3v2); } analyzeID3v1(data) { const result = []; const lastframe = data.frames && data.frames.audio.length > 0 ? data.frames.audio[data.frames.audio.length - 1] : undefined; if (data.raw && lastframe) { const audioEnd = (0, mp3_mpeg_frame_1.rawHeaderOffSet)(lastframe) + (0, mp3_mpeg_frame_1.rawHeaderSize)(lastframe); let id3v1s = data.raw.tags.filter(t => t.id === __1.ITagID.ID3v1 && t.start >= audioEnd); if (id3v1s.length > 0) { if (id3v1s.length > 1) { id3v1s = id3v1s.filter(t => { return t.value && t.value.title && t.value.title[0] !== 'E' && t.value.title[1] !== 'X' && t.end !== data.size; }); } if (id3v1s.length > 1) { result.push({ msg: 'ID3v1: Multiple tags', expected: 1, actual: id3v1s.length }); } if (id3v1s.length > 0) { const id3v1 = id3v1s[id3v1s.length - 1]; if (id3v1.end !== data.size) { result.push({ msg: 'ID3v1: Invalid tag position, not at end of file', expected: (data.size - 128), actual: id3v1.start }); } } } } return result; } analyzeMPEG(data) { const result = []; if (!data.frames || data.frames.audio.length === 0) { result.push({ msg: 'MPEG: No frames found', expected: '>0', actual: 0 }); return result; } let nextdata = (0, mp3_mpeg_frame_1.rawHeaderOffSet)(data.frames.audio[0]) + (0, mp3_mpeg_frame_1.rawHeaderSize)(data.frames.audio[0]); data.frames.audio.slice(1).forEach((f, index) => { if (nextdata !== (0, mp3_mpeg_frame_1.rawHeaderOffSet)(f)) { result.push({ msg: 'MPEG: stream error at position ' + nextdata + ', gap after frame ' + (index + 1), expected: 0, actual: (0, mp3_mpeg_frame_1.rawHeaderOffSet)(f) - nextdata }); } nextdata = (0, mp3_mpeg_frame_1.rawHeaderOffSet)(f) + (0, mp3_mpeg_frame_1.rawHeaderSize)(f); }); const audiostart = (0, mp3_mpeg_frame_1.rawHeaderOffSet)(data.frames.audio[0]); if (data.id3v2 && data.id3v2.head) { const shouldaudiostart = data.id3v2.start + data.id3v2.head.size + 10; if (audiostart !== shouldaudiostart) { result.push({ msg: 'MPEG: Unknown data found between ID3v2 and audio', expected: 0, actual: audiostart - shouldaudiostart }); } } else if (audiostart !== 0) { result.push({ msg: 'MPEG: Unknown data found before audio', expected: 0, actual: audiostart }); } return result; } analyzeXING(data, ignoreXingOffOne) { if (!data.mpeg || !data.frames) { return []; } const head = data.frames.headers[0]; const result = []; if (!head) { if (data.mpeg.encoded === 'VBR') { result.push({ msg: 'XING: VBR detected, but no VBR head frame found', expected: 'VBR Header', actual: 'nothing' }); } return result; } if (head.mode === 'Xing' && data.mpeg.encoded === 'CBR') { result.push({ msg: 'XING: Wrong MPEG head frame for CBR', expected: 'Info', actual: 'Xing' }); } if (head.mode === 'Info' && data.mpeg.encoded === 'VBR') { result.push({ msg: 'XING: Wrong head frame for VBR', expected: 'Xing', actual: 'Info' }); } if (!ignoreXingOffOne && (data.mpeg.frameCount - data.mpeg.frameCountDeclared === 1) && (data.mpeg.audioBytes - data.mpeg.audioBytesDeclared === head.header.size)) { result.push({ msg: 'XING: Wrong ' + head.mode + ' declaration (frameCount and audioBytes must include the ' + head.mode + ' Header itself)', expected: data.mpeg.frameCount, actual: data.mpeg.frameCountDeclared }); } else { if (data.mpeg.frameCount !== data.mpeg.frameCountDeclared) { if (!ignoreXingOffOne || Math.abs(data.mpeg.frameCount - data.mpeg.frameCountDeclared) !== 1) { result.push({ msg: 'XING: Wrong number of frames declared in ' + head.mode + ' Header', expected: data.mpeg.frameCount, actual: data.mpeg.frameCountDeclared }); } } if (data.mpeg.audioBytes !== data.mpeg.audioBytesDeclared) { if (!ignoreXingOffOne || data.mpeg.audioBytes + head.header.size - data.mpeg.audioBytesDeclared === 0) { result.push({ msg: 'XING: Wrong number of data bytes declared in ' + head.mode + ' Header', expected: data.mpeg.audioBytes, actual: data.mpeg.audioBytesDeclared }); } } } return result; } read(filename, options) { return __awaiter(this, void 0, void 0, function* () { const mp3 = new mp3_1.MP3(); const data = yield mp3.read(filename, { id3v1: true, id3v2: true, mpeg: true, raw: true }); if (!data || !data.mpeg || !data.frames) { return Promise.reject(Error('No mpeg data in file:' + filename)); } const head = data.frames.headers[0]; const info = { filename, mode: data.mpeg.encoded, bitRate: data.mpeg.bitRate, channelMode: data.mpeg.mode && data.mpeg.mode.length > 0 ? data.mpeg.mode : undefined, channels: data.mpeg.channels, durationMS: data.mpeg.durationRead * 1000, format: data.mpeg.version && data.mpeg.version.length > 0 ? ('MPEG ' + data.mpeg.version + ' ' + data.mpeg.layer).trim() : 'unknown', header: head ? head.mode : undefined, frames: data.mpeg.frameCount, id3v1: !!data.id3v1, id3v2: !!data.id3v2, warnings: [], tags: { id3v1: data.id3v1, id3v2: data.id3v2, } }; if (options.mpeg) { info.warnings = info.warnings.concat(this.analyzeMPEG(data)); } if (options.xing) { info.warnings = info.warnings.concat(this.analyzeXING(data, !!options.ignoreXingOffOne)); } if (options.id3v1) { info.warnings = info.warnings.concat(this.analyzeID3v1(data)); } if (options.id3v2 && data.id3v2) { info.warnings = info.warnings.concat(this.analyzeID3v2(data)); } return info; }); } } exports.MP3Analyzer = MP3Analyzer; //# sourceMappingURL=mp3.analyzer.js.map