taglib-wasm 2.2.0 → 2.2.2

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 (61) hide show
  1. package/dist/disc-folder.d.ts +3 -1
  2. package/dist/disc-folder.d.ts.map +1 -1
  3. package/dist/disc-folder.js +187 -152
  4. package/dist/index.browser.js +7 -4
  5. package/dist/simple.browser.js +22 -9
  6. package/dist/src/folder-api/album-corroborate.d.ts +13 -0
  7. package/dist/src/folder-api/album-corroborate.d.ts.map +1 -0
  8. package/dist/src/folder-api/album-corroborate.js +36 -0
  9. package/dist/src/folder-api/album-discs.d.ts +13 -0
  10. package/dist/src/folder-api/album-discs.d.ts.map +1 -0
  11. package/dist/src/folder-api/album-discs.js +122 -0
  12. package/dist/src/folder-api/album-grouping.d.ts +24 -0
  13. package/dist/src/folder-api/album-grouping.d.ts.map +1 -0
  14. package/dist/src/folder-api/album-grouping.js +118 -0
  15. package/dist/src/folder-api/album-identity.d.ts +27 -0
  16. package/dist/src/folder-api/album-identity.d.ts.map +1 -0
  17. package/dist/src/folder-api/album-identity.js +151 -0
  18. package/dist/src/folder-api/{group-albums.d.ts → album-types.d.ts} +55 -26
  19. package/dist/src/folder-api/album-types.d.ts.map +1 -0
  20. package/dist/src/folder-api/album-types.js +0 -0
  21. package/dist/src/folder-api/folder-disc.d.ts +24 -0
  22. package/dist/src/folder-api/folder-disc.d.ts.map +1 -0
  23. package/dist/src/folder-api/folder-disc.js +169 -0
  24. package/dist/src/folder-api/index.d.ts +3 -1
  25. package/dist/src/folder-api/index.d.ts.map +1 -1
  26. package/dist/src/folder-api/index.js +2 -4
  27. package/dist/src/folder-api/scan-for-albums.d.ts +1 -1
  28. package/dist/src/folder-api/scan-for-albums.d.ts.map +1 -1
  29. package/dist/src/folder-api/scan-for-albums.js +1 -3
  30. package/dist/src/runtime/wasi-adapter/audio-properties.d.ts +20 -0
  31. package/dist/src/runtime/wasi-adapter/audio-properties.d.ts.map +1 -0
  32. package/dist/src/runtime/wasi-adapter/audio-properties.js +84 -0
  33. package/dist/src/runtime/wasi-adapter/file-handle.d.ts +5 -19
  34. package/dist/src/runtime/wasi-adapter/file-handle.d.ts.map +1 -1
  35. package/dist/src/runtime/wasi-adapter/file-handle.js +67 -389
  36. package/dist/src/runtime/wasi-adapter/handle-state.d.ts +28 -0
  37. package/dist/src/runtime/wasi-adapter/handle-state.d.ts.map +1 -0
  38. package/dist/src/runtime/wasi-adapter/handle-state.js +58 -0
  39. package/dist/src/runtime/wasi-adapter/mp4-items.d.ts +34 -0
  40. package/dist/src/runtime/wasi-adapter/mp4-items.d.ts.map +1 -0
  41. package/dist/src/runtime/wasi-adapter/mp4-items.js +80 -0
  42. package/dist/src/runtime/wasi-adapter/property-surface.d.ts +25 -0
  43. package/dist/src/runtime/wasi-adapter/property-surface.d.ts.map +1 -0
  44. package/dist/src/runtime/wasi-adapter/property-surface.js +104 -0
  45. package/dist/src/runtime/wasi-adapter/structured-fields.d.ts +44 -0
  46. package/dist/src/runtime/wasi-adapter/structured-fields.d.ts.map +1 -0
  47. package/dist/src/runtime/wasi-adapter/structured-fields.js +139 -0
  48. package/dist/src/runtime/wasi-adapter/tag-keys.d.ts +28 -0
  49. package/dist/src/runtime/wasi-adapter/tag-keys.d.ts.map +1 -0
  50. package/dist/src/runtime/wasi-adapter/tag-keys.js +46 -0
  51. package/dist/src/simple/batch-operations.d.ts +28 -11
  52. package/dist/src/simple/batch-operations.d.ts.map +1 -1
  53. package/dist/src/simple/batch-operations.js +17 -7
  54. package/dist/src/utils/tag-mapping.d.ts +8 -0
  55. package/dist/src/utils/tag-mapping.d.ts.map +1 -1
  56. package/dist/src/utils/tag-mapping.js +6 -3
  57. package/dist/src/version.d.ts +1 -1
  58. package/dist/src/version.js +1 -1
  59. package/package.json +2 -1
  60. package/dist/src/folder-api/group-albums.d.ts.map +0 -1
  61. package/dist/src/folder-api/group-albums.js +0 -544
@@ -0,0 +1,34 @@
1
+ /**
2
+ * MP4 item operations over the WASI tag-data snapshot.
3
+ *
4
+ * Pure functions over tagData: the WasiFileHandle wrappers add destruction
5
+ * guards. Extracted from file-handle.ts in the taglib-1dfc split.
6
+ */
7
+ /**
8
+ * Normalize an MP4 item key for the PropertyMap path. TagLib's MP4 PropertyMap
9
+ * keys a freeform `----:mean:NAME` atom by its bare NAME, uppercased (the usual
10
+ * key remap then maps known atoms like iTunNORM -> appleSoundCheck). WASI MP4
11
+ * items ride the PropertyMap, so the full iTunes atom key that Emscripten's
12
+ * dedicated Item API uses must be normalized or the value is silently dropped on
13
+ * save (taglib-1qn). Non-freeform keys pass through unchanged.
14
+ */
15
+ export declare function mp4ItemPropertyKey(key: string): string;
16
+ /** Read an MP4 item; foreign-mean atoms ride their full `----:` slot. */
17
+ export declare function getMP4Item(tagData: Record<string, unknown> | null, key: string): string;
18
+ /** Write an MP4 item; returns the next snapshot record. */
19
+ export declare function setMP4Item(tagData: Record<string, unknown> | null, key: string, value: string): Record<string, unknown>;
20
+ /** Record an exact atom name to be repaired after the PropertyMap write. */
21
+ export declare function registerMp4ItemName(tagData: Record<string, unknown> | null, name: string): Record<string, unknown>;
22
+ /**
23
+ * Record a foreign-mean freeform atom for deletion at save (taglib-65nm).
24
+ * The PropertyMap erase pass cannot express foreign atoms, so the C++ shim
25
+ * consumes this list and calls removeItem on each exact atom name.
26
+ */
27
+ export declare function registerMp4ItemRemoval(tagData: Record<string, unknown> | null, name: string): Record<string, unknown>;
28
+ /** The accumulated foreign-atom removals pending the next save. */
29
+ export declare function getMp4ItemRemovals(tagData: Record<string, unknown> | null): string[] | undefined;
30
+ /** Replace the pending foreign-atom removal list. */
31
+ export declare function setMp4ItemRemovals(tagData: Record<string, unknown> | null, removals: string[]): Record<string, unknown>;
32
+ /** Remove an MP4 item; returns the next snapshot record (null on no-op). */
33
+ export declare function removeMP4Item(tagData: Record<string, unknown> | null, key: string): Record<string, unknown> | null;
34
+ //# sourceMappingURL=mp4-items.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mp4-items.d.ts","sourceRoot":"","sources":["../../../../src/runtime/wasi-adapter/mp4-items.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAStD;AAED,yEAAyE;AACzE,wBAAgB,UAAU,CACxB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,EACvC,GAAG,EAAE,MAAM,GACV,MAAM,CAiBR;AAED,2DAA2D;AAC3D,wBAAgB,UAAU,CACxB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,EACvC,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,GACZ,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAgBzB;AAED,4EAA4E;AAC5E,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,EACvC,IAAI,EAAE,MAAM,GACX,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAOzB;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,EACvC,IAAI,EAAE,MAAM,GACX,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAOzB;AAED,mEAAmE;AACnE,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,GACtC,MAAM,EAAE,GAAG,SAAS,CAEtB;AAED,qDAAqD;AACrD,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,EACvC,QAAQ,EAAE,MAAM,EAAE,GACjB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAKzB;AAED,4EAA4E;AAC5E,wBAAgB,aAAa,CAC3B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,EACvC,GAAG,EAAE,MAAM,GACV,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAmBhC"}
@@ -0,0 +1,80 @@
1
+ import { fromTagLibKey, mp4AtomWireKey } from "../../constants/properties.js";
2
+ import { mirrorForRawKey, stringifyScalar } from "../../utils/mirror-fields.js";
3
+ import { getProperty, setProperty } from "./property-surface.js";
4
+ function mp4ItemPropertyKey(key) {
5
+ if (key.startsWith("----:")) {
6
+ return key.slice(key.lastIndexOf(":") + 1).toUpperCase();
7
+ }
8
+ return mp4AtomWireKey(key) ?? key;
9
+ }
10
+ function getMP4Item(tagData, key) {
11
+ if (key.startsWith("----:")) {
12
+ const foreign = tagData?.[key];
13
+ if (foreign !== void 0) {
14
+ if (Array.isArray(foreign)) {
15
+ const first = foreign[0];
16
+ return stringifyScalar(first);
17
+ }
18
+ return stringifyScalar(foreign);
19
+ }
20
+ }
21
+ return getProperty(tagData, mp4ItemPropertyKey(key));
22
+ }
23
+ function setMP4Item(tagData, key, value) {
24
+ let next = setProperty(tagData, mp4ItemPropertyKey(key), value);
25
+ if (key.startsWith("----:")) {
26
+ next = registerMp4ItemName(next, key);
27
+ if (next?.[key] !== void 0) {
28
+ next = { ...next, [key]: value };
29
+ }
30
+ }
31
+ return next;
32
+ }
33
+ function registerMp4ItemName(tagData, name) {
34
+ const existing = tagData?._mp4ItemNames ?? [];
35
+ if (existing.includes(name)) return tagData ?? {};
36
+ return {
37
+ ...tagData,
38
+ _mp4ItemNames: [...existing, name]
39
+ };
40
+ }
41
+ function registerMp4ItemRemoval(tagData, name) {
42
+ const existing = tagData?._mp4ItemRemovals ?? [];
43
+ if (existing.includes(name)) return tagData ?? {};
44
+ return {
45
+ ...tagData,
46
+ _mp4ItemRemovals: [...existing, name]
47
+ };
48
+ }
49
+ function getMp4ItemRemovals(tagData) {
50
+ return tagData?._mp4ItemRemovals;
51
+ }
52
+ function setMp4ItemRemovals(tagData, removals) {
53
+ return {
54
+ ...tagData,
55
+ _mp4ItemRemovals: [...new Set(removals)]
56
+ };
57
+ }
58
+ function removeMP4Item(tagData, key) {
59
+ if (!tagData) return null;
60
+ let next = tagData;
61
+ if (key.startsWith("----:")) {
62
+ delete next[key];
63
+ next = registerMp4ItemRemoval(next, key);
64
+ }
65
+ const mappedKey = fromTagLibKey(mp4ItemPropertyKey(key));
66
+ delete next[mappedKey];
67
+ const mirror = mirrorForRawKey(mappedKey);
68
+ if (mirror !== void 0) delete next[mirror.numeric];
69
+ return next;
70
+ }
71
+ export {
72
+ getMP4Item,
73
+ getMp4ItemRemovals,
74
+ mp4ItemPropertyKey,
75
+ registerMp4ItemName,
76
+ registerMp4ItemRemoval,
77
+ removeMP4Item,
78
+ setMP4Item,
79
+ setMp4ItemRemovals
80
+ };
@@ -0,0 +1,25 @@
1
+ /**
2
+ * The property surface over the WASI tag-data snapshot.
3
+ *
4
+ * Pure functions over tagData: the WasiFileHandle wrappers add destruction
5
+ * guards. Extracted from file-handle.ts in the taglib-1dfc split.
6
+ */
7
+ import type { BasicTagData } from "../../types/tags.js";
8
+ /** The readable basic-tag surface (first-value idiom, numeric mirrors). */
9
+ export declare function getBasicTagData(tagData: Record<string, unknown> | null): BasicTagData;
10
+ /**
11
+ * Merge a basic-tag write into the snapshot. setYear()/setTrack() are
12
+ * authoritative for their wire key: the raw mirror is replaced so a stale
13
+ * "1975-10-31" or "3/12" cannot shadow the new number, and a non-positive
14
+ * value clears the field outright (taglib-bk7, taglib-qpl).
15
+ */
16
+ export declare function setBasicTagData(tagData: Record<string, unknown> | null, data: Partial<BasicTagData>): Record<string, unknown>;
17
+ /** The property map over the snapshot, excluding audio/internal keys. */
18
+ export declare function getProperties(tagData: Record<string, unknown> | null): Record<string, string[]>;
19
+ /** Replace-style property write; returns the next snapshot record. */
20
+ export declare function setProperties(tagData: Record<string, unknown> | null, props: Record<string, string[]>): Record<string, unknown>;
21
+ /** Single-property read (first value of arrays). */
22
+ export declare function getProperty(tagData: Record<string, unknown> | null, key: string): string;
23
+ /** Single-property write; returns the next snapshot record. */
24
+ export declare function setProperty(tagData: Record<string, unknown> | null, key: string, value: string): Record<string, unknown>;
25
+ //# sourceMappingURL=property-surface.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"property-surface.d.ts","sourceRoot":"","sources":["../../../../src/runtime/wasi-adapter/property-surface.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAexD,2EAA2E;AAC3E,wBAAgB,eAAe,CAC7B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,GACtC,YAAY,CAWd;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,EACvC,IAAI,EAAE,OAAO,CAAC,YAAY,CAAC,GAC1B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CASzB;AAED,yEAAyE;AACzE,wBAAgB,aAAa,CAC3B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,GACtC,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAsC1B;AAED,sEAAsE;AACtE,wBAAgB,aAAa,CAC3B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,EACvC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,GAC9B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAgCzB;AAED,oDAAoD;AACpD,wBAAgB,WAAW,CACzB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,EACvC,GAAG,EAAE,MAAM,GACV,MAAM,CAQR;AAED,+DAA+D;AAC/D,wBAAgB,WAAW,CACzB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,EACvC,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,GACZ,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAazB"}
@@ -0,0 +1,104 @@
1
+ import { fromTagLibKey, toTagLibKey } from "../../constants/properties.js";
2
+ import {
3
+ DATE_MIRROR,
4
+ firstValueString,
5
+ isShadowedNumericMirror,
6
+ mirrorForRawKey,
7
+ readNumericMirror,
8
+ stageNumericWrite,
9
+ stageRawWrite,
10
+ stringifyScalar,
11
+ TRACK_MIRROR
12
+ } from "../../utils/mirror-fields.js";
13
+ import { AUDIO_KEYS, INTERNAL_KEYS } from "./tag-keys.js";
14
+ function getBasicTagData(tagData) {
15
+ const d = tagData ?? {};
16
+ return {
17
+ title: firstValueString(d.title),
18
+ artist: firstValueString(d.artist),
19
+ album: firstValueString(d.album),
20
+ comment: firstValueString(d.comment),
21
+ genre: firstValueString(d.genre),
22
+ year: readNumericMirror(d, DATE_MIRROR),
23
+ track: readNumericMirror(d, TRACK_MIRROR)
24
+ };
25
+ }
26
+ function setBasicTagData(tagData, data) {
27
+ const merged = { ...tagData, ...data };
28
+ if (data.year !== void 0) {
29
+ stageNumericWrite(merged, DATE_MIRROR, data.year);
30
+ }
31
+ if (data.track !== void 0) {
32
+ stageNumericWrite(merged, TRACK_MIRROR, data.track);
33
+ }
34
+ return merged;
35
+ }
36
+ function getProperties(tagData) {
37
+ const result = {};
38
+ const data = tagData ?? {};
39
+ for (const [key, value] of Object.entries(data)) {
40
+ if (AUDIO_KEYS.has(key) || INTERNAL_KEYS.has(key)) continue;
41
+ if (key.startsWith("----:")) continue;
42
+ if (isShadowedNumericMirror(data, key)) continue;
43
+ if (value === void 0 || value === null) continue;
44
+ if (value === 0 || value === "") continue;
45
+ const propKey = toTagLibKey(key);
46
+ if (Array.isArray(value)) {
47
+ if (value.length === 0) continue;
48
+ result[propKey] = value.map(String);
49
+ } else if (typeof value === "object") {
50
+ continue;
51
+ } else if (typeof value === "string" || typeof value === "number" || typeof value === "boolean" || typeof value === "bigint" || typeof value === "symbol") {
52
+ result[propKey] = [String(value)];
53
+ }
54
+ }
55
+ return result;
56
+ }
57
+ function setProperties(tagData, props) {
58
+ const next = { ...tagData };
59
+ for (const [key, values] of Object.entries(props)) {
60
+ const camelKey = fromTagLibKey(key);
61
+ if (camelKey === "_mp4ItemNames") {
62
+ const existing = next._mp4ItemNames ?? [];
63
+ next._mp4ItemNames = [.../* @__PURE__ */ new Set([...existing, ...values])];
64
+ continue;
65
+ }
66
+ const mirror = mirrorForRawKey(camelKey);
67
+ if (mirror !== void 0) {
68
+ stageRawWrite(next, mirror, values);
69
+ } else if (values.length === 0) {
70
+ if (camelKey === "itunesAdvisory") next[camelKey] = [];
71
+ else delete next[camelKey];
72
+ } else {
73
+ next[camelKey] = values;
74
+ }
75
+ }
76
+ return next;
77
+ }
78
+ function getProperty(tagData, key) {
79
+ const mappedKey = fromTagLibKey(key);
80
+ const stored = tagData?.[mappedKey];
81
+ if (Array.isArray(stored)) {
82
+ const first = stored[0];
83
+ return stringifyScalar(first);
84
+ }
85
+ return stringifyScalar(stored);
86
+ }
87
+ function setProperty(tagData, key, value) {
88
+ const mappedKey = fromTagLibKey(key);
89
+ const mirror = mirrorForRawKey(mappedKey);
90
+ if (mirror !== void 0) {
91
+ const next = { ...tagData };
92
+ stageRawWrite(next, mirror, value === "" ? [] : [value]);
93
+ return next;
94
+ }
95
+ return { ...tagData, [mappedKey]: value };
96
+ }
97
+ export {
98
+ getBasicTagData,
99
+ getProperties,
100
+ getProperty,
101
+ setBasicTagData,
102
+ setProperties,
103
+ setProperty
104
+ };
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Structured-field accessors over the WASI tag-data snapshot.
3
+ *
4
+ * Pure functions over the tag-data snapshot: the WasiFileHandle wrappers add
5
+ * destruction guards. Extracted from file-handle.ts in the taglib-1dfc split.
6
+ */
7
+ import type { RawChapter, RawId3v2Frame, RawLyrics, RawPicture } from "../../wasm.js";
8
+ import type { HandleState } from "./handle-state.js";
9
+ export declare function getPictures(tagData: Record<string, unknown> | null): RawPicture[];
10
+ export declare function setPictures(tagData: Record<string, unknown> | null, pictures: RawPicture[]): Record<string, unknown>;
11
+ export declare function addPicture(tagData: Record<string, unknown> | null, picture: RawPicture): Record<string, unknown>;
12
+ export declare function removePictures(tagData: Record<string, unknown> | null): Record<string, unknown>;
13
+ export declare function getChapters(tagData: Record<string, unknown> | null): RawChapter[];
14
+ export declare function setChapters(tagData: Record<string, unknown> | null, chapters: RawChapter[], mp4ChapterStyle: string): Record<string, unknown>;
15
+ export declare function getBextData(tagData: Record<string, unknown> | null): Uint8Array | undefined;
16
+ export declare function setBextData(tagData: Record<string, unknown> | null, data: Uint8Array | null): Record<string, unknown>;
17
+ export declare function getIxml(tagData: Record<string, unknown> | null): string | undefined;
18
+ export declare function setIxml(tagData: Record<string, unknown> | null, data: string | null): Record<string, unknown>;
19
+ export declare function hasId3Tags(tagData: Record<string, unknown> | null): {
20
+ v1: boolean;
21
+ v2: boolean;
22
+ };
23
+ export declare function stripId3Tags(tagData: Record<string, unknown> | null, opts: {
24
+ v1: boolean;
25
+ v2: boolean;
26
+ }): Record<string, unknown> | null;
27
+ export declare function getRatings(tagData: Record<string, unknown> | null): {
28
+ rating: number;
29
+ email: string;
30
+ counter: number;
31
+ }[];
32
+ export declare function setRatings(tagData: Record<string, unknown> | null, ratings: {
33
+ rating: number;
34
+ email?: string;
35
+ counter?: number;
36
+ }[]): Record<string, unknown>;
37
+ export declare function getLyrics(tagData: Record<string, unknown> | null): RawLyrics[];
38
+ export declare function setLyrics(tagData: Record<string, unknown> | null, lyrics: RawLyrics[]): Record<string, unknown>;
39
+ export declare function getStagedId3v2Frames(tagData: Record<string, unknown> | null): Record<string, Uint8Array[]> | undefined;
40
+ export declare function setId3v2Frames(tagData: Record<string, unknown> | null, id: string, data: Uint8Array[]): Record<string, unknown>;
41
+ export declare function removeId3v2Frames(tagData: Record<string, unknown> | null, id: string): Record<string, unknown>;
42
+ /** Raw ID3v2 frames: file state filtered by staged per-ID replacements. */
43
+ export declare function getId3v2Frames(state: HandleState, id: string): RawId3v2Frame[];
44
+ //# sourceMappingURL=structured-fields.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"structured-fields.d.ts","sourceRoot":"","sources":["../../../../src/runtime/wasi-adapter/structured-fields.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,139 @@
1
+ import { readId3v2FramesFromWasm } from "./wasm-io.js";
2
+ function getPictures(tagData) {
3
+ return tagData?.pictures ?? [];
4
+ }
5
+ function setPictures(tagData, pictures) {
6
+ return { ...tagData, pictures };
7
+ }
8
+ function addPicture(tagData, picture) {
9
+ const pictures = getPictures(tagData);
10
+ pictures.push(picture);
11
+ return setPictures(tagData, pictures);
12
+ }
13
+ function removePictures(tagData) {
14
+ return { ...tagData, pictures: [] };
15
+ }
16
+ function getChapters(tagData) {
17
+ return tagData?.chapters ?? [];
18
+ }
19
+ function setChapters(tagData, chapters, mp4ChapterStyle) {
20
+ return {
21
+ ...tagData,
22
+ _mp4ChapterStyle: mp4ChapterStyle,
23
+ chapters
24
+ };
25
+ }
26
+ function getBextData(tagData) {
27
+ return tagData?.bextData ?? void 0;
28
+ }
29
+ function setBextData(tagData, data) {
30
+ return { ...tagData, bextData: data };
31
+ }
32
+ function getIxml(tagData) {
33
+ const v = tagData?.ixml;
34
+ return typeof v === "string" && v.length > 0 ? v : void 0;
35
+ }
36
+ function setIxml(tagData, data) {
37
+ return { ...tagData, ixml: data };
38
+ }
39
+ function hasId3Tags(tagData) {
40
+ const t = tagData?.id3Tags;
41
+ return { v1: t?.v1 ?? false, v2: t?.v2 ?? false };
42
+ }
43
+ function stripId3Tags(tagData, opts) {
44
+ const current = tagData?.id3Tags;
45
+ if (!current) return tagData;
46
+ const prior = tagData?._stripId3;
47
+ const stripV1 = (prior?.v1 ?? false) || opts.v1;
48
+ const stripV2 = (prior?.v2 ?? false) || opts.v2;
49
+ return {
50
+ ...tagData,
51
+ _stripId3: { v1: stripV1, v2: stripV2 },
52
+ id3Tags: {
53
+ v1: (current.v1 ?? false) && !stripV1,
54
+ v2: (current.v2 ?? false) && !stripV2
55
+ }
56
+ };
57
+ }
58
+ function getRatings(tagData) {
59
+ return tagData?.ratings ?? [];
60
+ }
61
+ function setRatings(tagData, ratings) {
62
+ const normalizedRatings = ratings.map((r) => ({
63
+ rating: r.rating,
64
+ email: r.email ?? "",
65
+ counter: r.counter ?? 0
66
+ }));
67
+ return {
68
+ ...tagData,
69
+ ratings: normalizedRatings
70
+ };
71
+ }
72
+ function getLyrics(tagData) {
73
+ const value = tagData?.lyrics;
74
+ if (value === void 0 || value === null) return [];
75
+ const entries = Array.isArray(value) ? value : [value];
76
+ return entries.map(
77
+ (entry) => typeof entry === "string" ? { text: entry, description: "", language: "" } : {
78
+ text: entry?.text ?? "",
79
+ description: entry?.description ?? "",
80
+ language: entry?.language ?? ""
81
+ }
82
+ );
83
+ }
84
+ function setLyrics(tagData, lyrics) {
85
+ return { ...tagData, lyrics };
86
+ }
87
+ function getStagedId3v2Frames(tagData) {
88
+ return tagData?.id3v2Frames;
89
+ }
90
+ function setId3v2Frames(tagData, id, data) {
91
+ const staged = { ...getStagedId3v2Frames(tagData) ?? {} };
92
+ staged[id] = data.map((d) => new Uint8Array(d));
93
+ return {
94
+ ...tagData,
95
+ id3v2Frames: staged
96
+ };
97
+ }
98
+ function removeId3v2Frames(tagData, id) {
99
+ return setId3v2Frames(tagData, id, []);
100
+ }
101
+ function getId3v2Frames(state, id) {
102
+ const filter = id === "" ? void 0 : id;
103
+ const source = state.filePath ?? state.fileData;
104
+ let frames = [];
105
+ if (source) {
106
+ frames = readId3v2FramesFromWasm(state.wasi, source, filter);
107
+ }
108
+ const staged = getStagedId3v2Frames(state.tagData);
109
+ if (!staged) return frames;
110
+ const stagedIds = new Set(Object.keys(staged));
111
+ frames = frames.filter((f) => !stagedIds.has(f.id));
112
+ for (const [sid, bodies] of Object.entries(staged)) {
113
+ if (filter && sid !== filter) continue;
114
+ for (const data of bodies) frames.push({ id: sid, data: data.slice() });
115
+ }
116
+ return frames;
117
+ }
118
+ export {
119
+ addPicture,
120
+ getBextData,
121
+ getChapters,
122
+ getId3v2Frames,
123
+ getIxml,
124
+ getLyrics,
125
+ getPictures,
126
+ getRatings,
127
+ getStagedId3v2Frames,
128
+ hasId3Tags,
129
+ removeId3v2Frames,
130
+ removePictures,
131
+ setBextData,
132
+ setChapters,
133
+ setId3v2Frames,
134
+ setIxml,
135
+ setLyrics,
136
+ setPictures,
137
+ setRatings,
138
+ stripId3Tags
139
+ };
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Internal key classification for the WASI tag-data snapshot.
3
+ *
4
+ * AUDIO_KEYS and INTERNAL_KEYS are excluded from the property surface
5
+ * (getProperties); preserveEmptyValues keeps empty-string fields alive for
6
+ * the save round-trip (taglib-yc1x). Extracted from file-handle.ts in the
7
+ * taglib-1dfc split.
8
+ */
9
+ /** Audio metrics emitted by the C++ read path, not tag properties. */
10
+ export declare const AUDIO_KEYS: Set<string>;
11
+ /** Structured fields and write-time directives, never properties. */
12
+ export declare const INTERNAL_KEYS: Set<string>;
13
+ /**
14
+ * A frame that exists holding an empty string is not the same state as no frame
15
+ * at all, and the read snapshot must say so — otherwise the save cannot carry
16
+ * the value back, `setProperties()` sees the field as absent, and TagLib deletes
17
+ * a frame the caller never touched (taglib-yc1x).
18
+ *
19
+ * The C++ encoder emits a single value as a bare string, and both `cleanObject`
20
+ * and `getProperties` drop a bare `""`. An ARRAY containing `""` already
21
+ * survives every one of those layers, so promoting the bare form to `[""]` here
22
+ * carries the value end to end without touching the encoder or the decoder.
23
+ *
24
+ * This only concerns readable tag properties: audio metrics and the internal
25
+ * write-time channels keep their own shapes.
26
+ */
27
+ export declare function preserveEmptyValues(data: Record<string, unknown>): Record<string, unknown>;
28
+ //# sourceMappingURL=tag-keys.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tag-keys.d.ts","sourceRoot":"","sources":["../../../../src/runtime/wasi-adapter/tag-keys.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,sEAAsE;AACtE,eAAO,MAAM,UAAU,aAiBrB,CAAC;AAEH,qEAAqE;AACrE,eAAO,MAAM,aAAa,aAaxB,CAAC;AAEH;;;;;;;;;;;;;GAaG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAQzB"}
@@ -0,0 +1,46 @@
1
+ const AUDIO_KEYS = /* @__PURE__ */ new Set([
2
+ "bitrate",
3
+ "bitrateMode",
4
+ "bitsPerSample",
5
+ "channels",
6
+ "codec",
7
+ "containerFormat",
8
+ "formatVersion",
9
+ "isEncrypted",
10
+ "isLossless",
11
+ "duration",
12
+ "length",
13
+ "lengthMs",
14
+ "mpegLayer",
15
+ "mpegVersion",
16
+ "outputGainDb",
17
+ "sampleRate"
18
+ ]);
19
+ const INTERNAL_KEYS = /* @__PURE__ */ new Set([
20
+ "pictures",
21
+ "ratings",
22
+ "lyrics",
23
+ "chapters",
24
+ "_mp4ChapterStyle",
25
+ "bextData",
26
+ "ixml",
27
+ // Exact MP4 atom names for the write path, not a readable property.
28
+ "_mp4ItemNames",
29
+ // Foreign-mean MP4 atom names to delete at save (taglib-65nm): a write-time
30
+ // directive, never a property.
31
+ "_mp4ItemRemovals"
32
+ ]);
33
+ function preserveEmptyValues(data) {
34
+ for (const [key, value] of Object.entries(data)) {
35
+ if (value !== "") continue;
36
+ if (AUDIO_KEYS.has(key) || INTERNAL_KEYS.has(key)) continue;
37
+ if (key.startsWith("----:")) continue;
38
+ data[key] = [""];
39
+ }
40
+ return data;
41
+ }
42
+ export {
43
+ AUDIO_KEYS,
44
+ INTERNAL_KEYS,
45
+ preserveEmptyValues
46
+ };
@@ -82,19 +82,33 @@ export interface WriteTagUpdate {
82
82
  /** File path on disk; the file is updated in place. */
83
83
  path: string;
84
84
  /**
85
- * Partial tag fields to merge with the file's existing metadata. An empty
86
- * object performs a save with no tag changes (a no-op rewrite).
85
+ * Partial typed tag fields to merge with the file's existing metadata. An
86
+ * empty object performs a save with no tag changes (a no-op rewrite).
87
87
  */
88
- tags: Partial<TagInput>;
88
+ tags?: Partial<TagInput>;
89
+ /**
90
+ * Raw WIRE-key map, merged verbatim — the same shape and rule as the Full
91
+ * API's `setProperties` (taglib-pmhp review): keys outside the typed
92
+ * TagInput surface (barcode, unmodeled TXXX fields) ride here, and an
93
+ * EMPTY ARRAY removes the key (the qyw2/nc5 clearing contract; no
94
+ * empty-string carrier). Applied in the same single save as `tags`;
95
+ * entries here take precedence over `tags` for the same normalized key.
96
+ * Writes are verbatim: unlike the typed `tags` channel (which clears the
97
+ * legacy YEAR field when DATE is set), a raw `properties` write does not
98
+ * reconcile related keys — exactly like the Full API's `setProperties`.
99
+ */
100
+ properties?: Record<string, string[]>;
89
101
  }
90
102
  /**
91
103
  * Apply tag updates to multiple files with configurable concurrency.
92
104
  *
93
105
  * The batch write counterpart to {@link readTagsBatch}: same
94
106
  * {@link BatchOptions} contract (concurrency, continueOnError, onProgress,
95
- * signal). Each file is updated via the same path as
96
- * {@link applyTagsToFile} — merge semantics, atomic temp-file save, so a
97
- * failed file is left in its pre-write state.
107
+ * signal). Each file is updated in ONE save via a single `setProperties`
108
+ * fold: the typed `tags` merge ({@link mergeTagUpdatesInto} — the same
109
+ * semantics as {@link applyTagsToFile}) plus the raw wire-key `properties`
110
+ * entries. Atomic temp-file save, so a failed file is left in its pre-write
111
+ * state.
98
112
  *
99
113
  * @param updates - Per-file tag updates; a path may appear more than once
100
114
  * (the last update for a path wins).
@@ -108,16 +122,19 @@ export declare function writeTagsBatch(updates: WriteTagUpdate[], options?: Batc
108
122
  * Open, mutate, and save multiple files with configurable concurrency.
109
123
  *
110
124
  * Mutator-callback variant of {@link writeTagsBatch}: the callback receives
111
- * the open {@link AudioFile} (Full-API style) and its changes are saved per
112
- * file. Same {@link BatchOptions} contract and atomicity guarantees.
125
+ * the open {@link AudioFile} (Full-API style) plus the `path` it was opened
126
+ * from the pair is a per-invocation contract, so precomputed per-path
127
+ * plans can be looked up without relying on invocation order (taglib-pmhp
128
+ * review). A one-argument mutator keeps working. Changes are saved per file;
129
+ * same {@link BatchOptions} contract and atomicity guarantees.
113
130
  *
114
131
  * @param files - File paths on disk, updated in place.
115
- * @param mutator - Called once per file with the open audio file; changes are
116
- * saved automatically.
132
+ * @param mutator - Called once per file with the open audio file and its
133
+ * path; changes are saved automatically.
117
134
  * @param options - Batch processing options (concurrency, error handling,
118
135
  * progress, abort).
119
136
  * @returns A `BatchItem` per file in input order plus total duration in ms.
120
137
  * @throws If `continueOnError` is `false` and any file fails to process.
121
138
  */
122
- export declare function editTagsBatch(files: string[], mutator: (audioFile: AudioFile) => void, options?: BatchOptions): Promise<BatchResult<void>>;
139
+ export declare function editTagsBatch(files: string[], mutator: (audioFile: AudioFile, path: string) => void, options?: BatchOptions): Promise<BatchResult<void>>;
123
140
  //# sourceMappingURL=batch-operations.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"batch-operations.d.ts","sourceRoot":"","sources":["../../../src/simple/batch-operations.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,KAAK,EACV,cAAc,EACd,eAAe,EACf,WAAW,EACX,QAAQ,EACT,MAAM,aAAa,CAAC;AAQrB,qDAAqD;AACrD,MAAM,WAAW,YAAY;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,UAAU,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7E,gEAAgE;IAChE,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC9B;AAED,yEAAyE;AACzE,MAAM,MAAM,SAAS,CAAC,CAAC,IACnB;IAAE,MAAM,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,CAAC,CAAA;CAAE,GACvC;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,KAAK,CAAA;CAAE,CAAC;AAEpD,mEAAmE;AACnE,MAAM,WAAW,WAAW,CAAC,CAAC;IAC5B,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAyDD;;;;;;;GAOG;AACH,wBAAsB,aAAa,CACjC,KAAK,EAAE,cAAc,EAAE,EACvB,OAAO,GAAE,YAAiB,GACzB,OAAO,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC,CAMnC;AAED;;;;;;;GAOG;AACH,wBAAsB,mBAAmB,CACvC,KAAK,EAAE,cAAc,EAAE,EACvB,OAAO,GAAE,YAAiB,GACzB,OAAO,CAAC,WAAW,CAAC,eAAe,GAAG,SAAS,CAAC,CAAC,CAMnD;AAED,oHAAoH;AACpH,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,WAAW,CAAC;IAClB,UAAU,EAAE,eAAe,GAAG,SAAS,CAAC;IACxC,WAAW,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,EAAE,aAAa,CAAC;CAC1B;AAsBD;;;;;;;;GAQG;AACH,wBAAsB,YAAY,CAChC,IAAI,EAAE,cAAc,EACpB,OAAO,GAAE;IAAE,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAA;CAAO,GAC7C,OAAO,CAAC,YAAY,CAAC,CA6BvB;AAED;;;;;;;GAOG;AACH,wBAAsB,iBAAiB,CACrC,KAAK,EAAE,cAAc,EAAE,EACvB,OAAO,GAAE,YAAiB,GACzB,OAAO,CAAC,WAAW,CAAC,YAAY,CAAC,CAAC,CAUpC;AAED,2DAA2D;AAC3D,MAAM,WAAW,cAAc;IAC7B,uDAAuD;IACvD,IAAI,EAAE,MAAM,CAAC;IACb;;;OAGG;IACH,IAAI,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC;CACzB;AAsDD;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,cAAc,CAClC,OAAO,EAAE,cAAc,EAAE,EACzB,OAAO,GAAE,YAAiB,GACzB,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAM5B;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,aAAa,CACjC,KAAK,EAAE,MAAM,EAAE,EACf,OAAO,EAAE,CAAC,SAAS,EAAE,SAAS,KAAK,IAAI,EACvC,OAAO,GAAE,YAAiB,GACzB,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAM5B"}
1
+ {"version":3,"file":"batch-operations.d.ts","sourceRoot":"","sources":["../../../src/simple/batch-operations.ts"],"names":[],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import { isNamedAudioInput } from "../types/audio-formats.js";
2
2
  import { InvalidFormatError } from "../errors.js";
3
- import { readExtendedTag } from "../utils/tag-mapping.js";
4
- import { applyTagsToFile } from "./tag-operations.js";
3
+ import { fromTagLibKey } from "../constants/properties.js";
4
+ import { mergeTagUpdatesInto, readExtendedTag } from "../utils/tag-mapping.js";
5
5
  import { withAudioFileSaveToFile } from "./with-audio-file.js";
6
6
  import { getTagLib } from "./config.js";
7
7
  async function executeBatch(files, options, processor) {
@@ -154,16 +154,26 @@ async function executeWriteBatch(entries, options, writer) {
154
154
  }
155
155
  async function writeTagsBatch(updates, options = {}) {
156
156
  const byPath = new Map(updates.map((u) => [u.path, u]));
157
- return executeWriteBatch(updates, options, (path) => {
158
- const update = byPath.get(path);
159
- return applyTagsToFile(path, update.tags);
160
- });
157
+ return executeWriteBatch(
158
+ updates,
159
+ options,
160
+ (path) => withAudioFileSaveToFile(path, (audioFile) => {
161
+ const update = byPath.get(path);
162
+ const merged = update.tags !== void 0 ? mergeTagUpdatesInto(audioFile.properties(), update.tags) : audioFile.properties();
163
+ if (update.properties !== void 0) {
164
+ for (const [key, values] of Object.entries(update.properties)) {
165
+ merged[fromTagLibKey(key)] = values;
166
+ }
167
+ }
168
+ audioFile.setProperties(merged);
169
+ })
170
+ );
161
171
  }
162
172
  async function editTagsBatch(files, mutator, options = {}) {
163
173
  return executeWriteBatch(
164
174
  files,
165
175
  options,
166
- (path) => withAudioFileSaveToFile(path, mutator)
176
+ (path) => withAudioFileSaveToFile(path, (audioFile) => mutator(audioFile, path))
167
177
  );
168
178
  }
169
179
  export {