taglib-wasm 0.3.1 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +170 -94
- package/index.ts +2 -0
- package/package.json +4 -1
- package/src/constants.ts +227 -0
- package/src/types.ts +5 -0
package/README.md
CHANGED
|
@@ -1,25 +1,45 @@
|
|
|
1
1
|
# taglib-wasm
|
|
2
2
|
|
|
3
|
-
This is the Wasm version of [**TagLib**](https://taglib.org/), the most robust,
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
This is the Wasm version of [**TagLib**](https://taglib.org/), the most robust,
|
|
4
|
+
de-facto standard for reading and editing metadata tags (Title, Album, Artist,
|
|
5
|
+
etc.) in all popular audio formats. `taglib-wasm` exists because the
|
|
6
|
+
JavaScipt/TypeScipt ecosystem had no battle-tested audio tagging library that
|
|
7
|
+
supports reading and writing music metadata to all popular audio formats — until
|
|
8
|
+
now!
|
|
9
|
+
|
|
10
|
+
`taglib-wasm` stands on the shoulders of giants, including
|
|
11
|
+
[TagLib](https://taglib.org/) itself, [Emscripten](https://emscripten.org/), and
|
|
12
|
+
[Wasm](https://webassembly.org/) ([WebAssembly](https://webassembly.org/)).
|
|
13
|
+
|
|
14
|
+
`taglib-wasm` aspires to be a universal solution for **JavaScript/TypeScript**
|
|
15
|
+
platforms — Deno, Node.js, Bun, web browsers, and Cloudflare Workers. Note: This
|
|
16
|
+
project is a baby, and you’re likely to experience some surprises at this stage
|
|
17
|
+
of its development. I’m extremely motivated to help address them, since I’ll
|
|
18
|
+
also be depending on this project.
|
|
8
19
|
|
|
9
20
|
## 🤔 Why?
|
|
10
21
|
|
|
11
|
-
Because there’s nothing like it. [`mp3tag.js`](https://mp3tag.js.org/) is mature
|
|
22
|
+
Because there’s nothing like it. [`mp3tag.js`](https://mp3tag.js.org/) is mature
|
|
23
|
+
and active, but only supports MP3 files and ID3 tags. TagLib was an ideal choice
|
|
24
|
+
from a maturity and capabilities point of view, but wrappers like `node-taglib`
|
|
25
|
+
appeared to be dormant, and I wanted to avoid making users install
|
|
26
|
+
platform-specific dependencies whenever possible.
|
|
12
27
|
|
|
13
28
|
## 🎯 Features
|
|
14
29
|
|
|
15
|
-
- **✅ Universal compatibility** – Works with Deno, Node.js, Bun, web browsers,
|
|
30
|
+
- **✅ Universal compatibility** – Works with Deno, Node.js, Bun, web browsers,
|
|
31
|
+
and Cloudflare Workers
|
|
16
32
|
- **✅ TypeScript first** – Complete type definitions and modern API
|
|
17
|
-
- **✅ Full audio format support** – Supports all audio formats supported by
|
|
18
|
-
|
|
33
|
+
- **✅ Full audio format support** – Supports all audio formats supported by
|
|
34
|
+
TagLib
|
|
35
|
+
- **✅ Format abstraction** – `taglib-wasm` deals with how tags are read
|
|
36
|
+
from/written to in different file formats
|
|
19
37
|
- **✅ Zero dependencies** – Self-contained WASM bundle
|
|
20
38
|
- **✅ Memory efficient** – In-memory processing without filesystem access
|
|
21
|
-
- **✅ Production ready** – Growing test suite helps ensure safety and
|
|
22
|
-
|
|
39
|
+
- **✅ Production ready** – Growing test suite helps ensure safety and
|
|
40
|
+
reliability
|
|
41
|
+
- **🆕 Two API styles** – Choose between Simple (3 functions) or Core (full
|
|
42
|
+
control) APIs
|
|
23
43
|
|
|
24
44
|
## 📦 Installation
|
|
25
45
|
|
|
@@ -35,7 +55,19 @@ import { TagLib } from "npm:taglib-wasm";
|
|
|
35
55
|
npm install taglib-wasm
|
|
36
56
|
```
|
|
37
57
|
|
|
38
|
-
|
|
58
|
+
The package uses TypeScript. You have two options:
|
|
59
|
+
|
|
60
|
+
#### Option 1: Use Node’s native TypeScript support
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
# Node.js 22.6.0+ with experimental flag
|
|
64
|
+
node --experimental-strip-types your-script.ts
|
|
65
|
+
|
|
66
|
+
# Node.js 23.6.0+ (no flag needed)
|
|
67
|
+
node your-script.ts
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
#### Option 2: TypeScript loader (recommended for production)
|
|
39
71
|
|
|
40
72
|
```bash
|
|
41
73
|
npm install --save-dev tsx
|
|
@@ -52,16 +84,17 @@ bun add taglib-wasm
|
|
|
52
84
|
|
|
53
85
|
### Simple API
|
|
54
86
|
|
|
55
|
-
|
|
87
|
+
This was inspired by [go-taglib](https://github.com/sentriz/go-taglib), a
|
|
88
|
+
similar project for Go created at about the same time.
|
|
56
89
|
|
|
57
90
|
```typescript
|
|
58
91
|
import { readProperties, readTags, writeTags } from "taglib-wasm/simple";
|
|
59
92
|
|
|
60
|
-
// Read tags
|
|
93
|
+
// Read tags
|
|
61
94
|
const tags = await readTags("song.mp3");
|
|
62
95
|
console.log(tags.title, tags.artist, tags.album);
|
|
63
96
|
|
|
64
|
-
// Write tags
|
|
97
|
+
// Write tags
|
|
65
98
|
await writeTags("song.mp3", {
|
|
66
99
|
title: "New Title",
|
|
67
100
|
artist: "New Artist",
|
|
@@ -75,7 +108,7 @@ console.log(`Duration: ${props.length}s, Bitrate: ${props.bitrate} kbps`);
|
|
|
75
108
|
|
|
76
109
|
### Core API
|
|
77
110
|
|
|
78
|
-
|
|
111
|
+
The Core API provides full control for more advanced applications.
|
|
79
112
|
|
|
80
113
|
```typescript
|
|
81
114
|
import { TagLib } from "taglib-wasm";
|
|
@@ -111,19 +144,50 @@ console.log("Updated tags:", file.tag());
|
|
|
111
144
|
file.dispose();
|
|
112
145
|
```
|
|
113
146
|
|
|
147
|
+
### Tag Constants
|
|
148
|
+
|
|
149
|
+
taglib-wasm provides type-safe tag constants with IDE autocomplete:
|
|
150
|
+
|
|
151
|
+
```typescript
|
|
152
|
+
import { Tags } from "taglib-wasm";
|
|
153
|
+
|
|
154
|
+
// Use constants for better type safety and autocomplete
|
|
155
|
+
const properties = file.properties();
|
|
156
|
+
|
|
157
|
+
// Read properties
|
|
158
|
+
const title = properties[Tags.Title]?.[0];
|
|
159
|
+
const albumArtist = properties[Tags.AlbumArtist]?.[0];
|
|
160
|
+
const musicBrainzId = properties[Tags.MusicBrainzArtistId]?.[0];
|
|
161
|
+
|
|
162
|
+
// Write properties
|
|
163
|
+
file.setProperties({
|
|
164
|
+
[Tags.Title]: ["My Song"],
|
|
165
|
+
[Tags.AlbumArtist]: ["Various Artists"],
|
|
166
|
+
[Tags.Bpm]: ["128"]
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
// All constants provide IDE autocomplete
|
|
170
|
+
Tags.Title // → "TITLE"
|
|
171
|
+
Tags.Artist // → "ARTIST"
|
|
172
|
+
Tags.AlbumArtist // → "ALBUMARTIST"
|
|
173
|
+
Tags.TrackGain // → "REPLAYGAIN_TRACK_GAIN"
|
|
174
|
+
// ... and many more
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
See [Tag Name Constants](docs/Tag-Name-Constants.md) for the complete list of available tags and format-specific mappings.
|
|
178
|
+
|
|
114
179
|
## Platform examples
|
|
115
180
|
|
|
116
|
-
###
|
|
181
|
+
### Deno
|
|
117
182
|
|
|
118
183
|
```typescript
|
|
119
|
-
import { TagLib } from "taglib-wasm";
|
|
120
|
-
import { readFile } from "fs/promises";
|
|
184
|
+
import { TagLib } from "npm:taglib-wasm";
|
|
121
185
|
|
|
122
186
|
// Initialize taglib-wasm
|
|
123
187
|
const taglib = await TagLib.initialize();
|
|
124
188
|
|
|
125
189
|
// Load audio file from filesystem
|
|
126
|
-
const audioData = await readFile("song.mp3");
|
|
190
|
+
const audioData = await Deno.readFile("song.mp3");
|
|
127
191
|
const file = taglib.openFile(audioData);
|
|
128
192
|
|
|
129
193
|
// Read metadata
|
|
@@ -150,17 +214,18 @@ console.log("Updated tags:", file.tag());
|
|
|
150
214
|
file.dispose();
|
|
151
215
|
```
|
|
152
216
|
|
|
153
|
-
###
|
|
217
|
+
### Node.js
|
|
154
218
|
|
|
155
219
|
```typescript
|
|
156
220
|
import { TagLib } from "taglib-wasm";
|
|
221
|
+
import { readFile } from "fs/promises";
|
|
157
222
|
|
|
158
223
|
// Initialize taglib-wasm
|
|
159
224
|
const taglib = await TagLib.initialize();
|
|
160
225
|
|
|
161
|
-
// Load
|
|
162
|
-
const audioData = await
|
|
163
|
-
const file = taglib.openFile(
|
|
226
|
+
// Load audio file from filesystem
|
|
227
|
+
const audioData = await readFile("song.mp3");
|
|
228
|
+
const file = taglib.openFile(audioData);
|
|
164
229
|
|
|
165
230
|
// Read metadata
|
|
166
231
|
const tags = file.tag();
|
|
@@ -186,7 +251,7 @@ console.log("Updated tags:", file.tag());
|
|
|
186
251
|
file.dispose();
|
|
187
252
|
```
|
|
188
253
|
|
|
189
|
-
###
|
|
254
|
+
### Bun
|
|
190
255
|
|
|
191
256
|
```typescript
|
|
192
257
|
import { TagLib } from "taglib-wasm";
|
|
@@ -194,11 +259,9 @@ import { TagLib } from "taglib-wasm";
|
|
|
194
259
|
// Initialize taglib-wasm
|
|
195
260
|
const taglib = await TagLib.initialize();
|
|
196
261
|
|
|
197
|
-
// Load from file
|
|
198
|
-
const
|
|
199
|
-
const
|
|
200
|
-
const audioData = new Uint8Array(await audioFile.arrayBuffer());
|
|
201
|
-
const file = taglib.openFile(audioData);
|
|
262
|
+
// Load from file system (Bun's native file API)
|
|
263
|
+
const audioData = await Bun.file("song.mp3").arrayBuffer();
|
|
264
|
+
const file = taglib.openFile(new Uint8Array(audioData));
|
|
202
265
|
|
|
203
266
|
// Read metadata
|
|
204
267
|
const tags = file.tag();
|
|
@@ -224,16 +287,18 @@ console.log("Updated tags:", file.tag());
|
|
|
224
287
|
file.dispose();
|
|
225
288
|
```
|
|
226
289
|
|
|
227
|
-
###
|
|
290
|
+
### Browser
|
|
228
291
|
|
|
229
292
|
```typescript
|
|
230
|
-
import { TagLib } from "
|
|
293
|
+
import { TagLib } from "taglib-wasm";
|
|
231
294
|
|
|
232
295
|
// Initialize taglib-wasm
|
|
233
296
|
const taglib = await TagLib.initialize();
|
|
234
297
|
|
|
235
|
-
// Load
|
|
236
|
-
const
|
|
298
|
+
// Load from file input or fetch
|
|
299
|
+
const fileInput = document.querySelector('input[type="file"]');
|
|
300
|
+
const audioFile = fileInput.files[0];
|
|
301
|
+
const audioData = new Uint8Array(await audioFile.arrayBuffer());
|
|
237
302
|
const file = taglib.openFile(audioData);
|
|
238
303
|
|
|
239
304
|
// Read metadata
|
|
@@ -269,7 +334,8 @@ export default {
|
|
|
269
334
|
async fetch(request: Request): Promise<Response> {
|
|
270
335
|
if (request.method === "POST") {
|
|
271
336
|
try {
|
|
272
|
-
// Initialize taglib-wasm
|
|
337
|
+
// Initialize taglib-wasm with Workers-specific configuration
|
|
338
|
+
// See docs/Cloudflare-Workers.md for memory configuration details
|
|
273
339
|
const taglib = await TagLib.initialize({
|
|
274
340
|
memory: { initial: 8 * 1024 * 1024 }, // 8MB for Workers
|
|
275
341
|
});
|
|
@@ -319,22 +385,30 @@ export default {
|
|
|
319
385
|
|
|
320
386
|
`tag-wasm` is designed to support all formats supported by TagLib:
|
|
321
387
|
|
|
322
|
-
- ✅ **.m4a (.mp4)** – Standard MPEG-4/AAC metadata for AAC and Apple Lossless
|
|
388
|
+
- ✅ **.m4a (.mp4)** – Standard MPEG-4/AAC metadata for AAC and Apple Lossless
|
|
389
|
+
audio
|
|
323
390
|
- ✅ **.mp3** – ID3v2 and ID3v1 tags
|
|
324
391
|
- ✅ **.flac** – Vorbis comments and audio properties
|
|
325
392
|
- ✅ **.wav** – INFO chunk metadata
|
|
326
|
-
- ✅ **Legacy formats
|
|
393
|
+
- ✅ **Legacy formats** – Opus, APE, MPC, WavPack, TrueAudio, and more
|
|
327
394
|
|
|
328
395
|
## 🎯 Extended Metadata with PropertyMap
|
|
329
396
|
|
|
330
|
-
`taglib-wasm` provides a **PropertyMap API** for accessing extended metadata
|
|
397
|
+
`taglib-wasm` provides a **PropertyMap API** for accessing extended metadata
|
|
398
|
+
beyond the basic tags. This allows you to read and write format-specific fields
|
|
399
|
+
and custom metadata.
|
|
331
400
|
|
|
332
401
|
### AcoustID example
|
|
333
402
|
|
|
334
403
|
```typescript
|
|
335
|
-
|
|
404
|
+
import { Tags } from "taglib-wasm";
|
|
405
|
+
|
|
406
|
+
// Using PropertyMap API to set extended metadata with tag constants
|
|
407
|
+
file.setProperty(Tags.AcoustidFingerprint, "AQADtMmybfGO8NCNEESLnzHyXNOHeHnG...");
|
|
408
|
+
file.setProperty(Tags.AcoustidId, "e7359e88-f1f7-41ed-b9f6-16e58e906997");
|
|
409
|
+
|
|
410
|
+
// Or using string property names
|
|
336
411
|
file.setProperty("ACOUSTID_FINGERPRINT", "AQADtMmybfGO8NCNEESLnzHyXNOHeHnG...");
|
|
337
|
-
file.setProperty("ACOUSTID_ID", "e7359e88-f1f7-41ed-b9f6-16e58e906997");
|
|
338
412
|
|
|
339
413
|
// Note: Property keys may vary by format
|
|
340
414
|
// Use file.properties() to see all available properties
|
|
@@ -344,20 +418,23 @@ file.save(); // Don't forget to save!
|
|
|
344
418
|
### MusicBrainz example
|
|
345
419
|
|
|
346
420
|
```typescript
|
|
347
|
-
// MusicBrainz metadata using PropertyMap
|
|
348
|
-
file.setProperty(
|
|
349
|
-
file.setProperty(
|
|
350
|
-
file.setProperty(
|
|
421
|
+
// MusicBrainz metadata using PropertyMap with tag constants
|
|
422
|
+
file.setProperty(Tags.MusicBrainzTrackId, "f4d1b6b8-8c1e-4d9a-9f2a-1234567890ab");
|
|
423
|
+
file.setProperty(Tags.MusicBrainzAlbumId, "a1b2c3d4-e5f6-7890-abcd-ef1234567890");
|
|
424
|
+
file.setProperty(
|
|
425
|
+
Tags.MusicBrainzArtistId,
|
|
426
|
+
"12345678-90ab-cdef-1234-567890abcdef",
|
|
427
|
+
);
|
|
351
428
|
```
|
|
352
429
|
|
|
353
430
|
### Volume example
|
|
354
431
|
|
|
355
432
|
```typescript
|
|
356
|
-
// ReplayGain volume normalization
|
|
357
|
-
file.setProperty(
|
|
358
|
-
file.setProperty(
|
|
359
|
-
file.setProperty(
|
|
360
|
-
file.setProperty(
|
|
433
|
+
// ReplayGain volume normalization with tag constants
|
|
434
|
+
file.setProperty(Tags.TrackGain, "-6.54 dB");
|
|
435
|
+
file.setProperty(Tags.TrackPeak, "0.987654");
|
|
436
|
+
file.setProperty(Tags.AlbumGain, "-8.12 dB");
|
|
437
|
+
file.setProperty(Tags.AlbumPeak, "0.995432");
|
|
361
438
|
```
|
|
362
439
|
|
|
363
440
|
### Extended fields
|
|
@@ -366,20 +443,20 @@ file.setProperty("REPLAYGAIN_ALBUM_PEAK", "0.995432");
|
|
|
366
443
|
// Using PropertyMap to set multiple properties at once
|
|
367
444
|
const properties = file.properties(); // Get current properties
|
|
368
445
|
|
|
369
|
-
// Set extended metadata
|
|
446
|
+
// Set extended metadata with tag constants
|
|
370
447
|
file.setProperties({
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
448
|
+
[Tags.AlbumArtist]: ["Various Artists"],
|
|
449
|
+
[Tags.Composer]: ["Composer Name"],
|
|
450
|
+
[Tags.Bpm]: ["120"],
|
|
451
|
+
[Tags.Compilation]: ["1"],
|
|
452
|
+
[Tags.DiscNumber]: ["1"],
|
|
453
|
+
[Tags.TrackTotal]: ["12"],
|
|
377
454
|
// Note: Property keys vary by format
|
|
378
455
|
});
|
|
379
456
|
|
|
380
457
|
// Or set individual properties
|
|
381
|
-
file.setProperty(
|
|
382
|
-
file.setProperty(
|
|
458
|
+
file.setProperty(Tags.AlbumArtist, "Various Artists");
|
|
459
|
+
file.setProperty(Tags.Composer, "Composer Name");
|
|
383
460
|
```
|
|
384
461
|
|
|
385
462
|
## 🏗️ Development
|
|
@@ -442,23 +519,29 @@ npm test
|
|
|
442
519
|
# ✅ MP3 - ID3v1/v2 tag support
|
|
443
520
|
# ✅ FLAC - Vorbis comments and properties
|
|
444
521
|
# ✅ OGG - Vorbis comments
|
|
445
|
-
# ✅ M4A -
|
|
522
|
+
# ✅ M4A - MPEG-4 (AAC and Apple Lossless) metadata
|
|
446
523
|
```
|
|
447
524
|
|
|
448
525
|
## 🔧 Technical Implementation
|
|
449
526
|
|
|
450
527
|
### Key architecture decisions
|
|
451
528
|
|
|
452
|
-
1. **Memory Management**: Uses Emscripten's `allocate()` for reliable JS↔WASM
|
|
453
|
-
|
|
529
|
+
1. **Memory Management**: Uses Emscripten's `allocate()` for reliable JS↔WASM
|
|
530
|
+
data transfer
|
|
531
|
+
2. **Buffer-Based Processing**: `TagLib::ByteVectorStream` enables in-memory
|
|
532
|
+
file processing
|
|
454
533
|
3. **C++ Wrapper**: Custom C functions bridge TagLib's C++ API to WASM exports
|
|
455
|
-
4. **Type Safety**: Complete TypeScript definitions for all audio formats and
|
|
534
|
+
4. **Type Safety**: Complete TypeScript definitions for all audio formats and
|
|
535
|
+
metadata
|
|
456
536
|
|
|
457
537
|
### Critical implementation details
|
|
458
538
|
|
|
459
|
-
- **ByteVectorStream**: Enables processing audio files from memory buffers
|
|
460
|
-
|
|
461
|
-
- **
|
|
539
|
+
- **ByteVectorStream**: Enables processing audio files from memory buffers
|
|
540
|
+
without filesystem
|
|
541
|
+
- **ID-based Object Management**: C++ objects managed via integer IDs for memory
|
|
542
|
+
safety
|
|
543
|
+
- **Emscripten allocate()**: Ensures proper memory synchronization between JS
|
|
544
|
+
and WASM
|
|
462
545
|
- **UTF-8 String Handling**: Proper encoding for international metadata
|
|
463
546
|
|
|
464
547
|
## 📚 API Reference
|
|
@@ -536,51 +619,44 @@ interface Tag {
|
|
|
536
619
|
type PropertyMap = { [key: string]: string[] };
|
|
537
620
|
```
|
|
538
621
|
|
|
539
|
-
##
|
|
622
|
+
## 📖 Additional Documentation
|
|
540
623
|
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
maximum?: number; // Maximum memory size (default: 256MB)
|
|
546
|
-
};
|
|
547
|
-
debug?: boolean; // Enable debug output
|
|
548
|
-
}
|
|
549
|
-
```
|
|
624
|
+
- [**Tag Name Constants**](docs/Tag-Name-Constants.md) - Comprehensive reference for standard tag names and cross-format mapping
|
|
625
|
+
- [**Automatic Tag Mapping**](docs/Automatic-Tag-Mapping.md) - How taglib-wasm handles format-specific tag differences
|
|
626
|
+
- [**Implementation Details**](docs/Implementation.md) - Technical details about the Wasm implementation
|
|
627
|
+
- [**Runtime Compatibility**](docs/Runtime-Compatibility.md) - Platform-specific setup and considerations
|
|
550
628
|
|
|
551
629
|
## 🌐 Runtime Compatibility
|
|
552
630
|
|
|
553
|
-
`taglib-wasm` works
|
|
631
|
+
`taglib-wasm` works across all major JavaScript runtimes:
|
|
554
632
|
|
|
555
|
-
| Runtime | Status | Installation
|
|
556
|
-
| ----------- | ------- |
|
|
557
|
-
| **Deno** | ✅ Full | `npm:taglib-wasm`
|
|
558
|
-
| **Bun** | ✅ Full | `bun add taglib-wasm`
|
|
559
|
-
| **Node.js** | ✅ Full | `npm install taglib-wasm`
|
|
560
|
-
| **Browser** | ✅ Full | CDN/bundler
|
|
633
|
+
| Runtime | Status | Installation | Performance | TypeScript |
|
|
634
|
+
| ----------- | ------- | ------------------------- | ----------- | ---------- |
|
|
635
|
+
| **Deno** | ✅ Full | `npm:taglib-wasm` | Excellent | Native |
|
|
636
|
+
| **Bun** | ✅ Full | `bun add taglib-wasm` | Excellent | Native |
|
|
637
|
+
| **Node.js** | ✅ Full | `npm install taglib-wasm` | Good | Native/tsx |
|
|
638
|
+
| **Browser** | ✅ Full | CDN/bundler | Good | Via build |
|
|
561
639
|
|
|
562
|
-
**📖 See [docs/Runtime-Compatibility.md](docs/Runtime-Compatibility.md) for
|
|
640
|
+
**📖 See [docs/Runtime-Compatibility.md](docs/Runtime-Compatibility.md) for
|
|
641
|
+
detailed runtime information**
|
|
563
642
|
|
|
564
643
|
## 🚧 Known Limitations
|
|
565
644
|
|
|
566
|
-
- **File Writing
|
|
567
|
-
|
|
568
|
-
- **
|
|
645
|
+
- **File Writing** – Saves only affect in-memory representation (no filesystem
|
|
646
|
+
persistence)
|
|
647
|
+
- **Large Files** – Memory usage scales with file size (entire file loaded into
|
|
648
|
+
memory)
|
|
649
|
+
- **Concurrent Access** – Not thread-safe (JavaScript single-threaded nature)
|
|
569
650
|
|
|
570
651
|
## 🤝 Contributing
|
|
571
652
|
|
|
572
|
-
Contributions welcome!
|
|
573
|
-
|
|
574
|
-
- Additional format support (DSF, DSDIFF, etc.)
|
|
575
|
-
- Advanced metadata implementation (PropertyMap integration)
|
|
576
|
-
- Performance optimizations
|
|
577
|
-
- Runtime-specific optimizations
|
|
578
|
-
- Documentation improvements
|
|
653
|
+
Contributions welcome!
|
|
579
654
|
|
|
580
655
|
## 📄 License
|
|
581
656
|
|
|
582
657
|
- **This project**: MIT License (see [LICENSE](LICENSE))
|
|
583
|
-
- **TagLib library**: LGPL/MPL dual license (see
|
|
658
|
+
- **TagLib library**: LGPL/MPL dual license (see
|
|
659
|
+
[lib/taglib/COPYING.LGPL](lib/taglib/COPYING.LGPL))
|
|
584
660
|
|
|
585
661
|
## 🙏 Acknowledgments
|
|
586
662
|
|
package/index.ts
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
export { AudioFileImpl as AudioFile, TagLib, createTagLib } from "./src/taglib.ts";
|
|
9
9
|
export { readTags, writeTags, readProperties } from "./src/simple.ts";
|
|
10
|
+
export { Tags, FormatMappings, isValidTagName, getAllTagNames } from "./src/constants.ts";
|
|
10
11
|
export type {
|
|
11
12
|
AudioFormat,
|
|
12
13
|
AudioProperties,
|
|
@@ -18,6 +19,7 @@ export type {
|
|
|
18
19
|
PropertyMap,
|
|
19
20
|
Tag,
|
|
20
21
|
TagLibConfig,
|
|
22
|
+
TagName,
|
|
21
23
|
} from "./src/types.ts";
|
|
22
24
|
export type { TagLibModule, WasmModule } from "./src/wasm.ts";
|
|
23
25
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "taglib-wasm",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.2",
|
|
4
4
|
"description": "TagLib compiled to WebAssembly with TypeScript bindings for universal audio metadata handling",
|
|
5
5
|
"main": "index.ts",
|
|
6
6
|
"types": "index.ts",
|
|
@@ -32,6 +32,9 @@
|
|
|
32
32
|
"publish:npm": "echo 'Use GitHub Actions workflow for publishing'",
|
|
33
33
|
"publish:github": "echo 'Use GitHub Actions workflow for publishing'"
|
|
34
34
|
},
|
|
35
|
+
"engines": {
|
|
36
|
+
"node": ">=22.6.0"
|
|
37
|
+
},
|
|
35
38
|
"keywords": [
|
|
36
39
|
"taglib",
|
|
37
40
|
"webassembly",
|
package/src/constants.ts
ADDED
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Standard tag property names used by TagLib.
|
|
3
|
+
* These constants provide type-safe access to tag properties with IDE autocomplete.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```typescript
|
|
7
|
+
* import { Tags } from 'taglib-wasm';
|
|
8
|
+
*
|
|
9
|
+
* // Read tags
|
|
10
|
+
* const title = file.tag.properties.get(Tags.Title);
|
|
11
|
+
* const artist = file.tag.properties.get(Tags.Artist);
|
|
12
|
+
*
|
|
13
|
+
* // Write tags
|
|
14
|
+
* file.tag.properties.set(Tags.Album, "Dark Side of the Moon");
|
|
15
|
+
* file.tag.properties.set(Tags.Year, "1973");
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
export const Tags = {
|
|
19
|
+
// Basic Properties
|
|
20
|
+
/** Track/song title */
|
|
21
|
+
Title: 'TITLE',
|
|
22
|
+
/** Primary performer(s) */
|
|
23
|
+
Artist: 'ARTIST',
|
|
24
|
+
/** Album/collection name */
|
|
25
|
+
Album: 'ALBUM',
|
|
26
|
+
/** Date of recording (year) */
|
|
27
|
+
Date: 'DATE',
|
|
28
|
+
/** Track number on album */
|
|
29
|
+
TrackNumber: 'TRACKNUMBER',
|
|
30
|
+
/** Musical genre */
|
|
31
|
+
Genre: 'GENRE',
|
|
32
|
+
/** Comments/notes */
|
|
33
|
+
Comment: 'COMMENT',
|
|
34
|
+
|
|
35
|
+
// Extended Properties
|
|
36
|
+
/** Band/orchestra/ensemble */
|
|
37
|
+
AlbumArtist: 'ALBUMARTIST',
|
|
38
|
+
/** Original composer(s) */
|
|
39
|
+
Composer: 'COMPOSER',
|
|
40
|
+
/** Copyright information */
|
|
41
|
+
Copyright: 'COPYRIGHT',
|
|
42
|
+
/** Encoding software/person */
|
|
43
|
+
EncodedBy: 'ENCODEDBY',
|
|
44
|
+
/** Disc number for multi-disc sets */
|
|
45
|
+
DiscNumber: 'DISCNUMBER',
|
|
46
|
+
/** Beats per minute */
|
|
47
|
+
Bpm: 'BPM',
|
|
48
|
+
/** Lyrics/text writer(s) */
|
|
49
|
+
Lyricist: 'LYRICIST',
|
|
50
|
+
/** Conductor */
|
|
51
|
+
Conductor: 'CONDUCTOR',
|
|
52
|
+
/** Person who remixed */
|
|
53
|
+
Remixer: 'REMIXEDBY',
|
|
54
|
+
/** Language of vocals/lyrics */
|
|
55
|
+
Language: 'LANGUAGE',
|
|
56
|
+
/** Publisher */
|
|
57
|
+
Publisher: 'PUBLISHER',
|
|
58
|
+
/** Mood/atmosphere */
|
|
59
|
+
Mood: 'MOOD',
|
|
60
|
+
/** Media type (CD, vinyl, etc.) */
|
|
61
|
+
Media: 'MEDIA',
|
|
62
|
+
/** Radio station owner */
|
|
63
|
+
RadioStationOwner: 'RADIOSTATIONOWNER',
|
|
64
|
+
/** Producer */
|
|
65
|
+
Producer: 'PRODUCER',
|
|
66
|
+
/** Album subtitle */
|
|
67
|
+
Subtitle: 'SUBTITLE',
|
|
68
|
+
/** Release label */
|
|
69
|
+
Label: 'LABEL',
|
|
70
|
+
|
|
71
|
+
// Sorting Properties
|
|
72
|
+
/** Sort name for title */
|
|
73
|
+
TitleSort: 'TITLESORT',
|
|
74
|
+
/** Sort name for artist */
|
|
75
|
+
ArtistSort: 'ARTISTSORT',
|
|
76
|
+
/** Sort name for album artist */
|
|
77
|
+
AlbumArtistSort: 'ALBUMARTISTSORT',
|
|
78
|
+
/** Sort name for album */
|
|
79
|
+
AlbumSort: 'ALBUMSORT',
|
|
80
|
+
/** Sort name for composer */
|
|
81
|
+
ComposerSort: 'COMPOSERSORT',
|
|
82
|
+
|
|
83
|
+
// Identifiers
|
|
84
|
+
/** International Standard Recording Code */
|
|
85
|
+
Isrc: 'ISRC',
|
|
86
|
+
/** Amazon Standard Identification Number */
|
|
87
|
+
Asin: 'ASIN',
|
|
88
|
+
/** Catalog number */
|
|
89
|
+
CatalogNumber: 'CATALOGNUMBER',
|
|
90
|
+
/** Barcode (EAN/UPC) */
|
|
91
|
+
Barcode: 'BARCODE',
|
|
92
|
+
|
|
93
|
+
// MusicBrainz Identifiers
|
|
94
|
+
/** MusicBrainz Artist ID */
|
|
95
|
+
MusicBrainzArtistId: 'MUSICBRAINZ_ARTISTID',
|
|
96
|
+
/** MusicBrainz Release Artist ID */
|
|
97
|
+
MusicBrainzReleaseArtistId: 'MUSICBRAINZ_ALBUMARTISTID',
|
|
98
|
+
/** MusicBrainz Work ID */
|
|
99
|
+
MusicBrainzWorkId: 'MUSICBRAINZ_WORKID',
|
|
100
|
+
/** MusicBrainz Release ID */
|
|
101
|
+
MusicBrainzReleaseId: 'MUSICBRAINZ_ALBUMID',
|
|
102
|
+
/** MusicBrainz Recording ID */
|
|
103
|
+
MusicBrainzRecordingId: 'MUSICBRAINZ_TRACKID',
|
|
104
|
+
/** MusicBrainz Track ID (deprecated, use RecordingId) */
|
|
105
|
+
MusicBrainzTrackId: 'MUSICBRAINZ_TRACKID',
|
|
106
|
+
/** MusicBrainz Release Group ID */
|
|
107
|
+
MusicBrainzReleaseGroupId: 'MUSICBRAINZ_RELEASEGROUPID',
|
|
108
|
+
/** MusicBrainz Release Track ID */
|
|
109
|
+
MusicBrainzReleaseTrackId: 'MUSICBRAINZ_RELEASETRACKID',
|
|
110
|
+
|
|
111
|
+
// Podcast Properties
|
|
112
|
+
/** Podcast identifier */
|
|
113
|
+
PodcastId: 'PODCASTID',
|
|
114
|
+
/** Podcast URL */
|
|
115
|
+
PodcastUrl: 'PODCASTURL',
|
|
116
|
+
|
|
117
|
+
// Grouping and Work
|
|
118
|
+
/** Content group/work */
|
|
119
|
+
Grouping: 'GROUPING',
|
|
120
|
+
/** Work name */
|
|
121
|
+
Work: 'WORK',
|
|
122
|
+
|
|
123
|
+
// Additional Metadata
|
|
124
|
+
/** Lyrics content */
|
|
125
|
+
Lyrics: 'LYRICS',
|
|
126
|
+
/** Album gain (ReplayGain) */
|
|
127
|
+
AlbumGain: 'REPLAYGAIN_ALBUM_GAIN',
|
|
128
|
+
/** Album peak (ReplayGain) */
|
|
129
|
+
AlbumPeak: 'REPLAYGAIN_ALBUM_PEAK',
|
|
130
|
+
/** Track gain (ReplayGain) */
|
|
131
|
+
TrackGain: 'REPLAYGAIN_TRACK_GAIN',
|
|
132
|
+
/** Track peak (ReplayGain) */
|
|
133
|
+
TrackPeak: 'REPLAYGAIN_TRACK_PEAK',
|
|
134
|
+
|
|
135
|
+
// Special handling
|
|
136
|
+
/** Original artist for covers */
|
|
137
|
+
OriginalArtist: 'ORIGINALARTIST',
|
|
138
|
+
/** Original album */
|
|
139
|
+
OriginalAlbum: 'ORIGINALALBUM',
|
|
140
|
+
/** Original release date */
|
|
141
|
+
OriginalDate: 'ORIGINALDATE',
|
|
142
|
+
/** Script/writing system */
|
|
143
|
+
Script: 'SCRIPT',
|
|
144
|
+
/** Involved people list */
|
|
145
|
+
InvolvedPeopleList: 'INVOLVEDPEOPLELIST',
|
|
146
|
+
|
|
147
|
+
// Technical Properties
|
|
148
|
+
/** Encoder settings/software */
|
|
149
|
+
EncoderSettings: 'ENCODERSETTINGS',
|
|
150
|
+
/** Source media */
|
|
151
|
+
SourceMedia: 'SOURCEMEDIA',
|
|
152
|
+
} as const;
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Type representing all valid tag property names
|
|
156
|
+
*/
|
|
157
|
+
export type TagName = typeof Tags[keyof typeof Tags];
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Type guard to check if a string is a valid tag name
|
|
161
|
+
*/
|
|
162
|
+
export function isValidTagName(name: string): name is TagName {
|
|
163
|
+
return Object.values(Tags).includes(name as TagName);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Get all available tag names as an array
|
|
168
|
+
*/
|
|
169
|
+
export function getAllTagNames(): readonly TagName[] {
|
|
170
|
+
return Object.values(Tags);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Format-specific tag mappings (for reference only - TagLib handles these automatically)
|
|
175
|
+
* This shows how standard property names map to format-specific identifiers.
|
|
176
|
+
*/
|
|
177
|
+
export const FormatMappings = {
|
|
178
|
+
Title: {
|
|
179
|
+
id3v2: 'TIT2',
|
|
180
|
+
mp4: '©nam',
|
|
181
|
+
vorbis: 'TITLE',
|
|
182
|
+
ape: 'Title',
|
|
183
|
+
riff: 'INAM'
|
|
184
|
+
},
|
|
185
|
+
Artist: {
|
|
186
|
+
id3v2: 'TPE1',
|
|
187
|
+
mp4: '©ART',
|
|
188
|
+
vorbis: 'ARTIST',
|
|
189
|
+
ape: 'Artist',
|
|
190
|
+
riff: 'IART'
|
|
191
|
+
},
|
|
192
|
+
Album: {
|
|
193
|
+
id3v2: 'TALB',
|
|
194
|
+
mp4: '©alb',
|
|
195
|
+
vorbis: 'ALBUM',
|
|
196
|
+
ape: 'Album',
|
|
197
|
+
riff: 'IPRD'
|
|
198
|
+
},
|
|
199
|
+
Date: {
|
|
200
|
+
id3v2: 'TDRC',
|
|
201
|
+
mp4: '©day',
|
|
202
|
+
vorbis: 'DATE',
|
|
203
|
+
ape: 'Year',
|
|
204
|
+
riff: 'ICRD'
|
|
205
|
+
},
|
|
206
|
+
Genre: {
|
|
207
|
+
id3v2: 'TCON',
|
|
208
|
+
mp4: '©gen',
|
|
209
|
+
vorbis: 'GENRE',
|
|
210
|
+
ape: 'Genre',
|
|
211
|
+
riff: 'IGNR'
|
|
212
|
+
},
|
|
213
|
+
Comment: {
|
|
214
|
+
id3v2: 'COMM',
|
|
215
|
+
mp4: '©cmt',
|
|
216
|
+
vorbis: 'COMMENT',
|
|
217
|
+
ape: 'Comment',
|
|
218
|
+
riff: 'ICMT'
|
|
219
|
+
},
|
|
220
|
+
TrackNumber: {
|
|
221
|
+
id3v2: 'TRCK',
|
|
222
|
+
mp4: 'trkn',
|
|
223
|
+
vorbis: 'TRACKNUMBER',
|
|
224
|
+
ape: 'Track',
|
|
225
|
+
riff: 'ITRK'
|
|
226
|
+
}
|
|
227
|
+
} as const;
|
package/src/types.ts
CHANGED