music-metadata
Version:
Music metadata parser for Node.js, supporting virtual any audio and tag format.
66 lines • 3.13 kB
JavaScript
import initDebug from 'debug';
import * as AtomToken from './AtomToken.js';
import { Header } from './AtomToken.js';
const debug = initDebug('music-metadata:parser:MP4:Atom');
export class Atom {
static async readAtom(tokenizer, dataHandler, parent, remaining) {
// Parse atom header
const offset = tokenizer.position;
debug(`Reading next token on offset=${offset}...`); // buf.toString('ascii')
const header = await tokenizer.readToken(AtomToken.Header);
const extended = header.length === 1n;
if (extended) {
header.length = await tokenizer.readToken(AtomToken.ExtendedSize);
}
const atomBean = new Atom(header, extended, parent);
const payloadLength = atomBean.getPayloadLength(remaining);
debug(`parse atom name=${atomBean.atomPath}, extended=${atomBean.extended}, offset=${offset}, len=${atomBean.header.length}`); // buf.toString('ascii')
await atomBean.readData(tokenizer, dataHandler, payloadLength);
return atomBean;
}
constructor(header, extended, parent) {
this.header = header;
this.extended = extended;
this.parent = parent;
this.children = [];
this.atomPath = (this.parent ? `${this.parent.atomPath}.` : '') + this.header.name;
}
getHeaderLength() {
return this.extended ? 16 : 8;
}
getPayloadLength(remaining) {
return (this.header.length === 0n ? remaining : Number(this.header.length)) - this.getHeaderLength();
}
async readAtoms(tokenizer, dataHandler, size) {
while (size > 0) {
const atomBean = await Atom.readAtom(tokenizer, dataHandler, this, size);
this.children.push(atomBean);
size -= atomBean.header.length === 0n ? size : Number(atomBean.header.length);
}
}
async readData(tokenizer, dataHandler, remaining) {
switch (this.header.name) {
// "Container" atoms, contains nested atoms
case 'moov': // The Movie Atom: contains other atoms
case 'udta': // User defined atom
case 'trak':
case 'mdia': // Media atom
case 'minf': // Media Information Atom
case 'stbl': // The Sample Table Atom
case '<id>':
case 'ilst':
case 'tref':
return this.readAtoms(tokenizer, dataHandler, this.getPayloadLength(remaining));
case 'meta': { // Metadata Atom, ref: https://developer.apple.com/library/content/documentation/QuickTime/QTFF/Metadata/Metadata.html#//apple_ref/doc/uid/TP40000939-CH1-SW8
// meta has 4 bytes of padding, ignore
const peekHeader = await tokenizer.peekToken(Header);
const paddingLength = peekHeader.name === 'hdlr' ? 0 : 4;
await tokenizer.ignore(paddingLength);
return this.readAtoms(tokenizer, dataHandler, this.getPayloadLength(remaining) - paddingLength);
}
default:
return dataHandler(this, remaining);
}
}
}
//# sourceMappingURL=Atom.js.map