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

207 lines (206 loc) 6.87 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 { isNamedAudioInput } from "../types/audio-formats.js"; import { InvalidFormatError, TagLibInitializationError } from "../errors.js"; import { isDeno } from "../runtime/detector.js"; import { AudioFileImpl } from "./audio-file-impl.js"; import { loadAudioData } from "./load-audio-data.js"; import { mergeTagUpdates } from "../utils/tag-mapping.js"; import { FileOperationError } from "../errors.js"; import { VERSION } from "../version.js"; import { wrapEmbindHandle } from "./embind-adapter.js"; function toWasiPath(osPath) { if (osPath.startsWith("\\\\") || osPath.startsWith("//")) { throw new FileOperationError( "read", `UNC paths are not supported. Path: ${osPath}` ); } let p = osPath; if (!p.startsWith("/") && !/^[A-Za-z]:/.test(p)) { const g = globalThis; const cwd = isDeno() ? Deno.cwd() : g.process?.cwd?.(); if (cwd) { p = cwd.replace(/[/\\]+$/, "") + "/" + p; } } p = p.replaceAll("\\", "/"); const driveMatch = p.match(/^([A-Za-z]):\//); if (driveMatch) { p = `/${driveMatch[1].toUpperCase()}${p.slice(2)}`; } p = p.replace(/\/\.\//g, "/").replace(/\/+/g, "/"); if (!p.startsWith("/")) p = "/" + p; return p; } class TagLib { constructor(module) { __publicField(this, "module"); this.module = module; } /** * Initialize the TagLib Wasm module and return a ready-to-use instance. * @param options - Wasm loading configuration (binary, URL, backend selection). * @returns A configured TagLib instance. * @throws {TagLibInitializationError} If the Wasm module fails to load. */ static async initialize(options) { const { loadTagLibModule } = await import("../runtime/module-loader.js"); const module = await loadTagLibModule(options); return new TagLib(module); } /** * Open an audio file for reading and writing metadata. * @param input - File path, Uint8Array, ArrayBuffer, File object, or NamedAudioInput. * @param options - Partial-loading options for large files. * @returns An AudioFile instance (use `using` for automatic cleanup). * @throws {TagLibInitializationError} If the module is not properly initialized. * @throws {InvalidFormatError} If the file is corrupted or unsupported. */ async open(input, options) { if (!this.module.createFileHandle) { throw new TagLibInitializationError( "TagLib module not properly initialized: createFileHandle not found. Make sure the module is fully loaded before calling open." ); } const actualInput = isNamedAudioInput(input) ? input.data : input; const sourcePath = typeof actualInput === "string" ? actualInput : void 0; if (typeof actualInput === "string" && this.module.isWasi) { const fileHandle2 = this.module.createFileHandle(); try { const fh = fileHandle2; if (fh.loadFromPath) { const wasiPath = toWasiPath(actualInput); const success = fh.loadFromPath(wasiPath); if (!success) { throw new InvalidFormatError( `Failed to load audio file. Path: ${actualInput}` ); } return new AudioFileImpl( this.module, fileHandle2, sourcePath, actualInput, false ); } } catch (error) { if (typeof fileHandle2.destroy === "function") { fileHandle2.destroy(); } throw error; } } const opts = { partial: true, maxHeaderSize: 1024 * 1024, maxFooterSize: 128 * 1024, ...options }; const { data: audioData, isPartiallyLoaded } = await loadAudioData( actualInput, opts ); const uint8Array = actualInput instanceof Uint8Array && audioData.buffer === actualInput.buffer ? new Uint8Array( audioData.buffer.slice( audioData.byteOffset, audioData.byteOffset + audioData.byteLength ) ) : audioData; const rawHandle = this.module.createFileHandle(); const fileHandle = this.module.isWasi ? rawHandle : wrapEmbindHandle(rawHandle); try { const success = fileHandle.loadFromBuffer(uint8Array); if (!success) { throw new InvalidFormatError( "Failed to load audio file. File may be corrupted or in an unsupported format", uint8Array.byteLength ); } return new AudioFileImpl( this.module, fileHandle, sourcePath, actualInput, isPartiallyLoaded, opts ); } catch (error) { if (typeof fileHandle.destroy === "function") { fileHandle.destroy(); } throw error; } } async edit(input, fn) { const file = await this.open(input); try { await fn(file); if (typeof input === "string") { await file.saveToFile(); } else { file.save(); return file.getFileBuffer(); } } finally { file.dispose(); } } /** * Update tags in a file and save to disk in one operation. * @param path - Path to the audio file. * @param tags - Tag fields to update (partial update supported). * @throws {InvalidFormatError} If the file is corrupted or unsupported. * @throws {FileOperationError} If saving to disk fails. */ async updateFile(path, tags) { const file = await this.open(path); try { mergeTagUpdates(file, tags); await file.saveToFile(); } finally { file.dispose(); } } /** * Copy an audio file to a new path with updated tags. * @param sourcePath - Path to the source audio file. * @param destPath - Destination path for the tagged copy. * @param tags - Tag fields to set on the copy. * @throws {InvalidFormatError} If the source file is corrupted or unsupported. * @throws {FileOperationError} If reading or writing fails. */ async copyWithTags(sourcePath, destPath, tags) { const file = await this.open(sourcePath); try { mergeTagUpdates(file, tags); await file.saveToFile(destPath); } finally { file.dispose(); } } /** Returns the taglib-wasm version with embedded TagLib version. */ version() { return `${VERSION} (TagLib ${this.taglibVersion()})`; } taglibVersion() { if (this.module.getVersion) { return this.module.getVersion(); } if (this.module.version) { return this.module.version(); } return "unknown"; } } async function createTagLib(module) { return new TagLib(module); } export { TagLib, createTagLib };