taglib-wasm 1.5.2 → 1.6.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.
- package/LICENSE +12 -9
- package/README.md +6 -3
- package/dist/index.browser.js +255 -85
- package/dist/index.d.ts +1 -1
- package/dist/simple.browser.js +253 -84
- package/dist/src/constants/complex-properties.d.ts +18 -12
- package/dist/src/constants/complex-properties.d.ts.map +1 -1
- package/dist/src/constants/properties.d.ts +24 -0
- package/dist/src/constants/properties.d.ts.map +1 -1
- package/dist/src/constants/properties.js +34 -0
- package/dist/src/constants/specialized-properties.d.ts +23 -0
- package/dist/src/constants/specialized-properties.d.ts.map +1 -1
- package/dist/src/constants/specialized-properties.js +20 -0
- package/dist/src/constants/tags.d.ts.map +1 -1
- package/dist/src/constants/tags.js +2 -1
- package/dist/src/deno-compile.d.ts +12 -0
- package/dist/src/deno-compile.d.ts.map +1 -1
- package/dist/src/deno-compile.js +9 -8
- package/dist/src/msgpack/encoder.d.ts.map +1 -1
- package/dist/src/msgpack/encoder.js +6 -4
- package/dist/src/runtime/detector.d.ts +0 -6
- package/dist/src/runtime/detector.d.ts.map +1 -1
- package/dist/src/runtime/detector.js +1 -14
- package/dist/src/runtime/wasi-adapter/file-handle.d.ts +2 -0
- package/dist/src/runtime/wasi-adapter/file-handle.d.ts.map +1 -1
- package/dist/src/runtime/wasi-adapter/file-handle.js +61 -52
- package/dist/src/taglib/audio-file-base.d.ts.map +1 -1
- package/dist/src/taglib/audio-file-base.js +33 -77
- package/dist/src/taglib/mp4-item-names.d.ts +12 -0
- package/dist/src/taglib/mp4-item-names.d.ts.map +1 -0
- package/dist/src/taglib/mp4-item-names.js +4 -0
- package/dist/src/taglib/mutable-tag-impl.d.ts +14 -0
- package/dist/src/taglib/mutable-tag-impl.d.ts.map +1 -0
- package/dist/src/taglib/mutable-tag-impl.js +88 -0
- package/dist/src/taglib/save-reconstruct.d.ts.map +1 -1
- package/dist/src/taglib/save-reconstruct.js +4 -0
- package/dist/src/types/metadata-mappings.d.ts.map +1 -1
- package/dist/src/types/metadata-mappings.js +14 -0
- package/dist/src/types/tags.d.ts +16 -0
- package/dist/src/types/tags.d.ts.map +1 -1
- package/dist/src/utils/mirror-fields.d.ts +79 -0
- package/dist/src/utils/mirror-fields.d.ts.map +1 -0
- package/dist/src/utils/mirror-fields.js +67 -0
- package/dist/src/utils/tag-mapping.d.ts.map +1 -1
- package/dist/src/utils/tag-mapping.js +47 -5
- package/dist/src/version.d.ts +1 -1
- package/dist/src/version.js +1 -1
- package/dist/taglib-wasi.wasm +0 -0
- package/dist/taglib-web.wasm +0 -0
- package/dist/taglib-wrapper.js +1 -1
- package/package.json +8 -5
package/LICENSE
CHANGED
|
@@ -27,21 +27,24 @@ ADDITIONAL LICENSING INFORMATION:
|
|
|
27
27
|
This product includes software developed by Scott Wheeler and the TagLib
|
|
28
28
|
contributors (https://taglib.org/).
|
|
29
29
|
|
|
30
|
-
The WebAssembly
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
30
|
+
The WebAssembly binaries (build/taglib-web.wasm for the Emscripten backend
|
|
31
|
+
and build/taglib-wasi.wasm for the WASI backend, shipped as dist/*.wasm)
|
|
32
|
+
contain compiled TagLib code which is licensed under LGPL-2.1-or-later.
|
|
33
|
+
The original TagLib source code is located in lib/taglib/. See
|
|
34
|
+
lib/taglib/COPYING.LGPL for the full LGPL license terms.
|
|
34
35
|
|
|
35
36
|
LGPL COMPLIANCE:
|
|
36
|
-
The WebAssembly
|
|
37
|
-
- You may use
|
|
37
|
+
The WebAssembly binaries are subject to LGPL requirements. This means:
|
|
38
|
+
- You may use them in commercial applications
|
|
38
39
|
- Modifications to TagLib must be shared under LGPL
|
|
39
40
|
- You must enable users to relink with modified TagLib versions
|
|
40
41
|
|
|
41
|
-
To rebuild the WebAssembly
|
|
42
|
+
To rebuild the WebAssembly binaries with a modified TagLib:
|
|
42
43
|
1. Modify the TagLib source in lib/taglib/
|
|
43
|
-
2. Run: npm run build:wasm
|
|
44
|
-
|
|
44
|
+
2. Run: npm run build:wasm (builds both backends; requires the
|
|
45
|
+
Emscripten SDK and WASI SDK 33 — see CONTRIBUTING.md)
|
|
46
|
+
3. The new binaries will be in build/taglib-web.wasm and
|
|
47
|
+
build/taglib-wasi.wasm
|
|
45
48
|
|
|
46
49
|
For more information about LGPL compliance and dual licensing, see:
|
|
47
50
|
https://github.com/CharlesWiltgen/TagLib-Wasm#licensing
|
package/README.md
CHANGED
|
@@ -33,8 +33,11 @@ TagLib-Wasm is the **universal tagging library for TypeScript/JavaScript**
|
|
|
33
33
|
possible
|
|
34
34
|
- **Rich metadata** – Cover art, ratings, chapters (MP3 `CHAP`, MP4
|
|
35
35
|
QuickTime/Nero), and broadcast metadata (BWF `bext`/iXML for WAV/FLAC)
|
|
36
|
+
- **Value fidelity** – Tag values round-trip verbatim: a `"3/12"` track or a
|
|
37
|
+
zero-padded `"03"` is preserved, and MP4 freeform atoms keep their exact
|
|
38
|
+
casing and vendor `mean`, so other tools still recognise them
|
|
36
39
|
- **Zero dependencies** – Self-contained Wasm bundle
|
|
37
|
-
- **Tested** –
|
|
40
|
+
- **Tested** – 448 tests across all formats, with cross-backend parity coverage
|
|
38
41
|
- **Two API styles** – Use the "Simple" API (3 functions), or the full "Core"
|
|
39
42
|
API for more advanced applications
|
|
40
43
|
- **Batch folder operations** – Scan directories, process multiple files, find
|
|
@@ -48,8 +51,8 @@ TagLib-Wasm is the **universal tagging library for TypeScript/JavaScript**
|
|
|
48
51
|
npm install taglib-wasm
|
|
49
52
|
```
|
|
50
53
|
|
|
51
|
-
> **Note:** Requires Node.js
|
|
52
|
-
> exception handling support. To consume the package as TypeScript source
|
|
54
|
+
> **Note:** Requires Node.js v24 or higher (the Active LTS line) for WASI and
|
|
55
|
+
> WebAssembly exception handling support. To consume the package as TypeScript source
|
|
53
56
|
> (e.g., via `tsx`), see the
|
|
54
57
|
> [installation guide](https://charleswiltgen.github.io/TagLib-Wasm/guide/installation.html).
|
|
55
58
|
|
package/dist/index.browser.js
CHANGED
|
@@ -1443,6 +1443,26 @@ var init_specialized_properties = __esm({
|
|
|
1443
1443
|
vorbis: "ITUNNORM",
|
|
1444
1444
|
mp4: "----:com.apple.iTunes:iTunNORM"
|
|
1445
1445
|
}
|
|
1446
|
+
},
|
|
1447
|
+
/**
|
|
1448
|
+
* Apple gapless-playback data (encoder delay, padding, original sample count).
|
|
1449
|
+
*
|
|
1450
|
+
* Exists so the two Apple freeform atoms are presented consistently: without
|
|
1451
|
+
* it, `properties()` answered `appleSoundCheck` for iTunNORM but the raw
|
|
1452
|
+
* uppercased `ITUNSMPB` for its sibling, because only one had an alias. The
|
|
1453
|
+
* name mirrors `appleSoundCheck` — vendor-prefixed, feature-named rather than
|
|
1454
|
+
* atom-named (tuneup-ibo).
|
|
1455
|
+
*/
|
|
1456
|
+
appleGaplessInfo: {
|
|
1457
|
+
key: "ITUNSMPB",
|
|
1458
|
+
description: "Apple gapless playback data (encoder delay and padding)",
|
|
1459
|
+
type: "string",
|
|
1460
|
+
supportedFormats: ["ID3v2", "MP4", "Vorbis"],
|
|
1461
|
+
mappings: {
|
|
1462
|
+
id3v2: { frame: "TXXX", description: "iTunSMPB" },
|
|
1463
|
+
vorbis: "ITUNSMPB",
|
|
1464
|
+
mp4: "----:com.apple.iTunes:iTunSMPB"
|
|
1465
|
+
}
|
|
1446
1466
|
}
|
|
1447
1467
|
};
|
|
1448
1468
|
}
|
|
@@ -1452,6 +1472,17 @@ var init_specialized_properties = __esm({
|
|
|
1452
1472
|
function toTagLibKey(key) {
|
|
1453
1473
|
return _toTagLib[key] ?? key;
|
|
1454
1474
|
}
|
|
1475
|
+
function mp4AtomWireKey(atom) {
|
|
1476
|
+
return _mp4AtomToWireKey[atom];
|
|
1477
|
+
}
|
|
1478
|
+
function mp4FreeformAtomNames(wireKeys) {
|
|
1479
|
+
const names = [];
|
|
1480
|
+
for (const key of wireKeys) {
|
|
1481
|
+
const atom = _mp4FreeformAtoms[key];
|
|
1482
|
+
if (atom !== void 0) names.push(atom);
|
|
1483
|
+
}
|
|
1484
|
+
return names;
|
|
1485
|
+
}
|
|
1455
1486
|
function fromTagLibKey(key) {
|
|
1456
1487
|
return _fromTagLib[key] ?? key;
|
|
1457
1488
|
}
|
|
@@ -1462,7 +1493,7 @@ function remapKeysFromTagLib(obj) {
|
|
|
1462
1493
|
}
|
|
1463
1494
|
return result;
|
|
1464
1495
|
}
|
|
1465
|
-
var PROPERTIES, _toTagLib, _fromTagLib;
|
|
1496
|
+
var PROPERTIES, _toTagLib, _fromTagLib, _mp4FreeformAtoms, _mp4AtomToWireKey;
|
|
1466
1497
|
var init_properties = __esm({
|
|
1467
1498
|
"src/constants/properties.ts"() {
|
|
1468
1499
|
"use strict";
|
|
@@ -1486,6 +1517,129 @@ var init_properties = __esm({
|
|
|
1486
1517
|
_toTagLib["year"] = "DATE";
|
|
1487
1518
|
_toTagLib["track"] = "TRACKNUMBER";
|
|
1488
1519
|
_fromTagLib["disc"] = "discNumber";
|
|
1520
|
+
_mp4FreeformAtoms = {};
|
|
1521
|
+
for (const meta of Object.values(PROPERTIES)) {
|
|
1522
|
+
const { key, mappings } = meta;
|
|
1523
|
+
const atom = mappings?.mp4;
|
|
1524
|
+
if (typeof atom !== "string" || !atom.startsWith("----:")) continue;
|
|
1525
|
+
const bare = atom.slice(atom.lastIndexOf(":") + 1);
|
|
1526
|
+
if (bare === bare.toUpperCase()) continue;
|
|
1527
|
+
_mp4FreeformAtoms[key] = atom;
|
|
1528
|
+
const readKey = bare.toUpperCase();
|
|
1529
|
+
const camel = _fromTagLib[key];
|
|
1530
|
+
if (camel !== void 0 && _fromTagLib[readKey] === void 0) {
|
|
1531
|
+
_fromTagLib[readKey] = camel;
|
|
1532
|
+
}
|
|
1533
|
+
}
|
|
1534
|
+
_mp4AtomToWireKey = {};
|
|
1535
|
+
for (const meta of Object.values(PROPERTIES)) {
|
|
1536
|
+
const { key, mappings } = meta;
|
|
1537
|
+
const atom = mappings?.mp4;
|
|
1538
|
+
if (typeof atom !== "string" || atom.startsWith("----:")) continue;
|
|
1539
|
+
if (_mp4AtomToWireKey[atom] === void 0) _mp4AtomToWireKey[atom] = key;
|
|
1540
|
+
}
|
|
1541
|
+
}
|
|
1542
|
+
});
|
|
1543
|
+
|
|
1544
|
+
// src/taglib/mp4-item-names.ts
|
|
1545
|
+
var MP4_ITEM_NAMES_KEY;
|
|
1546
|
+
var init_mp4_item_names = __esm({
|
|
1547
|
+
"src/taglib/mp4-item-names.ts"() {
|
|
1548
|
+
"use strict";
|
|
1549
|
+
MP4_ITEM_NAMES_KEY = "_mp4ItemNames";
|
|
1550
|
+
}
|
|
1551
|
+
});
|
|
1552
|
+
|
|
1553
|
+
// src/taglib/mutable-tag-impl.ts
|
|
1554
|
+
function buildMutableTag(handle) {
|
|
1555
|
+
let data = handle.getTagData();
|
|
1556
|
+
const tag = {
|
|
1557
|
+
get title() {
|
|
1558
|
+
return data.title;
|
|
1559
|
+
},
|
|
1560
|
+
get artist() {
|
|
1561
|
+
return data.artist;
|
|
1562
|
+
},
|
|
1563
|
+
get album() {
|
|
1564
|
+
return data.album;
|
|
1565
|
+
},
|
|
1566
|
+
get comment() {
|
|
1567
|
+
return data.comment;
|
|
1568
|
+
},
|
|
1569
|
+
get genre() {
|
|
1570
|
+
return data.genre;
|
|
1571
|
+
},
|
|
1572
|
+
get year() {
|
|
1573
|
+
return data.year;
|
|
1574
|
+
},
|
|
1575
|
+
get track() {
|
|
1576
|
+
return data.track;
|
|
1577
|
+
},
|
|
1578
|
+
get date() {
|
|
1579
|
+
return handle.getProperty("DATE") || void 0;
|
|
1580
|
+
},
|
|
1581
|
+
setTitle: (value) => {
|
|
1582
|
+
handle.setTagData({ title: value });
|
|
1583
|
+
data = handle.getTagData();
|
|
1584
|
+
return tag;
|
|
1585
|
+
},
|
|
1586
|
+
setArtist: (value) => {
|
|
1587
|
+
handle.setTagData({ artist: value });
|
|
1588
|
+
data = handle.getTagData();
|
|
1589
|
+
return tag;
|
|
1590
|
+
},
|
|
1591
|
+
setAlbum: (value) => {
|
|
1592
|
+
handle.setTagData({ album: value });
|
|
1593
|
+
data = handle.getTagData();
|
|
1594
|
+
return tag;
|
|
1595
|
+
},
|
|
1596
|
+
setComment: (value) => {
|
|
1597
|
+
handle.setTagData({ comment: value });
|
|
1598
|
+
data = handle.getTagData();
|
|
1599
|
+
return tag;
|
|
1600
|
+
},
|
|
1601
|
+
setGenre: (value) => {
|
|
1602
|
+
handle.setTagData({ genre: value });
|
|
1603
|
+
data = handle.getTagData();
|
|
1604
|
+
return tag;
|
|
1605
|
+
},
|
|
1606
|
+
setYear: (value) => {
|
|
1607
|
+
handle.setTagData({ year: value });
|
|
1608
|
+
data = handle.getTagData();
|
|
1609
|
+
return tag;
|
|
1610
|
+
},
|
|
1611
|
+
setTrack: (value) => {
|
|
1612
|
+
const existing = value > 0 ? handle.getProperty(RAW_TRACK_WIRE_KEY) : "";
|
|
1613
|
+
const slash = existing.indexOf("/");
|
|
1614
|
+
if (slash !== -1) {
|
|
1615
|
+
handle.setProperty(
|
|
1616
|
+
RAW_TRACK_WIRE_KEY,
|
|
1617
|
+
`${value}${existing.slice(slash)}`
|
|
1618
|
+
);
|
|
1619
|
+
} else {
|
|
1620
|
+
handle.setTagData({ track: value });
|
|
1621
|
+
}
|
|
1622
|
+
data = handle.getTagData();
|
|
1623
|
+
return tag;
|
|
1624
|
+
},
|
|
1625
|
+
setDate: (value) => {
|
|
1626
|
+
if (value === "") {
|
|
1627
|
+
handle.setTagData({ year: 0 });
|
|
1628
|
+
} else {
|
|
1629
|
+
handle.setProperty("DATE", value);
|
|
1630
|
+
}
|
|
1631
|
+
data = handle.getTagData();
|
|
1632
|
+
return tag;
|
|
1633
|
+
}
|
|
1634
|
+
};
|
|
1635
|
+
return tag;
|
|
1636
|
+
}
|
|
1637
|
+
var RAW_TRACK_WIRE_KEY;
|
|
1638
|
+
var init_mutable_tag_impl = __esm({
|
|
1639
|
+
"src/taglib/mutable-tag-impl.ts"() {
|
|
1640
|
+
"use strict";
|
|
1641
|
+
init_properties();
|
|
1642
|
+
RAW_TRACK_WIRE_KEY = toTagLibKey("trackNumber");
|
|
1489
1643
|
}
|
|
1490
1644
|
});
|
|
1491
1645
|
|
|
@@ -1496,6 +1650,8 @@ var init_audio_file_base = __esm({
|
|
|
1496
1650
|
"use strict";
|
|
1497
1651
|
init_properties();
|
|
1498
1652
|
init_errors2();
|
|
1653
|
+
init_mp4_item_names();
|
|
1654
|
+
init_mutable_tag_impl();
|
|
1499
1655
|
LYRICS_PROPERTY_KEY = "lyrics";
|
|
1500
1656
|
LYRICS_WIRE_KEY = toTagLibKey(LYRICS_PROPERTY_KEY);
|
|
1501
1657
|
EMPTY_KEY_SET = /* @__PURE__ */ new Set();
|
|
@@ -1546,79 +1702,7 @@ var init_audio_file_base = __esm({
|
|
|
1546
1702
|
return this.getFormat() === format;
|
|
1547
1703
|
}
|
|
1548
1704
|
tag() {
|
|
1549
|
-
|
|
1550
|
-
let data = handle.getTagData();
|
|
1551
|
-
const tag = {
|
|
1552
|
-
get title() {
|
|
1553
|
-
return data.title;
|
|
1554
|
-
},
|
|
1555
|
-
get artist() {
|
|
1556
|
-
return data.artist;
|
|
1557
|
-
},
|
|
1558
|
-
get album() {
|
|
1559
|
-
return data.album;
|
|
1560
|
-
},
|
|
1561
|
-
get comment() {
|
|
1562
|
-
return data.comment;
|
|
1563
|
-
},
|
|
1564
|
-
get genre() {
|
|
1565
|
-
return data.genre;
|
|
1566
|
-
},
|
|
1567
|
-
get year() {
|
|
1568
|
-
return data.year;
|
|
1569
|
-
},
|
|
1570
|
-
get track() {
|
|
1571
|
-
return data.track;
|
|
1572
|
-
},
|
|
1573
|
-
get date() {
|
|
1574
|
-
return handle.getProperty("DATE") || void 0;
|
|
1575
|
-
},
|
|
1576
|
-
setTitle: (value) => {
|
|
1577
|
-
handle.setTagData({ title: value });
|
|
1578
|
-
data = handle.getTagData();
|
|
1579
|
-
return tag;
|
|
1580
|
-
},
|
|
1581
|
-
setArtist: (value) => {
|
|
1582
|
-
handle.setTagData({ artist: value });
|
|
1583
|
-
data = handle.getTagData();
|
|
1584
|
-
return tag;
|
|
1585
|
-
},
|
|
1586
|
-
setAlbum: (value) => {
|
|
1587
|
-
handle.setTagData({ album: value });
|
|
1588
|
-
data = handle.getTagData();
|
|
1589
|
-
return tag;
|
|
1590
|
-
},
|
|
1591
|
-
setComment: (value) => {
|
|
1592
|
-
handle.setTagData({ comment: value });
|
|
1593
|
-
data = handle.getTagData();
|
|
1594
|
-
return tag;
|
|
1595
|
-
},
|
|
1596
|
-
setGenre: (value) => {
|
|
1597
|
-
handle.setTagData({ genre: value });
|
|
1598
|
-
data = handle.getTagData();
|
|
1599
|
-
return tag;
|
|
1600
|
-
},
|
|
1601
|
-
setYear: (value) => {
|
|
1602
|
-
handle.setTagData({ year: value });
|
|
1603
|
-
data = handle.getTagData();
|
|
1604
|
-
return tag;
|
|
1605
|
-
},
|
|
1606
|
-
setTrack: (value) => {
|
|
1607
|
-
handle.setTagData({ track: value });
|
|
1608
|
-
data = handle.getTagData();
|
|
1609
|
-
return tag;
|
|
1610
|
-
},
|
|
1611
|
-
setDate: (value) => {
|
|
1612
|
-
if (value === "") {
|
|
1613
|
-
handle.setTagData({ year: 0 });
|
|
1614
|
-
} else {
|
|
1615
|
-
handle.setProperty("DATE", value);
|
|
1616
|
-
}
|
|
1617
|
-
data = handle.getTagData();
|
|
1618
|
-
return tag;
|
|
1619
|
-
}
|
|
1620
|
-
};
|
|
1621
|
-
return tag;
|
|
1705
|
+
return buildMutableTag(this.handle);
|
|
1622
1706
|
}
|
|
1623
1707
|
audioProperties() {
|
|
1624
1708
|
if (!this.cachedAudioProperties) {
|
|
@@ -1640,14 +1724,29 @@ var init_audio_file_base = __esm({
|
|
|
1640
1724
|
const existing = this.handle.getProperties()[LYRICS_WIRE_KEY];
|
|
1641
1725
|
if (existing !== void 0) translated[LYRICS_WIRE_KEY] = existing;
|
|
1642
1726
|
}
|
|
1727
|
+
const atomNames = mp4FreeformAtomNames(Object.keys(translated));
|
|
1728
|
+
if (atomNames.length > 0) translated[MP4_ITEM_NAMES_KEY] = atomNames;
|
|
1643
1729
|
this.handle.setProperties(translated);
|
|
1644
1730
|
}
|
|
1645
1731
|
getProperty(key) {
|
|
1646
1732
|
const value = this.handle.getProperty(toTagLibKey(key));
|
|
1647
|
-
|
|
1733
|
+
if (value !== "") return value;
|
|
1734
|
+
if (!this.isMP4()) return void 0;
|
|
1735
|
+
const remapped = this.properties()[key]?.[0];
|
|
1736
|
+
return remapped === "" ? void 0 : remapped;
|
|
1648
1737
|
}
|
|
1649
1738
|
setProperty(key, value) {
|
|
1650
|
-
|
|
1739
|
+
const wireKey = toTagLibKey(key);
|
|
1740
|
+
const atomNames = mp4FreeformAtomNames([wireKey]);
|
|
1741
|
+
if (atomNames.length > 0 && this.isMP4()) {
|
|
1742
|
+
this.handle.setProperties({
|
|
1743
|
+
...this.handle.getProperties(),
|
|
1744
|
+
[wireKey]: [value],
|
|
1745
|
+
[MP4_ITEM_NAMES_KEY]: atomNames
|
|
1746
|
+
});
|
|
1747
|
+
return;
|
|
1748
|
+
}
|
|
1749
|
+
this.handle.setProperty(wireKey, value);
|
|
1651
1750
|
}
|
|
1652
1751
|
isMP4() {
|
|
1653
1752
|
return this.handle.isMP4();
|
|
@@ -1656,13 +1755,19 @@ var init_audio_file_base = __esm({
|
|
|
1656
1755
|
if (!this.isMP4()) {
|
|
1657
1756
|
throw new UnsupportedFormatError(this.getFormat(), ["MP4", "M4A"]);
|
|
1658
1757
|
}
|
|
1659
|
-
const
|
|
1758
|
+
const wireKey = mp4AtomWireKey(key);
|
|
1759
|
+
const value = wireKey !== void 0 ? this.handle.getProperty(wireKey) : this.handle.getMP4Item(key);
|
|
1660
1760
|
return value === "" ? void 0 : value;
|
|
1661
1761
|
}
|
|
1662
1762
|
setMP4Item(key, value) {
|
|
1663
1763
|
if (!this.isMP4()) {
|
|
1664
1764
|
throw new UnsupportedFormatError(this.getFormat(), ["MP4", "M4A"]);
|
|
1665
1765
|
}
|
|
1766
|
+
const wireKey = mp4AtomWireKey(key);
|
|
1767
|
+
if (wireKey !== void 0) {
|
|
1768
|
+
this.handle.setProperty(wireKey, value);
|
|
1769
|
+
return;
|
|
1770
|
+
}
|
|
1666
1771
|
this.handle.setMP4Item(key, value);
|
|
1667
1772
|
}
|
|
1668
1773
|
removeMP4Item(key) {
|
|
@@ -1945,6 +2050,8 @@ function copyEditedState(target, source, sourceComplete, deletedKeys) {
|
|
|
1945
2050
|
target.setTagData(source.getTagData());
|
|
1946
2051
|
const merged = sourceComplete ? source.getProperties() : { ...target.getProperties(), ...source.getProperties() };
|
|
1947
2052
|
for (const key of deletedKeys) delete merged[key];
|
|
2053
|
+
const atomNames = mp4FreeformAtomNames(Object.keys(merged));
|
|
2054
|
+
if (atomNames.length > 0) merged[MP4_ITEM_NAMES_KEY] = atomNames;
|
|
1948
2055
|
target.setProperties(merged);
|
|
1949
2056
|
copyExtraState(target, source, sourceComplete);
|
|
1950
2057
|
}
|
|
@@ -1980,6 +2087,8 @@ var init_save_reconstruct = __esm({
|
|
|
1980
2087
|
init_write();
|
|
1981
2088
|
init_embind_adapter();
|
|
1982
2089
|
init_extra_state_registry();
|
|
2090
|
+
init_properties();
|
|
2091
|
+
init_mp4_item_names();
|
|
1983
2092
|
}
|
|
1984
2093
|
});
|
|
1985
2094
|
|
|
@@ -2285,11 +2394,25 @@ var init_load_audio_data = __esm({
|
|
|
2285
2394
|
}
|
|
2286
2395
|
});
|
|
2287
2396
|
|
|
2288
|
-
// src/utils/
|
|
2289
|
-
function
|
|
2290
|
-
const parsed = Number.parseInt(
|
|
2397
|
+
// src/utils/mirror-fields.ts
|
|
2398
|
+
function parseLeadingInt(raw) {
|
|
2399
|
+
const parsed = Number.parseInt(raw, 10);
|
|
2291
2400
|
return Number.isNaN(parsed) ? void 0 : parsed;
|
|
2292
2401
|
}
|
|
2402
|
+
var MIRROR_FIELDS, DATE_MIRROR, TRACK_MIRROR;
|
|
2403
|
+
var init_mirror_fields = __esm({
|
|
2404
|
+
"src/utils/mirror-fields.ts"() {
|
|
2405
|
+
"use strict";
|
|
2406
|
+
MIRROR_FIELDS = [
|
|
2407
|
+
{ raw: "date", numeric: "year" },
|
|
2408
|
+
{ raw: "trackNumber", numeric: "track" }
|
|
2409
|
+
];
|
|
2410
|
+
DATE_MIRROR = MIRROR_FIELDS[0];
|
|
2411
|
+
TRACK_MIRROR = MIRROR_FIELDS[1];
|
|
2412
|
+
}
|
|
2413
|
+
});
|
|
2414
|
+
|
|
2415
|
+
// src/utils/tag-mapping.ts
|
|
2293
2416
|
function readExtendedTag(audioFile) {
|
|
2294
2417
|
const tag = mapPropertiesToExtendedTag(audioFile.properties());
|
|
2295
2418
|
const pictures = audioFile.getPictures();
|
|
@@ -2314,21 +2437,35 @@ function mapPropertiesToExtendedTag(props) {
|
|
|
2314
2437
|
const values = props[propKey];
|
|
2315
2438
|
if (!values || values.length === 0) continue;
|
|
2316
2439
|
if (tagField === "year" || tagField === "track") {
|
|
2317
|
-
const num =
|
|
2440
|
+
const num = parseLeadingInt(values[0]);
|
|
2318
2441
|
if (num !== void 0) tag[tagField] = num;
|
|
2319
2442
|
if (propKey === "date") {
|
|
2320
2443
|
tag.date = values.length === 1 ? values[0] : values;
|
|
2321
2444
|
}
|
|
2445
|
+
if (propKey === "trackNumber") {
|
|
2446
|
+
tag.trackNumber = values.length === 1 ? values[0] : values;
|
|
2447
|
+
}
|
|
2322
2448
|
} else {
|
|
2323
2449
|
tag[tagField] = values;
|
|
2324
2450
|
}
|
|
2325
2451
|
}
|
|
2452
|
+
for (const [propKey, totalField] of [
|
|
2453
|
+
["trackNumber", "totalTracks"],
|
|
2454
|
+
["discNumber", "totalDiscs"]
|
|
2455
|
+
]) {
|
|
2456
|
+
if (tag[totalField] !== void 0) continue;
|
|
2457
|
+
const raw = props[propKey]?.[0];
|
|
2458
|
+
const slash = raw?.indexOf("/") ?? -1;
|
|
2459
|
+
if (raw === void 0 || slash === -1) continue;
|
|
2460
|
+
const total = parseLeadingInt(raw.slice(slash + 1));
|
|
2461
|
+
if (total !== void 0) tag[totalField] = total;
|
|
2462
|
+
}
|
|
2326
2463
|
for (const [key, values] of Object.entries(props)) {
|
|
2327
2464
|
if (BASIC_PROPERTY_KEYS[key]) continue;
|
|
2328
2465
|
if (!values || values.length === 0) continue;
|
|
2329
2466
|
const camelKey = fromTagLibKey(key);
|
|
2330
2467
|
if (NUMERIC_FIELDS.has(camelKey)) {
|
|
2331
|
-
const num =
|
|
2468
|
+
const num = parseLeadingInt(values[0]);
|
|
2332
2469
|
if (num !== void 0) tag[camelKey] = num;
|
|
2333
2470
|
} else if (camelKey === "compilation") {
|
|
2334
2471
|
tag[camelKey] = values[0] === "1";
|
|
@@ -2341,8 +2478,29 @@ function mapPropertiesToExtendedTag(props) {
|
|
|
2341
2478
|
function mergeTagUpdates(file, tags) {
|
|
2342
2479
|
const currentProps = file.properties();
|
|
2343
2480
|
const newProps = normalizeTagInput(tags);
|
|
2481
|
+
reconcilePairFields(currentProps, newProps, tags);
|
|
2344
2482
|
file.setProperties({ ...currentProps, ...newProps });
|
|
2345
2483
|
}
|
|
2484
|
+
function reconcilePairFields(currentProps, newProps, input) {
|
|
2485
|
+
const pairs = [
|
|
2486
|
+
["trackNumber", "totalTracks", "track"],
|
|
2487
|
+
["discNumber", "totalDiscs", "discNumber"]
|
|
2488
|
+
];
|
|
2489
|
+
for (const [rawKey, totalKey, numericKey] of pairs) {
|
|
2490
|
+
const incoming = newProps[rawKey]?.[0];
|
|
2491
|
+
if (incoming?.includes("/")) {
|
|
2492
|
+
newProps[totalKey] = [];
|
|
2493
|
+
continue;
|
|
2494
|
+
}
|
|
2495
|
+
const numericOnly = input[numericKey] !== void 0 && input[rawKey] === void 0;
|
|
2496
|
+
if (!numericOnly || incoming === void 0) continue;
|
|
2497
|
+
const existing = currentProps[rawKey]?.[0];
|
|
2498
|
+
const slash = existing?.indexOf("/") ?? -1;
|
|
2499
|
+
if (existing !== void 0 && slash !== -1) {
|
|
2500
|
+
newProps[rawKey] = [`${incoming}${existing.slice(slash)}`];
|
|
2501
|
+
}
|
|
2502
|
+
}
|
|
2503
|
+
}
|
|
2346
2504
|
function normalizeTagInput(input) {
|
|
2347
2505
|
const props = {};
|
|
2348
2506
|
for (const field of [
|
|
@@ -2365,6 +2523,9 @@ function normalizeTagInput(input) {
|
|
|
2365
2523
|
if (input.track !== void 0) {
|
|
2366
2524
|
props.trackNumber = [String(input.track)];
|
|
2367
2525
|
}
|
|
2526
|
+
if (input.trackNumber !== void 0) {
|
|
2527
|
+
props.trackNumber = Array.isArray(input.trackNumber) ? input.trackNumber : [input.trackNumber];
|
|
2528
|
+
}
|
|
2368
2529
|
for (const [field, val] of Object.entries(input)) {
|
|
2369
2530
|
if (BASIC_FIELDS.has(field) || val === void 0) continue;
|
|
2370
2531
|
if (field === "compilation") {
|
|
@@ -2377,6 +2538,12 @@ function normalizeTagInput(input) {
|
|
|
2377
2538
|
props[field] = val;
|
|
2378
2539
|
}
|
|
2379
2540
|
}
|
|
2541
|
+
for (const [rawField, totalField] of [
|
|
2542
|
+
["trackNumber", "totalTracks"],
|
|
2543
|
+
["discNumber", "totalDiscs"]
|
|
2544
|
+
]) {
|
|
2545
|
+
if (props[rawField]?.[0]?.includes("/")) delete props[totalField];
|
|
2546
|
+
}
|
|
2380
2547
|
return props;
|
|
2381
2548
|
}
|
|
2382
2549
|
var BASIC_PROPERTY_KEYS, BASIC_FIELDS, NUMERIC_FIELDS;
|
|
@@ -2384,6 +2551,7 @@ var init_tag_mapping = __esm({
|
|
|
2384
2551
|
"src/utils/tag-mapping.ts"() {
|
|
2385
2552
|
"use strict";
|
|
2386
2553
|
init_properties();
|
|
2554
|
+
init_mirror_fields();
|
|
2387
2555
|
BASIC_PROPERTY_KEYS = {
|
|
2388
2556
|
title: "title",
|
|
2389
2557
|
artist: "artist",
|
|
@@ -2401,7 +2569,8 @@ var init_tag_mapping = __esm({
|
|
|
2401
2569
|
"genre",
|
|
2402
2570
|
"year",
|
|
2403
2571
|
"date",
|
|
2404
|
-
"track"
|
|
2572
|
+
"track",
|
|
2573
|
+
"trackNumber"
|
|
2405
2574
|
]);
|
|
2406
2575
|
NUMERIC_FIELDS = /* @__PURE__ */ new Set([
|
|
2407
2576
|
"year",
|
|
@@ -2419,7 +2588,7 @@ var VERSION;
|
|
|
2419
2588
|
var init_version = __esm({
|
|
2420
2589
|
"src/version.ts"() {
|
|
2421
2590
|
"use strict";
|
|
2422
|
-
VERSION = "1.
|
|
2591
|
+
VERSION = "1.6.0";
|
|
2423
2592
|
}
|
|
2424
2593
|
});
|
|
2425
2594
|
|
|
@@ -3101,8 +3270,9 @@ var Tags = {
|
|
|
3101
3270
|
InvolvedPeople: "involvedPeople",
|
|
3102
3271
|
Encoding: "encoding"
|
|
3103
3272
|
};
|
|
3273
|
+
var VALID_TAG_NAMES = new Set(Object.values(Tags));
|
|
3104
3274
|
function isValidTagName(name) {
|
|
3105
|
-
return
|
|
3275
|
+
return VALID_TAG_NAMES.has(name);
|
|
3106
3276
|
}
|
|
3107
3277
|
function getAllTagNames() {
|
|
3108
3278
|
return Object.values(Tags);
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @fileoverview Main module exports for TagLib-Wasm
|
|
3
3
|
*
|
|
4
|
-
* TagLib v2.3 compiled to WebAssembly with TypeScript bindings
|
|
4
|
+
* TagLib v2.3.1 compiled to WebAssembly with TypeScript bindings
|
|
5
5
|
* for universal audio metadata handling across all JavaScript runtimes.
|
|
6
6
|
*
|
|
7
7
|
* @module TagLib-Wasm
|