taglib-wasm 0.3.3 → 0.3.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/CONTRIBUTING.md +293 -0
- package/NOTICE +34 -0
- package/README.md +122 -511
- package/dist/index.d.ts +132 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +137 -0
- package/dist/index.ts +220 -0
- package/dist/src/constants.d.ts +201 -0
- package/dist/src/constants.d.ts.map +1 -0
- package/dist/src/constants.ts +227 -0
- package/dist/src/errors.d.ts +89 -0
- package/dist/src/errors.d.ts.map +1 -0
- package/dist/src/errors.ts +237 -0
- package/dist/src/file-utils.d.ts +205 -0
- package/dist/src/file-utils.d.ts.map +1 -0
- package/dist/src/file-utils.ts +467 -0
- package/dist/src/file.js +47 -0
- package/dist/src/global.d.ts +10 -0
- package/dist/src/mod.d.ts +9 -0
- package/dist/src/mod.d.ts.map +1 -0
- package/dist/src/mod.ts +19 -0
- package/dist/src/simple.d.ts +347 -0
- package/dist/src/simple.d.ts.map +1 -0
- package/dist/src/simple.ts +659 -0
- package/dist/src/taglib.d.ts +502 -0
- package/dist/src/taglib.d.ts.map +1 -0
- package/dist/src/taglib.ts +959 -0
- package/dist/src/types.d.ts +323 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.ts +538 -0
- package/dist/src/utils/file.d.ts +15 -0
- package/dist/src/utils/file.d.ts.map +1 -0
- package/dist/src/utils/file.ts +82 -0
- package/dist/src/utils/write.d.ts +15 -0
- package/dist/src/utils/write.d.ts.map +1 -0
- package/dist/src/utils/write.ts +61 -0
- package/dist/src/wasm-workers.d.ts +33 -0
- package/dist/src/wasm-workers.d.ts.map +1 -0
- package/dist/src/wasm-workers.ts +176 -0
- package/dist/src/wasm.d.ts +97 -0
- package/dist/src/wasm.d.ts.map +1 -0
- package/dist/src/wasm.ts +133 -0
- package/dist/src/web-utils.d.ts +180 -0
- package/dist/src/web-utils.d.ts.map +1 -0
- package/dist/src/web-utils.ts +347 -0
- package/dist/src/workers.d.ts +219 -0
- package/dist/src/workers.d.ts.map +1 -0
- package/dist/src/workers.ts +465 -0
- package/dist/src/write.js +33 -0
- package/dist/taglib-wrapper.d.ts +5 -0
- package/dist/taglib-wrapper.js +14 -0
- package/dist/taglib.wasm +0 -0
- package/index.ts +100 -7
- package/package.json +40 -16
- package/src/errors.ts +237 -0
- package/src/file-utils.ts +467 -0
- package/src/global.d.ts +10 -0
- package/src/simple.ts +399 -84
- package/src/taglib.ts +522 -28
- package/src/types.ts +1 -1
- package/src/utils/file.ts +82 -0
- package/src/utils/write.ts +61 -0
- package/src/wasm-workers.ts +13 -4
- package/src/wasm.ts +1 -1
- package/src/web-utils.ts +347 -0
- package/src/workers.ts +32 -13
- package/build/taglib.js +0 -2407
- package/build/taglib.wasm +0 -0
package/README.md
CHANGED
|
@@ -1,48 +1,48 @@
|
|
|
1
1
|
# TagLib-Wasm
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/taglib-wasm)
|
|
4
|
+
[](https://www.npmjs.com/package/taglib-wasm)
|
|
5
|
+
[](https://github.com/CharlesWiltgen/taglib-wasm/blob/main/LICENSE)
|
|
6
|
+
[](https://www.typescriptlang.org/)
|
|
7
|
+
[](https://emscripten.org/)
|
|
8
|
+
<br>[](https://deno.land/)
|
|
9
|
+
[](https://nodejs.org/)
|
|
10
|
+
[](https://bun.sh/)
|
|
11
|
+
[](https://workers.cloudflare.com/)
|
|
12
|
+
[](https://www.electronjs.org/)
|
|
13
|
+
[]()
|
|
14
|
+
|
|
15
|
+
**TagLib-Wasm** is the universal tagging library for TypeScript platforms: Deno,
|
|
16
|
+
Node.js, Bun, Cloudflare Workers, Electron, and browsers. It’s the answer to the
|
|
17
|
+
question:
|
|
18
|
+
|
|
19
|
+
> “What if [**TagLib**](https://taglib.org/), but TypeScript?”
|
|
20
|
+
|
|
21
|
+
This project exists because the TypeScipt/JavaScript ecosystem had no
|
|
22
|
+
battle-tested audio tagging library that supports reading and writing music
|
|
23
|
+
metadata to all popular audio formats. It aspires to be a universal solution for
|
|
24
|
+
all **TypeScript**-capable platforms — Deno, Node.js, Bun, Electron, Cloudflare
|
|
25
|
+
Workers, and browsers.
|
|
26
|
+
|
|
27
|
+
TagLib-Wasm stands on the shoulders of giants, including
|
|
14
28
|
[TagLib](https://taglib.org/) itself, [Emscripten](https://emscripten.org/), and
|
|
15
29
|
[Wasm](https://webassembly.org/) ([WebAssembly](https://webassembly.org/)).
|
|
16
|
-
|
|
17
|
-
`taglib-wasm` aspires to be a universal solution for **JavaScript/TypeScript**
|
|
18
|
-
platforms — Deno, Node.js, Bun, web browsers, and Cloudflare Workers. Note: This
|
|
19
|
-
project is a baby, and you’re likely to experience some surprises at this stage
|
|
20
|
-
of its development. I’m extremely motivated to help address them, since I’ll
|
|
21
|
-
also be depending on this project.
|
|
22
|
-
|
|
23
|
-
## 🤔 Why?
|
|
24
|
-
|
|
25
|
-
Because there’s nothing like it. [`mp3tag.js`](https://mp3tag.js.org/) is mature
|
|
26
|
-
and active, but only supports MP3 files and ID3 tags. TagLib was an ideal choice
|
|
27
|
-
from a maturity and capabilities point of view, but wrappers like `node-taglib`
|
|
28
|
-
appeared to be dormant, and I wanted to avoid making users install
|
|
29
|
-
platform-specific dependencies whenever possible.
|
|
30
|
+
TagLib itself is legendary and a core dependency of many music apps.
|
|
30
31
|
|
|
31
32
|
## 🎯 Features
|
|
32
33
|
|
|
33
|
-
- **✅ Universal compatibility** – Works with Deno, Node.js, Bun, web browsers,
|
|
34
|
-
and Cloudflare Workers
|
|
35
|
-
- **✅ TypeScript first** – Complete type definitions and modern API
|
|
36
34
|
- **✅ Full audio format support** – Supports all audio formats supported by
|
|
37
35
|
TagLib
|
|
38
|
-
- **✅
|
|
39
|
-
|
|
36
|
+
- **✅ TypeScript first** – Complete type definitions and modern API
|
|
37
|
+
- **✅ Wide TS/JS runtime support** – Deno, Node.js, Bun, Electron, Cloudflare
|
|
38
|
+
Workers, and browsers
|
|
39
|
+
- **✅ Format abstraction** – Handles container format details automagically
|
|
40
|
+
when possible
|
|
40
41
|
- **✅ Zero dependencies** – Self-contained Wasm bundle
|
|
41
|
-
- **✅ Memory efficient** – In-memory processing without filesystem access
|
|
42
42
|
- **✅ Production ready** – Growing test suite helps ensure safety and
|
|
43
43
|
reliability
|
|
44
|
-
-
|
|
45
|
-
|
|
44
|
+
- **✅ Two API styles** – Use the “Simple” API (3 functions), or the full “Core”
|
|
45
|
+
API for more advanced applications
|
|
46
46
|
|
|
47
47
|
## 📦 Installation
|
|
48
48
|
|
|
@@ -83,307 +83,103 @@ npx tsx your-script.ts
|
|
|
83
83
|
bun add taglib-wasm
|
|
84
84
|
```
|
|
85
85
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
### Simple API
|
|
89
|
-
|
|
90
|
-
This was inspired by [go-taglib](https://github.com/sentriz/go-taglib), a
|
|
91
|
-
similar project for Go created at about the same time.
|
|
92
|
-
|
|
93
|
-
```typescript
|
|
94
|
-
import { readProperties, readTags, writeTags } from "taglib-wasm/simple";
|
|
95
|
-
|
|
96
|
-
// Read tags
|
|
97
|
-
const tags = await readTags("song.mp3");
|
|
98
|
-
console.log(tags.title, tags.artist, tags.album);
|
|
86
|
+
### Electron
|
|
99
87
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
title: "New Title",
|
|
103
|
-
artist: "New Artist",
|
|
104
|
-
album: "New Album",
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
// Read audio properties
|
|
108
|
-
const props = await readProperties("song.mp3");
|
|
109
|
-
console.log(`Duration: ${props.length}s, Bitrate: ${props.bitrate} kbps`);
|
|
88
|
+
```bash
|
|
89
|
+
npm install taglib-wasm
|
|
110
90
|
```
|
|
111
91
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
The Core API provides full control for more advanced applications.
|
|
92
|
+
Works in both main and renderer processes:
|
|
115
93
|
|
|
116
94
|
```typescript
|
|
95
|
+
// Main process
|
|
117
96
|
import { TagLib } from "taglib-wasm";
|
|
118
97
|
|
|
119
|
-
//
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
// Load audio file from buffer
|
|
123
|
-
const audioData = await readFile("song.mp3"); // Node.js/Bun: fs.readFile, Deno: Deno.readFile
|
|
124
|
-
const file = taglib.openFile(audioData);
|
|
125
|
-
|
|
126
|
-
// Read metadata
|
|
127
|
-
const tags = file.tag();
|
|
128
|
-
const props = file.audioProperties();
|
|
129
|
-
|
|
130
|
-
console.log(`Title: ${tags.title}`);
|
|
131
|
-
console.log(`Artist: ${tags.artist}`);
|
|
132
|
-
console.log(`Duration: ${props.length}s`);
|
|
133
|
-
console.log(`Bitrate: ${props.bitrate} kbps`);
|
|
134
|
-
|
|
135
|
-
// Write metadata
|
|
136
|
-
const tag = file.tag();
|
|
137
|
-
tag.setTitle("New Title");
|
|
138
|
-
tag.setArtist("New Artist");
|
|
139
|
-
tag.setAlbum("New Album");
|
|
140
|
-
|
|
141
|
-
// Save changes
|
|
142
|
-
file.save();
|
|
143
|
-
|
|
144
|
-
console.log("Updated tags:", file.tag());
|
|
145
|
-
|
|
146
|
-
// Clean up
|
|
147
|
-
file.dispose();
|
|
98
|
+
// Renderer process (with nodeIntegration: true)
|
|
99
|
+
const { TagLib } = require("taglib-wasm");
|
|
148
100
|
```
|
|
149
101
|
|
|
150
|
-
|
|
102
|
+
## 🚀 Quick Start
|
|
151
103
|
|
|
152
|
-
|
|
104
|
+
### Simple API (Recommended)
|
|
153
105
|
|
|
154
106
|
```typescript
|
|
155
|
-
import {
|
|
107
|
+
import { readTags, updateTags } from "taglib-wasm/simple";
|
|
156
108
|
|
|
157
|
-
//
|
|
158
|
-
const
|
|
159
|
-
|
|
160
|
-
// Read properties
|
|
161
|
-
const title = properties[Tags.Title]?.[0];
|
|
162
|
-
const albumArtist = properties[Tags.AlbumArtist]?.[0];
|
|
163
|
-
const musicBrainzId = properties[Tags.MusicBrainzArtistId]?.[0];
|
|
109
|
+
// Read tags - just one function call!
|
|
110
|
+
const tags = await readTags("song.mp3");
|
|
111
|
+
console.log(tags.title, tags.artist, tags.album);
|
|
164
112
|
|
|
165
|
-
//
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
113
|
+
// Update tags in-place - even simpler!
|
|
114
|
+
await updateTags("song.mp3", {
|
|
115
|
+
title: "New Title",
|
|
116
|
+
artist: "New Artist",
|
|
117
|
+
album: "New Album",
|
|
170
118
|
});
|
|
171
|
-
|
|
172
|
-
// All constants provide IDE autocomplete
|
|
173
|
-
Tags.Title; // → "TITLE"
|
|
174
|
-
Tags.Artist; // → "ARTIST"
|
|
175
|
-
Tags.AlbumArtist; // → "ALBUMARTIST"
|
|
176
|
-
Tags.TrackGain; // → "REPLAYGAIN_TRACK_GAIN"
|
|
177
|
-
// ... and many more
|
|
178
119
|
```
|
|
179
120
|
|
|
180
|
-
|
|
181
|
-
available tags and format-specific mappings.
|
|
182
|
-
|
|
183
|
-
## Platform examples
|
|
184
|
-
|
|
185
|
-
### Deno
|
|
186
|
-
|
|
187
|
-
```typescript
|
|
188
|
-
import { TagLib } from "npm:taglib-wasm";
|
|
189
|
-
|
|
190
|
-
// Initialize taglib-wasm
|
|
191
|
-
const taglib = await TagLib.initialize();
|
|
192
|
-
|
|
193
|
-
// Load audio file from filesystem
|
|
194
|
-
const audioData = await Deno.readFile("song.mp3");
|
|
195
|
-
const file = taglib.openFile(audioData);
|
|
196
|
-
|
|
197
|
-
// Read metadata
|
|
198
|
-
const tags = file.tag();
|
|
199
|
-
const props = file.audioProperties();
|
|
200
|
-
|
|
201
|
-
console.log(`Title: ${tags.title}`);
|
|
202
|
-
console.log(`Artist: ${tags.artist}`);
|
|
203
|
-
console.log(`Duration: ${props.length}s`);
|
|
204
|
-
console.log(`Bitrate: ${props.bitrate} kbps`);
|
|
205
|
-
|
|
206
|
-
// Write metadata
|
|
207
|
-
const tag = file.tag();
|
|
208
|
-
tag.setTitle("New Title");
|
|
209
|
-
tag.setArtist("New Artist");
|
|
210
|
-
tag.setAlbum("New Album");
|
|
211
|
-
|
|
212
|
-
// Save changes
|
|
213
|
-
file.save();
|
|
214
|
-
|
|
215
|
-
console.log("Updated tags:", file.tag());
|
|
216
|
-
|
|
217
|
-
// Clean up
|
|
218
|
-
file.dispose();
|
|
219
|
-
```
|
|
220
|
-
|
|
221
|
-
### Node.js
|
|
121
|
+
### Core API (Advanced)
|
|
222
122
|
|
|
223
123
|
```typescript
|
|
224
124
|
import { TagLib } from "taglib-wasm";
|
|
225
|
-
import { readFile } from "fs/promises";
|
|
226
125
|
|
|
227
126
|
// Initialize taglib-wasm
|
|
228
127
|
const taglib = await TagLib.initialize();
|
|
229
128
|
|
|
230
|
-
// Load audio file
|
|
231
|
-
const
|
|
232
|
-
const file = taglib.openFile(audioData);
|
|
233
|
-
|
|
234
|
-
// Read metadata
|
|
235
|
-
const tags = file.tag();
|
|
236
|
-
const props = file.audioProperties();
|
|
237
|
-
|
|
238
|
-
console.log(`Title: ${tags.title}`);
|
|
239
|
-
console.log(`Artist: ${tags.artist}`);
|
|
240
|
-
console.log(`Duration: ${props.length}s`);
|
|
241
|
-
console.log(`Bitrate: ${props.bitrate} kbps`);
|
|
129
|
+
// Load audio file
|
|
130
|
+
const file = await taglib.open("song.mp3");
|
|
242
131
|
|
|
243
|
-
//
|
|
132
|
+
// Read and update metadata
|
|
244
133
|
const tag = file.tag();
|
|
245
134
|
tag.setTitle("New Title");
|
|
246
135
|
tag.setArtist("New Artist");
|
|
247
|
-
tag.setAlbum("New Album");
|
|
248
136
|
|
|
249
137
|
// Save changes
|
|
250
138
|
file.save();
|
|
251
139
|
|
|
252
|
-
console.log("Updated tags:", file.tag());
|
|
253
|
-
|
|
254
140
|
// Clean up
|
|
255
141
|
file.dispose();
|
|
256
142
|
```
|
|
257
143
|
|
|
258
|
-
###
|
|
144
|
+
### Working with Cover Art
|
|
259
145
|
|
|
260
146
|
```typescript
|
|
261
|
-
import {
|
|
262
|
-
|
|
263
|
-
// Initialize taglib-wasm
|
|
264
|
-
const taglib = await TagLib.initialize();
|
|
265
|
-
|
|
266
|
-
// Load from file system (Bun's native file API)
|
|
267
|
-
const audioData = await Bun.file("song.mp3").arrayBuffer();
|
|
268
|
-
const file = taglib.openFile(new Uint8Array(audioData));
|
|
269
|
-
|
|
270
|
-
// Read metadata
|
|
271
|
-
const tags = file.tag();
|
|
272
|
-
const props = file.audioProperties();
|
|
147
|
+
import { getCoverArt, setCoverArt } from "taglib-wasm/simple";
|
|
273
148
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
// Write metadata
|
|
280
|
-
const tag = file.tag();
|
|
281
|
-
tag.setTitle("New Title");
|
|
282
|
-
tag.setArtist("New Artist");
|
|
283
|
-
tag.setAlbum("New Album");
|
|
284
|
-
|
|
285
|
-
// Save changes
|
|
286
|
-
file.save();
|
|
287
|
-
|
|
288
|
-
console.log("Updated tags:", file.tag());
|
|
149
|
+
// Extract cover art
|
|
150
|
+
const coverData = await getCoverArt("song.mp3");
|
|
151
|
+
if (coverData) {
|
|
152
|
+
await Deno.writeFile("cover.jpg", coverData);
|
|
153
|
+
}
|
|
289
154
|
|
|
290
|
-
//
|
|
291
|
-
|
|
155
|
+
// Set new cover art
|
|
156
|
+
const imageData = await Deno.readFile("new-cover.jpg");
|
|
157
|
+
await setCoverArt("song.mp3", imageData, "image/jpeg");
|
|
292
158
|
```
|
|
293
159
|
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
```typescript
|
|
297
|
-
import { TagLib } from "taglib-wasm";
|
|
160
|
+
## 📚 Documentation
|
|
298
161
|
|
|
299
|
-
|
|
300
|
-
const taglib = await TagLib.initialize();
|
|
162
|
+
**[📖 View Full Documentation](https://charleswiltgen.github.io/taglib-wasm/)**
|
|
301
163
|
|
|
302
|
-
|
|
303
|
-
const fileInput = document.querySelector('input[type="file"]');
|
|
304
|
-
const audioFile = fileInput.files[0];
|
|
305
|
-
const audioData = new Uint8Array(await audioFile.arrayBuffer());
|
|
306
|
-
const file = taglib.openFile(audioData);
|
|
164
|
+
### Getting Started
|
|
307
165
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
166
|
+
- [Installation Guide](https://charleswiltgen.github.io/taglib-wasm/guide/installation.html)
|
|
167
|
+
- [Quick Start Tutorial](https://charleswiltgen.github.io/taglib-wasm/guide/quick-start.html)
|
|
168
|
+
- [All Examples](https://charleswiltgen.github.io/taglib-wasm/guide/examples.html)
|
|
311
169
|
|
|
312
|
-
|
|
313
|
-
console.log(`Artist: ${tags.artist}`);
|
|
314
|
-
console.log(`Duration: ${props.length}s`);
|
|
315
|
-
console.log(`Bitrate: ${props.bitrate} kbps`);
|
|
170
|
+
### Guides
|
|
316
171
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
172
|
+
- [API Reference](https://charleswiltgen.github.io/taglib-wasm/API.html)
|
|
173
|
+
- [Platform Examples](https://charleswiltgen.github.io/taglib-wasm/guide/platform-examples.html)
|
|
174
|
+
- [Working with Cover Art](https://charleswiltgen.github.io/taglib-wasm/guide/cover-art.html)
|
|
175
|
+
- [Cloudflare Workers Setup](https://charleswiltgen.github.io/taglib-wasm/guide/workers-setup.html)
|
|
176
|
+
- [Error Handling](https://charleswiltgen.github.io/taglib-wasm/Error-Handling.html)
|
|
322
177
|
|
|
323
|
-
|
|
324
|
-
file.save();
|
|
178
|
+
### Development
|
|
325
179
|
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
file.dispose();
|
|
330
|
-
```
|
|
331
|
-
|
|
332
|
-
### Cloudflare Workers
|
|
333
|
-
|
|
334
|
-
```typescript
|
|
335
|
-
import { TagLib } from "taglib-wasm/workers";
|
|
336
|
-
|
|
337
|
-
export default {
|
|
338
|
-
async fetch(request: Request): Promise<Response> {
|
|
339
|
-
if (request.method === "POST") {
|
|
340
|
-
try {
|
|
341
|
-
// Initialize taglib-wasm with Workers-specific configuration
|
|
342
|
-
// See docs/Cloudflare-Workers.md for memory configuration details
|
|
343
|
-
const taglib = await TagLib.initialize({
|
|
344
|
-
memory: { initial: 8 * 1024 * 1024 }, // 8MB for Workers
|
|
345
|
-
});
|
|
346
|
-
|
|
347
|
-
// Get audio data from request
|
|
348
|
-
const audioData = new Uint8Array(await request.arrayBuffer());
|
|
349
|
-
const file = taglib.openFile(audioData);
|
|
350
|
-
|
|
351
|
-
// Read metadata
|
|
352
|
-
const tags = file.tag();
|
|
353
|
-
const props = file.audioProperties();
|
|
354
|
-
|
|
355
|
-
// Extract metadata
|
|
356
|
-
const metadata = {
|
|
357
|
-
title: tags.title,
|
|
358
|
-
artist: tags.artist,
|
|
359
|
-
album: tags.album,
|
|
360
|
-
year: tags.year,
|
|
361
|
-
genre: tags.genre,
|
|
362
|
-
duration: props.length,
|
|
363
|
-
bitrate: props.bitrate,
|
|
364
|
-
format: file.format(),
|
|
365
|
-
};
|
|
366
|
-
|
|
367
|
-
// Clean up
|
|
368
|
-
file.dispose();
|
|
369
|
-
|
|
370
|
-
return Response.json({
|
|
371
|
-
success: true,
|
|
372
|
-
metadata,
|
|
373
|
-
fileSize: audioData.length,
|
|
374
|
-
});
|
|
375
|
-
} catch (error) {
|
|
376
|
-
return Response.json({
|
|
377
|
-
error: "Failed to process audio file",
|
|
378
|
-
message: (error as Error).message,
|
|
379
|
-
}, { status: 500 });
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
return new Response("Send POST request with audio file", { status: 400 });
|
|
384
|
-
},
|
|
385
|
-
};
|
|
386
|
-
```
|
|
180
|
+
- [Testing Guide](https://charleswiltgen.github.io/taglib-wasm/development/testing.html)
|
|
181
|
+
- [Future Improvements](https://charleswiltgen.github.io/taglib-wasm/development/improvements.html)
|
|
182
|
+
- [Contributing](https://charleswiltgen.github.io/taglib-wasm/CONTRIBUTING.html)
|
|
387
183
|
|
|
388
184
|
## 📋 Supported Formats
|
|
389
185
|
|
|
@@ -396,81 +192,33 @@ export default {
|
|
|
396
192
|
- ✅ **.wav** – INFO chunk metadata
|
|
397
193
|
- ✅ **Legacy formats** – Opus, APE, MPC, WavPack, TrueAudio, and more
|
|
398
194
|
|
|
399
|
-
## 🎯
|
|
195
|
+
## 🎯 Key Features
|
|
400
196
|
|
|
401
|
-
|
|
402
|
-
beyond the basic tags. This allows you to read and write format-specific fields
|
|
403
|
-
and custom metadata.
|
|
197
|
+
### Extended Metadata Support
|
|
404
198
|
|
|
405
|
-
|
|
199
|
+
Beyond basic tags, taglib-wasm supports extended metadata:
|
|
406
200
|
|
|
407
201
|
```typescript
|
|
408
202
|
import { Tags } from "taglib-wasm";
|
|
409
203
|
|
|
410
|
-
//
|
|
204
|
+
// AcoustID fingerprints
|
|
411
205
|
file.setProperty(
|
|
412
206
|
Tags.AcoustidFingerprint,
|
|
413
207
|
"AQADtMmybfGO8NCNEESLnzHyXNOHeHnG...",
|
|
414
208
|
);
|
|
415
|
-
file.setProperty(Tags.AcoustidId, "e7359e88-f1f7-41ed-b9f6-16e58e906997");
|
|
416
|
-
|
|
417
|
-
// Or using string property names
|
|
418
|
-
file.setProperty("ACOUSTID_FINGERPRINT", "AQADtMmybfGO8NCNEESLnzHyXNOHeHnG...");
|
|
419
209
|
|
|
420
|
-
//
|
|
421
|
-
// Use file.properties() to see all available properties
|
|
422
|
-
file.save(); // Don't forget to save!
|
|
423
|
-
```
|
|
424
|
-
|
|
425
|
-
### MusicBrainz example
|
|
426
|
-
|
|
427
|
-
```typescript
|
|
428
|
-
// MusicBrainz metadata using PropertyMap with tag constants
|
|
210
|
+
// MusicBrainz IDs
|
|
429
211
|
file.setProperty(
|
|
430
212
|
Tags.MusicBrainzTrackId,
|
|
431
213
|
"f4d1b6b8-8c1e-4d9a-9f2a-1234567890ab",
|
|
432
214
|
);
|
|
433
|
-
file.setProperty(
|
|
434
|
-
Tags.MusicBrainzAlbumId,
|
|
435
|
-
"a1b2c3d4-e5f6-7890-abcd-ef1234567890",
|
|
436
|
-
);
|
|
437
|
-
file.setProperty(
|
|
438
|
-
Tags.MusicBrainzArtistId,
|
|
439
|
-
"12345678-90ab-cdef-1234-567890abcdef",
|
|
440
|
-
);
|
|
441
|
-
```
|
|
442
|
-
|
|
443
|
-
### Volume example
|
|
444
215
|
|
|
445
|
-
|
|
446
|
-
// ReplayGain volume normalization with tag constants
|
|
216
|
+
// ReplayGain volume normalization
|
|
447
217
|
file.setProperty(Tags.TrackGain, "-6.54 dB");
|
|
448
218
|
file.setProperty(Tags.TrackPeak, "0.987654");
|
|
449
|
-
file.setProperty(Tags.AlbumGain, "-8.12 dB");
|
|
450
|
-
file.setProperty(Tags.AlbumPeak, "0.995432");
|
|
451
219
|
```
|
|
452
220
|
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
```typescript
|
|
456
|
-
// Using PropertyMap to set multiple properties at once
|
|
457
|
-
const properties = file.properties(); // Get current properties
|
|
458
|
-
|
|
459
|
-
// Set extended metadata with tag constants
|
|
460
|
-
file.setProperties({
|
|
461
|
-
[Tags.AlbumArtist]: ["Various Artists"],
|
|
462
|
-
[Tags.Composer]: ["Composer Name"],
|
|
463
|
-
[Tags.Bpm]: ["120"],
|
|
464
|
-
[Tags.Compilation]: ["1"],
|
|
465
|
-
[Tags.DiscNumber]: ["1"],
|
|
466
|
-
[Tags.TrackTotal]: ["12"],
|
|
467
|
-
// Note: Property keys vary by format
|
|
468
|
-
});
|
|
469
|
-
|
|
470
|
-
// Or set individual properties
|
|
471
|
-
file.setProperty(Tags.AlbumArtist, "Various Artists");
|
|
472
|
-
file.setProperty(Tags.Composer, "Composer Name");
|
|
473
|
-
```
|
|
221
|
+
[View all supported tag constants →](https://charleswiltgen.github.io/taglib-wasm/Tag-Name-Constants.html)
|
|
474
222
|
|
|
475
223
|
## 🏗️ Development
|
|
476
224
|
|
|
@@ -481,199 +229,62 @@ file.setProperty(Tags.Composer, "Composer Name");
|
|
|
481
229
|
# Install via: https://emscripten.org/docs/getting_started/downloads.html
|
|
482
230
|
|
|
483
231
|
# Clone and build
|
|
484
|
-
git clone
|
|
232
|
+
git clone https://github.com/CharlesWiltgen/taglib-wasm.git
|
|
485
233
|
cd taglib-wasm
|
|
486
234
|
|
|
487
235
|
# Build Wasm module
|
|
488
|
-
|
|
236
|
+
npm run build:wasm
|
|
489
237
|
|
|
490
238
|
# Run tests
|
|
491
|
-
deno task test
|
|
492
|
-
```
|
|
493
|
-
|
|
494
|
-
### Project Structure
|
|
495
|
-
|
|
496
|
-
```text
|
|
497
|
-
src/
|
|
498
|
-
├── mod.ts # Main module exports
|
|
499
|
-
├── taglib.ts # Core TagLib and AudioFile classes
|
|
500
|
-
├── types.ts # TypeScript type definitions
|
|
501
|
-
└── wasm.ts # Wasm module interface and utilities
|
|
502
|
-
|
|
503
|
-
build/
|
|
504
|
-
├── build-wasm.sh # Complete build script with C++ wrapper
|
|
505
|
-
├── taglib.js # Generated Emscripten JavaScript
|
|
506
|
-
└── taglib.wasm # Compiled WebAssembly module
|
|
507
|
-
|
|
508
|
-
tests/ # Test suite and sample audio files
|
|
509
|
-
examples/ # Usage examples for different runtimes
|
|
510
|
-
├── deno/ # Deno-specific examples
|
|
511
|
-
├── bun/ # Bun-specific examples
|
|
512
|
-
├── basic-usage.ts # General usage example
|
|
513
|
-
└── *.ts # Advanced feature examples
|
|
514
|
-
```
|
|
515
|
-
|
|
516
|
-
## 🧪 Testing
|
|
517
|
-
|
|
518
|
-
Comprehensive test suite validates all functionality:
|
|
519
|
-
|
|
520
|
-
```bash
|
|
521
|
-
# Run with Deno
|
|
522
|
-
deno run --allow-read test-systematic.ts
|
|
523
|
-
|
|
524
|
-
# Run with Bun
|
|
525
|
-
bun run test-systematic.ts
|
|
526
|
-
|
|
527
|
-
# Run with Node.js
|
|
528
239
|
npm test
|
|
529
|
-
|
|
530
|
-
# Results: All formats working ✅ across all runtimes
|
|
531
|
-
# ✅ WAV - INFO chunk metadata support
|
|
532
|
-
# ✅ MP3 - ID3v1/v2 tag support
|
|
533
|
-
# ✅ FLAC - Vorbis comments and properties
|
|
534
|
-
# ✅ OGG - Vorbis comments
|
|
535
|
-
# ✅ M4A - MPEG-4 (AAC and Apple Lossless) metadata
|
|
536
|
-
```
|
|
537
|
-
|
|
538
|
-
## 🔧 Technical Implementation
|
|
539
|
-
|
|
540
|
-
### Key architecture decisions
|
|
541
|
-
|
|
542
|
-
1. **Memory Management**: Uses Emscripten's `allocate()` for reliable JS↔Wasm
|
|
543
|
-
data transfer
|
|
544
|
-
2. **Buffer-Based Processing**: `TagLib::ByteVectorStream` enables in-memory
|
|
545
|
-
file processing
|
|
546
|
-
3. **C++ Wrapper**: Custom C functions bridge TagLib's C++ API to Wasm exports
|
|
547
|
-
4. **Type Safety**: Complete TypeScript definitions for all audio formats and
|
|
548
|
-
metadata
|
|
549
|
-
|
|
550
|
-
### Critical implementation details
|
|
551
|
-
|
|
552
|
-
- **ByteVectorStream**: Enables processing audio files from memory buffers
|
|
553
|
-
without filesystem
|
|
554
|
-
- **ID-based Object Management**: C++ objects managed via integer IDs for memory
|
|
555
|
-
safety
|
|
556
|
-
- **Emscripten allocate()**: Ensures proper memory synchronization between JS
|
|
557
|
-
and Wasm
|
|
558
|
-
- **UTF-8 String Handling**: Proper encoding for international metadata
|
|
559
|
-
|
|
560
|
-
## 📚 API Reference
|
|
561
|
-
|
|
562
|
-
### TagLib class
|
|
563
|
-
|
|
564
|
-
```typescript
|
|
565
|
-
class TagLib {
|
|
566
|
-
static async initialize(config?: TagLibConfig): Promise<TagLib>;
|
|
567
|
-
openFile(buffer: Uint8Array): AudioFile;
|
|
568
|
-
getModule(): TagLibModule;
|
|
569
|
-
}
|
|
570
|
-
```
|
|
571
|
-
|
|
572
|
-
### AudioFile class
|
|
573
|
-
|
|
574
|
-
```typescript
|
|
575
|
-
class AudioFile {
|
|
576
|
-
// Validation
|
|
577
|
-
isValid(): boolean;
|
|
578
|
-
getFormat(): string;
|
|
579
|
-
|
|
580
|
-
// Properties
|
|
581
|
-
audioProperties(): AudioProperties;
|
|
582
|
-
|
|
583
|
-
// Tag Access (returns Tag object with getters and setters)
|
|
584
|
-
tag(): Tag;
|
|
585
|
-
|
|
586
|
-
// PropertyMap API for extended metadata
|
|
587
|
-
properties(): PropertyMap;
|
|
588
|
-
setProperties(properties: PropertyMap): void;
|
|
589
|
-
getProperty(key: string): string | undefined;
|
|
590
|
-
setProperty(key: string, value: string): void;
|
|
591
|
-
|
|
592
|
-
// MP4-specific methods
|
|
593
|
-
isMP4(): boolean;
|
|
594
|
-
getMP4Item(key: string): string | undefined;
|
|
595
|
-
setMP4Item(key: string, value: string): void;
|
|
596
|
-
removeMP4Item(key: string): void;
|
|
597
|
-
|
|
598
|
-
// File Operations
|
|
599
|
-
save(): boolean;
|
|
600
|
-
getFileBuffer(): Uint8Array;
|
|
601
|
-
dispose(): void;
|
|
602
|
-
}
|
|
603
|
-
```
|
|
604
|
-
|
|
605
|
-
### Tag interface
|
|
606
|
-
|
|
607
|
-
```typescript
|
|
608
|
-
interface Tag {
|
|
609
|
-
// Basic metadata (getters)
|
|
610
|
-
title: string;
|
|
611
|
-
artist: string;
|
|
612
|
-
album: string;
|
|
613
|
-
comment: string;
|
|
614
|
-
genre: string;
|
|
615
|
-
year: number;
|
|
616
|
-
track: number;
|
|
617
|
-
|
|
618
|
-
// Setters
|
|
619
|
-
setTitle(value: string): void;
|
|
620
|
-
setArtist(value: string): void;
|
|
621
|
-
setAlbum(value: string): void;
|
|
622
|
-
setComment(value: string): void;
|
|
623
|
-
setGenre(value: string): void;
|
|
624
|
-
setYear(value: number): void;
|
|
625
|
-
setTrack(value: number): void;
|
|
626
|
-
}
|
|
627
240
|
```
|
|
628
241
|
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
```typescript
|
|
632
|
-
type PropertyMap = { [key: string]: string[] };
|
|
633
|
-
```
|
|
634
|
-
|
|
635
|
-
## 📖 Additional Documentation
|
|
636
|
-
|
|
637
|
-
- [**Tag Name Constants**](docs/Tag-Name-Constants.md) - Comprehensive reference
|
|
638
|
-
for standard tag names and cross-format mapping
|
|
639
|
-
- [**Automatic Tag Mapping**](docs/Automatic-Tag-Mapping.md) - How taglib-wasm
|
|
640
|
-
handles format-specific tag differences
|
|
641
|
-
- [**Implementation Details**](docs/Implementation.md) - Technical details about
|
|
642
|
-
the Wasm implementation
|
|
643
|
-
- [**Runtime Compatibility**](docs/Runtime-Compatibility.md) - Platform-specific
|
|
644
|
-
setup and considerations
|
|
242
|
+
[View full development guide →](CONTRIBUTING.md)
|
|
645
243
|
|
|
646
244
|
## 🌐 Runtime Compatibility
|
|
647
245
|
|
|
648
246
|
`taglib-wasm` works across all major JavaScript runtimes:
|
|
649
247
|
|
|
650
|
-
| Runtime
|
|
651
|
-
|
|
|
652
|
-
| **Deno**
|
|
653
|
-
| **
|
|
654
|
-
| **
|
|
655
|
-
| **Browser**
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
detailed runtime information**
|
|
248
|
+
| Runtime | Status | Installation | Notes |
|
|
249
|
+
| ---------------------- | ------- | ------------------------- | ------------------------- |
|
|
250
|
+
| **Deno** | ✅ Full | `npm:taglib-wasm` | Native TypeScript |
|
|
251
|
+
| **Node.js** | ✅ Full | `npm install taglib-wasm` | TypeScript via tsx |
|
|
252
|
+
| **Bun** | ✅ Full | `bun add taglib-wasm` | Native TypeScript |
|
|
253
|
+
| **Browser** | ✅ Full | Via bundler | Full API support |
|
|
254
|
+
| **Cloudflare Workers** | ✅ Full | `taglib-wasm/workers` | Memory-optimized build |
|
|
255
|
+
| **Electron** | ✅ Full | `npm install taglib-wasm` | Main & renderer processes |
|
|
659
256
|
|
|
660
257
|
## 🚧 Known Limitations
|
|
661
258
|
|
|
662
|
-
- **
|
|
663
|
-
|
|
664
|
-
- **
|
|
665
|
-
|
|
666
|
-
- **
|
|
259
|
+
- **Memory Usage** – Entire file must be loaded into memory (may be an issue for
|
|
260
|
+
very large files)
|
|
261
|
+
- **Concurrent Access** – Not thread-safe (JavaScript single-threaded nature
|
|
262
|
+
mitigates this)
|
|
263
|
+
- **Cloudflare Workers** – Limited to 128MB memory per request; files larger
|
|
264
|
+
than ~100MB may fail
|
|
667
265
|
|
|
668
266
|
## 🤝 Contributing
|
|
669
267
|
|
|
670
|
-
Contributions welcome!
|
|
268
|
+
Contributions are welcome! Please read our [Contributing Guide](CONTRIBUTING.md)
|
|
269
|
+
for details on our code of conduct and the process for submitting pull requests.
|
|
671
270
|
|
|
672
271
|
## 📄 License
|
|
673
272
|
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
273
|
+
This project uses dual licensing:
|
|
274
|
+
|
|
275
|
+
- **TypeScript/JavaScript code** – MIT License (see [LICENSE](LICENSE))
|
|
276
|
+
- **WebAssembly binary (taglib.wasm)** – LGPL-2.1-or-later (inherited from
|
|
277
|
+
TagLib)
|
|
278
|
+
|
|
279
|
+
The TagLib library is dual-licensed under LGPL/MPL. When compiled to
|
|
280
|
+
WebAssembly, the resulting binary must comply with LGPL requirements. This
|
|
281
|
+
means:
|
|
282
|
+
|
|
283
|
+
- You can use taglib-wasm in commercial projects
|
|
284
|
+
- If you modify the TagLib C++ code, you must share those changes
|
|
285
|
+
- You must provide a way for users to relink with a modified TagLib
|
|
286
|
+
|
|
287
|
+
For details, see [lib/taglib/COPYING.LGPL](lib/taglib/COPYING.LGPL)
|
|
677
288
|
|
|
678
289
|
## 🙏 Acknowledgments
|
|
679
290
|
|