taglib-wasm 1.2.2 → 1.3.1

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 (37) hide show
  1. package/dist/index.browser.js +238 -91
  2. package/dist/simple.browser.js +238 -91
  3. package/dist/src/constants/complex-properties.d.ts +0 -14
  4. package/dist/src/constants/complex-properties.d.ts.map +1 -1
  5. package/dist/src/msgpack/encoder.d.ts +6 -0
  6. package/dist/src/msgpack/encoder.d.ts.map +1 -1
  7. package/dist/src/msgpack/encoder.js +12 -0
  8. package/dist/src/runtime/wasi-adapter/file-handle.d.ts +3 -1
  9. package/dist/src/runtime/wasi-adapter/file-handle.d.ts.map +1 -1
  10. package/dist/src/runtime/wasi-adapter/file-handle.js +17 -0
  11. package/dist/src/runtime/wasi-memory.d.ts +0 -5
  12. package/dist/src/runtime/wasi-memory.d.ts.map +1 -1
  13. package/dist/src/runtime/wasi-memory.js +1 -8
  14. package/dist/src/simple/tag-operations.d.ts.map +1 -1
  15. package/dist/src/simple/tag-operations.js +1 -0
  16. package/dist/src/taglib/audio-file-base.d.ts.map +1 -1
  17. package/dist/src/taglib/audio-file-base.js +21 -1
  18. package/dist/src/taglib/audio-file-impl.d.ts +3 -1
  19. package/dist/src/taglib/audio-file-impl.d.ts.map +1 -1
  20. package/dist/src/taglib/audio-file-impl.js +37 -39
  21. package/dist/src/taglib/audio-file-interface.d.ts +8 -1
  22. package/dist/src/taglib/audio-file-interface.d.ts.map +1 -1
  23. package/dist/src/taglib/embind-adapter.d.ts.map +1 -1
  24. package/dist/src/taglib/embind-adapter.js +18 -0
  25. package/dist/src/taglib/extra-state-registry.d.ts +26 -0
  26. package/dist/src/taglib/extra-state-registry.d.ts.map +1 -0
  27. package/dist/src/taglib/extra-state-registry.js +59 -0
  28. package/dist/src/taglib/mutable-tag.d.ts +7 -0
  29. package/dist/src/taglib/mutable-tag.d.ts.map +1 -1
  30. package/dist/src/taglib/save-reconstruct.d.ts +13 -0
  31. package/dist/src/taglib/save-reconstruct.d.ts.map +1 -0
  32. package/dist/src/taglib/save-reconstruct.js +39 -0
  33. package/dist/src/version.d.ts +1 -1
  34. package/dist/src/version.js +1 -1
  35. package/dist/src/wasm.d.ts +14 -0
  36. package/dist/src/wasm.d.ts.map +1 -1
  37. package/package.json +1 -1
@@ -573,60 +573,6 @@ var init_platform_io_browser_stub = __esm({
573
573
  }
574
574
  });
575
575
 
576
- // src/utils/file.ts
577
- async function readFileData(file) {
578
- if (file instanceof Uint8Array) return file;
579
- if (file instanceof ArrayBuffer) return new Uint8Array(file);
580
- if (typeof File !== "undefined" && file instanceof File) {
581
- return new Uint8Array(await file.arrayBuffer());
582
- }
583
- if (typeof file === "string") {
584
- try {
585
- return await getPlatformIO().readFile(file);
586
- } catch (error) {
587
- if (error instanceof EnvironmentError) throw error;
588
- throw new FileOperationError("read", error.message, file);
589
- }
590
- }
591
- const inputType = Object.prototype.toString.call(file);
592
- throw new FileOperationError(
593
- "read",
594
- `Invalid file input type: ${inputType}. Expected string path, Uint8Array, ArrayBuffer, or File object.`
595
- );
596
- }
597
- async function getFileSize(path) {
598
- try {
599
- const s = await getPlatformIO().stat(path);
600
- return s.size;
601
- } catch (error) {
602
- if (error instanceof EnvironmentError) throw error;
603
- throw new FileOperationError("stat", error.message, path);
604
- }
605
- }
606
- async function readPartialFileData(path, headerSize, footerSize) {
607
- const io = getPlatformIO();
608
- if (!io.readPartial) {
609
- throw new EnvironmentError(
610
- "current runtime",
611
- "does not support partial file reading",
612
- "filesystem access with seek support"
613
- );
614
- }
615
- try {
616
- return await io.readPartial(path, headerSize, footerSize);
617
- } catch (error) {
618
- if (error instanceof EnvironmentError) throw error;
619
- throw new FileOperationError("read", error.message, path);
620
- }
621
- }
622
- var init_file = __esm({
623
- "src/utils/file.ts"() {
624
- "use strict";
625
- init_errors2();
626
- init_platform_io_browser_stub();
627
- }
628
- });
629
-
630
576
  // src/utils/write.ts
631
577
  async function writeFileData(path, data) {
632
578
  try {
@@ -1387,12 +1333,14 @@ var init_properties = __esm({
1387
1333
  });
1388
1334
 
1389
1335
  // src/taglib/audio-file-base.ts
1390
- var BaseAudioFileImpl;
1336
+ var LYRICS_PROPERTY_KEY, LYRICS_WIRE_KEY, BaseAudioFileImpl;
1391
1337
  var init_audio_file_base = __esm({
1392
1338
  "src/taglib/audio-file-base.ts"() {
1393
1339
  "use strict";
1394
1340
  init_properties();
1395
1341
  init_errors2();
1342
+ LYRICS_PROPERTY_KEY = "lyrics";
1343
+ LYRICS_WIRE_KEY = toTagLibKey(LYRICS_PROPERTY_KEY);
1396
1344
  BaseAudioFileImpl = class {
1397
1345
  constructor(module, fileHandle, sourcePath, originalSource, isPartiallyLoaded = false, partialLoadOptions) {
1398
1346
  __publicField(this, "module", module);
@@ -1445,6 +1393,9 @@ var init_audio_file_base = __esm({
1445
1393
  get track() {
1446
1394
  return data.track;
1447
1395
  },
1396
+ get date() {
1397
+ return handle.getProperty("DATE") || void 0;
1398
+ },
1448
1399
  setTitle: (value) => {
1449
1400
  handle.setTagData({ title: value });
1450
1401
  data = handle.getTagData();
@@ -1479,6 +1430,15 @@ var init_audio_file_base = __esm({
1479
1430
  handle.setTagData({ track: value });
1480
1431
  data = handle.getTagData();
1481
1432
  return tag;
1433
+ },
1434
+ setDate: (value) => {
1435
+ if (value === "") {
1436
+ handle.setTagData({ year: 0 });
1437
+ } else {
1438
+ handle.setProperty("DATE", value);
1439
+ }
1440
+ data = handle.getTagData();
1441
+ return tag;
1482
1442
  }
1483
1443
  };
1484
1444
  return tag;
@@ -1490,13 +1450,19 @@ var init_audio_file_base = __esm({
1490
1450
  return this.cachedAudioProperties ?? void 0;
1491
1451
  }
1492
1452
  properties() {
1493
- return remapKeysFromTagLib(this.handle.getProperties());
1453
+ const remapped = remapKeysFromTagLib(this.handle.getProperties());
1454
+ delete remapped[LYRICS_PROPERTY_KEY];
1455
+ return remapped;
1494
1456
  }
1495
1457
  setProperties(properties) {
1496
1458
  const translated = {};
1497
1459
  for (const [key, values] of Object.entries(properties)) {
1498
1460
  if (values !== void 0) translated[toTagLibKey(key)] = values;
1499
1461
  }
1462
+ if (!(LYRICS_WIRE_KEY in translated)) {
1463
+ const existing = this.handle.getProperties()[LYRICS_WIRE_KEY];
1464
+ if (existing !== void 0) translated[LYRICS_WIRE_KEY] = existing;
1465
+ }
1500
1466
  this.handle.setProperties(translated);
1501
1467
  }
1502
1468
  getProperty(key) {
@@ -1545,6 +1511,60 @@ var init_audio_file_base = __esm({
1545
1511
  }
1546
1512
  });
1547
1513
 
1514
+ // src/utils/file.ts
1515
+ async function readFileData(file) {
1516
+ if (file instanceof Uint8Array) return file;
1517
+ if (file instanceof ArrayBuffer) return new Uint8Array(file);
1518
+ if (typeof File !== "undefined" && file instanceof File) {
1519
+ return new Uint8Array(await file.arrayBuffer());
1520
+ }
1521
+ if (typeof file === "string") {
1522
+ try {
1523
+ return await getPlatformIO().readFile(file);
1524
+ } catch (error) {
1525
+ if (error instanceof EnvironmentError) throw error;
1526
+ throw new FileOperationError("read", error.message, file);
1527
+ }
1528
+ }
1529
+ const inputType = Object.prototype.toString.call(file);
1530
+ throw new FileOperationError(
1531
+ "read",
1532
+ `Invalid file input type: ${inputType}. Expected string path, Uint8Array, ArrayBuffer, or File object.`
1533
+ );
1534
+ }
1535
+ async function getFileSize(path) {
1536
+ try {
1537
+ const s = await getPlatformIO().stat(path);
1538
+ return s.size;
1539
+ } catch (error) {
1540
+ if (error instanceof EnvironmentError) throw error;
1541
+ throw new FileOperationError("stat", error.message, path);
1542
+ }
1543
+ }
1544
+ async function readPartialFileData(path, headerSize, footerSize) {
1545
+ const io = getPlatformIO();
1546
+ if (!io.readPartial) {
1547
+ throw new EnvironmentError(
1548
+ "current runtime",
1549
+ "does not support partial file reading",
1550
+ "filesystem access with seek support"
1551
+ );
1552
+ }
1553
+ try {
1554
+ return await io.readPartial(path, headerSize, footerSize);
1555
+ } catch (error) {
1556
+ if (error instanceof EnvironmentError) throw error;
1557
+ throw new FileOperationError("read", error.message, path);
1558
+ }
1559
+ }
1560
+ var init_file = __esm({
1561
+ "src/utils/file.ts"() {
1562
+ "use strict";
1563
+ init_errors2();
1564
+ init_platform_io_browser_stub();
1565
+ }
1566
+ });
1567
+
1548
1568
  // src/taglib/embind-adapter.ts
1549
1569
  function isValidBitrateMode(value) {
1550
1570
  return value === "CBR" || value === "VBR" || value === "ABR";
@@ -1591,6 +1611,24 @@ function wrapEmbindHandle(raw) {
1591
1611
  data ?? null
1592
1612
  );
1593
1613
  },
1614
+ // Emscripten exposes USLT only through the PropertyMap "LYRICS" key (no
1615
+ // dedicated Embind binding), so bridge get/setLyrics to get/setProperties.
1616
+ // Text only — language/description are not surfaced by the PropertyMap.
1617
+ getLyrics() {
1618
+ const props = raw.getProperties();
1619
+ return (props["LYRICS"] ?? []).map((text) => ({
1620
+ text,
1621
+ description: "",
1622
+ language: ""
1623
+ }));
1624
+ },
1625
+ setLyrics(lyrics) {
1626
+ const handle = raw;
1627
+ const props = handle.getProperties();
1628
+ if (lyrics.length > 0) props["LYRICS"] = lyrics.map((l) => l.text);
1629
+ else delete props["LYRICS"];
1630
+ handle.setProperties(props);
1631
+ },
1594
1632
  hasId3Tags() {
1595
1633
  const v = raw.hasId3Tags();
1596
1634
  if (!v || typeof v !== "object") return { v1: false, v2: false };
@@ -1640,6 +1678,112 @@ var init_embind_adapter = __esm({
1640
1678
  }
1641
1679
  });
1642
1680
 
1681
+ // src/taglib/extra-state-registry.ts
1682
+ function chapterStyle(chapters) {
1683
+ const s = chapters[0]?.source;
1684
+ return s === "nero" || s === "both" ? s : "quicktime";
1685
+ }
1686
+ function copyExtraState(target, source, sourceComplete) {
1687
+ for (const field of EXTRA_FIELDS) {
1688
+ field.copy(target, source, sourceComplete);
1689
+ }
1690
+ }
1691
+ var EXTRA_FIELDS;
1692
+ var init_extra_state_registry = __esm({
1693
+ "src/taglib/extra-state-registry.ts"() {
1694
+ "use strict";
1695
+ EXTRA_FIELDS = [
1696
+ {
1697
+ name: "pictures",
1698
+ copy(target, source, complete) {
1699
+ const v = source.getPictures();
1700
+ if (complete || v.length > 0) target.setPictures(v);
1701
+ }
1702
+ },
1703
+ {
1704
+ name: "ratings",
1705
+ copy(target, source, complete) {
1706
+ const v = source.getRatings();
1707
+ if (complete || v.length > 0) target.setRatings(v);
1708
+ }
1709
+ },
1710
+ {
1711
+ name: "lyrics",
1712
+ copy(target, source, complete) {
1713
+ const v = source.getLyrics();
1714
+ if (complete || v.length > 0) target.setLyrics(v);
1715
+ }
1716
+ },
1717
+ {
1718
+ name: "chapters",
1719
+ copy(target, source, complete) {
1720
+ const v = source.getChapters();
1721
+ if (complete || v.length > 0) target.setChapters(v, chapterStyle(v));
1722
+ }
1723
+ },
1724
+ {
1725
+ name: "bextData",
1726
+ copy(target, source, complete) {
1727
+ const v = source.getBextData();
1728
+ if (v !== void 0) target.setBextData(v);
1729
+ else if (complete) target.setBextData(null);
1730
+ }
1731
+ },
1732
+ {
1733
+ name: "ixml",
1734
+ copy(target, source, complete) {
1735
+ const v = source.getIxml();
1736
+ if (v !== void 0) target.setIxml(v);
1737
+ else if (complete) target.setIxml(null);
1738
+ }
1739
+ }
1740
+ ];
1741
+ }
1742
+ });
1743
+
1744
+ // src/taglib/save-reconstruct.ts
1745
+ function copyEditedState(target, source, sourceComplete) {
1746
+ target.setTagData(source.getTagData());
1747
+ target.setProperties(
1748
+ sourceComplete ? source.getProperties() : { ...target.getProperties(), ...source.getProperties() }
1749
+ );
1750
+ copyExtraState(target, source, sourceComplete);
1751
+ }
1752
+ async function saveViaFreshHandle(module, editing, source, targetPath, sourceComplete) {
1753
+ const rawFullHandle = module.createFileHandle();
1754
+ const fullFileHandle = module.isWasi ? rawFullHandle : wrapEmbindHandle(rawFullHandle);
1755
+ try {
1756
+ {
1757
+ const data = await readFileData(source);
1758
+ if (!fullFileHandle.loadFromBuffer(data)) {
1759
+ throw new InvalidFormatError(
1760
+ "Failed to load full audio file for saving"
1761
+ );
1762
+ }
1763
+ }
1764
+ copyEditedState(fullFileHandle, editing, sourceComplete);
1765
+ if (!fullFileHandle.save()) {
1766
+ throw new FileOperationError(
1767
+ "save",
1768
+ "Failed to save changes to full file"
1769
+ );
1770
+ }
1771
+ await writeFileData(targetPath, fullFileHandle.getBuffer());
1772
+ } finally {
1773
+ fullFileHandle.destroy();
1774
+ }
1775
+ }
1776
+ var init_save_reconstruct = __esm({
1777
+ "src/taglib/save-reconstruct.ts"() {
1778
+ "use strict";
1779
+ init_errors2();
1780
+ init_file();
1781
+ init_write();
1782
+ init_embind_adapter();
1783
+ init_extra_state_registry();
1784
+ }
1785
+ });
1786
+
1643
1787
  // src/taglib/audio-file-impl.ts
1644
1788
  function sortChapters(list) {
1645
1789
  return [...list].sort((a, b) => a.startTimeMs - b.startTimeMs);
@@ -1669,10 +1813,9 @@ var init_audio_file_impl = __esm({
1669
1813
  init_types2();
1670
1814
  init_audio_file_bwf();
1671
1815
  init_errors2();
1672
- init_file();
1673
1816
  init_write();
1674
1817
  init_audio_file_base();
1675
- init_embind_adapter();
1818
+ init_save_reconstruct();
1676
1819
  AudioFileImpl = class extends BaseAudioFileImpl {
1677
1820
  constructor(module, fileHandle, sourcePath, originalSource, isPartiallyLoaded = false, partialLoadOptions) {
1678
1821
  super(
@@ -1718,38 +1861,23 @@ var init_audio_file_impl = __esm({
1718
1861
  );
1719
1862
  }
1720
1863
  if (this.isPartiallyLoaded && this.originalSource) {
1721
- const rawFullHandle = this.module.createFileHandle();
1722
- const fullFileHandle = this.module.isWasi ? rawFullHandle : wrapEmbindHandle(rawFullHandle);
1723
- try {
1724
- const success = await (async () => {
1725
- const data = await readFileData(this.originalSource);
1726
- return fullFileHandle.loadFromBuffer(data);
1727
- })();
1728
- if (!success) {
1729
- throw new InvalidFormatError(
1730
- "Failed to load full audio file for saving"
1731
- );
1732
- }
1733
- fullFileHandle.setTagData(this.handle.getTagData());
1734
- fullFileHandle.setProperties(this.handle.getProperties());
1735
- fullFileHandle.setPictures(this.handle.getPictures());
1736
- const bextBytes = this.handle.getBextData();
1737
- if (bextBytes !== void 0) fullFileHandle.setBextData(bextBytes);
1738
- const ixmlStr = this.handle.getIxml();
1739
- if (ixmlStr !== void 0) fullFileHandle.setIxml(ixmlStr);
1740
- if (!fullFileHandle.save()) {
1741
- throw new FileOperationError(
1742
- "save",
1743
- "Failed to save changes to full file"
1744
- );
1745
- }
1746
- const buffer = fullFileHandle.getBuffer();
1747
- await writeFileData(targetPath, buffer);
1748
- } finally {
1749
- fullFileHandle.destroy();
1750
- }
1864
+ await saveViaFreshHandle(
1865
+ this.module,
1866
+ this.handle,
1867
+ this.originalSource,
1868
+ targetPath,
1869
+ false
1870
+ );
1751
1871
  this.isPartiallyLoaded = false;
1752
1872
  this.originalSource = void 0;
1873
+ } else if (this.module.isWasi && this.sourcePath && targetPath !== this.sourcePath) {
1874
+ await saveViaFreshHandle(
1875
+ this.module,
1876
+ this.handle,
1877
+ this.sourcePath,
1878
+ targetPath,
1879
+ true
1880
+ );
1753
1881
  } else {
1754
1882
  if (!this.save()) {
1755
1883
  throw new FileOperationError(
@@ -1811,13 +1939,16 @@ var init_audio_file_impl = __esm({
1811
1939
  }
1812
1940
  const sorted = sortChapters(chapters);
1813
1941
  const trackEndMs = this.audioProperties()?.durationMs;
1942
+ const style = options?.mp4ChapterStyle ?? "quicktime";
1943
+ const source = fmt === "MP4" ? style : "id3";
1814
1944
  const raw = sorted.map((c, i) => ({
1815
1945
  id: c.id,
1816
1946
  startTimeMs: c.startTimeMs,
1817
1947
  endTimeMs: inferEndTimeMs(sorted, i, trackEndMs) ?? c.startTimeMs,
1818
- title: c.title
1948
+ title: c.title,
1949
+ source
1819
1950
  }));
1820
- this.handle.setChapters(raw, options?.mp4ChapterStyle ?? "quicktime");
1951
+ this.handle.setChapters(raw, style);
1821
1952
  }
1822
1953
  getBext() {
1823
1954
  return getBext(this.handle);
@@ -1853,6 +1984,21 @@ var init_audio_file_impl = __esm({
1853
1984
  counter: r.counter ?? 0
1854
1985
  })));
1855
1986
  }
1987
+ getLyrics() {
1988
+ return this.handle.getLyrics().map((l) => {
1989
+ const out = { text: l.text };
1990
+ if (l.description) out.description = l.description;
1991
+ if (l.language) out.language = l.language;
1992
+ return out;
1993
+ });
1994
+ }
1995
+ setLyrics(lyrics) {
1996
+ this.handle.setLyrics(lyrics.map((l) => ({
1997
+ text: l.text,
1998
+ description: l.description ?? "",
1999
+ language: l.language ?? ""
2000
+ })));
2001
+ }
1856
2002
  getRating() {
1857
2003
  const ratings = this.getRatings();
1858
2004
  return ratings.length > 0 ? ratings[0].rating : void 0;
@@ -2026,7 +2172,7 @@ var VERSION;
2026
2172
  var init_version = __esm({
2027
2173
  "src/version.ts"() {
2028
2174
  "use strict";
2029
- VERSION = "1.2.2";
2175
+ VERSION = "1.3.1";
2030
2176
  }
2031
2177
  });
2032
2178
 
@@ -2444,6 +2590,7 @@ async function clearTags(file) {
2444
2590
  return withAudioFileSave(file, (audioFile) => {
2445
2591
  audioFile.setProperties({});
2446
2592
  audioFile.removePictures();
2593
+ audioFile.setLyrics([]);
2447
2594
  });
2448
2595
  }
2449
2596