taglib-wasm 1.0.0-beta.7 → 1.0.0-beta.9
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 +33 -108
- package/dist/src/runtime/wasi-adapter/file-handle.d.ts.map +1 -1
- package/dist/src/runtime/wasi-adapter/file-handle.js +16 -7
- package/dist/src/runtime/wasi-adapter/wasm-io.d.ts.map +1 -1
- package/dist/src/runtime/wasi-adapter/wasm-io.js +21 -38
- package/dist/src/runtime/wasmer-sdk-loader/high-level-api.d.ts +1 -1
- package/dist/src/runtime/wasmer-sdk-loader/high-level-api.d.ts.map +1 -1
- package/dist/src/runtime/wasmer-sdk-loader/high-level-api.js +10 -21
- package/dist/src/types/tags.d.ts +30 -30
- package/dist/src/types/tags.d.ts.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -12,22 +12,12 @@
|
|
|
12
12
|
[](https://nodejs.org/)
|
|
13
13
|
[](https://bun.sh/)
|
|
14
14
|
[](https://workers.cloudflare.com/)
|
|
15
|
-
[](https://www.electronjs.org/)
|
|
15
|
+
[](https://www.electronjs.org/)
|
|
16
16
|
[](https://html.spec.whatwg.org/multipage/)
|
|
17
17
|
|
|
18
18
|
TagLib-Wasm is the **universal tagging library for TypeScript/JavaScript**
|
|
19
19
|
(TS|JS) platforms: **Deno**, **Node.js**, **Bun**, **Cloudflare Workers**,
|
|
20
|
-
**Electron
|
|
21
|
-
|
|
22
|
-
This project exists because the TS|JS ecosystem had no battle-tested audio
|
|
23
|
-
tagging library that supports reading and writing music metadata to all popular
|
|
24
|
-
audio formats. It aspires to be a universal solution for all TS|JS-capable
|
|
25
|
-
platforms — Deno, Node.js, Bun, Electron, Cloudflare Workers, and browsers.
|
|
26
|
-
|
|
27
|
-
TagLib-Wasm stands on the shoulders of giants, including
|
|
28
|
-
[TagLib](https://taglib.org/) itself, [Emscripten](https://emscripten.org/), and
|
|
29
|
-
[Wasm](https://webassembly.org/) ([WebAssembly](https://webassembly.org/)).
|
|
30
|
-
TagLib itself is legendary, and a core dependency of many music apps.
|
|
20
|
+
**Electron** (via Node.js), and **browsers**.
|
|
31
21
|
|
|
32
22
|
## Features
|
|
33
23
|
|
|
@@ -37,12 +27,12 @@ TagLib itself is legendary, and a core dependency of many music apps.
|
|
|
37
27
|
(browser) for optimal performance with no configuration
|
|
38
28
|
- **Full audio format support** – Supports all audio formats supported by TagLib
|
|
39
29
|
- **TypeScript first** – Complete type definitions and modern API
|
|
40
|
-
- **Wide TS/JS runtime support** – Deno, Node.js, Bun, Electron,
|
|
41
|
-
Workers, and browsers
|
|
42
|
-
- **Format abstraction** – Handles container format details
|
|
30
|
+
- **Wide TS/JS runtime support** – Deno, Node.js, Bun, Electron (Node.js),
|
|
31
|
+
Cloudflare Workers, and browsers
|
|
32
|
+
- **Format abstraction** – Handles container format details automatically when
|
|
43
33
|
possible
|
|
44
34
|
- **Zero dependencies** – Self-contained Wasm bundle
|
|
45
|
-
- **
|
|
35
|
+
- **Tested** – 135+ tests across all formats
|
|
46
36
|
- **Two API styles** – Use the "Simple" API (3 functions), or the full "Core"
|
|
47
37
|
API for more advanced applications
|
|
48
38
|
- **Batch folder operations** – Scan directories, process multiple files, find
|
|
@@ -72,22 +62,23 @@ npm install taglib-wasm
|
|
|
72
62
|
bun add taglib-wasm
|
|
73
63
|
```
|
|
74
64
|
|
|
75
|
-
### Electron
|
|
65
|
+
### Electron (Node.js)
|
|
76
66
|
|
|
77
67
|
```bash
|
|
78
68
|
npm install taglib-wasm
|
|
79
69
|
```
|
|
80
70
|
|
|
81
|
-
|
|
71
|
+
taglib-wasm works in Electron's main process (which is Node.js). For the
|
|
72
|
+
renderer process, expose metadata through IPC:
|
|
82
73
|
|
|
83
74
|
```typescript
|
|
84
75
|
// Main process
|
|
85
76
|
import { TagLib } from "taglib-wasm";
|
|
86
|
-
|
|
87
|
-
// Renderer process (with nodeIntegration: true)
|
|
88
|
-
const { TagLib } = require("taglib-wasm");
|
|
89
77
|
```
|
|
90
78
|
|
|
79
|
+
See [Platform Examples](docs/guide/platform-examples.md#electron) for full IPC
|
|
80
|
+
setup.
|
|
81
|
+
|
|
91
82
|
### Deno Compiled Binaries (Offline Support)
|
|
92
83
|
|
|
93
84
|
For Deno compiled binaries that need to work offline, you can embed the WASM
|
|
@@ -130,7 +121,7 @@ const taglib = await TagLib.initialize({ wasmBinary });
|
|
|
130
121
|
```typescript
|
|
131
122
|
import { applyTags, readTags, updateTags } from "taglib-wasm/simple";
|
|
132
123
|
|
|
133
|
-
// Read tags
|
|
124
|
+
// Read tags
|
|
134
125
|
const tags = await readTags("song.mp3");
|
|
135
126
|
console.log(tags.title, tags.artist, tags.album);
|
|
136
127
|
|
|
@@ -153,7 +144,7 @@ await updateTags("song.mp3", {
|
|
|
153
144
|
```typescript
|
|
154
145
|
import { readMetadataBatch, readTagsBatch } from "taglib-wasm/simple";
|
|
155
146
|
|
|
156
|
-
// Process multiple files in parallel
|
|
147
|
+
// Process multiple files in parallel
|
|
157
148
|
const files = ["track01.mp3", "track02.mp3", /* ... */ "track20.mp3"];
|
|
158
149
|
|
|
159
150
|
// Read just tags (18x faster than sequential)
|
|
@@ -327,49 +318,21 @@ Supported formats:
|
|
|
327
318
|
- **Additional formats** – Opus, APE, MPC, WavPack, TrueAudio, AIFF, WMA, and
|
|
328
319
|
more
|
|
329
320
|
|
|
330
|
-
## Key Features
|
|
331
|
-
|
|
332
|
-
### Extended Metadata Support
|
|
333
|
-
|
|
334
|
-
Beyond basic tags, taglib-wasm supports extended metadata:
|
|
335
|
-
|
|
336
|
-
```typescript
|
|
337
|
-
import { Tags } from "taglib-wasm";
|
|
338
|
-
|
|
339
|
-
// AcoustID fingerprints
|
|
340
|
-
file.setProperty(
|
|
341
|
-
Tags.AcoustidFingerprint,
|
|
342
|
-
"AQADtMmybfGO8NCNEESLnzHyXNOHeHnG...",
|
|
343
|
-
);
|
|
344
|
-
|
|
345
|
-
// MusicBrainz IDs
|
|
346
|
-
file.setProperty(
|
|
347
|
-
Tags.MusicBrainzTrackId,
|
|
348
|
-
"f4d1b6b8-8c1e-4d9a-9f2a-1234567890ab",
|
|
349
|
-
);
|
|
350
|
-
|
|
351
|
-
// ReplayGain volume normalization
|
|
352
|
-
file.setProperty(Tags.TrackGain, "-6.54 dB");
|
|
353
|
-
file.setProperty(Tags.TrackPeak, "0.987654");
|
|
354
|
-
```
|
|
355
|
-
|
|
356
|
-
[View all supported tag constants →](https://charleswiltgen.github.io/taglib-wasm/api/tag-constants.html)
|
|
357
|
-
|
|
358
321
|
## Performance and Best Practices
|
|
359
322
|
|
|
360
323
|
### Batch Processing for Multiple Files
|
|
361
324
|
|
|
362
|
-
When processing multiple audio files, use the optimized batch APIs for
|
|
325
|
+
When processing multiple audio files, use the optimized batch APIs for better performance:
|
|
363
326
|
|
|
364
327
|
```typescript
|
|
365
328
|
import { readMetadataBatch, readTagsBatch } from "taglib-wasm/simple";
|
|
366
329
|
|
|
367
|
-
//
|
|
330
|
+
// Processing files one by one (can take 90+ seconds for 19 files)
|
|
368
331
|
for (const file of files) {
|
|
369
332
|
const tags = await readTags(file); // Re-initializes for each file
|
|
370
333
|
}
|
|
371
334
|
|
|
372
|
-
//
|
|
335
|
+
// Batch processing (10-20x faster)
|
|
373
336
|
const result = await readTagsBatch(files, {
|
|
374
337
|
concurrency: 8, // Process 8 files in parallel
|
|
375
338
|
onProgress: (processed, total) => {
|
|
@@ -377,7 +340,7 @@ const result = await readTagsBatch(files, {
|
|
|
377
340
|
},
|
|
378
341
|
});
|
|
379
342
|
|
|
380
|
-
//
|
|
343
|
+
// Read complete metadata in one batch
|
|
381
344
|
const metadata = await readMetadataBatch(files, { concurrency: 8 });
|
|
382
345
|
```
|
|
383
346
|
|
|
@@ -389,7 +352,7 @@ const metadata = await readMetadataBatch(files, { concurrency: 8 });
|
|
|
389
352
|
|
|
390
353
|
### Smart Partial Loading
|
|
391
354
|
|
|
392
|
-
For large audio files (>50MB), enable partial loading to
|
|
355
|
+
For large audio files (>50MB), enable partial loading to reduce memory usage:
|
|
393
356
|
|
|
394
357
|
```typescript
|
|
395
358
|
// Enable partial loading for large files
|
|
@@ -417,13 +380,13 @@ await file.saveToFile(); // Full file loaded only here
|
|
|
417
380
|
|
|
418
381
|
taglib-wasm auto-selects the fastest available backend — no configuration needed:
|
|
419
382
|
|
|
420
|
-
| Environment
|
|
421
|
-
|
|
|
422
|
-
| **Deno / Node.js**
|
|
423
|
-
| **Browsers / Workers**
|
|
424
|
-
| **Opt-in**
|
|
383
|
+
| Environment | Backend | How it works | Performance |
|
|
384
|
+
| ------------------------ | ----------------- | ------------------------------------------------------ | -------------- |
|
|
385
|
+
| **Deno / Node.js / Bun** | WASI (auto) | Seek-based filesystem I/O; reads only headers and tags | Fastest |
|
|
386
|
+
| **Browsers / Workers** | Emscripten (auto) | Entire file loaded into memory as buffer | Baseline |
|
|
387
|
+
| **Opt-in** | Wasmtime sidecar | Out-of-process WASI with direct filesystem access | Best for batch |
|
|
425
388
|
|
|
426
|
-
On Deno
|
|
389
|
+
On Deno, Node.js, and Bun you get WASI automatically — nothing to configure. For
|
|
427
390
|
heavy batch workloads, the optional Wasmtime sidecar provides direct filesystem
|
|
428
391
|
access via a sandboxed subprocess:
|
|
429
392
|
|
|
@@ -448,56 +411,18 @@ See the
|
|
|
448
411
|
[Runtime Compatibility Guide](https://charleswiltgen.github.io/taglib-wasm/concepts/runtime-compatibility.html)
|
|
449
412
|
for full sidecar configuration options.
|
|
450
413
|
|
|
451
|
-
### WebAssembly Streaming
|
|
452
|
-
|
|
453
|
-
For web applications, use CDN URLs to enable WebAssembly streaming compilation:
|
|
454
|
-
|
|
455
|
-
```typescript
|
|
456
|
-
// ✅ FAST: Streaming compilation (200-400ms)
|
|
457
|
-
const taglib = await TagLib.initialize({
|
|
458
|
-
wasmUrl: "https://cdn.jsdelivr.net/npm/taglib-wasm@latest/dist/taglib.wasm",
|
|
459
|
-
});
|
|
460
|
-
|
|
461
|
-
// ❌ SLOWER: ArrayBuffer loading (400-800ms)
|
|
462
|
-
const wasmBinary = await fetch("taglib.wasm").then((r) => r.arrayBuffer());
|
|
463
|
-
const taglib = await TagLib.initialize({ wasmBinary });
|
|
464
|
-
```
|
|
465
|
-
|
|
466
|
-
[View complete performance guide →](https://charleswiltgen.github.io/taglib-wasm/concepts/performance.html)
|
|
467
|
-
|
|
468
|
-
## Development
|
|
469
|
-
|
|
470
|
-
### Build from Source
|
|
471
|
-
|
|
472
|
-
```bash
|
|
473
|
-
# Prerequisites: Emscripten SDK
|
|
474
|
-
# Install via: https://emscripten.org/docs/getting_started/downloads.html
|
|
475
|
-
|
|
476
|
-
# Clone and build
|
|
477
|
-
git clone https://github.com/CharlesWiltgen/taglib-wasm.git
|
|
478
|
-
cd taglib-wasm
|
|
479
|
-
|
|
480
|
-
# Build Wasm module
|
|
481
|
-
npm run build:wasm
|
|
482
|
-
|
|
483
|
-
# Run tests
|
|
484
|
-
npm test
|
|
485
|
-
```
|
|
486
|
-
|
|
487
|
-
[View full development guide →](CONTRIBUTING.md)
|
|
488
|
-
|
|
489
414
|
## Runtime Compatibility
|
|
490
415
|
|
|
491
416
|
`taglib-wasm` works across all major JavaScript runtimes:
|
|
492
417
|
|
|
493
|
-
| Runtime | Status
|
|
494
|
-
| ---------------------- |
|
|
495
|
-
| **Deno** | Full
|
|
496
|
-
| **Node.js** | Full
|
|
497
|
-
| **Bun** |
|
|
498
|
-
| **Browser** | Full
|
|
499
|
-
| **Cloudflare Workers** |
|
|
500
|
-
| **Electron** |
|
|
418
|
+
| Runtime | Status | Installation | Notes |
|
|
419
|
+
| ---------------------- | ------- | ------------------------- | ------------------------------------------------------------------------------------------------------- |
|
|
420
|
+
| **Deno** | Full | `npm:taglib-wasm` | Native TypeScript |
|
|
421
|
+
| **Node.js** | Full | `npm install taglib-wasm` | TypeScript via tsx |
|
|
422
|
+
| **Bun** | Partial | `bun add taglib-wasm` | Import + init verified; full test suite is Deno-only |
|
|
423
|
+
| **Browser** | Full | Via bundler | Full API support |
|
|
424
|
+
| **Cloudflare Workers** | Partial | `taglib-wasm/workers` | Basic tags only; see [Workers limitations](docs/advanced/cloudflare-workers.md#workers-api-limitations) |
|
|
425
|
+
| **Electron** | Node.js | `npm install taglib-wasm` | Main process; renderer via IPC |
|
|
501
426
|
|
|
502
427
|
## Known Limitations
|
|
503
428
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-handle.d.ts","sourceRoot":"","sources":["../../../../src/runtime/wasi-adapter/file-handle.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EACV,sBAAsB,EACtB,UAAU,EACV,UAAU,EACX,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAGhE,OAAO,KAAK,EAAe,OAAO,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"file-handle.d.ts","sourceRoot":"","sources":["../../../../src/runtime/wasi-adapter/file-handle.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EACV,sBAAsB,EACtB,UAAU,EACV,UAAU,EACX,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAGhE,OAAO,KAAK,EAAe,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAU3D,qBAAa,cAAe,YAAW,UAAU;IAC/C,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAa;IAClC,OAAO,CAAC,QAAQ,CAA2B;IAC3C,OAAO,CAAC,OAAO,CAA4B;IAC3C,OAAO,CAAC,SAAS,CAAS;gBAEd,UAAU,EAAE,UAAU;IAIlC,OAAO,CAAC,iBAAiB;IAQzB,cAAc,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO;IAQ3C,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;IAOpC,OAAO,IAAI,OAAO;IAKlB,IAAI,IAAI,OAAO;IAcf,MAAM,IAAI,UAAU;IASpB,OAAO,CAAC,gBAAgB;IAkCxB,kBAAkB,IAAI,sBAAsB;IAgB5C,SAAS,IAAI,MAAM;IAgBnB,SAAS,IAAI,UAAU;IAKvB,aAAa,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC;IAKzC,aAAa,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,GAAG,IAAI;IAKpD,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM;IAOhC,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAM7C,KAAK,IAAI,OAAO;IAYhB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM;IAK/B,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAK5C,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAQhC,WAAW,IAAI,OAAO,EAAE;IAKxB,WAAW,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,IAAI;IAKtC,UAAU,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;IAOlC,cAAc,IAAI,IAAI;IAKtB,UAAU,IAAI;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,EAAE;IAKlE,UAAU,CACR,OAAO,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,GAC9D,IAAI;IAaP,OAAO,IAAI,IAAI;CAKhB"}
|
|
@@ -4,6 +4,12 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
4
4
|
import { WasmerExecutionError } from "../wasmer-sdk-loader/index.js";
|
|
5
5
|
import { decodeTagData } from "../../msgpack/decoder.js";
|
|
6
6
|
import { readTagsFromWasm, writeTagsToWasm } from "./wasm-io.js";
|
|
7
|
+
const PROPERTY_KEY_MAP = {
|
|
8
|
+
ALBUMARTIST: "albumArtist",
|
|
9
|
+
COMPOSER: "composer",
|
|
10
|
+
DISCNUMBER: "disc",
|
|
11
|
+
BPM: "bpm"
|
|
12
|
+
};
|
|
7
13
|
class WasiFileHandle {
|
|
8
14
|
constructor(wasiModule) {
|
|
9
15
|
__publicField(this, "wasi");
|
|
@@ -89,12 +95,13 @@ class WasiFileHandle {
|
|
|
89
95
|
}
|
|
90
96
|
getAudioProperties() {
|
|
91
97
|
this.checkNotDestroyed();
|
|
98
|
+
const data = this.tagData;
|
|
92
99
|
return {
|
|
93
|
-
lengthInSeconds: () => 0,
|
|
94
|
-
lengthInMilliseconds: () => 0,
|
|
95
|
-
bitrate: () => 0,
|
|
96
|
-
sampleRate: () => 0,
|
|
97
|
-
channels: () => 0,
|
|
100
|
+
lengthInSeconds: () => data?.length ?? 0,
|
|
101
|
+
lengthInMilliseconds: () => data?.lengthMs ?? 0,
|
|
102
|
+
bitrate: () => data?.bitrate ?? 0,
|
|
103
|
+
sampleRate: () => data?.sampleRate ?? 0,
|
|
104
|
+
channels: () => data?.channels ?? 0,
|
|
98
105
|
bitsPerSample: () => 0,
|
|
99
106
|
codec: () => "",
|
|
100
107
|
containerFormat: () => "",
|
|
@@ -124,12 +131,14 @@ class WasiFileHandle {
|
|
|
124
131
|
}
|
|
125
132
|
getProperty(key) {
|
|
126
133
|
this.checkNotDestroyed();
|
|
134
|
+
const mappedKey = PROPERTY_KEY_MAP[key] ?? key;
|
|
127
135
|
const props = this.tagData;
|
|
128
|
-
return props?.[
|
|
136
|
+
return props?.[mappedKey]?.toString() ?? "";
|
|
129
137
|
}
|
|
130
138
|
setProperty(key, value) {
|
|
131
139
|
this.checkNotDestroyed();
|
|
132
|
-
|
|
140
|
+
const mappedKey = PROPERTY_KEY_MAP[key] ?? key;
|
|
141
|
+
this.tagData = { ...this.tagData, [mappedKey]: value };
|
|
133
142
|
}
|
|
134
143
|
isMP4() {
|
|
135
144
|
this.checkNotDestroyed();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wasm-io.d.ts","sourceRoot":"","sources":["../../../../src/runtime/wasi-adapter/wasm-io.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAOhE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAElD,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,UAAU,EAChB,MAAM,EAAE,UAAU,GACjB,UAAU,
|
|
1
|
+
{"version":3,"file":"wasm-io.d.ts","sourceRoot":"","sources":["../../../../src/runtime/wasi-adapter/wasm-io.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAOhE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAElD,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,UAAU,EAChB,MAAM,EAAE,UAAU,GACjB,UAAU,CA2BZ;AAED,wBAAgB,eAAe,CAC7B,IAAI,EAAE,UAAU,EAChB,QAAQ,EAAE,UAAU,EACpB,OAAO,EAAE,WAAW,GACnB,UAAU,GAAG,IAAI,CA8BnB"}
|
|
@@ -54,36 +54,25 @@ function readTagsFromWasm(wasi, buffer) {
|
|
|
54
54
|
const arena = __using(_stack, new WasmArena(wasi));
|
|
55
55
|
const inputBuf = arena.allocBuffer(buffer);
|
|
56
56
|
const outSizePtr = arena.allocUint32();
|
|
57
|
-
const
|
|
57
|
+
const resultPtr = wasi.tl_read_tags(
|
|
58
58
|
0,
|
|
59
59
|
inputBuf.ptr,
|
|
60
60
|
inputBuf.size,
|
|
61
61
|
outSizePtr.ptr
|
|
62
62
|
);
|
|
63
|
-
if (
|
|
63
|
+
if (resultPtr === 0) {
|
|
64
64
|
const errorCode = wasi.tl_get_last_error_code();
|
|
65
65
|
throw new WasmMemoryError(
|
|
66
|
-
`error code ${errorCode}`,
|
|
67
|
-
"read tags
|
|
66
|
+
`error code ${errorCode}. Buffer size: ${buffer.length} bytes`,
|
|
67
|
+
"read tags",
|
|
68
68
|
errorCode
|
|
69
69
|
);
|
|
70
70
|
}
|
|
71
|
-
const
|
|
72
|
-
const
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
inputBuf.size,
|
|
77
|
-
outputBuf.ptr
|
|
78
|
-
);
|
|
79
|
-
if (readResult !== 0) {
|
|
80
|
-
throw new WasmMemoryError(
|
|
81
|
-
"failed to read data into buffer",
|
|
82
|
-
"read tags data",
|
|
83
|
-
readResult
|
|
84
|
-
);
|
|
85
|
-
}
|
|
86
|
-
return new Uint8Array(outputBuf.read().slice());
|
|
71
|
+
const outSize = outSizePtr.readUint32();
|
|
72
|
+
const u8 = new Uint8Array(wasi.memory.buffer);
|
|
73
|
+
const result = new Uint8Array(u8.slice(resultPtr, resultPtr + outSize));
|
|
74
|
+
wasi.free(resultPtr);
|
|
75
|
+
return result;
|
|
87
76
|
} catch (_) {
|
|
88
77
|
var _error = _, _hasError = true;
|
|
89
78
|
} finally {
|
|
@@ -97,32 +86,26 @@ function writeTagsToWasm(wasi, fileData, tagData) {
|
|
|
97
86
|
const tagBytes = encodeTagData(tagData);
|
|
98
87
|
const inputBuf = arena.allocBuffer(fileData);
|
|
99
88
|
const tagBuf = arena.allocBuffer(tagBytes);
|
|
89
|
+
const outBufPtr = arena.allocUint32();
|
|
100
90
|
const outSizePtr = arena.allocUint32();
|
|
101
|
-
const
|
|
91
|
+
const result = wasi.tl_write_tags(
|
|
102
92
|
0,
|
|
103
93
|
inputBuf.ptr,
|
|
104
94
|
inputBuf.size,
|
|
105
95
|
tagBuf.ptr,
|
|
106
96
|
tagBuf.size,
|
|
107
|
-
|
|
97
|
+
outBufPtr.ptr,
|
|
108
98
|
outSizePtr.ptr
|
|
109
99
|
);
|
|
110
|
-
if (
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
tagBuf.ptr,
|
|
120
|
-
tagBuf.size,
|
|
121
|
-
outputBuf.ptr,
|
|
122
|
-
outSizePtr.ptr
|
|
123
|
-
);
|
|
124
|
-
if (writeResult === 0) {
|
|
125
|
-
return new Uint8Array(outputBuf.read().slice());
|
|
100
|
+
if (result === 0) {
|
|
101
|
+
const bufferPtr = outBufPtr.readUint32();
|
|
102
|
+
const size = outSizePtr.readUint32();
|
|
103
|
+
if (bufferPtr && size > 0) {
|
|
104
|
+
const u8 = new Uint8Array(wasi.memory.buffer);
|
|
105
|
+
const output = new Uint8Array(u8.slice(bufferPtr, bufferPtr + size));
|
|
106
|
+
wasi.free(bufferPtr);
|
|
107
|
+
return output;
|
|
108
|
+
}
|
|
126
109
|
}
|
|
127
110
|
return null;
|
|
128
111
|
} catch (_) {
|
|
@@ -6,5 +6,5 @@ import type { WasiModule } from "./types.ts";
|
|
|
6
6
|
* High-level API for reading tags from audio files using WASI
|
|
7
7
|
* Uses RAII pattern with automatic memory cleanup
|
|
8
8
|
*/
|
|
9
|
-
export declare function readTagsWithWasi(audioBuffer: Uint8Array, wasiModule: WasiModule):
|
|
9
|
+
export declare function readTagsWithWasi(audioBuffer: Uint8Array, wasiModule: WasiModule): Uint8Array;
|
|
10
10
|
//# sourceMappingURL=high-level-api.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"high-level-api.d.ts","sourceRoot":"","sources":["../../../../src/runtime/wasmer-sdk-loader/high-level-api.ts"],"names":[],"mappings":"AAAA;;GAEG;AAOH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C;;;GAGG;AACH,
|
|
1
|
+
{"version":3,"file":"high-level-api.d.ts","sourceRoot":"","sources":["../../../../src/runtime/wasmer-sdk-loader/high-level-api.ts"],"names":[],"mappings":"AAAA;;GAEG;AAOH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,WAAW,EAAE,UAAU,EACvB,UAAU,EAAE,UAAU,GACrB,UAAU,CA2BZ"}
|
|
@@ -47,42 +47,31 @@ import {
|
|
|
47
47
|
WasmArena,
|
|
48
48
|
WasmMemoryError
|
|
49
49
|
} from "../wasi-memory.js";
|
|
50
|
-
|
|
50
|
+
function readTagsWithWasi(audioBuffer, wasiModule) {
|
|
51
51
|
var _stack = [];
|
|
52
52
|
try {
|
|
53
53
|
const arena = __using(_stack, new WasmArena(wasiModule));
|
|
54
54
|
const inputBuf = arena.allocBuffer(audioBuffer);
|
|
55
55
|
const outSizePtr = arena.allocUint32();
|
|
56
|
-
const
|
|
56
|
+
const resultPtr = wasiModule.tl_read_tags(
|
|
57
57
|
0,
|
|
58
58
|
inputBuf.ptr,
|
|
59
59
|
inputBuf.size,
|
|
60
60
|
outSizePtr.ptr
|
|
61
61
|
);
|
|
62
|
-
if (
|
|
62
|
+
if (resultPtr === 0) {
|
|
63
63
|
const errorCode = wasiModule.tl_get_last_error_code();
|
|
64
64
|
throw new WasmMemoryError(
|
|
65
|
-
`error code ${errorCode}`,
|
|
66
|
-
"read tags
|
|
65
|
+
`error code ${errorCode}. Buffer size: ${audioBuffer.length} bytes`,
|
|
66
|
+
"read tags",
|
|
67
67
|
errorCode
|
|
68
68
|
);
|
|
69
69
|
}
|
|
70
|
-
const
|
|
71
|
-
const
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
inputBuf.size,
|
|
76
|
-
outputBuf.ptr
|
|
77
|
-
);
|
|
78
|
-
if (readResult !== 0) {
|
|
79
|
-
throw new WasmMemoryError(
|
|
80
|
-
"failed to read data into buffer",
|
|
81
|
-
"read tags data",
|
|
82
|
-
readResult
|
|
83
|
-
);
|
|
84
|
-
}
|
|
85
|
-
return new Uint8Array(outputBuf.read().slice());
|
|
70
|
+
const outSize = outSizePtr.readUint32();
|
|
71
|
+
const u8 = new Uint8Array(wasiModule.memory.buffer);
|
|
72
|
+
const result = new Uint8Array(u8.slice(resultPtr, resultPtr + outSize));
|
|
73
|
+
wasiModule.free(resultPtr);
|
|
74
|
+
return result;
|
|
86
75
|
} catch (_) {
|
|
87
76
|
var _error = _, _hasError = true;
|
|
88
77
|
} finally {
|
package/dist/src/types/tags.d.ts
CHANGED
|
@@ -16,19 +16,19 @@
|
|
|
16
16
|
*/
|
|
17
17
|
export interface Tag {
|
|
18
18
|
/** Track title */
|
|
19
|
-
title?: string;
|
|
19
|
+
readonly title?: string;
|
|
20
20
|
/** Artist name */
|
|
21
|
-
artist?: string;
|
|
21
|
+
readonly artist?: string;
|
|
22
22
|
/** Album name */
|
|
23
|
-
album?: string;
|
|
23
|
+
readonly album?: string;
|
|
24
24
|
/** Comment */
|
|
25
|
-
comment?: string;
|
|
25
|
+
readonly comment?: string;
|
|
26
26
|
/** Genre */
|
|
27
|
-
genre?: string;
|
|
27
|
+
readonly genre?: string;
|
|
28
28
|
/** Year */
|
|
29
|
-
year?: number;
|
|
29
|
+
readonly year?: number;
|
|
30
30
|
/** Track number */
|
|
31
|
-
track?: number;
|
|
31
|
+
readonly track?: number;
|
|
32
32
|
}
|
|
33
33
|
/**
|
|
34
34
|
* Extended metadata with format-agnostic field names.
|
|
@@ -49,51 +49,51 @@ export interface Tag {
|
|
|
49
49
|
*/
|
|
50
50
|
export interface ExtendedTag extends Tag {
|
|
51
51
|
/** AcoustID fingerprint (Chromaprint) */
|
|
52
|
-
acoustidFingerprint?: string;
|
|
52
|
+
readonly acoustidFingerprint?: string;
|
|
53
53
|
/** AcoustID UUID */
|
|
54
|
-
acoustidId?: string;
|
|
54
|
+
readonly acoustidId?: string;
|
|
55
55
|
/** MusicBrainz Track ID */
|
|
56
|
-
musicbrainzTrackId?: string;
|
|
56
|
+
readonly musicbrainzTrackId?: string;
|
|
57
57
|
/** MusicBrainz Release ID */
|
|
58
|
-
musicbrainzReleaseId?: string;
|
|
58
|
+
readonly musicbrainzReleaseId?: string;
|
|
59
59
|
/** MusicBrainz Artist ID */
|
|
60
|
-
musicbrainzArtistId?: string;
|
|
60
|
+
readonly musicbrainzArtistId?: string;
|
|
61
61
|
/** MusicBrainz Release Group ID */
|
|
62
|
-
musicbrainzReleaseGroupId?: string;
|
|
62
|
+
readonly musicbrainzReleaseGroupId?: string;
|
|
63
63
|
/** Album artist (different from track artist) */
|
|
64
|
-
albumArtist?: string;
|
|
64
|
+
readonly albumArtist?: string;
|
|
65
65
|
/** Composer */
|
|
66
|
-
composer?: string;
|
|
66
|
+
readonly composer?: string;
|
|
67
67
|
/** Disc number */
|
|
68
|
-
discNumber?: number;
|
|
68
|
+
readonly discNumber?: number;
|
|
69
69
|
/** Total tracks on album */
|
|
70
|
-
totalTracks?: number;
|
|
70
|
+
readonly totalTracks?: number;
|
|
71
71
|
/** Total discs in release */
|
|
72
|
-
totalDiscs?: number;
|
|
72
|
+
readonly totalDiscs?: number;
|
|
73
73
|
/** BPM (beats per minute) */
|
|
74
|
-
bpm?: number;
|
|
74
|
+
readonly bpm?: number;
|
|
75
75
|
/** Compilation flag */
|
|
76
|
-
compilation?: boolean;
|
|
76
|
+
readonly compilation?: boolean;
|
|
77
77
|
/** Sort title for alphabetization */
|
|
78
|
-
titleSort?: string;
|
|
78
|
+
readonly titleSort?: string;
|
|
79
79
|
/** Sort artist for alphabetization */
|
|
80
|
-
artistSort?: string;
|
|
80
|
+
readonly artistSort?: string;
|
|
81
81
|
/** Sort album for alphabetization */
|
|
82
|
-
albumSort?: string;
|
|
82
|
+
readonly albumSort?: string;
|
|
83
83
|
/** ReplayGain track gain in dB (e.g., "-6.54 dB") */
|
|
84
|
-
replayGainTrackGain?: string;
|
|
84
|
+
readonly replayGainTrackGain?: string;
|
|
85
85
|
/** ReplayGain track peak value (0.0-1.0) */
|
|
86
|
-
replayGainTrackPeak?: string;
|
|
86
|
+
readonly replayGainTrackPeak?: string;
|
|
87
87
|
/** ReplayGain album gain in dB */
|
|
88
|
-
replayGainAlbumGain?: string;
|
|
88
|
+
readonly replayGainAlbumGain?: string;
|
|
89
89
|
/** ReplayGain album peak value (0.0-1.0) */
|
|
90
|
-
replayGainAlbumPeak?: string;
|
|
90
|
+
readonly replayGainAlbumPeak?: string;
|
|
91
91
|
/** Apple Sound Check normalization data (iTunNORM) */
|
|
92
|
-
appleSoundCheck?: string;
|
|
92
|
+
readonly appleSoundCheck?: string;
|
|
93
93
|
/** Embedded pictures/artwork */
|
|
94
|
-
pictures?: import("./pictures.ts").Picture[];
|
|
94
|
+
readonly pictures?: import("./pictures.ts").Picture[];
|
|
95
95
|
/** Popularity/rating data */
|
|
96
|
-
ratings?: Array<{
|
|
96
|
+
readonly ratings?: Array<{
|
|
97
97
|
rating: number;
|
|
98
98
|
email: string;
|
|
99
99
|
counter: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tags.d.ts","sourceRoot":"","sources":["../../../src/types/tags.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,MAAM,WAAW,GAAG;IAClB,kBAAkB;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"tags.d.ts","sourceRoot":"","sources":["../../../src/types/tags.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,MAAM,WAAW,GAAG;IAClB,kBAAkB;IAClB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,kBAAkB;IAClB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,iBAAiB;IACjB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc;IACd,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY;IACZ,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW;IACX,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,mBAAmB;IACnB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,WAAW,WAAY,SAAQ,GAAG;IACtC,yCAAyC;IACzC,QAAQ,CAAC,mBAAmB,CAAC,EAAE,MAAM,CAAC;IACtC,oBAAoB;IACpB,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,2BAA2B;IAC3B,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IACrC,6BAA6B;IAC7B,QAAQ,CAAC,oBAAoB,CAAC,EAAE,MAAM,CAAC;IACvC,4BAA4B;IAC5B,QAAQ,CAAC,mBAAmB,CAAC,EAAE,MAAM,CAAC;IACtC,mCAAmC;IACnC,QAAQ,CAAC,yBAAyB,CAAC,EAAE,MAAM,CAAC;IAC5C,iDAAiD;IACjD,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,eAAe;IACf,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,kBAAkB;IAClB,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,4BAA4B;IAC5B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,6BAA6B;IAC7B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,6BAA6B;IAC7B,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,uBAAuB;IACvB,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC;IAC/B,qCAAqC;IACrC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,sCAAsC;IACtC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,qCAAqC;IACrC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAG5B,qDAAqD;IACrD,QAAQ,CAAC,mBAAmB,CAAC,EAAE,MAAM,CAAC;IACtC,4CAA4C;IAC5C,QAAQ,CAAC,mBAAmB,CAAC,EAAE,MAAM,CAAC;IACtC,kCAAkC;IAClC,QAAQ,CAAC,mBAAmB,CAAC,EAAE,MAAM,CAAC;IACtC,4CAA4C;IAC5C,QAAQ,CAAC,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAGtC,sDAAsD;IACtD,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC,gCAAgC;IAChC,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,eAAe,EAAE,OAAO,EAAE,CAAC;IACtD,6BAA6B;IAC7B,QAAQ,CAAC,OAAO,CAAC,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC9E;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,WAAW;IAC1B,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;CACzB;AAED;;GAEG;AACH,YAAY,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "taglib-wasm",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.9",
|
|
4
4
|
"description": "TagLib-Wasm is the universal tagging library for TypeScript/JavaScript platforms: Browsers, Node.js, Deno, Bun, Cloudflare Workers, and Electron apps",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|