taglib-wasm
Version:
TagLib for TypeScript platforms: Deno, Node.js, Bun, Electron, browsers, and Cloudflare Workers
459 lines (336 loc) β’ 15.6 kB
Markdown
# TagLib-Wasm
[](https://github.com/CharlesWiltgen/taglib-wasm/actions/workflows/test.yml)
[](https://www.npmjs.com/package/taglib-wasm)
[](https://www.npmjs.com/package/taglib-wasm)
[](https://github.com/CharlesWiltgen/taglib-wasm/blob/main/LICENSE)
<br>[](https://www.typescriptlang.org/)
[](https://emscripten.org/)
[](https://webassembly.org/)
[](https://taglib.org/)
<br>[](https://deno.land/)
[](https://nodejs.org/)
[](https://bun.sh/)
[](https://workers.cloudflare.com/)
[](https://www.electronjs.org/)
[](https://html.spec.whatwg.org/multipage/)
TagLib-Wasm is the **universal tagging library for TypeScript/JavaScript**
(TS|JS) platforms: **Deno**, **Node.js**, **Bun**, **Cloudflare Workers**,
**Electron**, and **browsers**.
This project exists because the TS|JS ecosystem had no battle-tested audio
tagging library that supports reading and writing music metadata to all popular
audio formats. It aspires to be a universal solution for all TS|JS-capable
platforms β Deno, Node.js, Bun, Electron, Cloudflare Workers, and browsers.
TagLib-Wasm stands on the shoulders of giants, including
[TagLib](https://taglib.org/) itself, [Emscripten](https://emscripten.org/), and
[Wasm](https://webassembly.org/) ([WebAssembly](https://webassembly.org/)).
TagLib itself is legendary, and a core dependency of many music apps.
## π― Features
- **β
Full audio format support** β Supports all audio formats supported by
TagLib
- **β
TypeScript first** β Complete type definitions and modern API
- **β
Wide TS/JS runtime support** β Deno, Node.js, Bun, Electron, Cloudflare
Workers, and browsers
- **β
Format abstraction** β Handles container format details automagically
when possible
- **β
Zero dependencies** β Self-contained Wasm bundle
- **β
Production ready** β Growing test suite helps ensure safety and
reliability
- **β
Two API styles** β Use the βSimpleβ API (3 functions), or the full βCoreβ
API for more advanced applications
- **β
Batch folder operations** β Scan directories, process multiple files,
find duplicates, and export metadata catalogs
## π¦ Installation
### Deno
```typescript
import { TagLib } from "@charlesw/taglib-wasm";
```
### Node.js
```bash
npm install taglib-wasm
```
> **Note:** Requires Node.js v22.6.0 or higher. If you want to use the
> TypeScript version with Node.js, see the
> [installation guide](https://charleswiltgen.github.io/taglib-wasm/guide/installation.html).
### Bun
```bash
bun add taglib-wasm
```
### Electron
```bash
npm install taglib-wasm
```
Works in both main and renderer processes:
```typescript
// Main process
import { TagLib } from "taglib-wasm";
// Renderer process (with nodeIntegration: true)
const { TagLib } = require("taglib-wasm");
```
### Deno Compiled Binaries (Offline Support)
For Deno compiled binaries that need to work offline, you can embed the WASM
file:
```typescript
// 1. Prepare your build by copying the WASM file
import { prepareWasmForEmbedding } from "@charlesw/taglib-wasm";
await prepareWasmForEmbedding("./taglib.wasm");
// 2. In your application, use the helper for automatic handling
import { initializeForDenoCompile } from "@charlesw/taglib-wasm";
const taglib = await initializeForDenoCompile();
// 3. Compile with the embedded WASM
// deno compile --allow-read --include taglib.wasm myapp.ts
```
See the
[complete Deno compile guide](https://charleswiltgen.github.io/taglib-wasm/guide/deno-compile.html)
for more options including CDN loading.
For manual control:
```typescript
// Load embedded WASM in compiled binaries
const wasmBinary = await Deno.readFile(
new URL("./taglib.wasm", import.meta.url),
);
const taglib = await TagLib.initialize({ wasmBinary });
```
## π Quick Start
### Simple API
```typescript
import { applyTags, readTags, updateTags } from "taglib-wasm/simple";
// Read tags - just one function call!
const tags = await readTags("song.mp3");
console.log(tags.title, tags.artist, tags.album);
// Apply tags and get modified buffer (in-memory)
const modifiedBuffer = await applyTags("song.mp3", {
title: "New Title",
artist: "New Artist",
album: "New Album",
});
// Or update tags on disk (requires file path)
await updateTags("song.mp3", {
title: "New Title",
artist: "New Artist",
});
```
### Full API
The Full API might be a better choice for apps and utilities focused on advanced
metadata management.
```typescript
import { TagLib } from "taglib-wasm";
// Initialize taglib-wasm
const taglib = await TagLib.initialize();
// Load audio file
const file = await taglib.open("song.mp3");
// Read and update metadata
const tag = file.tag();
tag.setTitle("New Title");
tag.setArtist("New Artist");
// Save changes
file.save();
// Clean up
file.dispose();
```
### Batch Folder Operations
Process entire music collections efficiently:
```typescript
import { findDuplicates, scanFolder } from "taglib-wasm/folder";
// Scan a music library
const result = await scanFolder("/path/to/music", {
recursive: true,
concurrency: 4,
onProgress: (processed, total, file) => {
console.log(`Processing ${processed}/${total}: ${file}`);
},
});
console.log(`Found ${result.totalFound} audio files`);
console.log(`Successfully processed ${result.totalProcessed} files`);
// Process results
for (const file of result.files) {
console.log(`${file.path}: ${file.tags.artist} - ${file.tags.title}`);
console.log(`Duration: ${file.properties?.duration}s`);
}
// Find duplicates
const duplicates = await findDuplicates("/path/to/music", ["artist", "title"]);
console.log(`Found ${duplicates.size} groups of duplicates`);
```
### Working with Cover Art
```typescript
import { getCoverArt, setCoverArt } from "taglib-wasm/simple";
// Extract cover art
const coverData = await getCoverArt("song.mp3");
if (coverData) {
await Deno.writeFile("cover.jpg", coverData);
}
// Set new cover art
const imageData = await Deno.readFile("new-cover.jpg");
const modifiedBuffer = await setCoverArt("song.mp3", imageData, "image/jpeg");
// Save modifiedBuffer to file if needed
```
### Container Format and Codec Detection
```typescript
import { readProperties } from "taglib-wasm/simple";
// Get detailed audio properties including container and codec info
const props = await readProperties("song.m4a");
console.log(props.containerFormat); // "MP4" (container format)
console.log(props.codec); // "AAC" or "ALAC" (compressed media format)
console.log(props.isLossless); // false for AAC, true for ALAC
console.log(props.bitsPerSample); // 16 for most formats
console.log(props.bitrate); // 256 (kbps)
console.log(props.sampleRate); // 44100 (Hz)
console.log(props.length); // 180 (duration in seconds)
```
Container format vs Codec:
- **Container format** β How audio data and metadata are packaged (e.g., MP4, OGG)
- **Codec** β How audio is compressed/encoded (e.g., AAC, Vorbis)
Supported formats:
- **MP4 container** (.mp4, .m4a) β Can contain AAC (lossy) or ALAC (lossless)
- **OGG container** (.ogg) β Can contain Vorbis, Opus, FLAC, or Speex
- **MP3** β Both container and codec (lossy)
- **FLAC** β Both container and codec (lossless)
- **WAV** β Container for PCM (uncompressed) audio
- **AIFF** β Container for PCM (uncompressed) audio
## π Documentation
**[π View Full Documentation](https://charleswiltgen.github.io/taglib-wasm/)**
### Getting Started
- [Installation Guide](https://charleswiltgen.github.io/taglib-wasm/guide/installation.html)
- [Quick Start Tutorial](https://charleswiltgen.github.io/taglib-wasm/guide/quick-start.html)
- [All Examples](https://charleswiltgen.github.io/taglib-wasm/guide/examples.html)
### Guides
- [API Reference](https://charleswiltgen.github.io/taglib-wasm/api/)
- [Platform Examples](https://charleswiltgen.github.io/taglib-wasm/guide/platform-examples.html)
- [Working with Cover Art](https://charleswiltgen.github.io/taglib-wasm/guide/cover-art.html)
- [Cloudflare Workers Setup](https://charleswiltgen.github.io/taglib-wasm/guide/workers-setup.html)
- [Error Handling](https://charleswiltgen.github.io/taglib-wasm/concepts/error-handling.html)
### Development
- [Testing Guide](https://charleswiltgen.github.io/taglib-wasm/development/testing.html)
- [Future Improvements](https://charleswiltgen.github.io/taglib-wasm/development/improvements.html)
- [Contributing](CONTRIBUTING.md)
## π Supported Formats
`taglib-wasm` is designed to support all formats supported by TagLib:
- β
**.mp3** β ID3v2 and ID3v1 tags
- β
**.m4a/.mp4** β MPEG-4/AAC metadata for AAC and Apple Lossless audio
- β
**.flac** β Vorbis comments and audio properties
- β
**.ogg** β Ogg Vorbis format with full metadata support
- β
**.wav** β INFO chunk metadata
- β
**Additional formats** β Opus, APE, MPC, WavPack, TrueAudio, AIFF, WMA, and
more
## π― Key Features
### Extended Metadata Support
Beyond basic tags, taglib-wasm supports extended metadata:
```typescript
import { Tags } from "taglib-wasm";
// AcoustID fingerprints
file.setProperty(
Tags.AcoustidFingerprint,
"AQADtMmybfGO8NCNEESLnzHyXNOHeHnG...",
);
// MusicBrainz IDs
file.setProperty(
Tags.MusicBrainzTrackId,
"f4d1b6b8-8c1e-4d9a-9f2a-1234567890ab",
);
// ReplayGain volume normalization
file.setProperty(Tags.TrackGain, "-6.54 dB");
file.setProperty(Tags.TrackPeak, "0.987654");
```
[View all supported tag constants β](https://charleswiltgen.github.io/taglib-wasm/api/tag-name-constants.html)
## β‘ Performance & Best Practices
### Batch Processing for Multiple Files
When processing multiple audio files, use the optimized batch APIs for dramatic performance improvements:
```typescript
import { readMetadataBatch, readTagsBatch } from "taglib-wasm/simple";
// β SLOW: Processing files one by one (can take 90+ seconds for 19 files)
for (const file of files) {
const tags = await readTags(file); // Re-initializes for each file
}
// β
FAST: Batch processing (10-20x faster)
const result = await readTagsBatch(files, {
concurrency: 8, // Process 8 files in parallel
onProgress: (processed, total) => {
console.log(`${processed}/${total} files processed`);
},
});
// β
FASTEST: Read complete metadata in one batch
const metadata = await readMetadataBatch(files, { concurrency: 8 });
```
**Performance comparison for 19 audio files:**
- Sequential: ~90 seconds (4.7s per file)
- Batch (concurrency=4): ~8 seconds (11x faster)
- Batch (concurrency=8): ~5 seconds (18x faster)
### Smart Partial Loading
For large audio files (>50MB), enable partial loading to dramatically reduce memory usage:
```typescript
// Enable partial loading for large files
const file = await taglib.open("large-concert.flac", {
partial: true,
maxHeaderSize: 2 * 1024 * 1024, // 2MB header
maxFooterSize: 256 * 1024, // 256KB footer
});
// Read operations work normally
const tags = file.tag();
console.log(tags.title, tags.artist);
// Smart save - automatically loads full file when needed
await file.saveToFile(); // Full file loaded only here
```
**Performance gains:**
- **500MB file**: ~450x less memory usage (1.1MB vs 500MB)
- **Initial load**: 50x faster (50ms vs 2500ms)
- **Memory peak**: 3.3MB instead of 1.5GB
### WebAssembly Streaming
For web applications, use CDN URLs to enable WebAssembly streaming compilation:
```typescript
// β
FAST: Streaming compilation (200-400ms)
const taglib = await TagLib.initialize({
wasmUrl: "https://cdn.jsdelivr.net/npm/taglib-wasm@latest/dist/taglib.wasm",
});
// β SLOWER: ArrayBuffer loading (400-800ms)
const wasmBinary = await fetch("taglib.wasm").then((r) => r.arrayBuffer());
const taglib = await TagLib.initialize({ wasmBinary });
```
[View complete performance guide β](https://charleswiltgen.github.io/taglib-wasm/concepts/performance.html)
## ποΈ Development
### Build from Source
```bash
# Prerequisites: Emscripten SDK
# Install via: https://emscripten.org/docs/getting_started/downloads.html
# Clone and build
git clone https://github.com/CharlesWiltgen/taglib-wasm.git
cd taglib-wasm
# Build Wasm module
npm run build:wasm
# Run tests
npm test
```
[View full development guide β](CONTRIBUTING.md)
## π Runtime Compatibility
`taglib-wasm` works across all major JavaScript runtimes:
| Runtime | Status | Installation | Notes |
| ---------------------- | ------- | ------------------------- | ------------------------- |
| **Deno** | β
Full | `npm:taglib-wasm` | Native TypeScript |
| **Node.js** | β
Full | `npm install taglib-wasm` | TypeScript via tsx |
| **Bun** | β
Full | `bun add taglib-wasm` | Native TypeScript |
| **Browser** | β
Full | Via bundler | Full API support |
| **Cloudflare Workers** | β
Full | `taglib-wasm/workers` | Memory-optimized build |
| **Electron** | β
Full | `npm install taglib-wasm` | Main & renderer processes |
## π§ Known Limitations
- **Memory Usage** β Entire file must be loaded into memory (may be an issue for
very large files)
- **Concurrent Access** β Not thread-safe (JavaScript single-threaded nature
mitigates this)
- **Cloudflare Workers** β Limited to 128MB memory per request; files larger
than ~100MB may fail
## π€ Contributing
Contributions are welcome! Please read our [Contributing Guide](CONTRIBUTING.md)
for details on our code of conduct and the process for submitting pull requests.
## π License
This project uses dual licensing:
- **TypeScript/JavaScript code** β MIT License (see [LICENSE](LICENSE))
- **WebAssembly binary (taglib.wasm)** β LGPL-2.1-or-later (inherited from
TagLib)
The TagLib library is dual-licensed under LGPL/MPL. When compiled to
WebAssembly, the resulting binary must comply with LGPL requirements. This
means:
- You can use taglib-wasm in commercial projects
- If you modify the TagLib C++ code, you must share those changes
- You must provide a way for users to relink with a modified TagLib
For details, see [lib/taglib/COPYING.LGPL](lib/taglib/COPYING.LGPL)
## π Acknowledgments
- [TagLib](https://taglib.org/) β Excellent audio metadata library
- [Emscripten](https://emscripten.org/) β WebAssembly compilation toolchain