UNPKG

jamp3

Version:

mp3, id3v1, id3v2 - reader & writer

95 lines (87 loc) 3.84 kB
import * as zlib from 'zlib'; import {IID3V2} from '../id3v2.types'; import {WriterStream} from '../../common/stream-writer'; import {MemoryWriterStream} from '../../common/stream-writer-memory'; import {matchFrame} from './id3v2.frame.match'; import {ID3v2_FRAME_HEADER_LENGTHS} from '../id3v2.header.consts'; import {BufferUtils} from '../../common/buffer'; import {ascii, Encodings, IEncoding} from '../../common/encodings'; import {ensureID3v2FrameVersionDef} from './id3v2.frame.version'; import {Id3v2RawWriter} from '../id3v2.writer.raw'; export async function writeRawSubFrames(frames: Array<IID3V2.Frame>, stream: WriterStream, head: IID3V2.TagHeader, defaultEncoding?: string): Promise<void> { const writer = new Id3v2RawWriter(stream, head, {paddingSize: 0}); const rawframes = await writeRawFrames(frames, head, defaultEncoding); for (const frame of rawframes) { await writer.writeFrame(frame); } } export async function writeRawFrames(frames: Array<IID3V2.Frame>, head: IID3V2.TagHeader, defaultEncoding?: string): Promise<Array<IID3V2.RawFrame>> { const result: Array<IID3V2.RawFrame> = []; for (const frame of frames) { const raw = await writeRawFrame(frame, head, defaultEncoding); result.push(raw); } return result; } async function writeRawFrame(frame: IID3V2.Frame, head: IID3V2.TagHeader, defaultEncoding?: string): Promise<IID3V2.RawFrame> { const frameHead: IID3V2.FrameHeader = frame.head || { size: 0, statusFlags: {}, formatFlags: {} }; let id = frame.id; let data: Buffer; if (frame.invalid) { const val = <IID3V2.FrameValue.Bin>frame.value; if (!val.bin) { return Promise.reject(Error('Invalid frame definition (trying to write a frame with parser error)')); } data = val.bin; } else { const stream = new MemoryWriterStream(); const orgDef = matchFrame(frame.id); if (orgDef.versions.indexOf(head.ver) < 0) { const toWriteFrameID = ensureID3v2FrameVersionDef(frame.id, head.ver); if (!toWriteFrameID) { await orgDef.impl.write(frame, stream, head, defaultEncoding); } else { id = toWriteFrameID; const toWriteFrameDef = matchFrame(toWriteFrameID); await toWriteFrameDef.impl.write(frame, stream, head, defaultEncoding); } } else { await orgDef.impl.write(frame, stream, head, defaultEncoding); } data = stream.toBuffer(); if ((frameHead.formatFlags) && (frameHead.formatFlags.compressed)) { const sizebytes = ID3v2_FRAME_HEADER_LENGTHS.SIZE[head.ver]; const uncompressedStream = new MemoryWriterStream(); if (sizebytes === 4) { await uncompressedStream.writeUInt4Byte(data.length); } else { await uncompressedStream.writeUInt3Byte(data.length); } data = BufferUtils.concatBuffer(uncompressedStream.toBuffer(), zlib.deflateSync(data)); } else if ((frameHead.formatFlags) && (frameHead.formatFlags.dataLengthIndicator)) { const dataLengthStream = new MemoryWriterStream(); await dataLengthStream.writeSyncSafeInt(data.length); data = BufferUtils.concatBuffer(dataLengthStream.toBuffer(), data); } } if (frameHead.formatFlags && frameHead.formatFlags.grouping) { if (frame.groupId === undefined) { return Promise.reject(Error('Missing frame groupId but flag is set')); } const buf = BufferUtils.zeroBuffer(1); buf[0] = frame.groupId; data = BufferUtils.concatBuffer(buf, data); } return {id: id, start: 0, end: 0, size: data.length, data: data, statusFlags: frameHead.statusFlags || {}, formatFlags: frameHead.formatFlags || {}}; } export function getWriteTextEncoding(frame: IID3V2.Frame, head: IID3V2.TagHeader, defaultEncoding?: string): IEncoding { let encoding = (frame.head ? frame.head.encoding : undefined) || defaultEncoding; if (!encoding || !Encodings[encoding]) { encoding = (head.ver === 4) ? 'utf-8' : 'ucs2'; } return Encodings[encoding] || ascii; }