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.
- package/dist/disc-folder.d.ts +3 -1
- package/dist/disc-folder.d.ts.map +1 -1
- package/dist/disc-folder.js +187 -152
- package/dist/index.browser.js +7 -4
- package/dist/simple.browser.js +22 -9
- package/dist/src/folder-api/album-corroborate.d.ts +13 -0
- package/dist/src/folder-api/album-corroborate.d.ts.map +1 -0
- package/dist/src/folder-api/album-corroborate.js +36 -0
- package/dist/src/folder-api/album-discs.d.ts +13 -0
- package/dist/src/folder-api/album-discs.d.ts.map +1 -0
- package/dist/src/folder-api/album-discs.js +122 -0
- package/dist/src/folder-api/album-grouping.d.ts +24 -0
- package/dist/src/folder-api/album-grouping.d.ts.map +1 -0
- package/dist/src/folder-api/album-grouping.js +118 -0
- package/dist/src/folder-api/album-identity.d.ts +27 -0
- package/dist/src/folder-api/album-identity.d.ts.map +1 -0
- package/dist/src/folder-api/album-identity.js +151 -0
- package/dist/src/folder-api/{group-albums.d.ts → album-types.d.ts} +55 -26
- package/dist/src/folder-api/album-types.d.ts.map +1 -0
- package/dist/src/folder-api/album-types.js +0 -0
- package/dist/src/folder-api/folder-disc.d.ts +24 -0
- package/dist/src/folder-api/folder-disc.d.ts.map +1 -0
- package/dist/src/folder-api/folder-disc.js +169 -0
- package/dist/src/folder-api/index.d.ts +3 -1
- package/dist/src/folder-api/index.d.ts.map +1 -1
- package/dist/src/folder-api/index.js +2 -4
- package/dist/src/folder-api/scan-for-albums.d.ts +1 -1
- package/dist/src/folder-api/scan-for-albums.d.ts.map +1 -1
- package/dist/src/folder-api/scan-for-albums.js +1 -3
- package/dist/src/runtime/wasi-adapter/audio-properties.d.ts +20 -0
- package/dist/src/runtime/wasi-adapter/audio-properties.d.ts.map +1 -0
- package/dist/src/runtime/wasi-adapter/audio-properties.js +84 -0
- package/dist/src/runtime/wasi-adapter/file-handle.d.ts +5 -19
- package/dist/src/runtime/wasi-adapter/file-handle.d.ts.map +1 -1
- package/dist/src/runtime/wasi-adapter/file-handle.js +67 -389
- package/dist/src/runtime/wasi-adapter/handle-state.d.ts +28 -0
- package/dist/src/runtime/wasi-adapter/handle-state.d.ts.map +1 -0
- package/dist/src/runtime/wasi-adapter/handle-state.js +58 -0
- package/dist/src/runtime/wasi-adapter/mp4-items.d.ts +34 -0
- package/dist/src/runtime/wasi-adapter/mp4-items.d.ts.map +1 -0
- package/dist/src/runtime/wasi-adapter/mp4-items.js +80 -0
- package/dist/src/runtime/wasi-adapter/property-surface.d.ts +25 -0
- package/dist/src/runtime/wasi-adapter/property-surface.d.ts.map +1 -0
- package/dist/src/runtime/wasi-adapter/property-surface.js +104 -0
- package/dist/src/runtime/wasi-adapter/structured-fields.d.ts +44 -0
- package/dist/src/runtime/wasi-adapter/structured-fields.d.ts.map +1 -0
- package/dist/src/runtime/wasi-adapter/structured-fields.js +139 -0
- package/dist/src/runtime/wasi-adapter/tag-keys.d.ts +28 -0
- package/dist/src/runtime/wasi-adapter/tag-keys.d.ts.map +1 -0
- package/dist/src/runtime/wasi-adapter/tag-keys.js +46 -0
- package/dist/src/simple/batch-operations.d.ts +28 -11
- package/dist/src/simple/batch-operations.d.ts.map +1 -1
- package/dist/src/simple/batch-operations.js +17 -7
- package/dist/src/utils/tag-mapping.d.ts +8 -0
- package/dist/src/utils/tag-mapping.d.ts.map +1 -1
- package/dist/src/utils/tag-mapping.js +6 -3
- package/dist/src/version.d.ts +1 -1
- package/dist/src/version.js +1 -1
- package/package.json +2 -1
- package/dist/src/folder-api/group-albums.d.ts.map +0 -1
- 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
|
|
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
|
|
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
|
|
96
|
-
* {@link
|
|
97
|
-
*
|
|
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)
|
|
112
|
-
*
|
|
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
|
|
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;
|
|
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 {
|
|
4
|
-
import {
|
|
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(
|
|
158
|
-
|
|
159
|
-
|
|
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 {
|