taglib-wasm 1.1.2 → 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.
Files changed (53) hide show
  1. package/README.md +512 -38
  2. package/dist/index.browser.js +275 -3
  3. package/dist/index.d.ts +2 -1
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/index.js +2 -0
  6. package/dist/simple.browser.js +275 -3
  7. package/dist/src/bwf/bext.d.ts +28 -0
  8. package/dist/src/bwf/bext.d.ts.map +1 -0
  9. package/dist/src/bwf/bext.js +126 -0
  10. package/dist/src/msgpack/encoder.d.ts.map +1 -1
  11. package/dist/src/msgpack/encoder.js +9 -1
  12. package/dist/src/runtime/unified-loader/module-loading.js +1 -1
  13. package/dist/src/runtime/wasi-adapter/file-handle.d.ts +7 -1
  14. package/dist/src/runtime/wasi-adapter/file-handle.d.ts.map +1 -1
  15. package/dist/src/runtime/wasi-adapter/file-handle.js +41 -2
  16. package/dist/src/runtime/wasi-host-loader.d.ts +1 -1
  17. package/dist/src/runtime/wasi-host-loader.js +1 -1
  18. package/dist/src/taglib/audio-file-bwf.d.ts +14 -0
  19. package/dist/src/taglib/audio-file-bwf.d.ts.map +1 -0
  20. package/dist/src/taglib/audio-file-bwf.js +41 -0
  21. package/dist/src/taglib/audio-file-impl.d.ts +10 -0
  22. package/dist/src/taglib/audio-file-impl.d.ts.map +1 -1
  23. package/dist/src/taglib/audio-file-impl.js +65 -1
  24. package/dist/src/taglib/audio-file-interface.d.ts +27 -0
  25. package/dist/src/taglib/audio-file-interface.d.ts.map +1 -1
  26. package/dist/src/taglib/embind-adapter.d.ts +2 -0
  27. package/dist/src/taglib/embind-adapter.d.ts.map +1 -1
  28. package/dist/src/taglib/embind-adapter.js +27 -2
  29. package/dist/src/types/audio-formats.d.ts +18 -0
  30. package/dist/src/types/audio-formats.d.ts.map +1 -1
  31. package/dist/src/types/bwf.d.ts +43 -0
  32. package/dist/src/types/bwf.d.ts.map +1 -0
  33. package/dist/src/types/bwf.js +0 -0
  34. package/dist/src/types/chapters.d.ts +47 -0
  35. package/dist/src/types/chapters.d.ts.map +1 -0
  36. package/dist/src/types/chapters.js +0 -0
  37. package/dist/src/types/index.d.ts +2 -0
  38. package/dist/src/types/index.d.ts.map +1 -1
  39. package/dist/src/types/index.js +2 -0
  40. package/dist/src/types/metadata-mappings.d.ts +1 -1
  41. package/dist/src/types/metadata-mappings.d.ts.map +1 -1
  42. package/dist/src/types/tags.d.ts +11 -7
  43. package/dist/src/types/tags.d.ts.map +1 -1
  44. package/dist/src/version.d.ts +1 -1
  45. package/dist/src/version.js +1 -1
  46. package/dist/src/wasm.d.ts +14 -2
  47. package/dist/src/wasm.d.ts.map +1 -1
  48. package/dist/taglib-wasi.wasm +0 -0
  49. package/dist/taglib-web.wasm +0 -0
  50. package/dist/taglib-wrapper.d.ts +0 -2
  51. package/dist/taglib-wrapper.js +1 -1
  52. package/package.json +5 -5
  53. package/dist/taglib_wasi.wasm +0 -0
package/README.md CHANGED
@@ -1,38 +1,512 @@
1
- # TagLib
2
-
3
- [![Build Status](../../actions/workflows/build.yml/badge.svg)](../../actions)
4
-
5
- ### TagLib Audio Metadata Library
6
-
7
- https://taglib.org/
8
-
9
- TagLib is a library for reading and editing the metadata of several
10
- popular audio formats. Currently, it supports various metadata containers such
11
- as [ID3v1][ID3v1], [ID3v2][ID3v2] and [Vorbis][Vorbis] comments for MP3, MP4, AAC,
12
- [Ogg][Ogg], [Opus][Opus], [FLAC][FLAC], [Speex][Speex], [APE][APE], [MPC][MPC], [WavPack][WavPack],
13
- [WAV][WAV], [AIFF][AIFF], [TrueAudio][TrueAudio], [Matroska][Matroska], [WebM][WebM], ASF, WMA, DSF, DFF and
14
- tracker (MOD, XM, S3M, IT) files.
15
-
16
- TagLib is distributed under the [GNU Lesser General Public License][GNU Lesser General Public License]
17
- (LGPL) and [Mozilla Public License][Mozilla Public License] (MPL). Essentially that means that
18
- it may be used in proprietary applications, but if changes are made to
19
- TagLib they must be contributed back to the project. Please review the
20
- licenses if you are considering using TagLib in your project.
21
-
22
- [ID3v1]: https://id3.org/ID3v1
23
- [ID3v2]: https://id3.org/Home
24
- [Vorbis]: https://xiph.org/vorbis/
25
- [Ogg]: https://www.xiph.org/ogg/
26
- [Opus]: https://opus-codec.org/
27
- [FLAC]: https://xiph.org/flac/
28
- [Speex]: https://www.speex.org/
29
- [APE]: https://www.monkeysaudio.com/
30
- [MPC]: https://musepack.net/
31
- [WavPack]: https://www.wavpack.com/
32
- [WAV]: https://www.mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/WAVE.html
33
- [AIFF]: https://www.mmsp.ece.mcgill.ca/Documents/AudioFormats/AIFF/AIFF.html
34
- [TrueAudio]: https://sourceforge.net/projects/tta/
35
- [Matroska]: https://www.matroska.org/
36
- [WebM]: https://www.webmproject.org/
37
- [GNU Lesser General Public License]: https://www.gnu.org/licenses/lgpl.html
38
- [Mozilla Public License]: https://www.mozilla.org/MPL/MPL-1.1.html
1
+ # TagLib-Wasm
2
+
3
+ [![Tests](https://github.com/CharlesWiltgen/TagLib-Wasm/actions/workflows/ci.yml/badge.svg)](https://github.com/CharlesWiltgen/TagLib-Wasm/actions/workflows/ci.yml)
4
+ [![npm version](https://img.shields.io/npm/v/taglib-wasm.svg)](https://www.npmjs.com/package/taglib-wasm)
5
+ [![npm downloads](https://img.shields.io/npm/dm/taglib-wasm.svg)](https://www.npmjs.com/package/taglib-wasm)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/CharlesWiltgen/TagLib-Wasm/blob/main/LICENSE)
7
+ <br>[![Built with](https://img.shields.io/badge/TypeScript-5-3178c6.svg?logo=typescript&logoColor=f5f5f5)](https://www.typescriptlang.org/)
8
+ [![Built with Emscripten](https://img.shields.io/badge/Built%20with-Emscripten-4B9BFF.svg)](https://emscripten.org/)
9
+ [![Built with WebAssembly](https://img.shields.io/badge/Built%20with-WebAssembly-654ff0.svg?logo=webassembly&logoColor=white)](https://webassembly.org/)
10
+ [![Built with TagLib](https://img.shields.io/badge/Built%20with-TagLib-brightgreen.svg)](https://taglib.org/)
11
+ <br>[![Node.js](https://img.shields.io/badge/Node.js-339933?logo=nodedotjs&logoColor=white)](https://nodejs.org/)
12
+ [![Deno](https://img.shields.io/badge/Deno-000000?logo=deno&logoColor=white)](https://deno.land/)
13
+ [![Bun](https://img.shields.io/badge/Bun-000000?logo=bun&logoColor=white)](https://bun.sh/)
14
+ [![Cloudflare Workers](https://img.shields.io/badge/Cloudflare%20Workers-F38020?logo=cloudflare&logoColor=white)](https://workers.cloudflare.com/)
15
+ [![Electron (Node.js)](https://img.shields.io/badge/Electron%20%28Node.js%29-47848F?logo=electron&logoColor=white)](https://www.electronjs.org/)
16
+ [![Browsers](https://img.shields.io/badge/Browsers-E34C26?logo=html5&logoColor=white)](https://html.spec.whatwg.org/multipage/)
17
+
18
+ TagLib-Wasm is the **universal tagging library for TypeScript/JavaScript**
19
+ (TS|JS) platforms: **Node.js**, **Deno**, **Bun**, **Cloudflare Workers**,
20
+ **Electron** (via Node.js), and **browsers**.
21
+
22
+ ## Features
23
+
24
+ - **Local filesystem support** – On Node.js and Deno, WASI enables seek-based
25
+ I/O that reads only headers and tags from disk — not entire files
26
+ - **Automatic runtime optimization** – Auto-selects WASI (server) or Emscripten
27
+ (browser) for optimal performance with no configuration
28
+ - **Full audio format support** – Supports all audio formats supported by TagLib
29
+ - **TypeScript first** – Complete type definitions and modern API
30
+ - **Wide TS/JS runtime support** – Node.js, Deno, Bun, Electron (Node.js),
31
+ Cloudflare Workers, and browsers
32
+ - **Format abstraction** – Handles container format details automatically when
33
+ possible
34
+ - **Rich metadata** – Cover art, ratings, chapters (MP3 `CHAP`, MP4
35
+ QuickTime/Nero), and broadcast metadata (BWF `bext`/iXML for WAV/FLAC)
36
+ - **Zero dependencies** – Self-contained Wasm bundle
37
+ - **Tested** 265 tests across all formats
38
+ - **Two API styles** – Use the "Simple" API (3 functions), or the full "Core"
39
+ API for more advanced applications
40
+ - **Batch folder operations** – Scan directories, process multiple files, find
41
+ duplicates, and export metadata catalogs
42
+
43
+ ## Installation
44
+
45
+ ### Node.js
46
+
47
+ ```bash
48
+ npm install taglib-wasm
49
+ ```
50
+
51
+ > **Note:** Requires Node.js v22.6.0 or higher for WASI and WebAssembly
52
+ > exception handling support. To consume the package as TypeScript source
53
+ > (e.g., via `tsx`), see the
54
+ > [installation guide](https://charleswiltgen.github.io/TagLib-Wasm/guide/installation.html).
55
+
56
+ ### Deno
57
+
58
+ ```typescript
59
+ import { TagLib } from "@charlesw/taglib-wasm";
60
+ ```
61
+
62
+ ### Bun
63
+
64
+ ```bash
65
+ bun add taglib-wasm
66
+ ```
67
+
68
+ ### Electron (Node.js)
69
+
70
+ ```bash
71
+ npm install taglib-wasm
72
+ ```
73
+
74
+ taglib-wasm works in Electron's main process (which is Node.js). For the
75
+ renderer process, expose metadata through IPC:
76
+
77
+ ```typescript
78
+ // Main process
79
+ import { TagLib } from "taglib-wasm";
80
+ ```
81
+
82
+ See [Platform Examples](docs/guide/platform-examples.md#electron) for full IPC
83
+ setup.
84
+
85
+ ### Deno Compiled Binaries (Offline Support)
86
+
87
+ For Deno compiled binaries that need to work offline, you can embed the WASM
88
+ file:
89
+
90
+ ```typescript
91
+ // 1. Prepare your build by copying the WASM file
92
+ import { prepareWasmForEmbedding } from "@charlesw/taglib-wasm";
93
+ await prepareWasmForEmbedding("./taglib.wasm");
94
+
95
+ // 2. In your application, use the helper for automatic handling
96
+ import { initializeForDenoCompile } from "@charlesw/taglib-wasm";
97
+ const taglib = await initializeForDenoCompile();
98
+
99
+ // 3. Compile with the embedded WASM
100
+ // deno compile --allow-read --include taglib.wasm myapp.ts
101
+ ```
102
+
103
+ See the
104
+ [complete Deno compile guide](https://charleswiltgen.github.io/TagLib-Wasm/guide/deno-compile.html)
105
+ for more options including CDN loading.
106
+
107
+ For manual control:
108
+
109
+ ```typescript
110
+ // Load embedded WASM in compiled binaries
111
+ const wasmBinary = await Deno.readFile(
112
+ new URL("./taglib.wasm", import.meta.url),
113
+ );
114
+ const taglib = await TagLib.initialize({ wasmBinary });
115
+ ```
116
+
117
+ ## Quick Start
118
+
119
+ > **Import paths:** Deno uses `@charlesw/taglib-wasm`, npm uses `taglib-wasm`.
120
+ > Examples below use npm paths — substitute accordingly.
121
+
122
+ ### Simple API
123
+
124
+ ```typescript
125
+ import { applyTags, applyTagsToFile, readTags } from "taglib-wasm/simple";
126
+
127
+ // Read tags (string fields are arrays to support multi-value metadata)
128
+ const tags = await readTags("song.mp3");
129
+ console.log(tags.title?.[0], tags.artist?.[0], tags.album?.[0]);
130
+
131
+ // Apply tags and get modified buffer (in-memory)
132
+ const modifiedBuffer = await applyTags("song.mp3", {
133
+ title: "New Title",
134
+ artist: "New Artist",
135
+ album: "New Album",
136
+ });
137
+
138
+ // Or update tags on disk (requires file path)
139
+ await applyTagsToFile("song.mp3", {
140
+ title: "New Title",
141
+ artist: "New Artist",
142
+ });
143
+ ```
144
+
145
+ ### High-Performance Batch Processing
146
+
147
+ ```typescript
148
+ import { readMetadataBatch, readTagsBatch } from "taglib-wasm/simple";
149
+
150
+ // Process multiple files in parallel
151
+ const files = ["track01.mp3", "track02.mp3", /* ... */ "track20.mp3"];
152
+
153
+ // Read just tags (18x faster than sequential)
154
+ const tags = await readTagsBatch(files, { concurrency: 8 });
155
+
156
+ // Read complete metadata including cover art detection (15x faster)
157
+ const metadata = await readMetadataBatch(files, { concurrency: 8 });
158
+
159
+ // Real-world performance:
160
+ // Sequential: ~100 seconds for 20 files
161
+ // Batch: ~5 seconds for 20 files (20x speedup!)
162
+ ```
163
+
164
+ ### Full API
165
+
166
+ The Full API might be a better choice for apps and utilities focused on advanced
167
+ metadata management.
168
+
169
+ ```typescript
170
+ import { TagLib } from "taglib-wasm";
171
+
172
+ // Initialize taglib-wasm
173
+ const taglib = await TagLib.initialize();
174
+
175
+ // Load audio file (automatically cleaned up when scope exits)
176
+ using file = await taglib.open("song.mp3");
177
+
178
+ // Read and update metadata
179
+ const tag = file.tag();
180
+ tag.setTitle("New Title");
181
+ tag.setArtist("New Artist");
182
+
183
+ // Save changes
184
+ file.save();
185
+ ```
186
+
187
+ ### Batch Folder Operations
188
+
189
+ Process entire music collections efficiently:
190
+
191
+ ```typescript
192
+ import { findDuplicates, scanFolder } from "taglib-wasm";
193
+
194
+ // Scan a music library
195
+ const result = await scanFolder("/path/to/music", {
196
+ recursive: true,
197
+ onProgress: (processed, total, file) => {
198
+ console.log(`Processing ${processed}/${total}: ${file}`);
199
+ },
200
+ });
201
+
202
+ console.log(`Found ${result.items.length} audio files`);
203
+ console.log(
204
+ `Successfully processed ${
205
+ result.items.filter((i) => i.status === "ok").length
206
+ } files`,
207
+ );
208
+
209
+ // Process results
210
+ for (const file of result.items) {
211
+ console.log(
212
+ `${file.path}: ${file.tags.artist?.[0]} - ${file.tags.title?.[0]}`,
213
+ );
214
+ console.log(`Duration: ${file.properties?.duration}s`);
215
+ }
216
+
217
+ // Find duplicates
218
+ const duplicates = await findDuplicates("/path/to/music", {
219
+ criteria: ["artist", "title"],
220
+ });
221
+ console.log(`Found ${duplicates.size} groups of duplicates`);
222
+ ```
223
+
224
+ ### Working with Cover Art
225
+
226
+ ```typescript
227
+ import { applyCoverArt, readCoverArt } from "taglib-wasm/simple";
228
+
229
+ // Extract cover art
230
+ const coverData = await readCoverArt("song.mp3");
231
+ if (coverData) {
232
+ await Deno.writeFile("cover.jpg", coverData);
233
+ }
234
+
235
+ // Set new cover art
236
+ const imageData = await Deno.readFile("new-cover.jpg");
237
+ const modifiedBuffer = await applyCoverArt("song.mp3", imageData, "image/jpeg");
238
+ // Save modifiedBuffer to file if needed
239
+ ```
240
+
241
+ ### Working with Ratings
242
+
243
+ ```typescript
244
+ import { RatingUtils, TagLib } from "taglib-wasm";
245
+
246
+ const taglib = await TagLib.initialize();
247
+ using file = await taglib.open("song.mp3");
248
+
249
+ // Read rating (normalized 0.0-1.0)
250
+ const rating = file.getRating();
251
+ if (rating !== undefined) {
252
+ console.log(`Rating: ${RatingUtils.toStars(rating)} stars`);
253
+ }
254
+
255
+ // Set rating (4 out of 5 stars)
256
+ file.setRating(0.8);
257
+ file.save();
258
+ ```
259
+
260
+ See the [Track Ratings Guide](https://charleswiltgen.github.io/TagLib-Wasm/guide/ratings.html)
261
+ for RatingUtils API and cross-format conversion details.
262
+
263
+ ### Working with Chapters
264
+
265
+ ```typescript
266
+ import { TagLib } from "taglib-wasm";
267
+
268
+ const taglib = await TagLib.initialize();
269
+ using file = await taglib.open("audiobook.m4b");
270
+
271
+ // Read chapters (ordered by start time)
272
+ for (const ch of file.getChapters()) {
273
+ console.log(`${ch.startTimeMs}–${ch.endTimeMs} ${ch.title} (${ch.source})`);
274
+ }
275
+
276
+ // Replace all chapters
277
+ file.setChapters([
278
+ { startTimeMs: 0, title: "Intro" },
279
+ { startTimeMs: 95_000, title: "Chapter 1" },
280
+ ]);
281
+ file.save();
282
+ ```
283
+
284
+ Chapters are read from ID3v2 `CHAP` frames (MP3) or, for MP4, a QuickTime
285
+ chapter track (preferred when present) or a Nero `chpl` atom — each chapter
286
+ reports its `source`. `setChapters()` supports MP3 and MP4 only; for MP4,
287
+ `mp4ChapterStyle` (`"quicktime"` default, `"nero"`, or `"both"`) selects which
288
+ structure(s) to write (the Nero atom is capped at 255 chapters). `endTimeMs` is
289
+ explicit for ID3v2 chapters and inferred for MP4 (the next chapter's start, or
290
+ the track duration for the last chapter).
291
+
292
+ ### Broadcast metadata (BWF `bext` / iXML)
293
+
294
+ ```typescript
295
+ import { TagLib } from "taglib-wasm";
296
+
297
+ const taglib = await TagLib.initialize();
298
+ using file = await taglib.open("recording.wav");
299
+
300
+ const bext = file.getBext(); // parsed EBU 3285 bext chunk, or undefined
301
+ console.log(bext?.description, bext?.timeReferenceSamples, bext?.codingHistory);
302
+ console.log(file.getIxml()); // raw iXML string, or undefined
303
+
304
+ file.setBext({
305
+ ...bext!,
306
+ description: "Updated",
307
+ version: 2,
308
+ loudnessValueDb: -16,
309
+ });
310
+ file.setIxml("<BWFXML>…</BWFXML>");
311
+ file.save();
312
+ ```
313
+
314
+ `getBext()` / `setBext()` (WAV and FLAC only) parse and serialize the BWF
315
+ Broadcast Audio Extension chunk; `getBextData()` / `setBextData()` expose the
316
+ raw chunk bytes for vendor extensions or malformed chunks, and `setBextData(null)`
317
+ removes the chunk. iXML is passed through verbatim as a string
318
+ (`setIxml(null)` removes it). The `bext` v2 loudness fields are EBU R128-style
319
+ measurements, distinct from ReplayGain tags. `bwf.decodeBext` / `bwf.encodeBext`
320
+ are also exported for working with raw `bext` bytes directly.
321
+
322
+ ### Container Format and Codec Detection
323
+
324
+ ```typescript
325
+ import { readProperties } from "taglib-wasm/simple";
326
+
327
+ // Get detailed audio properties including container and codec info
328
+ const props = await readProperties("song.m4a");
329
+
330
+ console.log(props.containerFormat); // "MP4" (container format)
331
+ console.log(props.codec); // "AAC" or "ALAC" (compressed media format)
332
+ console.log(props.isLossless); // false for AAC, true for ALAC
333
+ console.log(props.bitsPerSample); // 16 for most formats
334
+ console.log(props.bitrate); // 256 (kbps)
335
+ console.log(props.bitrateMode); // "CBR" | "VBR" | "ABR" | undefined (MP3 only)
336
+ console.log(props.sampleRate); // 44100 (Hz)
337
+ console.log(props.duration); // 180 (duration in seconds)
338
+ ```
339
+
340
+ For Opus files, `audioProperties()` additionally exposes `outputGainDb` — the
341
+ OpusHead output gain in decibels (RFC 7845). Players apply this unconditionally;
342
+ it is separate from, and stacks with, ReplayGain / R128 tags, and is almost
343
+ always `0`.
344
+
345
+ Container format vs Codec:
346
+
347
+ - **Container format** – How audio data and metadata are packaged (e.g., MP4, OGG)
348
+ - **Codec** – How audio is compressed/encoded (e.g., AAC, Vorbis)
349
+
350
+ Supported formats:
351
+
352
+ - **MP4 container** (.mp4, .m4a) – Can contain AAC (lossy) or ALAC (lossless)
353
+ - **OGG container** (.ogg) – Can contain Vorbis, Opus, FLAC, or Speex
354
+ - **MP3** – Both container and codec (lossy)
355
+ - **FLAC** – Both container and codec (lossless)
356
+ - **WAV** – Container for PCM (uncompressed) audio
357
+ - **AIFF** – Container for PCM (uncompressed) audio
358
+
359
+ ## Documentation
360
+
361
+ **[View Full Documentation](https://charleswiltgen.github.io/TagLib-Wasm/)**
362
+
363
+ ### Getting Started
364
+
365
+ - [Installation Guide](https://charleswiltgen.github.io/TagLib-Wasm/guide/installation.html)
366
+ - [Quick Start Tutorial](https://charleswiltgen.github.io/TagLib-Wasm/guide/quick-start.html)
367
+ - [All Examples](https://charleswiltgen.github.io/TagLib-Wasm/guide/examples.html)
368
+
369
+ ### Guides
370
+
371
+ - [API Reference](https://charleswiltgen.github.io/TagLib-Wasm/api/)
372
+ - [Performance Guide](https://charleswiltgen.github.io/TagLib-Wasm/concepts/performance.html)
373
+ - [Album Processing Guide](https://charleswiltgen.github.io/TagLib-Wasm/guide/album-processing.html)
374
+ - [Platform Examples](https://charleswiltgen.github.io/TagLib-Wasm/guide/platform-examples.html)
375
+ - [Working with Cover Art](https://charleswiltgen.github.io/TagLib-Wasm/guide/cover-art.html)
376
+ - [Track Ratings](https://charleswiltgen.github.io/TagLib-Wasm/guide/ratings.html)
377
+ - [Chapters](https://charleswiltgen.github.io/TagLib-Wasm/guide/chapters.html)
378
+ - [Broadcast Metadata (BWF bext / iXML)](https://charleswiltgen.github.io/TagLib-Wasm/guide/broadcast-metadata.html)
379
+ - [Cloudflare Workers](https://charleswiltgen.github.io/TagLib-Wasm/advanced/cloudflare-workers.html)
380
+ - [Error Handling](https://charleswiltgen.github.io/TagLib-Wasm/concepts/error-handling.html)
381
+ - [Contributing](CONTRIBUTING.md)
382
+ - [AI Agent Documentation](AGENTS.md)
383
+
384
+ ## Supported Formats
385
+
386
+ `taglib-wasm` is designed to support all formats supported by TagLib:
387
+
388
+ - **.mp3** – ID3v2 and ID3v1 tags
389
+ - **.m4a/.mp4** – MPEG-4/AAC metadata for AAC and Apple Lossless audio
390
+ - **.flac** – Vorbis comments and audio properties (plus BWF `bext`/iXML)
391
+ - **.ogg** – Ogg Vorbis format with full metadata support
392
+ - **.wav** – INFO chunk metadata, plus BWF `bext` and iXML
393
+ - **Additional formats** – Opus, APE, MPC, WavPack, TrueAudio, AIFF, WMA, and
394
+ more
395
+
396
+ ## Performance and Best Practices
397
+
398
+ ### Batch Processing for Multiple Files
399
+
400
+ When processing multiple audio files, use the optimized batch APIs for better performance:
401
+
402
+ ```typescript
403
+ import { readMetadataBatch, readTagsBatch } from "taglib-wasm/simple";
404
+
405
+ // Processing files one by one (can take 90+ seconds for 19 files)
406
+ for (const file of files) {
407
+ const tags = await readTags(file); // Re-initializes for each file
408
+ }
409
+
410
+ // Batch processing (10-20x faster)
411
+ const result = await readTagsBatch(files, {
412
+ concurrency: 8, // Process 8 files in parallel
413
+ onProgress: (processed, total) => {
414
+ console.log(`${processed}/${total} files processed`);
415
+ },
416
+ });
417
+
418
+ // Read complete metadata in one batch
419
+ const metadata = await readMetadataBatch(files, { concurrency: 8 });
420
+ ```
421
+
422
+ **Performance comparison for 19 audio files:**
423
+
424
+ - Sequential: ~90 seconds (4.7s per file)
425
+ - Batch (concurrency=4): ~8 seconds (11x faster)
426
+ - Batch (concurrency=8): ~5 seconds (18x faster)
427
+
428
+ ### Smart Partial Loading
429
+
430
+ For large audio files (>50MB), enable partial loading to reduce memory usage:
431
+
432
+ ```typescript
433
+ // Enable partial loading for large files
434
+ using file = await taglib.open("large-concert.flac", {
435
+ partial: true,
436
+ maxHeaderSize: 2 * 1024 * 1024, // 2MB header
437
+ maxFooterSize: 256 * 1024, // 256KB footer
438
+ });
439
+
440
+ // Read operations work normally
441
+ const tags = file.tag();
442
+ console.log(tags.title, tags.artist);
443
+
444
+ // Smart save - automatically loads full file when needed
445
+ await file.saveToFile(); // Full file loaded only here
446
+ ```
447
+
448
+ **Performance gains:**
449
+
450
+ - **500MB file**: ~450x less memory usage (1.1MB vs 500MB)
451
+ - **Initial load**: 50x faster (50ms vs 2500ms)
452
+ - **Memory peak**: 3.3MB instead of 1.5GB
453
+
454
+ ### Runtime Optimization Tiers
455
+
456
+ taglib-wasm auto-selects the fastest available backend — no configuration needed:
457
+
458
+ | Environment | Backend | How it works | Performance |
459
+ | ------------------------ | ----------------- | ------------------------------------------------------ | ----------- |
460
+ | **Node.js / Deno / Bun** | WASI (auto) | Seek-based filesystem I/O; reads only headers and tags | Fastest |
461
+ | **Browsers / Workers** | Emscripten (auto) | Entire file loaded into memory as buffer | Baseline |
462
+
463
+ On Node.js, Deno, and Bun you get WASI automatically — nothing to configure.
464
+
465
+ ## Runtime Compatibility
466
+
467
+ `taglib-wasm` works across all major JavaScript runtimes:
468
+
469
+ | Runtime | Status | Installation | Notes |
470
+ | ---------------------- | ------- | ------------------------- | -------------------------------------------------------------------------------------------------- |
471
+ | **Node.js** | Full | `npm install taglib-wasm` | TypeScript via tsx |
472
+ | **Deno** | Full | `npm:taglib-wasm` | Native TypeScript |
473
+ | **Bun** | Partial | `bun add taglib-wasm` | Import + init verified; full test suite is Deno-only |
474
+ | **Browser** | Full | Via bundler | Full API support |
475
+ | **Cloudflare Workers** | Full | `npm install taglib-wasm` | Buffer-based (Emscripten); no filesystem. See [Workers guide](docs/advanced/cloudflare-workers.md) |
476
+ | **Electron** | Node.js | `npm install taglib-wasm` | Main process; renderer via IPC |
477
+
478
+ ## Known Limitations
479
+
480
+ - **Memory Usage (browsers)** – In browser environments, entire files are loaded
481
+ into memory. On Node.js/Deno, WASI reads only headers and tags from disk.
482
+ - **Concurrent Access** – Not thread-safe (JavaScript single-threaded nature
483
+ mitigates this)
484
+
485
+ ## Contributing
486
+
487
+ Contributions are welcome! Please read our [Contributing Guide](CONTRIBUTING.md)
488
+ for details on our code of conduct and the process for submitting pull requests.
489
+
490
+ ## License
491
+
492
+ This project uses dual licensing:
493
+
494
+ - **TypeScript/JavaScript code** – MIT License (see [LICENSE](LICENSE))
495
+ - **WebAssembly binary (taglib.wasm)** – LGPL-2.1-or-later (inherited from
496
+ TagLib)
497
+
498
+ The TagLib library is dual-licensed under LGPL/MPL. When compiled to
499
+ WebAssembly, the resulting binary must comply with LGPL requirements. This
500
+ means:
501
+
502
+ - You can use taglib-wasm in commercial projects
503
+ - If you modify the TagLib C++ code, you must share those changes
504
+ - You must provide a way for users to relink with a modified TagLib
505
+
506
+ For details, see [lib/taglib/COPYING.LGPL](lib/taglib/COPYING.LGPL)
507
+
508
+ ## Acknowledgments
509
+
510
+ - [TagLib](https://taglib.org/) – Excellent audio metadata library
511
+ - [Emscripten](https://emscripten.org/) – WebAssembly compilation toolchain
512
+ - [WASI](https://wasi.dev/) – WebAssembly System Interface for server-side runtimes