taglib-wasm 1.8.3 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/README.md +8 -1
  2. package/dist/disc-folder.d.ts +19 -0
  3. package/dist/disc-folder.d.ts.map +1 -0
  4. package/dist/disc-folder.js +550 -0
  5. package/dist/index.browser.d.ts +3 -4
  6. package/dist/index.browser.d.ts.map +1 -1
  7. package/dist/index.browser.js +187 -176
  8. package/dist/index.d.ts +3 -4
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js +4 -12
  11. package/dist/simple.browser.js +172 -33
  12. package/dist/src/constants/complex-properties.d.ts +0 -114
  13. package/dist/src/constants/complex-properties.d.ts.map +1 -1
  14. package/dist/src/constants/complex-properties.js +0 -55
  15. package/dist/src/constants/index.d.ts +0 -1
  16. package/dist/src/constants/index.d.ts.map +1 -1
  17. package/dist/src/constants/index.js +0 -1
  18. package/dist/src/constants/utilities.d.ts +13 -0
  19. package/dist/src/constants/utilities.d.ts.map +1 -1
  20. package/dist/src/constants/utilities.js +9 -1
  21. package/dist/src/file-utils/import-operations.d.ts.map +1 -1
  22. package/dist/src/file-utils/import-operations.js +1 -1
  23. package/dist/src/folder-api/file-processors.d.ts.map +1 -1
  24. package/dist/src/folder-api/file-processors.js +6 -5
  25. package/dist/src/folder-api/scan-for-albums.js +3 -3
  26. package/dist/src/folder-api/scan-operations.js +2 -2
  27. package/dist/src/msgpack/decoder.d.ts +2 -2
  28. package/dist/src/msgpack/decoder.d.ts.map +1 -1
  29. package/dist/src/msgpack/decoder.js +5 -6
  30. package/dist/src/msgpack/encoder.d.ts.map +1 -1
  31. package/dist/src/msgpack/encoder.js +9 -6
  32. package/dist/src/msgpack/index.d.ts +1 -1
  33. package/dist/src/msgpack/index.d.ts.map +1 -1
  34. package/dist/src/msgpack/types.d.ts +10 -0
  35. package/dist/src/msgpack/types.d.ts.map +1 -1
  36. package/dist/src/msgpack/utils.d.ts +2 -1
  37. package/dist/src/msgpack/utils.d.ts.map +1 -1
  38. package/dist/src/runtime/module-loader-browser.d.ts.map +1 -1
  39. package/dist/src/runtime/module-loader.d.ts.map +1 -1
  40. package/dist/src/runtime/module-loader.js +3 -3
  41. package/dist/src/runtime/platform-io.js +1 -1
  42. package/dist/src/runtime/unified-loader/module-loading.d.ts.map +1 -1
  43. package/dist/src/runtime/unified-loader/module-loading.js +1 -1
  44. package/dist/src/runtime/wasi-adapter/file-handle.d.ts +8 -0
  45. package/dist/src/runtime/wasi-adapter/file-handle.d.ts.map +1 -1
  46. package/dist/src/runtime/wasi-adapter/file-handle.js +44 -4
  47. package/dist/src/runtime/wasi-fs-deno.d.ts.map +1 -1
  48. package/dist/src/runtime/wasi-fs-deno.js +1 -1
  49. package/dist/src/simple/batch-operations.d.ts.map +1 -1
  50. package/dist/src/simple/batch-operations.js +14 -10
  51. package/dist/src/simple/picture-operations.js +1 -1
  52. package/dist/src/taglib/audio-file-base.d.ts +15 -9
  53. package/dist/src/taglib/audio-file-base.d.ts.map +1 -1
  54. package/dist/src/taglib/audio-file-base.js +35 -4
  55. package/dist/src/taglib/audio-file-impl.d.ts +2 -2
  56. package/dist/src/taglib/audio-file-impl.d.ts.map +1 -1
  57. package/dist/src/taglib/audio-file-impl.js +25 -13
  58. package/dist/src/taglib/audio-file-interface.d.ts +27 -6
  59. package/dist/src/taglib/audio-file-interface.d.ts.map +1 -1
  60. package/dist/src/taglib/auto-dispose.d.ts +6 -0
  61. package/dist/src/taglib/auto-dispose.d.ts.map +1 -0
  62. package/dist/src/taglib/auto-dispose.js +13 -0
  63. package/dist/src/taglib/embind-adapter.d.ts +2 -2
  64. package/dist/src/taglib/embind-adapter.d.ts.map +1 -1
  65. package/dist/src/taglib/embind-adapter.js +17 -0
  66. package/dist/src/taglib/extra-state-registry.d.ts.map +1 -1
  67. package/dist/src/taglib/extra-state-registry.js +9 -0
  68. package/dist/src/taglib/metadata-extent.d.ts.map +1 -1
  69. package/dist/src/taglib/metadata-extent.js +44 -1
  70. package/dist/src/taglib/mutable-tag.d.ts +15 -15
  71. package/dist/src/taglib/mutable-tag.d.ts.map +1 -1
  72. package/dist/src/taglib/save-reconstruct.d.ts.map +1 -1
  73. package/dist/src/taglib/taglib-class.d.ts.map +1 -1
  74. package/dist/src/types/tags.d.ts +0 -4
  75. package/dist/src/types/tags.d.ts.map +1 -1
  76. package/dist/src/utils/mirror-fields.d.ts +2 -0
  77. package/dist/src/utils/mirror-fields.d.ts.map +1 -1
  78. package/dist/src/utils/mirror-fields.js +11 -3
  79. package/dist/src/utils/node-fs.d.ts +10 -10
  80. package/dist/src/utils/node-fs.d.ts.map +1 -1
  81. package/dist/src/utils/node-fs.js +2 -1
  82. package/dist/src/utils/tag-mapping.d.ts.map +1 -1
  83. package/dist/src/utils/tag-mapping.js +1 -1
  84. package/dist/src/version.d.ts +1 -1
  85. package/dist/src/version.js +1 -1
  86. package/dist/src/wasm.d.ts +20 -1
  87. package/dist/src/wasm.d.ts.map +1 -1
  88. package/dist/src/web-utils/canvas.d.ts.map +1 -1
  89. package/dist/src/web-utils/canvas.js +6 -1
  90. package/dist/src/web-utils/data-url.js +1 -1
  91. package/dist/taglib-wasi.wasm +0 -0
  92. package/dist/taglib-web.wasm +0 -0
  93. package/dist/taglib-wrapper.js +1 -1
  94. package/package.json +23 -15
  95. package/dist/src/constants/tags.d.ts +0 -81
  96. package/dist/src/constants/tags.d.ts.map +0 -1
  97. package/dist/src/constants/tags.js +0 -86
package/dist/index.js CHANGED
@@ -51,13 +51,12 @@ import {
51
51
  FormatMappings,
52
52
  getAllProperties,
53
53
  getAllPropertyKeys,
54
- getAllTagNames,
55
54
  getPropertiesByFormat,
56
55
  getPropertyMetadata,
57
56
  isValidProperty,
58
- isValidTagName,
59
57
  PROPERTIES,
60
- Tags
58
+ propertyValue,
59
+ propertyValues
61
60
  } from "./src/constants.js";
62
61
  import {
63
62
  copyCoverArt,
@@ -95,10 +94,6 @@ import {
95
94
  PICTURE_TYPE_NAMES,
96
95
  PICTURE_TYPE_VALUES
97
96
  } from "./src/types.js";
98
- import {
99
- COMPLEX_PROPERTIES,
100
- COMPLEX_PROPERTY_KEY
101
- } from "./src/constants/complex-properties.js";
102
97
  import { RatingUtils } from "./src/utils/rating.js";
103
98
  import * as bwf from "./src/bwf/bext.js";
104
99
  import { loadTagLibModule } from "./src/runtime/module-loader.js";
@@ -106,8 +101,6 @@ export {
106
101
  AudioFileImpl,
107
102
  BITRATE_CONTROL_MODE_NAMES,
108
103
  BITRATE_CONTROL_MODE_VALUES,
109
- COMPLEX_PROPERTIES,
110
- COMPLEX_PROPERTY_KEY,
111
104
  EnvironmentError,
112
105
  FileOperationError,
113
106
  FormatMappings,
@@ -122,7 +115,6 @@ export {
122
115
  TagLib,
123
116
  TagLibError,
124
117
  TagLibInitializationError,
125
- Tags,
126
118
  UnsupportedFormatError,
127
119
  addPicture,
128
120
  applyCoverArt,
@@ -149,7 +141,6 @@ export {
149
141
  findPictureByType,
150
142
  getAllProperties,
151
143
  getAllPropertyKeys,
152
- getAllTagNames,
153
144
  getPropertiesByFormat,
154
145
  getPropertyMetadata,
155
146
  groupAlbums,
@@ -168,11 +159,12 @@ export {
168
159
  isUnsupportedFormatError,
169
160
  isValidAudioFile,
170
161
  isValidProperty,
171
- isValidTagName,
172
162
  loadPictureFromFile,
173
163
  loadTagLibModule,
174
164
  pictureToDataURL,
175
165
  prepareWasmForEmbedding,
166
+ propertyValue,
167
+ propertyValues,
176
168
  readCoverArt,
177
169
  readFormat,
178
170
  readMetadata,
@@ -1,8 +1,13 @@
1
1
  var __defProp = Object.defineProperty;
2
2
  var __getOwnPropNames = Object.getOwnPropertyNames;
3
3
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
4
- var __esm = (fn, res) => function __init() {
5
- return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
4
+ var __esm = (fn, res, err) => function __init() {
5
+ if (err) throw err[0];
6
+ try {
7
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
8
+ } catch (e) {
9
+ throw err = [e], e;
10
+ }
6
11
  };
7
12
  var __export = (target, all) => {
8
13
  for (var name in all)
@@ -1648,6 +1653,23 @@ var init_mutable_tag_impl = __esm({
1648
1653
  }
1649
1654
  });
1650
1655
 
1656
+ // src/taglib/auto-dispose.ts
1657
+ function registerAutoDispose(target, handle) {
1658
+ registry.register(target, handle, target);
1659
+ }
1660
+ function unregisterAutoDispose(target) {
1661
+ registry.unregister(target);
1662
+ }
1663
+ var registry;
1664
+ var init_auto_dispose = __esm({
1665
+ "src/taglib/auto-dispose.ts"() {
1666
+ "use strict";
1667
+ registry = new FinalizationRegistry((handle) => {
1668
+ handle.destroy();
1669
+ });
1670
+ }
1671
+ });
1672
+
1651
1673
  // src/taglib/audio-file-base.ts
1652
1674
  var LYRICS_PROPERTY_KEY, LYRICS_WIRE_KEY, EMPTY_KEY_SET, BaseAudioFileImpl;
1653
1675
  var init_audio_file_base = __esm({
@@ -1657,6 +1679,7 @@ var init_audio_file_base = __esm({
1657
1679
  init_errors2();
1658
1680
  init_mp4_item_names();
1659
1681
  init_mutable_tag_impl();
1682
+ init_auto_dispose();
1660
1683
  LYRICS_PROPERTY_KEY = "lyrics";
1661
1684
  LYRICS_WIRE_KEY = toTagLibKey(LYRICS_PROPERTY_KEY);
1662
1685
  EMPTY_KEY_SET = /* @__PURE__ */ new Set();
@@ -1677,6 +1700,7 @@ var init_audio_file_base = __esm({
1677
1700
  this.isPartiallyLoaded = isPartiallyLoaded;
1678
1701
  this.partialLoadOptions = partialLoadOptions;
1679
1702
  this.partialKeysAtLoad = isPartiallyLoaded ? new Set(Object.keys(fileHandle.getProperties())) : void 0;
1703
+ registerAutoDispose(this, fileHandle);
1680
1704
  }
1681
1705
  /**
1682
1706
  * Wire-key text properties present in the partial header at load but now
@@ -1736,16 +1760,36 @@ var init_audio_file_base = __esm({
1736
1760
  this.handle.setProperties(translated);
1737
1761
  }
1738
1762
  getProperty(key) {
1739
- const value = this.handle.getProperty(toTagLibKey(fromTagLibKey(key)));
1740
- if (value !== "") return value;
1763
+ const wireKey = toTagLibKey(fromTagLibKey(key));
1764
+ const values = this.handle.getProperties()[wireKey]?.filter(
1765
+ (v) => v !== ""
1766
+ );
1767
+ if (values !== void 0 && values.length > 0) return values;
1741
1768
  if (!this.isMP4()) return void 0;
1742
- const remapped = this.properties()[key]?.[0];
1743
- return remapped === "" ? void 0 : remapped;
1769
+ const remapped = this.properties()[key]?.filter((v) => v !== "");
1770
+ return remapped !== void 0 && remapped.length > 0 ? remapped : void 0;
1744
1771
  }
1745
1772
  setProperty(key, value) {
1746
1773
  const wireKey = toTagLibKey(fromTagLibKey(key));
1747
1774
  const atomNames = mp4FreeformAtomNames([wireKey]);
1748
1775
  if (atomNames.length > 0 && this.isMP4()) {
1776
+ if (value === "") {
1777
+ const prior = this.handle.getMp4ItemRemovals?.() ?? [];
1778
+ this.handle.setMp4ItemRemovals?.([
1779
+ .../* @__PURE__ */ new Set([...prior, ...atomNames])
1780
+ ]);
1781
+ if (this.module.isWasi) {
1782
+ this.handle.setProperties({
1783
+ ...this.handle.getProperties(),
1784
+ [wireKey]: []
1785
+ });
1786
+ } else {
1787
+ const rest = { ...this.handle.getProperties() };
1788
+ delete rest[wireKey];
1789
+ this.handle.setProperties(rest);
1790
+ }
1791
+ return;
1792
+ }
1749
1793
  this.handle.setProperties({
1750
1794
  ...this.handle.getProperties(),
1751
1795
  [wireKey]: [value],
@@ -1755,6 +1799,14 @@ var init_audio_file_base = __esm({
1755
1799
  }
1756
1800
  this.handle.setProperty(wireKey, value);
1757
1801
  }
1802
+ /**
1803
+ * The empty-string clearing contract, named (taglib-qyw2): both backends
1804
+ * remove the property when the written value is empty, and setProperty's
1805
+ * wire-key normalization + MP4 freeform atom-name handling apply unchanged.
1806
+ */
1807
+ removeProperty(key) {
1808
+ this.setProperty(key, "");
1809
+ }
1758
1810
  isMP4() {
1759
1811
  return this.handle.isMP4();
1760
1812
  }
@@ -1787,6 +1839,7 @@ var init_audio_file_base = __esm({
1787
1839
  return this.handle.isValid();
1788
1840
  }
1789
1841
  dispose() {
1842
+ unregisterAutoDispose(this);
1790
1843
  if (this.fileHandle) {
1791
1844
  this.fileHandle.destroy();
1792
1845
  this.fileHandle = null;
@@ -1860,6 +1913,7 @@ function isValidBitrateMode(value) {
1860
1913
  }
1861
1914
  function wrapEmbindHandle(raw) {
1862
1915
  const stagedId3v2Frames = {};
1916
+ let destroyed = false;
1863
1917
  const overrides = {
1864
1918
  getTagData() {
1865
1919
  const tw = raw.getTag();
@@ -1939,6 +1993,22 @@ function wrapEmbindHandle(raw) {
1939
1993
  stripId3Tags(opts) {
1940
1994
  raw.stripId3Tags(opts);
1941
1995
  },
1996
+ destroy() {
1997
+ if (destroyed) return;
1998
+ destroyed = true;
1999
+ raw.destroy();
2000
+ },
2001
+ get destroyed() {
2002
+ return destroyed;
2003
+ },
2004
+ // taglib-qyw2: the PropertyMap erase pass computes freeform item names
2005
+ // from the property table (upper-cased) and misses the restored exact-case
2006
+ // atom (taglib-bnhl), so clears queue the exact name here; the imperative
2007
+ // Embind model applies removeItem immediately, before setProperties.
2008
+ setMp4ItemRemovals(removals) {
2009
+ const handle = raw;
2010
+ for (const name of removals) handle.removeMP4Item(name);
2011
+ },
1942
2012
  getAudioProperties() {
1943
2013
  const pw = raw.getAudioProperties();
1944
2014
  if (!pw) return null;
@@ -2038,6 +2108,15 @@ var init_extra_state_registry = __esm({
2038
2108
  else if (complete) target.setIxml(null);
2039
2109
  }
2040
2110
  },
2111
+ {
2112
+ name: "_mp4ItemRemovals",
2113
+ copy(target, source, complete) {
2114
+ const v = source.getMp4ItemRemovals?.();
2115
+ if (complete || v !== void 0 && v.length > 0) {
2116
+ target.setMp4ItemRemovals?.(v ?? []);
2117
+ }
2118
+ }
2119
+ },
2041
2120
  {
2042
2121
  name: "id3v2Frames",
2043
2122
  copy(target, source) {
@@ -2226,7 +2305,7 @@ var init_audio_file_impl = __esm({
2226
2305
  mimeType: pic.mimeType,
2227
2306
  data: pic.data,
2228
2307
  type: PICTURE_TYPE_NAMES[pic.type] ?? "Other",
2229
- description: pic.description
2308
+ ...pic.description !== void 0 ? { description: pic.description } : {}
2230
2309
  }));
2231
2310
  }
2232
2311
  setPictures(pictures) {
@@ -2251,13 +2330,20 @@ var init_audio_file_impl = __esm({
2251
2330
  getChapters() {
2252
2331
  const sorted = sortChapters(this.handle.getChapters());
2253
2332
  const trackEndMs = this.audioProperties()?.durationMs;
2254
- return sorted.map((c, i) => ({
2255
- startTimeMs: c.startTimeMs,
2256
- endTimeMs: inferEndTimeMs(sorted, i, trackEndMs),
2257
- title: c.title || void 0,
2258
- id: c.id || void 0,
2259
- source: c.source
2260
- }));
2333
+ return sorted.map((c, i) => {
2334
+ const endTimeMs = inferEndTimeMs(sorted, i, trackEndMs);
2335
+ return {
2336
+ startTimeMs: c.startTimeMs,
2337
+ ...endTimeMs !== void 0 ? { endTimeMs } : {},
2338
+ // Truthiness, not !== undefined: an empty CHAP title/id must read back
2339
+ // as ABSENT (the old `c.title || undefined` contract; the Embind
2340
+ // reader emits "" unconditionally, WASI omits — coalescing keeps the
2341
+ // backends agreeing).
2342
+ ...c.title ? { title: c.title } : {},
2343
+ ...c.id ? { id: c.id } : {},
2344
+ ...c.source !== void 0 ? { source: c.source } : {}
2345
+ };
2346
+ });
2261
2347
  }
2262
2348
  setChapters(chapters, options) {
2263
2349
  const fmt = this.getFormat();
@@ -2271,10 +2357,10 @@ var init_audio_file_impl = __esm({
2271
2357
  const style = options?.mp4ChapterStyle ?? "quicktime";
2272
2358
  const source = fmt === "MP4" ? style : "id3";
2273
2359
  const raw = sorted.map((c, i) => ({
2274
- id: c.id,
2360
+ ...c.id !== void 0 ? { id: c.id } : {},
2275
2361
  startTimeMs: c.startTimeMs,
2276
2362
  endTimeMs: inferEndTimeMs(sorted, i, trackEndMs) ?? c.startTimeMs,
2277
- title: c.title,
2363
+ ...c.title !== void 0 ? { title: c.title } : {},
2278
2364
  source
2279
2365
  }));
2280
2366
  this.handle.setChapters(raw, style);
@@ -2301,8 +2387,9 @@ var init_audio_file_impl = __esm({
2301
2387
  return this.handle.getRatings().map(
2302
2388
  (r) => ({
2303
2389
  rating: r.rating,
2304
- email: r.email || void 0,
2305
- counter: r.counter || void 0
2390
+ ...r.email !== void 0 && r.email !== "" ? { email: r.email } : {},
2391
+ // 0 must read back as ABSENT (old `r.counter || undefined` contract).
2392
+ ...r.counter !== 0 ? { counter: r.counter } : {}
2306
2393
  })
2307
2394
  );
2308
2395
  }
@@ -2333,7 +2420,11 @@ var init_audio_file_impl = __esm({
2333
2420
  return ratings.length > 0 ? normalized(ratings[0].rating) : void 0;
2334
2421
  }
2335
2422
  setRating(rating, email) {
2336
- this.setRatings([{ rating, email, counter: 0 }]);
2423
+ this.setRatings([{
2424
+ rating,
2425
+ ...email !== void 0 ? { email } : {},
2426
+ counter: 0
2427
+ }]);
2337
2428
  }
2338
2429
  getId3v2Frames(id) {
2339
2430
  assertMp3(this.getFormat());
@@ -2446,7 +2537,24 @@ function isMpegFrameSync(bytes, at = 0) {
2446
2537
  if (version === 1 || layer === 0) return false;
2447
2538
  const bitrateIndex = bytes[at + 2] >> 4 & 15;
2448
2539
  const sampleRateIndex = bytes[at + 2] >> 2 & 3;
2449
- return bitrateIndex !== 0 && bitrateIndex !== 15 && sampleRateIndex !== 3;
2540
+ if (bitrateIndex === 0 || bitrateIndex === 15 || sampleRateIndex === 3) {
2541
+ return false;
2542
+ }
2543
+ const mpeg1 = version === 3;
2544
+ const layerIndex = layer ^ 3;
2545
+ const bitrate = MPEG_BITRATES[mpeg1 ? 0 : 1][layerIndex][bitrateIndex];
2546
+ const sampleRateTable = mpeg1 ? 0 : version === 2 ? 1 : 2;
2547
+ const sampleRate = MPEG_SAMPLE_RATES[sampleRateTable][sampleRateIndex];
2548
+ const samplesPerFrame = layerIndex === 2 ? mpeg1 ? 1152 : 576 : layerIndex === 1 ? 1152 : 384;
2549
+ let frameLength = Math.floor(samplesPerFrame * bitrate * 125 / sampleRate);
2550
+ if ((bytes[at + 2] & 2) !== 0) frameLength += layerIndex === 0 ? 4 : 1;
2551
+ if (frameLength === 0) return false;
2552
+ const next = at + frameLength;
2553
+ if (bytes.length < next + 4) return false;
2554
+ const mask = 4294839296;
2555
+ const header = ((bytes[at] << 24 | bytes[at + 1] << 16 | bytes[at + 2] << 8 | bytes[at + 3]) >>> 0 & mask) >>> 0;
2556
+ const nextHeader = ((bytes[next] << 24 | bytes[next + 1] << 16 | bytes[next + 2] << 8 | bytes[next + 3]) >>> 0 & mask) >>> 0;
2557
+ return header === nextHeader;
2450
2558
  }
2451
2559
  function containerEnd(bytes, at, limit) {
2452
2560
  if (at >= bytes.length) return bytes.length < limit ? null : void 0;
@@ -2489,9 +2597,36 @@ function trailerFitsInFooter(tail, footerSize) {
2489
2597
  }
2490
2598
  return true;
2491
2599
  }
2600
+ var MPEG_BITRATES, MPEG_SAMPLE_RATES;
2492
2601
  var init_metadata_extent = __esm({
2493
2602
  "src/taglib/metadata-extent.ts"() {
2494
2603
  "use strict";
2604
+ MPEG_BITRATES = [
2605
+ [
2606
+ [0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 0],
2607
+ // Layer I
2608
+ [0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 0],
2609
+ // Layer II
2610
+ [0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0]
2611
+ // Layer III
2612
+ ],
2613
+ [
2614
+ [0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 0],
2615
+ // Layer I
2616
+ [0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0],
2617
+ // Layer II
2618
+ [0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0]
2619
+ // Layer III
2620
+ ]
2621
+ ];
2622
+ MPEG_SAMPLE_RATES = [
2623
+ [44100, 48e3, 32e3],
2624
+ // MPEG-1
2625
+ [22050, 24e3, 16e3],
2626
+ // MPEG-2
2627
+ [11025, 12e3, 8e3]
2628
+ // MPEG-2.5
2629
+ ];
2495
2630
  }
2496
2631
  });
2497
2632
 
@@ -2626,7 +2761,7 @@ function mapPropertiesToExtendedTag(props) {
2626
2761
  const num = parseLeadingInt(values[0]);
2627
2762
  if (num !== void 0) tag[camelKey] = num;
2628
2763
  } else if (camelKey === "compilation") {
2629
- tag[camelKey] = values[0] === "1";
2764
+ tag[camelKey] = values[0] === "1" || values[0] === "true";
2630
2765
  } else if (camelKey === "itunesAdvisory") {
2631
2766
  tag[camelKey] = values;
2632
2767
  if (values[0] === "1") tag.advisory = "explicit";
@@ -2763,7 +2898,7 @@ var VERSION;
2763
2898
  var init_version = __esm({
2764
2899
  "src/version.ts"() {
2765
2900
  "use strict";
2766
- VERSION = "1.8.3";
2901
+ VERSION = "2.1.0";
2767
2902
  }
2768
2903
  });
2769
2904
 
@@ -3236,7 +3371,7 @@ async function readPictureMetadata(file) {
3236
3371
  return pictures.map((pic) => ({
3237
3372
  type: pic.type,
3238
3373
  mimeType: pic.mimeType,
3239
- description: pic.description,
3374
+ ...pic.description !== void 0 ? { description: pic.description } : {},
3240
3375
  size: pic.data.length
3241
3376
  }));
3242
3377
  }
@@ -3310,10 +3445,10 @@ function extractDynamics(audioFile) {
3310
3445
  "replayGainAlbumPeak"
3311
3446
  ];
3312
3447
  for (const field of fields) {
3313
- const val = audioFile.getProperty(field);
3314
- if (val) dynamics[field] = val;
3448
+ const val = audioFile.getProperty(field)?.[0];
3449
+ if (val !== void 0) dynamics[field] = val;
3315
3450
  }
3316
- let appleSoundCheck = audioFile.getProperty("appleSoundCheck");
3451
+ let appleSoundCheck = audioFile.getProperty("appleSoundCheck")?.[0];
3317
3452
  if (!appleSoundCheck && audioFile.isMP4()) {
3318
3453
  appleSoundCheck = audioFile.getMP4Item("----:com.apple.iTunes:iTunNORM");
3319
3454
  }
@@ -3339,23 +3474,27 @@ async function readMetadata(file) {
3339
3474
  `File may be corrupted or in an unsupported format. File: ${name}`
3340
3475
  );
3341
3476
  }
3477
+ const dynamics = extractDynamics(audioFile);
3342
3478
  return {
3343
3479
  tags: readExtendedTag(audioFile),
3344
3480
  properties: audioFile.audioProperties(),
3345
3481
  hasCoverArt: audioFile.getPictures().length > 0,
3346
- dynamics: extractDynamics(audioFile)
3482
+ ...dynamics !== void 0 ? { dynamics } : {}
3347
3483
  };
3348
3484
  } finally {
3349
3485
  audioFile.dispose();
3350
3486
  }
3351
3487
  }
3352
3488
  async function readMetadataBatch(files, options = {}) {
3353
- return executeBatch(files, options, (audioFile) => ({
3354
- tags: readExtendedTag(audioFile),
3355
- properties: audioFile.audioProperties(),
3356
- hasCoverArt: audioFile.getPictures().length > 0,
3357
- dynamics: extractDynamics(audioFile)
3358
- }));
3489
+ return executeBatch(files, options, (audioFile) => {
3490
+ const dynamics = extractDynamics(audioFile);
3491
+ return {
3492
+ tags: readExtendedTag(audioFile),
3493
+ properties: audioFile.audioProperties(),
3494
+ hasCoverArt: audioFile.getPictures().length > 0,
3495
+ ...dynamics !== void 0 ? { dynamics } : {}
3496
+ };
3497
+ });
3359
3498
  }
3360
3499
 
3361
3500
  // src/simple/index.ts
@@ -21,8 +21,6 @@
21
21
  * file.setRatings([{ rating: 0.8, email: "user@example.com" }]);
22
22
  * ```
23
23
  */
24
- import type { Picture } from "../types.js";
25
- import type { Chapter } from "../types/chapters.js";
26
24
  /**
27
25
  * Rating metadata representing track popularity/rating.
28
26
  * Uses normalized 0.0-1.0 scale for cross-format compatibility.
@@ -76,116 +74,4 @@ export interface UnsyncedLyrics {
76
74
  * Used as escape hatch when type is not known at compile time.
77
75
  */
78
76
  export type VariantMap = Record<string, unknown>;
79
- /**
80
- * Type map linking complex property keys to their value types.
81
- *
82
- * This is a descriptive type map, not the accessor surface — each complex
83
- * property is reached through its own typed method:
84
- *
85
- * @example
86
- * ```typescript
87
- * const ratings = file.getRatings(); // Rating[]
88
- * const pictures = file.getPictures(); // Picture[]
89
- * const chapters = file.getChapters(); // Chapter[]
90
- * ```
91
- *
92
- * Consumers can extend via module augmentation:
93
- * ```typescript
94
- * declare module "taglib-wasm" {
95
- * interface ComplexPropertyValueMap {
96
- * MY_CUSTOM: { foo: string };
97
- * }
98
- * }
99
- * ```
100
- */
101
- export interface ComplexPropertyValueMap {
102
- /** Cover art and embedded images */
103
- PICTURE: Picture;
104
- /** Track/album rating (normalized 0.0-1.0) */
105
- RATING: Rating;
106
- /** Unsynchronized lyrics text */
107
- LYRICS: UnsyncedLyrics;
108
- /** Chapter markers (ID3v2 CHAP frames) */
109
- CHAPTER: Chapter;
110
- }
111
- /**
112
- * Union type of all valid complex property keys.
113
- */
114
- export type ComplexPropertyKey = keyof ComplexPropertyValueMap;
115
- /**
116
- * Rich metadata object for complex properties.
117
- * Contains descriptions, format support info, and underlying format mappings.
118
- *
119
- * Use this for introspection, documentation, or format-aware operations.
120
- */
121
- export declare const COMPLEX_PROPERTIES: {
122
- readonly PICTURE: {
123
- readonly key: "PICTURE";
124
- readonly description: "Embedded album art or images";
125
- readonly type: "binary";
126
- readonly supportedFormats: readonly ["ID3v2", "MP4", "Vorbis", "FLAC"];
127
- readonly mappings: {
128
- readonly id3v2: {
129
- readonly frame: "APIC";
130
- };
131
- readonly mp4: "covr";
132
- readonly vorbis: "METADATA_BLOCK_PICTURE";
133
- readonly flac: "PICTURE";
134
- };
135
- };
136
- readonly RATING: {
137
- readonly key: "RATING";
138
- readonly description: "Track rating (normalized 0.0-1.0)";
139
- readonly type: "object";
140
- readonly supportedFormats: readonly ["ID3v2", "Vorbis", "MP4"];
141
- readonly mappings: {
142
- readonly id3v2: {
143
- readonly frame: "POPM";
144
- };
145
- readonly vorbis: "RATING";
146
- readonly mp4: "----:com.apple.iTunes:RATING";
147
- };
148
- };
149
- readonly LYRICS: {
150
- readonly key: "LYRICS";
151
- readonly description: "Unsynchronized lyrics text";
152
- readonly type: "object";
153
- readonly supportedFormats: readonly ["ID3v2", "Vorbis", "MP4"];
154
- readonly mappings: {
155
- readonly id3v2: {
156
- readonly frame: "USLT";
157
- };
158
- readonly vorbis: "LYRICS";
159
- readonly mp4: "©lyr";
160
- };
161
- };
162
- readonly CHAPTER: {
163
- readonly key: "CHAPTER";
164
- readonly description: "Chapter markers with time ranges";
165
- readonly type: "object";
166
- readonly supportedFormats: readonly ["ID3v2"];
167
- readonly mappings: {
168
- readonly id3v2: {
169
- readonly frame: "CHAP";
170
- };
171
- };
172
- };
173
- };
174
- /**
175
- * Type for the simple key map.
176
- */
177
- export type ComplexPropertyKeyMap = {
178
- readonly [K in ComplexPropertyKey]: K;
179
- };
180
- /**
181
- * Simple string map for daily use.
182
- * Avoids the `.key` ceremony when you just need the key string.
183
- *
184
- * @example
185
- * ```typescript
186
- * // Instead of: COMPLEX_PROPERTIES.RATING.key
187
- * COMPLEX_PROPERTY_KEY.RATING; // "RATING"
188
- * ```
189
- */
190
- export declare const COMPLEX_PROPERTY_KEY: ComplexPropertyKeyMap;
191
77
  //# sourceMappingURL=complex-properties.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"complex-properties.d.ts","sourceRoot":"","sources":["../../../src/constants/complex-properties.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAEpD;;;;;;;;GAQG;AACH,MAAM,WAAW,MAAM;IACrB,6DAA6D;IAC7D,MAAM,EAAE,MAAM,CAAC;IACf,qDAAqD;IACrD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,4CAA4C;IAC5C,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,UAAU;IACzB,qEAAqE;IACrE,EAAE,EAAE,MAAM,CAAC;IACX,0DAA0D;IAC1D,IAAI,EAAE,UAAU,CAAC;IACjB;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,uBAAuB;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,kCAAkC;IAClC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,0DAA0D;IAC1D,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;GAGG;AACH,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEjD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,WAAW,uBAAuB;IACtC,oCAAoC;IACpC,OAAO,EAAE,OAAO,CAAC;IACjB,8CAA8C;IAC9C,MAAM,EAAE,MAAM,CAAC;IACf,iCAAiC;IACjC,MAAM,EAAE,cAAc,CAAC;IACvB,0CAA0C;IAC1C,OAAO,EAAE,OAAO,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,MAAM,kBAAkB,GAAG,MAAM,uBAAuB,CAAC;AAE/D;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4CrB,CAAC;AAEX;;GAEG;AACH,MAAM,MAAM,qBAAqB,GAAG;IAClC,QAAQ,EAAE,CAAC,IAAI,kBAAkB,GAAG,CAAC;CACtC,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,oBAAoB,EAAE,qBAKzB,CAAC"}
1
+ {"version":3,"file":"complex-properties.d.ts","sourceRoot":"","sources":["../../../src/constants/complex-properties.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAIH;;;;;;;;GAQG;AACH,MAAM,WAAW,MAAM;IACrB,6DAA6D;IAC7D,MAAM,EAAE,MAAM,CAAC;IACf,qDAAqD;IACrD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,4CAA4C;IAC5C,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,UAAU;IACzB,qEAAqE;IACrE,EAAE,EAAE,MAAM,CAAC;IACX,0DAA0D;IAC1D,IAAI,EAAE,UAAU,CAAC;IACjB;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,uBAAuB;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,kCAAkC;IAClC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,0DAA0D;IAC1D,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;GAGG;AACH,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC"}
@@ -1,55 +0,0 @@
1
- const COMPLEX_PROPERTIES = {
2
- PICTURE: {
3
- key: "PICTURE",
4
- description: "Embedded album art or images",
5
- type: "binary",
6
- supportedFormats: ["ID3v2", "MP4", "Vorbis", "FLAC"],
7
- mappings: {
8
- id3v2: { frame: "APIC" },
9
- mp4: "covr",
10
- vorbis: "METADATA_BLOCK_PICTURE",
11
- flac: "PICTURE"
12
- }
13
- },
14
- RATING: {
15
- key: "RATING",
16
- description: "Track rating (normalized 0.0-1.0)",
17
- type: "object",
18
- supportedFormats: ["ID3v2", "Vorbis", "MP4"],
19
- mappings: {
20
- id3v2: { frame: "POPM" },
21
- vorbis: "RATING",
22
- mp4: "----:com.apple.iTunes:RATING"
23
- }
24
- },
25
- LYRICS: {
26
- key: "LYRICS",
27
- description: "Unsynchronized lyrics text",
28
- type: "object",
29
- supportedFormats: ["ID3v2", "Vorbis", "MP4"],
30
- mappings: {
31
- id3v2: { frame: "USLT" },
32
- vorbis: "LYRICS",
33
- mp4: "\xA9lyr"
34
- }
35
- },
36
- CHAPTER: {
37
- key: "CHAPTER",
38
- description: "Chapter markers with time ranges",
39
- type: "object",
40
- supportedFormats: ["ID3v2"],
41
- mappings: {
42
- id3v2: { frame: "CHAP" }
43
- }
44
- }
45
- };
46
- const COMPLEX_PROPERTY_KEY = {
47
- PICTURE: "PICTURE",
48
- RATING: "RATING",
49
- LYRICS: "LYRICS",
50
- CHAPTER: "CHAPTER"
51
- };
52
- export {
53
- COMPLEX_PROPERTIES,
54
- COMPLEX_PROPERTY_KEY
55
- };
@@ -3,7 +3,6 @@
3
3
  * Maintains backward compatibility by re-exporting all constants and utilities
4
4
  */
5
5
  export * from "./properties.js";
6
- export * from "./tags.js";
7
6
  export * from "./format-mappings.js";
8
7
  export * from "./utilities.js";
9
8
  export * from "./complex-properties.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/constants/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,cAAc,iBAAiB,CAAC;AAGhC,cAAc,WAAW,CAAC;AAG1B,cAAc,sBAAsB,CAAC;AAGrC,cAAc,gBAAgB,CAAC;AAG/B,cAAc,yBAAyB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/constants/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,cAAc,iBAAiB,CAAC;AAGhC,cAAc,sBAAsB,CAAC;AAGrC,cAAc,gBAAgB,CAAC;AAG/B,cAAc,yBAAyB,CAAC"}
@@ -1,5 +1,4 @@
1
1
  export * from "./properties.js";
2
- export * from "./tags.js";
3
2
  export * from "./format-mappings.js";
4
3
  export * from "./utilities.js";
5
4
  export * from "./complex-properties.js";
@@ -1,4 +1,5 @@
1
1
  import { PROPERTIES, type PropertyKey } from "./properties.js";
2
+ import type { PropertyMap } from "../types/tags.js";
2
3
  import type { PropertyMetadata } from "./property-types.js";
3
4
  /**
4
5
  * Type guard to check if a string is a valid property key
@@ -23,4 +24,16 @@ export declare function getAllProperties(): readonly [
23
24
  * Filter properties by supported format
24
25
  */
25
26
  export declare function getPropertiesByFormat(format: string): PropertyKey[];
27
+ /**
28
+ * All values for a property key in a PropertyMap; `[]` when absent
29
+ * (taglib-lk8u). Removes the narrowing filter at the call site: the map's
30
+ * index signature must admit undefined, so a bare read is
31
+ * `string[] | undefined` even for known keys.
32
+ */
33
+ export declare function propertyValues(props: PropertyMap, key: string): string[];
34
+ /**
35
+ * First value for a property key in a PropertyMap; `undefined` when absent.
36
+ * The common single-value read, without the `?.[0]` at every call site.
37
+ */
38
+ export declare function propertyValue(props: PropertyMap, key: string): string | undefined;
26
39
  //# sourceMappingURL=utilities.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"utilities.d.ts","sourceRoot":"","sources":["../../../src/constants/utilities.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,KAAK,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC/D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAE5D;;GAEG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,IAAI,WAAW,CAE/D;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,SAAS,WAAW,EACvD,GAAG,EAAE,CAAC,GACL,gBAAgB,GAAG,SAAS,CAE9B;AAED;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,SAAS,WAAW,EAAE,CAE3D;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,SAAS;IAC3C,WAAW;IACX,OAAO,UAAU,CAAC,WAAW,CAAC;CAC/B,EAAE,CAKF;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,WAAW,EAAE,CAInE"}
1
+ {"version":3,"file":"utilities.d.ts","sourceRoot":"","sources":["../../../src/constants/utilities.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,KAAK,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC/D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAE5D;;GAEG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,IAAI,WAAW,CAE/D;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,SAAS,WAAW,EACvD,GAAG,EAAE,CAAC,GACL,gBAAgB,GAAG,SAAS,CAE9B;AAED;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,SAAS,WAAW,EAAE,CAE3D;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,SAAS;IAC3C,WAAW;IACX,OAAO,UAAU,CAAC,WAAW,CAAC;CAC/B,EAAE,CAKF;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,WAAW,EAAE,CAInE;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAExE;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,WAAW,EAClB,GAAG,EAAE,MAAM,GACV,MAAM,GAAG,SAAS,CAEpB"}