taglib-wasm 1.1.1 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +512 -38
- package/dist/index.browser.js +366 -56
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/simple.browser.js +366 -56
- package/dist/src/bwf/bext.d.ts +28 -0
- package/dist/src/bwf/bext.d.ts.map +1 -0
- package/dist/src/bwf/bext.js +126 -0
- package/dist/src/msgpack/encoder.d.ts.map +1 -1
- package/dist/src/msgpack/encoder.js +9 -1
- package/dist/src/runtime/unified-loader/module-loading.js +1 -1
- package/dist/src/runtime/wasi-adapter/adapter.d.ts +0 -2
- package/dist/src/runtime/wasi-adapter/adapter.d.ts.map +1 -1
- package/dist/src/runtime/wasi-adapter/adapter.js +0 -12
- package/dist/src/runtime/wasi-adapter/file-handle.d.ts +12 -4
- package/dist/src/runtime/wasi-adapter/file-handle.d.ts.map +1 -1
- package/dist/src/runtime/wasi-adapter/file-handle.js +73 -54
- package/dist/src/runtime/wasi-host-loader.d.ts +1 -1
- package/dist/src/runtime/wasi-host-loader.js +1 -1
- package/dist/src/taglib/audio-file-base.d.ts.map +1 -1
- package/dist/src/taglib/audio-file-base.js +25 -42
- package/dist/src/taglib/audio-file-bwf.d.ts +14 -0
- package/dist/src/taglib/audio-file-bwf.d.ts.map +1 -0
- package/dist/src/taglib/audio-file-bwf.js +41 -0
- package/dist/src/taglib/audio-file-impl.d.ts +10 -0
- package/dist/src/taglib/audio-file-impl.d.ts.map +1 -1
- package/dist/src/taglib/audio-file-impl.js +69 -13
- package/dist/src/taglib/audio-file-interface.d.ts +27 -0
- package/dist/src/taglib/audio-file-interface.d.ts.map +1 -1
- package/dist/src/taglib/embind-adapter.d.ts +53 -0
- package/dist/src/taglib/embind-adapter.d.ts.map +1 -0
- package/dist/src/taglib/embind-adapter.js +82 -0
- package/dist/src/taglib/taglib-class.d.ts.map +1 -1
- package/dist/src/taglib/taglib-class.js +3 -1
- package/dist/src/types/audio-formats.d.ts +20 -0
- package/dist/src/types/audio-formats.d.ts.map +1 -1
- package/dist/src/types/bwf.d.ts +43 -0
- package/dist/src/types/bwf.d.ts.map +1 -0
- package/dist/src/types/bwf.js +0 -0
- package/dist/src/types/chapters.d.ts +47 -0
- package/dist/src/types/chapters.d.ts.map +1 -0
- package/dist/src/types/chapters.js +0 -0
- package/dist/src/types/index.d.ts +2 -0
- package/dist/src/types/index.d.ts.map +1 -1
- package/dist/src/types/index.js +2 -0
- package/dist/src/types/metadata-mappings.d.ts +1 -1
- package/dist/src/types/metadata-mappings.d.ts.map +1 -1
- package/dist/src/types/tags.d.ts +25 -7
- package/dist/src/types/tags.d.ts.map +1 -1
- package/dist/src/version.d.ts +1 -1
- package/dist/src/version.js +1 -1
- package/dist/src/wasm.d.ts +17 -39
- package/dist/src/wasm.d.ts.map +1 -1
- package/dist/taglib-wasi.wasm +0 -0
- package/dist/taglib-web.wasm +0 -0
- package/dist/taglib-wrapper.d.ts +0 -2
- package/dist/taglib-wrapper.js +1 -1
- package/package.json +5 -5
- package/dist/taglib_wasi.wasm +0 -0
package/dist/simple.browser.js
CHANGED
|
@@ -88,6 +88,20 @@ var init_pictures = __esm({
|
|
|
88
88
|
}
|
|
89
89
|
});
|
|
90
90
|
|
|
91
|
+
// src/types/chapters.ts
|
|
92
|
+
var init_chapters = __esm({
|
|
93
|
+
"src/types/chapters.ts"() {
|
|
94
|
+
"use strict";
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
// src/types/bwf.ts
|
|
99
|
+
var init_bwf = __esm({
|
|
100
|
+
"src/types/bwf.ts"() {
|
|
101
|
+
"use strict";
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
|
|
91
105
|
// src/types/config.ts
|
|
92
106
|
var init_config = __esm({
|
|
93
107
|
"src/types/config.ts"() {
|
|
@@ -110,6 +124,8 @@ var init_types = __esm({
|
|
|
110
124
|
init_tags();
|
|
111
125
|
init_metadata_mappings();
|
|
112
126
|
init_pictures();
|
|
127
|
+
init_chapters();
|
|
128
|
+
init_bwf();
|
|
113
129
|
init_config();
|
|
114
130
|
init_format_property_keys();
|
|
115
131
|
}
|
|
@@ -123,6 +139,136 @@ var init_types2 = __esm({
|
|
|
123
139
|
}
|
|
124
140
|
});
|
|
125
141
|
|
|
142
|
+
// src/bwf/bext.ts
|
|
143
|
+
function readFixedString(bytes, offset, len) {
|
|
144
|
+
let end = offset;
|
|
145
|
+
const max = Math.min(offset + len, bytes.length);
|
|
146
|
+
while (end < max && bytes[end] !== 0) end++;
|
|
147
|
+
let s = "";
|
|
148
|
+
for (let i = offset; i < end; i++) s += String.fromCharCode(bytes[i]);
|
|
149
|
+
return s;
|
|
150
|
+
}
|
|
151
|
+
function writeFixedString(bytes, offset, len, value) {
|
|
152
|
+
for (let i = 0; i < len; i++) {
|
|
153
|
+
bytes[offset + i] = i < value.length ? value.charCodeAt(i) & 255 : 0;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
function clampInt16(n) {
|
|
157
|
+
return Math.max(-32768, Math.min(32767, Math.round(n)));
|
|
158
|
+
}
|
|
159
|
+
function loudnessRaw(v) {
|
|
160
|
+
return v === void 0 ? LOUDNESS_UNSET : clampInt16(v * 100);
|
|
161
|
+
}
|
|
162
|
+
function loudnessFromRaw(raw) {
|
|
163
|
+
return raw === LOUDNESS_UNSET ? void 0 : raw / 100;
|
|
164
|
+
}
|
|
165
|
+
function bytesToHex(bytes) {
|
|
166
|
+
let s = "";
|
|
167
|
+
for (const b of bytes) s += b.toString(16).padStart(2, "0");
|
|
168
|
+
return s;
|
|
169
|
+
}
|
|
170
|
+
function hexToBytes(hex, outLen) {
|
|
171
|
+
const clean = hex.replace(/[^0-9a-fA-F]/g, "");
|
|
172
|
+
const out = new Uint8Array(outLen);
|
|
173
|
+
for (let i = 0; i + 1 < clean.length + 1 && i / 2 < outLen; i += 2) {
|
|
174
|
+
const pair = clean.slice(i, i + 2);
|
|
175
|
+
if (pair.length === 2) out[i / 2] = parseInt(pair, 16);
|
|
176
|
+
}
|
|
177
|
+
return out;
|
|
178
|
+
}
|
|
179
|
+
function decodeBext(bytes) {
|
|
180
|
+
if (bytes.length < VERSION_OFFSET + 2) return void 0;
|
|
181
|
+
const dv = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
182
|
+
const timeRefLow = dv.getUint32(338, true);
|
|
183
|
+
const timeRefHigh = dv.getUint32(342, true);
|
|
184
|
+
const version = dv.getUint16(VERSION_OFFSET, true);
|
|
185
|
+
const result = {
|
|
186
|
+
description: readFixedString(bytes, 0, 256),
|
|
187
|
+
originator: readFixedString(bytes, 256, 32),
|
|
188
|
+
originatorReference: readFixedString(bytes, 288, 32),
|
|
189
|
+
originationDate: readFixedString(bytes, 320, 10),
|
|
190
|
+
originationTime: readFixedString(bytes, 330, 8),
|
|
191
|
+
timeReferenceSamples: BigInt(timeRefHigh) << 32n | BigInt(timeRefLow),
|
|
192
|
+
version,
|
|
193
|
+
codingHistory: bytes.length > FIXED_PREFIX_LEN ? readFixedString(
|
|
194
|
+
bytes,
|
|
195
|
+
FIXED_PREFIX_LEN,
|
|
196
|
+
bytes.length - FIXED_PREFIX_LEN
|
|
197
|
+
) : ""
|
|
198
|
+
};
|
|
199
|
+
if (version >= 1 && bytes.length >= UMID_OFFSET + UMID_LEN) {
|
|
200
|
+
result.umid = bytesToHex(
|
|
201
|
+
bytes.subarray(UMID_OFFSET, UMID_OFFSET + UMID_LEN)
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
if (version >= 2 && bytes.length >= LOUDNESS_OFFSET + 10) {
|
|
205
|
+
const lv = loudnessFromRaw(dv.getInt16(LOUDNESS_OFFSET, true));
|
|
206
|
+
const lr = loudnessFromRaw(dv.getInt16(LOUDNESS_OFFSET + 2, true));
|
|
207
|
+
const mtp = loudnessFromRaw(dv.getInt16(LOUDNESS_OFFSET + 4, true));
|
|
208
|
+
const mml = loudnessFromRaw(dv.getInt16(LOUDNESS_OFFSET + 6, true));
|
|
209
|
+
const msl = loudnessFromRaw(dv.getInt16(LOUDNESS_OFFSET + 8, true));
|
|
210
|
+
if (lv !== void 0) result.loudnessValueDb = lv;
|
|
211
|
+
if (lr !== void 0) result.loudnessRangeDb = lr;
|
|
212
|
+
if (mtp !== void 0) result.maxTruePeakLevelDbtp = mtp;
|
|
213
|
+
if (mml !== void 0) result.maxMomentaryLoudnessDb = mml;
|
|
214
|
+
if (msl !== void 0) result.maxShortTermLoudnessDb = msl;
|
|
215
|
+
}
|
|
216
|
+
return result;
|
|
217
|
+
}
|
|
218
|
+
function encodeBext(b) {
|
|
219
|
+
const codingHistory = b.codingHistory ?? "";
|
|
220
|
+
const bytes = new Uint8Array(FIXED_PREFIX_LEN + codingHistory.length);
|
|
221
|
+
const dv = new DataView(bytes.buffer);
|
|
222
|
+
const hasLoudness = b.loudnessValueDb !== void 0 || b.loudnessRangeDb !== void 0 || b.maxTruePeakLevelDbtp !== void 0 || b.maxMomentaryLoudnessDb !== void 0 || b.maxShortTermLoudnessDb !== void 0;
|
|
223
|
+
const version = Number.isInteger(b.version) ? b.version : hasLoudness ? 2 : b.umid ? 1 : 0;
|
|
224
|
+
writeFixedString(bytes, 0, 256, b.description ?? "");
|
|
225
|
+
writeFixedString(bytes, 256, 32, b.originator ?? "");
|
|
226
|
+
writeFixedString(bytes, 288, 32, b.originatorReference ?? "");
|
|
227
|
+
writeFixedString(bytes, 320, 10, b.originationDate ?? "");
|
|
228
|
+
writeFixedString(bytes, 330, 8, b.originationTime ?? "");
|
|
229
|
+
const tr = b.timeReferenceSamples ?? 0n;
|
|
230
|
+
dv.setUint32(338, Number(tr & 0xffffffffn), true);
|
|
231
|
+
dv.setUint32(342, Number(tr >> 32n & 0xffffffffn), true);
|
|
232
|
+
dv.setUint16(VERSION_OFFSET, version, true);
|
|
233
|
+
if (version >= 1 && b.umid) {
|
|
234
|
+
bytes.set(hexToBytes(b.umid, UMID_LEN), UMID_OFFSET);
|
|
235
|
+
}
|
|
236
|
+
if (version >= 2) {
|
|
237
|
+
dv.setInt16(LOUDNESS_OFFSET, loudnessRaw(b.loudnessValueDb), true);
|
|
238
|
+
dv.setInt16(LOUDNESS_OFFSET + 2, loudnessRaw(b.loudnessRangeDb), true);
|
|
239
|
+
dv.setInt16(LOUDNESS_OFFSET + 4, loudnessRaw(b.maxTruePeakLevelDbtp), true);
|
|
240
|
+
dv.setInt16(
|
|
241
|
+
LOUDNESS_OFFSET + 6,
|
|
242
|
+
loudnessRaw(b.maxMomentaryLoudnessDb),
|
|
243
|
+
true
|
|
244
|
+
);
|
|
245
|
+
dv.setInt16(
|
|
246
|
+
LOUDNESS_OFFSET + 8,
|
|
247
|
+
loudnessRaw(b.maxShortTermLoudnessDb),
|
|
248
|
+
true
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
writeFixedString(
|
|
252
|
+
bytes,
|
|
253
|
+
FIXED_PREFIX_LEN,
|
|
254
|
+
codingHistory.length,
|
|
255
|
+
codingHistory
|
|
256
|
+
);
|
|
257
|
+
return bytes;
|
|
258
|
+
}
|
|
259
|
+
var FIXED_PREFIX_LEN, VERSION_OFFSET, UMID_OFFSET, UMID_LEN, LOUDNESS_OFFSET, LOUDNESS_UNSET;
|
|
260
|
+
var init_bext = __esm({
|
|
261
|
+
"src/bwf/bext.ts"() {
|
|
262
|
+
"use strict";
|
|
263
|
+
FIXED_PREFIX_LEN = 602;
|
|
264
|
+
VERSION_OFFSET = 346;
|
|
265
|
+
UMID_OFFSET = 348;
|
|
266
|
+
UMID_LEN = 64;
|
|
267
|
+
LOUDNESS_OFFSET = 412;
|
|
268
|
+
LOUDNESS_UNSET = 32767;
|
|
269
|
+
}
|
|
270
|
+
});
|
|
271
|
+
|
|
126
272
|
// src/errors/base.ts
|
|
127
273
|
var SUPPORTED_FORMATS, TagLibError;
|
|
128
274
|
var init_base = __esm({
|
|
@@ -365,6 +511,47 @@ var init_errors2 = __esm({
|
|
|
365
511
|
}
|
|
366
512
|
});
|
|
367
513
|
|
|
514
|
+
// src/taglib/audio-file-bwf.ts
|
|
515
|
+
function requireBwf(format) {
|
|
516
|
+
if (!BWF_FORMATS.has(format)) {
|
|
517
|
+
throw new UnsupportedFormatError(format, ["WAV", "FLAC"], {
|
|
518
|
+
operation: "BWF metadata"
|
|
519
|
+
});
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
function getBextData(handle) {
|
|
523
|
+
const raw = handle.getBextData();
|
|
524
|
+
return raw && raw.length > 0 ? raw : void 0;
|
|
525
|
+
}
|
|
526
|
+
function setBextData(handle, format, data) {
|
|
527
|
+
requireBwf(format);
|
|
528
|
+
handle.setBextData(data);
|
|
529
|
+
}
|
|
530
|
+
function getBext(handle) {
|
|
531
|
+
const raw = handle.getBextData();
|
|
532
|
+
return raw && raw.length > 0 ? decodeBext(raw) : void 0;
|
|
533
|
+
}
|
|
534
|
+
function setBext(handle, format, bext) {
|
|
535
|
+
requireBwf(format);
|
|
536
|
+
handle.setBextData(encodeBext(bext));
|
|
537
|
+
}
|
|
538
|
+
function getIxml(handle) {
|
|
539
|
+
return handle.getIxml();
|
|
540
|
+
}
|
|
541
|
+
function setIxml(handle, format, data) {
|
|
542
|
+
requireBwf(format);
|
|
543
|
+
handle.setIxml(data);
|
|
544
|
+
}
|
|
545
|
+
var BWF_FORMATS;
|
|
546
|
+
var init_audio_file_bwf = __esm({
|
|
547
|
+
"src/taglib/audio-file-bwf.ts"() {
|
|
548
|
+
"use strict";
|
|
549
|
+
init_bext();
|
|
550
|
+
init_errors2();
|
|
551
|
+
BWF_FORMATS = /* @__PURE__ */ new Set(["WAV", "FLAC"]);
|
|
552
|
+
}
|
|
553
|
+
});
|
|
554
|
+
|
|
368
555
|
// browser-stub:platform-io-browser-stub
|
|
369
556
|
function getPlatformIO() {
|
|
370
557
|
throw new Error("Filesystem operations are not available in the browser.");
|
|
@@ -1222,61 +1409,63 @@ var init_audio_file_base = __esm({
|
|
|
1222
1409
|
return this.getFormat() === format;
|
|
1223
1410
|
}
|
|
1224
1411
|
tag() {
|
|
1225
|
-
const
|
|
1226
|
-
|
|
1227
|
-
throw new MetadataError(
|
|
1228
|
-
"read",
|
|
1229
|
-
"Tag may be corrupted or format not fully supported"
|
|
1230
|
-
);
|
|
1231
|
-
}
|
|
1412
|
+
const handle = this.handle;
|
|
1413
|
+
let data = handle.getTagData();
|
|
1232
1414
|
const tag = {
|
|
1233
1415
|
get title() {
|
|
1234
|
-
return
|
|
1416
|
+
return data.title;
|
|
1235
1417
|
},
|
|
1236
1418
|
get artist() {
|
|
1237
|
-
return
|
|
1419
|
+
return data.artist;
|
|
1238
1420
|
},
|
|
1239
1421
|
get album() {
|
|
1240
|
-
return
|
|
1422
|
+
return data.album;
|
|
1241
1423
|
},
|
|
1242
1424
|
get comment() {
|
|
1243
|
-
return
|
|
1425
|
+
return data.comment;
|
|
1244
1426
|
},
|
|
1245
1427
|
get genre() {
|
|
1246
|
-
return
|
|
1428
|
+
return data.genre;
|
|
1247
1429
|
},
|
|
1248
1430
|
get year() {
|
|
1249
|
-
return
|
|
1431
|
+
return data.year;
|
|
1250
1432
|
},
|
|
1251
1433
|
get track() {
|
|
1252
|
-
return
|
|
1434
|
+
return data.track;
|
|
1253
1435
|
},
|
|
1254
1436
|
setTitle: (value) => {
|
|
1255
|
-
|
|
1437
|
+
handle.setTagData({ title: value });
|
|
1438
|
+
data = handle.getTagData();
|
|
1256
1439
|
return tag;
|
|
1257
1440
|
},
|
|
1258
1441
|
setArtist: (value) => {
|
|
1259
|
-
|
|
1442
|
+
handle.setTagData({ artist: value });
|
|
1443
|
+
data = handle.getTagData();
|
|
1260
1444
|
return tag;
|
|
1261
1445
|
},
|
|
1262
1446
|
setAlbum: (value) => {
|
|
1263
|
-
|
|
1447
|
+
handle.setTagData({ album: value });
|
|
1448
|
+
data = handle.getTagData();
|
|
1264
1449
|
return tag;
|
|
1265
1450
|
},
|
|
1266
1451
|
setComment: (value) => {
|
|
1267
|
-
|
|
1452
|
+
handle.setTagData({ comment: value });
|
|
1453
|
+
data = handle.getTagData();
|
|
1268
1454
|
return tag;
|
|
1269
1455
|
},
|
|
1270
1456
|
setGenre: (value) => {
|
|
1271
|
-
|
|
1457
|
+
handle.setTagData({ genre: value });
|
|
1458
|
+
data = handle.getTagData();
|
|
1272
1459
|
return tag;
|
|
1273
1460
|
},
|
|
1274
1461
|
setYear: (value) => {
|
|
1275
|
-
|
|
1462
|
+
handle.setTagData({ year: value });
|
|
1463
|
+
data = handle.getTagData();
|
|
1276
1464
|
return tag;
|
|
1277
1465
|
},
|
|
1278
1466
|
setTrack: (value) => {
|
|
1279
|
-
|
|
1467
|
+
handle.setTagData({ track: value });
|
|
1468
|
+
data = handle.getTagData();
|
|
1280
1469
|
return tag;
|
|
1281
1470
|
}
|
|
1282
1471
|
};
|
|
@@ -1284,28 +1473,9 @@ var init_audio_file_base = __esm({
|
|
|
1284
1473
|
}
|
|
1285
1474
|
audioProperties() {
|
|
1286
1475
|
if (!this.cachedAudioProperties) {
|
|
1287
|
-
|
|
1288
|
-
if (!propsWrapper) {
|
|
1289
|
-
return void 0;
|
|
1290
|
-
}
|
|
1291
|
-
const containerFormat = propsWrapper.containerFormat() || "unknown";
|
|
1292
|
-
const mpegVersion = propsWrapper.mpegVersion();
|
|
1293
|
-
const formatVersion = propsWrapper.formatVersion();
|
|
1294
|
-
this.cachedAudioProperties = {
|
|
1295
|
-
duration: propsWrapper.lengthInSeconds(),
|
|
1296
|
-
bitrate: propsWrapper.bitrate(),
|
|
1297
|
-
sampleRate: propsWrapper.sampleRate(),
|
|
1298
|
-
channels: propsWrapper.channels(),
|
|
1299
|
-
bitsPerSample: propsWrapper.bitsPerSample(),
|
|
1300
|
-
codec: propsWrapper.codec() || "unknown",
|
|
1301
|
-
containerFormat,
|
|
1302
|
-
isLossless: propsWrapper.isLossless(),
|
|
1303
|
-
...mpegVersion > 0 ? { mpegVersion, mpegLayer: propsWrapper.mpegLayer() } : {},
|
|
1304
|
-
...containerFormat === "MP4" || containerFormat === "ASF" ? { isEncrypted: propsWrapper.isEncrypted() } : {},
|
|
1305
|
-
...formatVersion > 0 ? { formatVersion } : {}
|
|
1306
|
-
};
|
|
1476
|
+
this.cachedAudioProperties = this.handle.getAudioProperties() ?? null;
|
|
1307
1477
|
}
|
|
1308
|
-
return this.cachedAudioProperties;
|
|
1478
|
+
return this.cachedAudioProperties ?? void 0;
|
|
1309
1479
|
}
|
|
1310
1480
|
properties() {
|
|
1311
1481
|
return remapKeysFromTagLib(this.handle.getProperties());
|
|
@@ -1363,7 +1533,102 @@ var init_audio_file_base = __esm({
|
|
|
1363
1533
|
}
|
|
1364
1534
|
});
|
|
1365
1535
|
|
|
1536
|
+
// src/taglib/embind-adapter.ts
|
|
1537
|
+
function isValidBitrateMode(value) {
|
|
1538
|
+
return value === "CBR" || value === "VBR" || value === "ABR";
|
|
1539
|
+
}
|
|
1540
|
+
function wrapEmbindHandle(raw) {
|
|
1541
|
+
const overrides = {
|
|
1542
|
+
getTagData() {
|
|
1543
|
+
const tw = raw.getTag();
|
|
1544
|
+
return {
|
|
1545
|
+
title: tw.title(),
|
|
1546
|
+
artist: tw.artist(),
|
|
1547
|
+
album: tw.album(),
|
|
1548
|
+
comment: tw.comment(),
|
|
1549
|
+
genre: tw.genre(),
|
|
1550
|
+
year: tw.year(),
|
|
1551
|
+
track: tw.track()
|
|
1552
|
+
};
|
|
1553
|
+
},
|
|
1554
|
+
setTagData(data) {
|
|
1555
|
+
const tw = raw.getTag();
|
|
1556
|
+
if (data.title !== void 0) tw.setTitle(data.title);
|
|
1557
|
+
if (data.artist !== void 0) tw.setArtist(data.artist);
|
|
1558
|
+
if (data.album !== void 0) tw.setAlbum(data.album);
|
|
1559
|
+
if (data.comment !== void 0) tw.setComment(data.comment);
|
|
1560
|
+
if (data.genre !== void 0) tw.setGenre(data.genre);
|
|
1561
|
+
if (data.year !== void 0) tw.setYear(data.year);
|
|
1562
|
+
if (data.track !== void 0) tw.setTrack(data.track);
|
|
1563
|
+
},
|
|
1564
|
+
getBextData() {
|
|
1565
|
+
const v = raw.getBextData();
|
|
1566
|
+
if (v === void 0 || v === null) return void 0;
|
|
1567
|
+
const u8 = v instanceof Uint8Array ? v : new Uint8Array(v);
|
|
1568
|
+
return u8.length > 0 ? u8 : void 0;
|
|
1569
|
+
},
|
|
1570
|
+
setBextData(data) {
|
|
1571
|
+
raw.setBextData(data ?? null);
|
|
1572
|
+
},
|
|
1573
|
+
getIxml() {
|
|
1574
|
+
const v = raw.getIxml();
|
|
1575
|
+
return typeof v === "string" && v.length > 0 ? v : void 0;
|
|
1576
|
+
},
|
|
1577
|
+
setIxml(data) {
|
|
1578
|
+
raw.setIxml(
|
|
1579
|
+
data ?? null
|
|
1580
|
+
);
|
|
1581
|
+
},
|
|
1582
|
+
getAudioProperties() {
|
|
1583
|
+
const pw = raw.getAudioProperties();
|
|
1584
|
+
if (!pw) return null;
|
|
1585
|
+
const containerFormat = pw.containerFormat() || "unknown";
|
|
1586
|
+
const codec = pw.codec() || "unknown";
|
|
1587
|
+
const mpegVersion = pw.mpegVersion();
|
|
1588
|
+
const formatVersion = pw.formatVersion();
|
|
1589
|
+
const bitrateMode = pw.bitrateMode();
|
|
1590
|
+
return {
|
|
1591
|
+
duration: pw.lengthInSeconds(),
|
|
1592
|
+
durationMs: pw.lengthInMilliseconds(),
|
|
1593
|
+
bitrate: pw.bitrate(),
|
|
1594
|
+
sampleRate: pw.sampleRate(),
|
|
1595
|
+
channels: pw.channels(),
|
|
1596
|
+
bitsPerSample: pw.bitsPerSample(),
|
|
1597
|
+
codec,
|
|
1598
|
+
containerFormat,
|
|
1599
|
+
isLossless: pw.isLossless(),
|
|
1600
|
+
...mpegVersion > 0 ? { mpegVersion, mpegLayer: pw.mpegLayer() } : {},
|
|
1601
|
+
...containerFormat === "MP4" || containerFormat === "ASF" ? { isEncrypted: pw.isEncrypted() } : {},
|
|
1602
|
+
...formatVersion > 0 ? { formatVersion } : {},
|
|
1603
|
+
...isValidBitrateMode(bitrateMode) ? { bitrateMode } : {},
|
|
1604
|
+
...codec === "Opus" ? { outputGainDb: pw.outputGainDb() } : {}
|
|
1605
|
+
};
|
|
1606
|
+
}
|
|
1607
|
+
};
|
|
1608
|
+
return new Proxy(raw, {
|
|
1609
|
+
get(target, prop, receiver) {
|
|
1610
|
+
if (prop in overrides) return overrides[prop];
|
|
1611
|
+
const value = Reflect.get(target, prop, receiver);
|
|
1612
|
+
return typeof value === "function" ? value.bind(target) : value;
|
|
1613
|
+
}
|
|
1614
|
+
});
|
|
1615
|
+
}
|
|
1616
|
+
var init_embind_adapter = __esm({
|
|
1617
|
+
"src/taglib/embind-adapter.ts"() {
|
|
1618
|
+
"use strict";
|
|
1619
|
+
}
|
|
1620
|
+
});
|
|
1621
|
+
|
|
1366
1622
|
// src/taglib/audio-file-impl.ts
|
|
1623
|
+
function sortChapters(list) {
|
|
1624
|
+
return [...list].sort((a, b) => a.startTimeMs - b.startTimeMs);
|
|
1625
|
+
}
|
|
1626
|
+
function inferEndTimeMs(sorted, index, trackEndMs) {
|
|
1627
|
+
const own = sorted[index].endTimeMs;
|
|
1628
|
+
if (own !== void 0) return own;
|
|
1629
|
+
const next = sorted[index + 1];
|
|
1630
|
+
return next ? next.startTimeMs : trackEndMs;
|
|
1631
|
+
}
|
|
1367
1632
|
function readFileSync(path) {
|
|
1368
1633
|
if (typeof Deno !== "undefined") return Deno.readFileSync(path);
|
|
1369
1634
|
if (_nodeFs === void 0) {
|
|
@@ -1381,10 +1646,12 @@ var init_audio_file_impl = __esm({
|
|
|
1381
1646
|
"src/taglib/audio-file-impl.ts"() {
|
|
1382
1647
|
"use strict";
|
|
1383
1648
|
init_types2();
|
|
1649
|
+
init_audio_file_bwf();
|
|
1384
1650
|
init_errors2();
|
|
1385
1651
|
init_file();
|
|
1386
1652
|
init_write();
|
|
1387
1653
|
init_audio_file_base();
|
|
1654
|
+
init_embind_adapter();
|
|
1388
1655
|
AudioFileImpl = class extends BaseAudioFileImpl {
|
|
1389
1656
|
constructor(module, fileHandle, sourcePath, originalSource, isPartiallyLoaded = false, partialLoadOptions) {
|
|
1390
1657
|
super(
|
|
@@ -1430,7 +1697,8 @@ var init_audio_file_impl = __esm({
|
|
|
1430
1697
|
);
|
|
1431
1698
|
}
|
|
1432
1699
|
if (this.isPartiallyLoaded && this.originalSource) {
|
|
1433
|
-
const
|
|
1700
|
+
const rawFullHandle = this.module.createFileHandle();
|
|
1701
|
+
const fullFileHandle = this.module.isWasi ? rawFullHandle : wrapEmbindHandle(rawFullHandle);
|
|
1434
1702
|
try {
|
|
1435
1703
|
const success = await (async () => {
|
|
1436
1704
|
const data = await readFileData(this.originalSource);
|
|
@@ -1441,19 +1709,13 @@ var init_audio_file_impl = __esm({
|
|
|
1441
1709
|
"Failed to load full audio file for saving"
|
|
1442
1710
|
);
|
|
1443
1711
|
}
|
|
1444
|
-
|
|
1445
|
-
const fullTag = fullFileHandle.getTag();
|
|
1446
|
-
if (partialTag && fullTag) {
|
|
1447
|
-
fullTag.setTitle(partialTag.title());
|
|
1448
|
-
fullTag.setArtist(partialTag.artist());
|
|
1449
|
-
fullTag.setAlbum(partialTag.album());
|
|
1450
|
-
fullTag.setComment(partialTag.comment());
|
|
1451
|
-
fullTag.setGenre(partialTag.genre());
|
|
1452
|
-
fullTag.setYear(partialTag.year());
|
|
1453
|
-
fullTag.setTrack(partialTag.track());
|
|
1454
|
-
}
|
|
1712
|
+
fullFileHandle.setTagData(this.handle.getTagData());
|
|
1455
1713
|
fullFileHandle.setProperties(this.handle.getProperties());
|
|
1456
1714
|
fullFileHandle.setPictures(this.handle.getPictures());
|
|
1715
|
+
const bextBytes = this.handle.getBextData();
|
|
1716
|
+
if (bextBytes !== void 0) fullFileHandle.setBextData(bextBytes);
|
|
1717
|
+
const ixmlStr = this.handle.getIxml();
|
|
1718
|
+
if (ixmlStr !== void 0) fullFileHandle.setIxml(ixmlStr);
|
|
1457
1719
|
if (!fullFileHandle.save()) {
|
|
1458
1720
|
throw new FileOperationError(
|
|
1459
1721
|
"save",
|
|
@@ -1508,6 +1770,52 @@ var init_audio_file_impl = __esm({
|
|
|
1508
1770
|
removePictures() {
|
|
1509
1771
|
this.handle.removePictures();
|
|
1510
1772
|
}
|
|
1773
|
+
getChapters() {
|
|
1774
|
+
const sorted = sortChapters(this.handle.getChapters());
|
|
1775
|
+
const trackEndMs = this.audioProperties()?.durationMs;
|
|
1776
|
+
return sorted.map((c, i) => ({
|
|
1777
|
+
startTimeMs: c.startTimeMs,
|
|
1778
|
+
endTimeMs: inferEndTimeMs(sorted, i, trackEndMs),
|
|
1779
|
+
title: c.title || void 0,
|
|
1780
|
+
id: c.id || void 0,
|
|
1781
|
+
source: c.source
|
|
1782
|
+
}));
|
|
1783
|
+
}
|
|
1784
|
+
setChapters(chapters, options) {
|
|
1785
|
+
const fmt = this.getFormat();
|
|
1786
|
+
if (fmt !== "MP3" && fmt !== "MP4") {
|
|
1787
|
+
throw new UnsupportedFormatError(fmt, ["MP3", "MP4"], {
|
|
1788
|
+
operation: "setChapters"
|
|
1789
|
+
});
|
|
1790
|
+
}
|
|
1791
|
+
const sorted = sortChapters(chapters);
|
|
1792
|
+
const trackEndMs = this.audioProperties()?.durationMs;
|
|
1793
|
+
const raw = sorted.map((c, i) => ({
|
|
1794
|
+
id: c.id,
|
|
1795
|
+
startTimeMs: c.startTimeMs,
|
|
1796
|
+
endTimeMs: inferEndTimeMs(sorted, i, trackEndMs) ?? c.startTimeMs,
|
|
1797
|
+
title: c.title
|
|
1798
|
+
}));
|
|
1799
|
+
this.handle.setChapters(raw, options?.mp4ChapterStyle ?? "quicktime");
|
|
1800
|
+
}
|
|
1801
|
+
getBext() {
|
|
1802
|
+
return getBext(this.handle);
|
|
1803
|
+
}
|
|
1804
|
+
setBext(bext) {
|
|
1805
|
+
setBext(this.handle, this.getFormat(), bext);
|
|
1806
|
+
}
|
|
1807
|
+
getBextData() {
|
|
1808
|
+
return getBextData(this.handle);
|
|
1809
|
+
}
|
|
1810
|
+
setBextData(data) {
|
|
1811
|
+
setBextData(this.handle, this.getFormat(), data);
|
|
1812
|
+
}
|
|
1813
|
+
getIxml() {
|
|
1814
|
+
return getIxml(this.handle);
|
|
1815
|
+
}
|
|
1816
|
+
setIxml(data) {
|
|
1817
|
+
setIxml(this.handle, this.getFormat(), data);
|
|
1818
|
+
}
|
|
1511
1819
|
getRatings() {
|
|
1512
1820
|
return this.handle.getRatings().map(
|
|
1513
1821
|
(r) => ({
|
|
@@ -1681,7 +1989,7 @@ var VERSION;
|
|
|
1681
1989
|
var init_version = __esm({
|
|
1682
1990
|
"src/version.ts"() {
|
|
1683
1991
|
"use strict";
|
|
1684
|
-
VERSION = "1.
|
|
1992
|
+
VERSION = "1.2.0";
|
|
1685
1993
|
}
|
|
1686
1994
|
});
|
|
1687
1995
|
|
|
@@ -1768,6 +2076,7 @@ var init_taglib_class = __esm({
|
|
|
1768
2076
|
init_tag_mapping();
|
|
1769
2077
|
init_errors2();
|
|
1770
2078
|
init_version();
|
|
2079
|
+
init_embind_adapter();
|
|
1771
2080
|
TagLib = class _TagLib {
|
|
1772
2081
|
constructor(module) {
|
|
1773
2082
|
__publicField(this, "module");
|
|
@@ -1843,7 +2152,8 @@ var init_taglib_class = __esm({
|
|
|
1843
2152
|
audioData.byteOffset + audioData.byteLength
|
|
1844
2153
|
)
|
|
1845
2154
|
) : audioData;
|
|
1846
|
-
const
|
|
2155
|
+
const rawHandle = this.module.createFileHandle();
|
|
2156
|
+
const fileHandle = this.module.isWasi ? rawHandle : wrapEmbindHandle(rawHandle);
|
|
1847
2157
|
try {
|
|
1848
2158
|
const success = fileHandle.loadFromBuffer(uint8Array);
|
|
1849
2159
|
if (!success) {
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview EBU Tech 3285 BWF `bext` chunk codec. Pure functions — no Wasm
|
|
3
|
+
* or TagLib dependency. The fixed prefix is 602 bytes for all versions; the
|
|
4
|
+
* `version` field selects which optional fields are meaningful (0 = none,
|
|
5
|
+
* 1 = UMID, 2 = UMID + loudness). Fixed-width string fields are NUL-padded
|
|
6
|
+
* ASCII; `codingHistory` is exposed verbatim (the spec uses CR/LF lines) and
|
|
7
|
+
* is not promised to preserve embedded NUL bytes. Loudness fields use the EBU
|
|
8
|
+
* "value not set" sentinel `0x7FFF`: a sentinel decodes to `undefined`, and an
|
|
9
|
+
* `undefined` loudness field on a v2 encode is written as the sentinel.
|
|
10
|
+
*/
|
|
11
|
+
import type { BroadcastAudioExtension } from "../types/bwf.js";
|
|
12
|
+
/**
|
|
13
|
+
* Parse a raw `bext` chunk. Returns `undefined` only if `bytes` is shorter than
|
|
14
|
+
* 348 (cannot even read the `Version` field). Optional fields are populated per
|
|
15
|
+
* the chunk's declared version and the available length, so compact/legacy v0
|
|
16
|
+
* chunks parse into a partial struct.
|
|
17
|
+
*/
|
|
18
|
+
export declare function decodeBext(bytes: Uint8Array): BroadcastAudioExtension | undefined;
|
|
19
|
+
/**
|
|
20
|
+
* Serialize a `bext` chunk. The result is always `602 + codingHistory.length`
|
|
21
|
+
* bytes. `version` defaults to 2 when any loudness field is present (else 1 if
|
|
22
|
+
* `umid` is set, else 0). Over-long string fields are truncated to their widths;
|
|
23
|
+
* loudness values are clamped to the Int16 range after ×100, with `undefined`
|
|
24
|
+
* written as the EBU `0x7FFF` "not set" sentinel; UMID is written only for
|
|
25
|
+
* version ≥ 1.
|
|
26
|
+
*/
|
|
27
|
+
export declare function encodeBext(b: BroadcastAudioExtension): Uint8Array;
|
|
28
|
+
//# sourceMappingURL=bext.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bext.d.ts","sourceRoot":"","sources":["../../../src/bwf/bext.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AA6D/D;;;;;GAKG;AACH,wBAAgB,UAAU,CACxB,KAAK,EAAE,UAAU,GAChB,uBAAuB,GAAG,SAAS,CAwCrC;AAED;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,CAAC,EAAE,uBAAuB,GAAG,UAAU,CA6CjE"}
|