UNPKG

taglib-wasm

Version:

TagLib-Wasm is the universal tagging library for TypeScript/JavaScript platforms: Browsers, Node.js, Deno, Bun, Cloudflare Workers, and Electron apps

274 lines (273 loc) 8.02 kB
var __defProp = Object.defineProperty; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); import { PICTURE_TYPE_NAMES, PICTURE_TYPE_VALUES } from "../types.js"; import * as bwf from "./audio-file-bwf.js"; import { assertFrameBodies, assertFrameId, assertMp3, toPublicFrame } from "./id3v2-frames.js"; import { errorMessage, FileOperationError, UnsupportedFormatError } from "../errors.js"; import { writeFileData } from "../utils/write.js"; import { getNodeFsSync } from "../utils/node-fs.js"; import { normalized } from "../utils/rating.js"; import { isDeno } from "../runtime/detector.js"; import { BaseAudioFileImpl } from "./audio-file-base.js"; import { saveViaFreshHandle } from "./save-reconstruct.js"; function sortChapters(list) { return [...list].sort((a, b) => a.startTimeMs - b.startTimeMs); } function inferEndTimeMs(sorted, index, trackEndMs) { const own = sorted[index].endTimeMs; if (own !== void 0) return own; const next = sorted[index + 1]; return next ? next.startTimeMs : trackEndMs; } function readFileSync(path) { if (isDeno()) return Deno.readFileSync(path); const fs = getNodeFsSync(); if (fs) return new Uint8Array(fs.readFileSync(path)); throw new FileOperationError( "read", "node:fs is unavailable in this runtime: cannot read file data from disk", path ); } class AudioFileImpl extends BaseAudioFileImpl { constructor(module, fileHandle, sourcePath, originalSource, isPartiallyLoaded = false, partialLoadOptions) { super( module, fileHandle, sourcePath, originalSource, isPartiallyLoaded, partialLoadOptions ); __publicField(this, "pathModeBuffer", null); } save() { if (this.isPartiallyLoaded && this.originalSource) { throw new FileOperationError( "save", "Cannot save partially loaded file directly. Use saveToFile() instead" ); } this.cachedAudioProperties = null; this.pathModeBuffer = null; return this.handle.save(); } getFileBuffer() { const buffer = this.handle.getBuffer(); if (buffer.length > 0) return buffer; if (this.pathModeBuffer) return this.pathModeBuffer; if (this.sourcePath) { try { this.pathModeBuffer = readFileSync(this.sourcePath); return this.pathModeBuffer; } catch (error) { if (error instanceof FileOperationError) throw error; throw new FileOperationError( "read", `Cannot return file data: ${errorMessage(error)}`, this.sourcePath, { cause: error } ); } } throw new FileOperationError( "read", "No file data available: in-memory buffer is empty and no source path is set" ); } async saveToFile(path) { const targetPath = path ?? this.sourcePath; if (!targetPath) { throw new FileOperationError( "save", "No file path available. Provide a path or open the file from a path" ); } if (this.isPartiallyLoaded && this.originalSource) { await saveViaFreshHandle( this.module, this.handle, this.originalSource, targetPath, false, this.partialDeletedPropertyKeys() ); this.isPartiallyLoaded = false; this.originalSource = void 0; } else if (this.module.isWasi && this.sourcePath && targetPath !== this.sourcePath) { await saveViaFreshHandle( this.module, this.handle, this.sourcePath, targetPath, true ); } else { if (!this.save()) { throw new FileOperationError( "save", "Failed to save changes to in-memory buffer" ); } const buffer = this.handle.getBuffer(); if (buffer.length > 0) { await writeFileData(targetPath, buffer); } } this.pathModeBuffer = null; } getPictures() { const picturesArray = this.handle.getPictures(); return picturesArray.map((pic) => ({ mimeType: pic.mimeType, data: pic.data, type: PICTURE_TYPE_NAMES[pic.type] ?? "Other", description: pic.description })); } setPictures(pictures) { this.handle.setPictures(pictures.map((pic) => ({ mimeType: pic.mimeType, data: pic.data, type: PICTURE_TYPE_VALUES[pic.type] ?? 0, description: pic.description ?? "" }))); } addPicture(picture) { this.handle.addPicture({ mimeType: picture.mimeType, data: picture.data, type: PICTURE_TYPE_VALUES[picture.type] ?? 0, description: picture.description ?? "" }); } removePictures() { this.handle.removePictures(); } getChapters() { const sorted = sortChapters(this.handle.getChapters()); const trackEndMs = this.audioProperties()?.durationMs; return sorted.map((c, i) => ({ startTimeMs: c.startTimeMs, endTimeMs: inferEndTimeMs(sorted, i, trackEndMs), title: c.title || void 0, id: c.id || void 0, source: c.source })); } setChapters(chapters, options) { const fmt = this.getFormat(); if (fmt !== "MP3" && fmt !== "MP4") { throw new UnsupportedFormatError(fmt, ["MP3", "MP4"], { operation: "setChapters" }); } const sorted = sortChapters(chapters); const trackEndMs = this.audioProperties()?.durationMs; const style = options?.mp4ChapterStyle ?? "quicktime"; const source = fmt === "MP4" ? style : "id3"; const raw = sorted.map((c, i) => ({ id: c.id, startTimeMs: c.startTimeMs, endTimeMs: inferEndTimeMs(sorted, i, trackEndMs) ?? c.startTimeMs, title: c.title, source })); this.handle.setChapters(raw, style); } getBext() { return bwf.getBext(this.handle); } setBext(bext) { bwf.setBext(this.handle, this.getFormat(), bext); } getBextData() { return bwf.getBextData(this.handle); } setBextData(data) { bwf.setBextData(this.handle, this.getFormat(), data); } getIxml() { return bwf.getIxml(this.handle); } setIxml(data) { bwf.setIxml(this.handle, this.getFormat(), data); } getRatings() { return this.handle.getRatings().map( (r) => ({ rating: r.rating, email: r.email || void 0, counter: r.counter || void 0 }) ); } setRatings(ratings) { this.handle.setRatings(ratings.map((r) => ({ rating: r.rating, email: r.email ?? "", counter: r.counter ?? 0 }))); } getLyrics() { return this.handle.getLyrics().map((l) => { const out = { text: l.text }; if (l.description) out.description = l.description; if (l.language) out.language = l.language; return out; }); } setLyrics(lyrics) { this.handle.setLyrics(lyrics.map((l) => ({ text: l.text, description: l.description ?? "", language: l.language ?? "" }))); } getRating() { const ratings = this.getRatings(); return ratings.length > 0 ? normalized(ratings[0].rating) : void 0; } setRating(rating, email) { this.setRatings([{ rating, email, counter: 0 }]); } getId3v2Frames(id) { assertMp3(this.getFormat()); if (id !== void 0) assertFrameId(id, "read"); return this.handle.getId3v2Frames(id ?? "").map(toPublicFrame); } setId3v2Frames(id, data) { assertMp3(this.getFormat()); assertFrameId(id, "write"); assertFrameBodies(id, data); this.handle.setId3v2Frames(id, data); } removeId3v2Frames(id) { assertMp3(this.getFormat()); assertFrameId(id, "write"); this.handle.removeId3v2Frames(id); } hasId3Tags() { return this.handle.hasId3Tags(); } stripId3Tags(opts) { this.handle.stripId3Tags({ v1: opts?.v1 ?? true, v2: opts?.v2 ?? true }); } } export { AudioFileImpl, readFileSync };