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
|
@@ -2,515 +2,193 @@ var __defProp = Object.defineProperty;
|
|
|
2
2
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
3
3
|
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
4
4
|
import { WasmerExecutionError } from "../wasmer-sdk-loader/types.js";
|
|
5
|
-
import { decodeTagData } from "../../msgpack/decoder.js";
|
|
6
5
|
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
stringifyScalar,
|
|
20
|
-
TRACK_MIRROR
|
|
21
|
-
} from "../../utils/mirror-fields.js";
|
|
22
|
-
import {
|
|
23
|
-
readId3v2FramesFromWasm,
|
|
24
|
-
readTagsFromWasm,
|
|
25
|
-
readTagsFromWasmPath,
|
|
26
|
-
writeTagsToWasm,
|
|
27
|
-
writeTagsToWasmPath
|
|
28
|
-
} from "./wasm-io.js";
|
|
29
|
-
const AUDIO_KEYS = /* @__PURE__ */ new Set([
|
|
30
|
-
"bitrate",
|
|
31
|
-
"bitrateMode",
|
|
32
|
-
"bitsPerSample",
|
|
33
|
-
"channels",
|
|
34
|
-
"codec",
|
|
35
|
-
"containerFormat",
|
|
36
|
-
"formatVersion",
|
|
37
|
-
"isEncrypted",
|
|
38
|
-
"isLossless",
|
|
39
|
-
"duration",
|
|
40
|
-
"length",
|
|
41
|
-
"lengthMs",
|
|
42
|
-
"mpegLayer",
|
|
43
|
-
"mpegVersion",
|
|
44
|
-
"outputGainDb",
|
|
45
|
-
"sampleRate"
|
|
46
|
-
]);
|
|
47
|
-
const INTERNAL_KEYS = /* @__PURE__ */ new Set([
|
|
48
|
-
"pictures",
|
|
49
|
-
"ratings",
|
|
50
|
-
"lyrics",
|
|
51
|
-
"chapters",
|
|
52
|
-
"_mp4ChapterStyle",
|
|
53
|
-
"bextData",
|
|
54
|
-
"ixml",
|
|
55
|
-
// Exact MP4 atom names for the write path, not a readable property.
|
|
56
|
-
"_mp4ItemNames",
|
|
57
|
-
// Foreign-mean MP4 atom names to delete at save (taglib-65nm): a write-time
|
|
58
|
-
// directive, never a property.
|
|
59
|
-
"_mp4ItemRemovals"
|
|
60
|
-
]);
|
|
61
|
-
function preserveEmptyValues(data) {
|
|
62
|
-
for (const [key, value] of Object.entries(data)) {
|
|
63
|
-
if (value !== "") continue;
|
|
64
|
-
if (AUDIO_KEYS.has(key) || INTERNAL_KEYS.has(key)) continue;
|
|
65
|
-
if (key.startsWith("----:")) continue;
|
|
66
|
-
data[key] = [""];
|
|
67
|
-
}
|
|
68
|
-
return data;
|
|
69
|
-
}
|
|
70
|
-
const CONTAINER_TO_FORMAT = {
|
|
71
|
-
MP3: "MP3",
|
|
72
|
-
MP4: "MP4",
|
|
73
|
-
FLAC: "FLAC",
|
|
74
|
-
OGG: "OGG",
|
|
75
|
-
WAV: "WAV",
|
|
76
|
-
AIFF: "AIFF",
|
|
77
|
-
WavPack: "WV",
|
|
78
|
-
TTA: "TTA",
|
|
79
|
-
ASF: "ASF",
|
|
80
|
-
Matroska: "MATROSKA"
|
|
81
|
-
};
|
|
82
|
-
function mp4ItemPropertyKey(key) {
|
|
83
|
-
if (key.startsWith("----:")) {
|
|
84
|
-
return key.slice(key.lastIndexOf(":") + 1).toUpperCase();
|
|
85
|
-
}
|
|
86
|
-
return mp4AtomWireKey(key) ?? key;
|
|
87
|
-
}
|
|
6
|
+
createHandleState,
|
|
7
|
+
destroy,
|
|
8
|
+
getBuffer,
|
|
9
|
+
isValid,
|
|
10
|
+
loadBuffer,
|
|
11
|
+
loadPath,
|
|
12
|
+
save
|
|
13
|
+
} from "./handle-state.js";
|
|
14
|
+
import * as audio from "./audio-properties.js";
|
|
15
|
+
import * as props from "./property-surface.js";
|
|
16
|
+
import * as mp4 from "./mp4-items.js";
|
|
17
|
+
import * as fields from "./structured-fields.js";
|
|
88
18
|
class WasiFileHandle {
|
|
89
19
|
constructor(wasiModule) {
|
|
90
|
-
__publicField(this, "
|
|
91
|
-
__publicField(this, "fileData", null);
|
|
92
|
-
__publicField(this, "filePath", null);
|
|
93
|
-
__publicField(this, "tagData", null);
|
|
20
|
+
__publicField(this, "state");
|
|
94
21
|
__publicField(this, "destroyed", false);
|
|
95
|
-
this.
|
|
22
|
+
this.state = createHandleState(wasiModule);
|
|
96
23
|
}
|
|
97
24
|
checkNotDestroyed() {
|
|
98
25
|
if (this.destroyed) {
|
|
99
|
-
throw new WasmerExecutionError(
|
|
100
|
-
"FileHandle has been destroyed"
|
|
101
|
-
);
|
|
26
|
+
throw new WasmerExecutionError("FileHandle has been destroyed");
|
|
102
27
|
}
|
|
103
28
|
}
|
|
104
29
|
loadFromBuffer(buffer) {
|
|
105
30
|
this.checkNotDestroyed();
|
|
106
|
-
this.
|
|
107
|
-
const msgpackData = readTagsFromWasm(this.wasi, buffer);
|
|
108
|
-
this.tagData = preserveEmptyValues(
|
|
109
|
-
decodeTagData(msgpackData)
|
|
110
|
-
);
|
|
31
|
+
loadBuffer(this.state, buffer);
|
|
111
32
|
return true;
|
|
112
33
|
}
|
|
113
34
|
loadFromPath(path) {
|
|
114
35
|
this.checkNotDestroyed();
|
|
115
|
-
this.
|
|
116
|
-
const msgpackData = readTagsFromWasmPath(this.wasi, path);
|
|
117
|
-
this.tagData = preserveEmptyValues(
|
|
118
|
-
decodeTagData(msgpackData)
|
|
119
|
-
);
|
|
36
|
+
loadPath(this.state, path);
|
|
120
37
|
return true;
|
|
121
38
|
}
|
|
122
39
|
isValid() {
|
|
123
40
|
this.checkNotDestroyed();
|
|
124
|
-
return this.
|
|
41
|
+
return isValid(this.state);
|
|
125
42
|
}
|
|
126
43
|
save() {
|
|
127
44
|
this.checkNotDestroyed();
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
this.tagData
|
|
134
|
-
);
|
|
135
|
-
}
|
|
136
|
-
if (!this.fileData) return false;
|
|
137
|
-
const result = writeTagsToWasm(this.wasi, this.fileData, this.tagData);
|
|
138
|
-
if (result) {
|
|
139
|
-
this.fileData = result;
|
|
140
|
-
return true;
|
|
141
|
-
}
|
|
142
|
-
return false;
|
|
45
|
+
return save(this.state);
|
|
46
|
+
}
|
|
47
|
+
getBuffer() {
|
|
48
|
+
this.checkNotDestroyed();
|
|
49
|
+
return getBuffer(this.state);
|
|
143
50
|
}
|
|
144
51
|
getTagData() {
|
|
145
52
|
this.checkNotDestroyed();
|
|
146
|
-
|
|
147
|
-
return {
|
|
148
|
-
title: firstValueString(d.title),
|
|
149
|
-
artist: firstValueString(d.artist),
|
|
150
|
-
album: firstValueString(d.album),
|
|
151
|
-
comment: firstValueString(d.comment),
|
|
152
|
-
genre: firstValueString(d.genre),
|
|
153
|
-
year: readNumericMirror(d, DATE_MIRROR),
|
|
154
|
-
track: readNumericMirror(d, TRACK_MIRROR)
|
|
155
|
-
};
|
|
53
|
+
return props.getBasicTagData(this.state.tagData);
|
|
156
54
|
}
|
|
157
55
|
setTagData(data) {
|
|
158
56
|
this.checkNotDestroyed();
|
|
159
|
-
|
|
160
|
-
if (data.year !== void 0) {
|
|
161
|
-
stageNumericWrite(merged, DATE_MIRROR, data.year);
|
|
162
|
-
}
|
|
163
|
-
if (data.track !== void 0) {
|
|
164
|
-
stageNumericWrite(merged, TRACK_MIRROR, data.track);
|
|
165
|
-
}
|
|
166
|
-
this.tagData = merged;
|
|
57
|
+
this.state.tagData = props.setBasicTagData(this.state.tagData, data);
|
|
167
58
|
}
|
|
168
59
|
getAudioProperties() {
|
|
169
60
|
this.checkNotDestroyed();
|
|
170
|
-
|
|
171
|
-
const d = this.tagData;
|
|
172
|
-
const containerFormat = d.containerFormat || "unknown";
|
|
173
|
-
const mpegVersion = d.mpegVersion ?? 0;
|
|
174
|
-
const formatVersion = d.formatVersion ?? 0;
|
|
175
|
-
return {
|
|
176
|
-
duration: d.length ?? 0,
|
|
177
|
-
durationMs: d.lengthMs ?? 0,
|
|
178
|
-
bitrate: d.bitrate ?? 0,
|
|
179
|
-
sampleRate: d.sampleRate ?? 0,
|
|
180
|
-
channels: d.channels ?? 0,
|
|
181
|
-
bitsPerSample: d.bitsPerSample ?? 0,
|
|
182
|
-
codec: d.codec || "unknown",
|
|
183
|
-
containerFormat,
|
|
184
|
-
isLossless: d.isLossless ?? false,
|
|
185
|
-
...mpegVersion > 0 ? { mpegVersion, mpegLayer: d.mpegLayer ?? 0 } : {},
|
|
186
|
-
...containerFormat === "MP4" || containerFormat === "ASF" ? { isEncrypted: d.isEncrypted ?? false } : {},
|
|
187
|
-
...formatVersion > 0 ? { formatVersion } : {},
|
|
188
|
-
...d.outputGainDb !== void 0 ? { outputGainDb: d.outputGainDb } : {}
|
|
189
|
-
};
|
|
61
|
+
return audio.getAudioProperties(this.state.tagData);
|
|
190
62
|
}
|
|
191
63
|
getFormat() {
|
|
192
64
|
this.checkNotDestroyed();
|
|
193
|
-
|
|
194
|
-
if (container) {
|
|
195
|
-
const codec = this.tagData?.codec;
|
|
196
|
-
if (container === "OGG" && codec === "Opus") return "OPUS";
|
|
197
|
-
if (CONTAINER_TO_FORMAT[container]) return CONTAINER_TO_FORMAT[container];
|
|
198
|
-
}
|
|
199
|
-
if (!this.fileData || this.fileData.length < 8) return "unknown";
|
|
200
|
-
const magic = this.fileData.slice(0, 4);
|
|
201
|
-
if (magic[0] === 255 && (magic[1] & 224) === 224) return "MP3";
|
|
202
|
-
if (magic[0] === 73 && magic[1] === 68 && magic[2] === 51) {
|
|
203
|
-
return "MP3";
|
|
204
|
-
}
|
|
205
|
-
if (magic[0] === 102 && magic[1] === 76 && magic[2] === 97 && magic[3] === 67) return "FLAC";
|
|
206
|
-
if (magic[0] === 79 && magic[1] === 103 && magic[2] === 103 && magic[3] === 83) return this.detectOggCodec();
|
|
207
|
-
if (magic[0] === 82 && magic[1] === 73 && magic[2] === 70 && magic[3] === 70) return "WAV";
|
|
208
|
-
if (magic[0] === 119 && magic[1] === 118 && magic[2] === 112 && magic[3] === 107) return "WV";
|
|
209
|
-
if (magic[0] === 84 && magic[1] === 84 && magic[2] === 65 && magic[3] === 49) return "TTA";
|
|
210
|
-
if (this.fileData.length >= 16 && magic[0] === 48 && magic[1] === 38 && magic[2] === 178 && magic[3] === 117) return "ASF";
|
|
211
|
-
if (magic[0] === 26 && magic[1] === 69 && magic[2] === 223 && magic[3] === 163) return "MATROSKA";
|
|
212
|
-
const ftyp = this.fileData.slice(4, 8);
|
|
213
|
-
if (ftyp[0] === 102 && ftyp[1] === 116 && ftyp[2] === 121 && ftyp[3] === 112) return "MP4";
|
|
214
|
-
return "unknown";
|
|
215
|
-
}
|
|
216
|
-
detectOggCodec() {
|
|
217
|
-
if (!this.fileData || this.fileData.length < 37) return "OGG";
|
|
218
|
-
const segCount = this.fileData[26];
|
|
219
|
-
if (segCount === void 0) return "OGG";
|
|
220
|
-
const payloadStart = 27 + segCount;
|
|
221
|
-
if (this.fileData.length < payloadStart + 8) return "OGG";
|
|
222
|
-
const sig = String.fromCharCode(
|
|
223
|
-
...this.fileData.slice(payloadStart, payloadStart + 8)
|
|
224
|
-
);
|
|
225
|
-
if (sig === "OpusHead") return "OPUS";
|
|
226
|
-
return "OGG";
|
|
65
|
+
return audio.getFormat(this.state.tagData, this.state.fileData);
|
|
227
66
|
}
|
|
228
|
-
|
|
67
|
+
isMP4() {
|
|
229
68
|
this.checkNotDestroyed();
|
|
230
|
-
return this.fileData
|
|
69
|
+
return audio.isMP4(this.state.tagData, this.state.fileData);
|
|
231
70
|
}
|
|
232
71
|
getProperties() {
|
|
233
72
|
this.checkNotDestroyed();
|
|
234
|
-
|
|
235
|
-
const data = this.tagData ?? {};
|
|
236
|
-
for (const [key, value] of Object.entries(data)) {
|
|
237
|
-
if (AUDIO_KEYS.has(key) || INTERNAL_KEYS.has(key)) continue;
|
|
238
|
-
if (key.startsWith("----:")) continue;
|
|
239
|
-
if (isShadowedNumericMirror(data, key)) continue;
|
|
240
|
-
if (value === void 0 || value === null) continue;
|
|
241
|
-
if (value === 0 || value === "") continue;
|
|
242
|
-
const propKey = toTagLibKey(key);
|
|
243
|
-
if (Array.isArray(value)) {
|
|
244
|
-
if (value.length === 0) continue;
|
|
245
|
-
result[propKey] = value.map(String);
|
|
246
|
-
} else if (typeof value === "object") {
|
|
247
|
-
continue;
|
|
248
|
-
} else if (typeof value === "string" || typeof value === "number" || typeof value === "boolean" || typeof value === "bigint" || typeof value === "symbol") {
|
|
249
|
-
result[propKey] = [String(value)];
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
return result;
|
|
253
|
-
}
|
|
254
|
-
setProperties(props) {
|
|
255
|
-
this.checkNotDestroyed();
|
|
256
|
-
const next = { ...this.tagData };
|
|
257
|
-
for (const [key, values] of Object.entries(props)) {
|
|
258
|
-
const camelKey = fromTagLibKey(key);
|
|
259
|
-
if (camelKey === "_mp4ItemNames") {
|
|
260
|
-
const existing = next._mp4ItemNames ?? [];
|
|
261
|
-
next._mp4ItemNames = [.../* @__PURE__ */ new Set([...existing, ...values])];
|
|
262
|
-
continue;
|
|
263
|
-
}
|
|
264
|
-
const mirror = mirrorForRawKey(camelKey);
|
|
265
|
-
if (mirror !== void 0) {
|
|
266
|
-
stageRawWrite(next, mirror, values);
|
|
267
|
-
} else if (values.length === 0) {
|
|
268
|
-
if (camelKey === "itunesAdvisory") next[camelKey] = [];
|
|
269
|
-
else delete next[camelKey];
|
|
270
|
-
} else {
|
|
271
|
-
next[camelKey] = values;
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
this.tagData = next;
|
|
73
|
+
return props.getProperties(this.state.tagData);
|
|
275
74
|
}
|
|
276
|
-
|
|
75
|
+
setProperties(properties) {
|
|
277
76
|
this.checkNotDestroyed();
|
|
278
|
-
|
|
279
|
-
const stored = this.tagData?.[mappedKey];
|
|
280
|
-
if (Array.isArray(stored)) {
|
|
281
|
-
const first = stored[0];
|
|
282
|
-
return stringifyScalar(first);
|
|
283
|
-
}
|
|
284
|
-
return stringifyScalar(stored);
|
|
77
|
+
this.state.tagData = props.setProperties(this.state.tagData, properties);
|
|
285
78
|
}
|
|
286
|
-
|
|
79
|
+
getProperty(key) {
|
|
287
80
|
this.checkNotDestroyed();
|
|
288
|
-
|
|
289
|
-
const mirror = mirrorForRawKey(mappedKey);
|
|
290
|
-
if (mirror !== void 0) {
|
|
291
|
-
const next = { ...this.tagData };
|
|
292
|
-
stageRawWrite(next, mirror, value === "" ? [] : [value]);
|
|
293
|
-
this.tagData = next;
|
|
294
|
-
} else {
|
|
295
|
-
this.tagData = { ...this.tagData, [mappedKey]: value };
|
|
296
|
-
}
|
|
81
|
+
return props.getProperty(this.state.tagData, key);
|
|
297
82
|
}
|
|
298
|
-
|
|
83
|
+
setProperty(key, value) {
|
|
299
84
|
this.checkNotDestroyed();
|
|
300
|
-
|
|
301
|
-
return this.tagData?.containerFormat === "MP4";
|
|
302
|
-
}
|
|
303
|
-
if (this.fileData.length < 8) return false;
|
|
304
|
-
const magic = this.fileData.slice(4, 8);
|
|
305
|
-
return magic[0] === 102 && magic[1] === 116 && magic[2] === 121 && magic[3] === 112;
|
|
85
|
+
this.state.tagData = props.setProperty(this.state.tagData, key, value);
|
|
306
86
|
}
|
|
307
87
|
getMP4Item(key) {
|
|
308
88
|
this.checkNotDestroyed();
|
|
309
|
-
|
|
310
|
-
const foreign = this.tagData?.[key];
|
|
311
|
-
if (foreign !== void 0) {
|
|
312
|
-
if (Array.isArray(foreign)) {
|
|
313
|
-
const first = foreign[0];
|
|
314
|
-
return stringifyScalar(first);
|
|
315
|
-
}
|
|
316
|
-
return stringifyScalar(foreign);
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
return this.getProperty(mp4ItemPropertyKey(key));
|
|
89
|
+
return mp4.getMP4Item(this.state.tagData, key);
|
|
320
90
|
}
|
|
321
91
|
setMP4Item(key, value) {
|
|
322
92
|
this.checkNotDestroyed();
|
|
323
|
-
this.
|
|
324
|
-
if (key.startsWith("----:")) {
|
|
325
|
-
this.registerMp4ItemName(key);
|
|
326
|
-
if (this.tagData?.[key] !== void 0) {
|
|
327
|
-
this.tagData = { ...this.tagData, [key]: value };
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
/** Record an exact atom name to be repaired after the PropertyMap write. */
|
|
332
|
-
registerMp4ItemName(name) {
|
|
333
|
-
const existing = this.tagData?._mp4ItemNames ?? [];
|
|
334
|
-
if (existing.includes(name)) return;
|
|
335
|
-
this.tagData = {
|
|
336
|
-
...this.tagData,
|
|
337
|
-
_mp4ItemNames: [...existing, name]
|
|
338
|
-
};
|
|
339
|
-
}
|
|
340
|
-
/**
|
|
341
|
-
* Record a foreign-mean freeform atom for deletion at save (taglib-65nm).
|
|
342
|
-
* The PropertyMap erase pass cannot express foreign atoms, so the C++ shim
|
|
343
|
-
* consumes this list and calls removeItem on each exact atom name.
|
|
344
|
-
*/
|
|
345
|
-
registerMp4ItemRemoval(name) {
|
|
346
|
-
const existing = this.tagData?._mp4ItemRemovals ?? [];
|
|
347
|
-
if (existing.includes(name)) return;
|
|
348
|
-
this.tagData = {
|
|
349
|
-
...this.tagData,
|
|
350
|
-
_mp4ItemRemovals: [...existing, name]
|
|
351
|
-
};
|
|
93
|
+
this.state.tagData = mp4.setMP4Item(this.state.tagData, key, value);
|
|
352
94
|
}
|
|
353
95
|
getMp4ItemRemovals() {
|
|
354
96
|
this.checkNotDestroyed();
|
|
355
|
-
return this.tagData
|
|
97
|
+
return mp4.getMp4ItemRemovals(this.state.tagData);
|
|
356
98
|
}
|
|
357
99
|
setMp4ItemRemovals(removals) {
|
|
358
100
|
this.checkNotDestroyed();
|
|
359
|
-
this.tagData =
|
|
360
|
-
...this.tagData,
|
|
361
|
-
_mp4ItemRemovals: [...new Set(removals)]
|
|
362
|
-
};
|
|
101
|
+
this.state.tagData = mp4.setMp4ItemRemovals(this.state.tagData, removals);
|
|
363
102
|
}
|
|
364
103
|
removeMP4Item(key) {
|
|
365
104
|
this.checkNotDestroyed();
|
|
366
|
-
|
|
367
|
-
if (key.startsWith("----:")) {
|
|
368
|
-
delete this.tagData[key];
|
|
369
|
-
this.registerMp4ItemRemoval(key);
|
|
370
|
-
}
|
|
371
|
-
const mappedKey = fromTagLibKey(mp4ItemPropertyKey(key));
|
|
372
|
-
delete this.tagData[mappedKey];
|
|
373
|
-
const mirror = mirrorForRawKey(mappedKey);
|
|
374
|
-
if (mirror !== void 0) delete this.tagData[mirror.numeric];
|
|
375
|
-
}
|
|
105
|
+
this.state.tagData = mp4.removeMP4Item(this.state.tagData, key);
|
|
376
106
|
}
|
|
377
107
|
getPictures() {
|
|
378
108
|
this.checkNotDestroyed();
|
|
379
|
-
return this.tagData
|
|
109
|
+
return fields.getPictures(this.state.tagData);
|
|
380
110
|
}
|
|
381
111
|
setPictures(pictures) {
|
|
382
112
|
this.checkNotDestroyed();
|
|
383
|
-
this.tagData =
|
|
113
|
+
this.state.tagData = fields.setPictures(this.state.tagData, pictures);
|
|
384
114
|
}
|
|
385
115
|
addPicture(picture) {
|
|
386
116
|
this.checkNotDestroyed();
|
|
387
|
-
|
|
388
|
-
pictures.push(picture);
|
|
389
|
-
this.setPictures(pictures);
|
|
117
|
+
this.state.tagData = fields.addPicture(this.state.tagData, picture);
|
|
390
118
|
}
|
|
391
119
|
removePictures() {
|
|
392
120
|
this.checkNotDestroyed();
|
|
393
|
-
this.tagData =
|
|
121
|
+
this.state.tagData = fields.removePictures(this.state.tagData);
|
|
394
122
|
}
|
|
395
123
|
getChapters() {
|
|
396
124
|
this.checkNotDestroyed();
|
|
397
|
-
return this.tagData
|
|
125
|
+
return fields.getChapters(this.state.tagData);
|
|
398
126
|
}
|
|
399
127
|
setChapters(chapters, mp4ChapterStyle) {
|
|
400
128
|
this.checkNotDestroyed();
|
|
401
|
-
this.tagData =
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
129
|
+
this.state.tagData = fields.setChapters(
|
|
130
|
+
this.state.tagData,
|
|
131
|
+
chapters,
|
|
132
|
+
mp4ChapterStyle
|
|
133
|
+
);
|
|
406
134
|
}
|
|
407
135
|
getBextData() {
|
|
408
136
|
this.checkNotDestroyed();
|
|
409
|
-
return this.tagData
|
|
137
|
+
return fields.getBextData(this.state.tagData);
|
|
410
138
|
}
|
|
411
139
|
setBextData(data) {
|
|
412
140
|
this.checkNotDestroyed();
|
|
413
|
-
this.tagData =
|
|
141
|
+
this.state.tagData = fields.setBextData(this.state.tagData, data);
|
|
414
142
|
}
|
|
415
143
|
getIxml() {
|
|
416
144
|
this.checkNotDestroyed();
|
|
417
|
-
|
|
418
|
-
return typeof v === "string" && v.length > 0 ? v : void 0;
|
|
145
|
+
return fields.getIxml(this.state.tagData);
|
|
419
146
|
}
|
|
420
147
|
setIxml(data) {
|
|
421
148
|
this.checkNotDestroyed();
|
|
422
|
-
this.tagData =
|
|
149
|
+
this.state.tagData = fields.setIxml(this.state.tagData, data);
|
|
423
150
|
}
|
|
424
151
|
hasId3Tags() {
|
|
425
152
|
this.checkNotDestroyed();
|
|
426
|
-
|
|
427
|
-
return { v1: t?.v1 ?? false, v2: t?.v2 ?? false };
|
|
153
|
+
return fields.hasId3Tags(this.state.tagData);
|
|
428
154
|
}
|
|
429
155
|
stripId3Tags(opts) {
|
|
430
156
|
this.checkNotDestroyed();
|
|
431
|
-
|
|
432
|
-
if (!current) return;
|
|
433
|
-
const prior = this.tagData?._stripId3;
|
|
434
|
-
const stripV1 = (prior?.v1 ?? false) || opts.v1;
|
|
435
|
-
const stripV2 = (prior?.v2 ?? false) || opts.v2;
|
|
436
|
-
this.tagData = {
|
|
437
|
-
...this.tagData,
|
|
438
|
-
_stripId3: { v1: stripV1, v2: stripV2 },
|
|
439
|
-
id3Tags: {
|
|
440
|
-
v1: (current.v1 ?? false) && !stripV1,
|
|
441
|
-
v2: (current.v2 ?? false) && !stripV2
|
|
442
|
-
}
|
|
443
|
-
};
|
|
157
|
+
this.state.tagData = fields.stripId3Tags(this.state.tagData, opts);
|
|
444
158
|
}
|
|
445
159
|
getRatings() {
|
|
446
160
|
this.checkNotDestroyed();
|
|
447
|
-
return this.tagData
|
|
161
|
+
return fields.getRatings(this.state.tagData);
|
|
448
162
|
}
|
|
449
163
|
setRatings(ratings) {
|
|
450
164
|
this.checkNotDestroyed();
|
|
451
|
-
|
|
452
|
-
rating: r.rating,
|
|
453
|
-
email: r.email ?? "",
|
|
454
|
-
counter: r.counter ?? 0
|
|
455
|
-
}));
|
|
456
|
-
this.tagData = {
|
|
457
|
-
...this.tagData,
|
|
458
|
-
ratings: normalizedRatings
|
|
459
|
-
};
|
|
165
|
+
this.state.tagData = fields.setRatings(this.state.tagData, ratings);
|
|
460
166
|
}
|
|
461
167
|
getLyrics() {
|
|
462
168
|
this.checkNotDestroyed();
|
|
463
|
-
|
|
464
|
-
if (value === void 0 || value === null) return [];
|
|
465
|
-
const entries = Array.isArray(value) ? value : [value];
|
|
466
|
-
return entries.map(
|
|
467
|
-
(entry) => typeof entry === "string" ? { text: entry, description: "", language: "" } : {
|
|
468
|
-
text: entry?.text ?? "",
|
|
469
|
-
description: entry?.description ?? "",
|
|
470
|
-
language: entry?.language ?? ""
|
|
471
|
-
}
|
|
472
|
-
);
|
|
169
|
+
return fields.getLyrics(this.state.tagData);
|
|
473
170
|
}
|
|
474
171
|
setLyrics(lyrics) {
|
|
475
172
|
this.checkNotDestroyed();
|
|
476
|
-
this.tagData =
|
|
173
|
+
this.state.tagData = fields.setLyrics(this.state.tagData, lyrics);
|
|
477
174
|
}
|
|
478
175
|
getId3v2Frames(id) {
|
|
479
176
|
this.checkNotDestroyed();
|
|
480
|
-
|
|
481
|
-
const source = this.filePath ?? this.fileData;
|
|
482
|
-
let frames = [];
|
|
483
|
-
if (source) {
|
|
484
|
-
frames = readId3v2FramesFromWasm(this.wasi, source, filter);
|
|
485
|
-
}
|
|
486
|
-
const staged = this.getStagedId3v2Frames();
|
|
487
|
-
if (!staged) return frames;
|
|
488
|
-
const stagedIds = new Set(Object.keys(staged));
|
|
489
|
-
frames = frames.filter((f) => !stagedIds.has(f.id));
|
|
490
|
-
for (const [sid, bodies] of Object.entries(staged)) {
|
|
491
|
-
if (filter && sid !== filter) continue;
|
|
492
|
-
for (const data of bodies) frames.push({ id: sid, data: data.slice() });
|
|
493
|
-
}
|
|
494
|
-
return frames;
|
|
177
|
+
return fields.getId3v2Frames(this.state, id);
|
|
495
178
|
}
|
|
496
179
|
setId3v2Frames(id, data) {
|
|
497
180
|
this.checkNotDestroyed();
|
|
498
|
-
|
|
499
|
-
staged[id] = data.map((d) => new Uint8Array(d));
|
|
500
|
-
this.tagData = {
|
|
501
|
-
...this.tagData,
|
|
502
|
-
id3v2Frames: staged
|
|
503
|
-
};
|
|
181
|
+
this.state.tagData = fields.setId3v2Frames(this.state.tagData, id, data);
|
|
504
182
|
}
|
|
505
183
|
removeId3v2Frames(id) {
|
|
506
|
-
this.
|
|
184
|
+
this.checkNotDestroyed();
|
|
185
|
+
this.state.tagData = fields.removeId3v2Frames(this.state.tagData, id);
|
|
507
186
|
}
|
|
508
187
|
getStagedId3v2Frames() {
|
|
509
|
-
return this.tagData
|
|
188
|
+
return fields.getStagedId3v2Frames(this.state.tagData);
|
|
510
189
|
}
|
|
511
190
|
destroy() {
|
|
512
|
-
this.
|
|
513
|
-
this.tagData = null;
|
|
191
|
+
destroy(this.state);
|
|
514
192
|
this.destroyed = true;
|
|
515
193
|
}
|
|
516
194
|
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Handle state and lifecycle for the WASI FileHandle.
|
|
3
|
+
*
|
|
4
|
+
* The WasiFileHandle class owns destruction guarding and delegates here;
|
|
5
|
+
* this module's branching logic is testable without a Wasm module (pass a
|
|
6
|
+
* stub state/wasi). Extracted from file-handle.ts in the taglib-1dfc split.
|
|
7
|
+
*/
|
|
8
|
+
import type { WasiModule } from "../wasmer-sdk-loader/types.js";
|
|
9
|
+
/** The mutable handle state: source bytes/path + the tag-data snapshot. */
|
|
10
|
+
export interface HandleState {
|
|
11
|
+
wasi: WasiModule;
|
|
12
|
+
fileData: Uint8Array | null;
|
|
13
|
+
filePath: string | null;
|
|
14
|
+
tagData: Record<string, unknown> | null;
|
|
15
|
+
}
|
|
16
|
+
export declare function createHandleState(wasi: WasiModule): HandleState;
|
|
17
|
+
/** Read + decode + normalize a buffer source into the snapshot. */
|
|
18
|
+
export declare function loadBuffer(state: HandleState, buffer: Uint8Array): void;
|
|
19
|
+
/** Read + decode + normalize a path source into the snapshot. */
|
|
20
|
+
export declare function loadPath(state: HandleState, path: string): void;
|
|
21
|
+
export declare function isValid(state: HandleState): boolean;
|
|
22
|
+
/** Write the snapshot back; path mode writes in place, buffer mode returns
|
|
23
|
+
* the new bytes (stored back into state.fileData on success). */
|
|
24
|
+
export declare function save(state: HandleState): boolean;
|
|
25
|
+
export declare function getBuffer(state: HandleState): Uint8Array;
|
|
26
|
+
/** Null the source and snapshot; the class tracks the destroyed flag. */
|
|
27
|
+
export declare function destroy(state: HandleState): void;
|
|
28
|
+
//# sourceMappingURL=handle-state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handle-state.d.ts","sourceRoot":"","sources":["../../../../src/runtime/wasi-adapter/handle-state.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAKhE,2EAA2E;AAC3E,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,UAAU,CAAC;IACjB,QAAQ,EAAE,UAAU,GAAG,IAAI,CAAC;IAC5B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CACzC;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,UAAU,GAAG,WAAW,CAE/D;AAED,mEAAmE;AACnE,wBAAgB,UAAU,CAAC,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,UAAU,GAAG,IAAI,CAKvE;AAED,iEAAiE;AACjE,wBAAgB,QAAQ,CAAC,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAK/D;AAED,wBAAgB,OAAO,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAGnD;AAED;iEACiE;AACjE,wBAAgB,IAAI,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAsBhD;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,WAAW,GAAG,UAAU,CAExD;AAED,yEAAyE;AACzE,wBAAgB,OAAO,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI,CAGhD"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { decodeTagData } from "../../msgpack/decoder.js";
|
|
2
|
+
import { preserveEmptyValues } from "./tag-keys.js";
|
|
3
|
+
import * as wasmIo from "./wasm-io.js";
|
|
4
|
+
function createHandleState(wasi) {
|
|
5
|
+
return { wasi, fileData: null, filePath: null, tagData: null };
|
|
6
|
+
}
|
|
7
|
+
function loadBuffer(state, buffer) {
|
|
8
|
+
state.fileData = buffer;
|
|
9
|
+
state.tagData = preserveEmptyValues(
|
|
10
|
+
decodeTagData(wasmIo.readTagsFromWasm(state.wasi, buffer))
|
|
11
|
+
);
|
|
12
|
+
}
|
|
13
|
+
function loadPath(state, path) {
|
|
14
|
+
state.filePath = path;
|
|
15
|
+
state.tagData = preserveEmptyValues(
|
|
16
|
+
decodeTagData(wasmIo.readTagsFromWasmPath(state.wasi, path))
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
function isValid(state) {
|
|
20
|
+
return state.fileData !== null && state.fileData.length > 0 || state.filePath !== null && state.tagData !== null;
|
|
21
|
+
}
|
|
22
|
+
function save(state) {
|
|
23
|
+
if (!state.tagData) return false;
|
|
24
|
+
if (state.filePath) {
|
|
25
|
+
return wasmIo.writeTagsToWasmPath(
|
|
26
|
+
state.wasi,
|
|
27
|
+
state.filePath,
|
|
28
|
+
state.tagData
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
if (!state.fileData) return false;
|
|
32
|
+
const result = wasmIo.writeTagsToWasm(
|
|
33
|
+
state.wasi,
|
|
34
|
+
state.fileData,
|
|
35
|
+
state.tagData
|
|
36
|
+
);
|
|
37
|
+
if (result) {
|
|
38
|
+
state.fileData = result;
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
function getBuffer(state) {
|
|
44
|
+
return state.fileData ?? new Uint8Array(0);
|
|
45
|
+
}
|
|
46
|
+
function destroy(state) {
|
|
47
|
+
state.fileData = null;
|
|
48
|
+
state.tagData = null;
|
|
49
|
+
}
|
|
50
|
+
export {
|
|
51
|
+
createHandleState,
|
|
52
|
+
destroy,
|
|
53
|
+
getBuffer,
|
|
54
|
+
isValid,
|
|
55
|
+
loadBuffer,
|
|
56
|
+
loadPath,
|
|
57
|
+
save
|
|
58
|
+
};
|