taglib-wasm
Version:
TagLib-Wasm is the universal tagging library for TypeScript/JavaScript platforms: Browsers, Node.js, Deno, Bun, Cloudflare Workers, and Electron apps
154 lines (153 loc) • 5.46 kB
JavaScript
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 {
mp4AtomWireKey,
mp4FreeformAtomNames,
remapKeysFromTagLib,
toTagLibKey
} from "../constants/properties.js";
import { MetadataError, UnsupportedFormatError } from "../errors.js";
import { MP4_ITEM_NAMES_KEY } from "./mp4-item-names.js";
import { buildMutableTag } from "./mutable-tag-impl.js";
const LYRICS_PROPERTY_KEY = "lyrics";
const LYRICS_WIRE_KEY = toTagLibKey(LYRICS_PROPERTY_KEY);
const EMPTY_KEY_SET = /* @__PURE__ */ new Set();
class BaseAudioFileImpl {
constructor(module, fileHandle, sourcePath, originalSource, isPartiallyLoaded = false, partialLoadOptions) {
__publicField(this, "module", module);
__publicField(this, "fileHandle");
__publicField(this, "cachedAudioProperties", null);
__publicField(this, "sourcePath");
__publicField(this, "originalSource");
__publicField(this, "isPartiallyLoaded", false);
__publicField(this, "partialLoadOptions");
/** Text-property wire keys in the partial header at load; undefined if full. */
__publicField(this, "partialKeysAtLoad");
this.fileHandle = fileHandle;
this.sourcePath = sourcePath;
this.originalSource = originalSource;
this.isPartiallyLoaded = isPartiallyLoaded;
this.partialLoadOptions = partialLoadOptions;
this.partialKeysAtLoad = isPartiallyLoaded ? new Set(Object.keys(fileHandle.getProperties())) : void 0;
}
/**
* Wire-key text properties present in the partial header at load but now
* absent — the user deleted them. The partial-load reconstruct subtracts these
* from its preserve-the-original merge so a deletion persists without wiping
* frames beyond the loaded header (taglib-d14). Lyrics are excluded (they ride
* their own reconstruct path); empty for a full load.
*/
partialDeletedPropertyKeys() {
if (!this.partialKeysAtLoad) return EMPTY_KEY_SET;
const current = new Set(Object.keys(this.handle.getProperties()));
const deleted = /* @__PURE__ */ new Set();
for (const key of this.partialKeysAtLoad) {
if (key !== LYRICS_WIRE_KEY && !current.has(key)) deleted.add(key);
}
return deleted;
}
get handle() {
if (!this.fileHandle) {
throw new MetadataError("read", "File handle has been disposed");
}
return this.fileHandle;
}
getFormat() {
return this.handle.getFormat();
}
isFormat(format) {
return this.getFormat() === format;
}
tag() {
return buildMutableTag(this.handle);
}
audioProperties() {
if (!this.cachedAudioProperties) {
this.cachedAudioProperties = this.handle.getAudioProperties() ?? null;
}
return this.cachedAudioProperties ?? void 0;
}
properties() {
const remapped = remapKeysFromTagLib(this.handle.getProperties());
delete remapped[LYRICS_PROPERTY_KEY];
return remapped;
}
setProperties(properties) {
const translated = {};
for (const [key, values] of Object.entries(properties)) {
if (values !== void 0) translated[toTagLibKey(key)] = values;
}
if (!(LYRICS_WIRE_KEY in translated)) {
const existing = this.handle.getProperties()[LYRICS_WIRE_KEY];
if (existing !== void 0) translated[LYRICS_WIRE_KEY] = existing;
}
const atomNames = mp4FreeformAtomNames(Object.keys(translated));
if (atomNames.length > 0) translated[MP4_ITEM_NAMES_KEY] = atomNames;
this.handle.setProperties(translated);
}
getProperty(key) {
const value = this.handle.getProperty(toTagLibKey(key));
if (value !== "") return value;
if (!this.isMP4()) return void 0;
const remapped = this.properties()[key]?.[0];
return remapped === "" ? void 0 : remapped;
}
setProperty(key, value) {
const wireKey = toTagLibKey(key);
const atomNames = mp4FreeformAtomNames([wireKey]);
if (atomNames.length > 0 && this.isMP4()) {
this.handle.setProperties({
...this.handle.getProperties(),
[wireKey]: [value],
[MP4_ITEM_NAMES_KEY]: atomNames
});
return;
}
this.handle.setProperty(wireKey, value);
}
isMP4() {
return this.handle.isMP4();
}
getMP4Item(key) {
if (!this.isMP4()) {
throw new UnsupportedFormatError(this.getFormat(), ["MP4", "M4A"]);
}
const wireKey = mp4AtomWireKey(key);
const value = wireKey !== void 0 ? this.handle.getProperty(wireKey) : this.handle.getMP4Item(key);
return value === "" ? void 0 : value;
}
setMP4Item(key, value) {
if (!this.isMP4()) {
throw new UnsupportedFormatError(this.getFormat(), ["MP4", "M4A"]);
}
const wireKey = mp4AtomWireKey(key);
if (wireKey !== void 0) {
this.handle.setProperty(wireKey, value);
return;
}
this.handle.setMP4Item(key, value);
}
removeMP4Item(key) {
if (!this.isMP4()) {
throw new UnsupportedFormatError(this.getFormat(), ["MP4", "M4A"]);
}
this.handle.removeMP4Item(key);
}
isValid() {
return this.handle.isValid();
}
dispose() {
if (this.fileHandle) {
this.fileHandle.destroy();
this.fileHandle = null;
this.cachedAudioProperties = null;
}
}
[Symbol.dispose]() {
this.dispose();
}
}
export {
BaseAudioFileImpl
};