UNPKG

tagpilot-lib

Version:

A high-performance Node.js library for reading and writing audio metadata and cover art, built with Rust and NAPI-RS

422 lines (317 loc) 9.48 kB
# tagpilot-lib A high-performance Node.js library for reading and writing audio metadata and cover art, built with Rust and NAPI-RS. ## Features - 🚀 **High Performance**: Native Rust implementation for maximum speed - 🎵 **Audio Metadata**: Read and write ID3 tags, MP3 metadata, and more - 🖼️ **Cover Art**: Extract, embed, and manage album artwork - 📦 **Buffer Support**: Work with audio data directly in memory - 🔄 **Async/Await**: Promise-based API for modern JavaScript - 🛡️ **TypeScript**: Full TypeScript support with type definitions - 🌐 **Cross Platform**: Works on Windows, macOS, and Linux ## Installation ```bash npm install tagpilot-lib ``` ## Quick Start ```javascript const { readTags, writeTags, readCoverImage, writeCoverImage } = require('tagpilot-lib'); // Read audio metadata const tags = await readTags('./music/song.mp3'); console.log(tags.title); // "Song Title" console.log(tags.artist); // "Artist Name" // Write audio metadata await writeTags('./music/song.mp3', { title: 'New Title', artist: 'New Artist', album: 'Album Name', year: 2024, genre: 'Rock', track: 1, trackTotal: 12 }); // Read cover image const coverImage = await readCoverImage(audioBuffer); if (coverImage) { console.log('Cover image found:', coverImage.length, 'bytes'); } // Write cover image const imageBuffer = fs.readFileSync('./cover.jpg'); const modifiedAudio = await writeCoverImage(audioBuffer, imageBuffer); ``` ## API Reference ### Audio Tags #### `readTags(filePath: string): Promise<AudioTags>` Reads metadata from an audio file. **Parameters:** - `filePath` (string): Path to the audio file **Returns:** Promise<AudioTags> **Example:** ```javascript const tags = await readTags('./music/song.mp3'); console.log(tags); // { // title: "Song Title", // artist: "Artist Name", // album: "Album Name", // year: 2024, // genre: "Rock", // track: 1, // trackTotal: 12, // albumArtist: "Album Artist", // comment: "Comment", // disc: 1, // discTotal: 2 // } ``` #### `writeTags(filePath: string, tags: AudioTags): Promise<void>` Writes metadata to an audio file. **Parameters:** - `filePath` (string): Path to the audio file - `tags` (AudioTags): Metadata to write **Returns:** Promise<void> **Example:** ```javascript await writeTags('./music/song.mp3', { title: 'New Title', artist: 'New Artist', album: 'Album Name', year: 2024, genre: 'Rock', track: 1, trackTotal: 12, albumArtist: 'Album Artist', comment: 'My comment', disc: 1, discTotal: 2 }); ``` #### `clearTags(filePath: string): Promise<void>` Clears all metadata from an audio file. **Parameters:** - `filePath` (string): Path to the audio file **Returns:** Promise<void> **Example:** ```javascript await clearTags('./music/song.mp3'); ``` ### Buffer Operations #### `readTagsFromBuffer(buffer: Buffer): Promise<AudioTags>` Reads metadata from an audio buffer. **Parameters:** - `buffer` (Buffer): Audio data buffer **Returns:** Promise<AudioTags> **Example:** ```javascript const audioBuffer = fs.readFileSync('./music/song.mp3'); const tags = await readTagsFromBuffer(audioBuffer); ``` #### `writeTagsToBuffer(buffer: Buffer, tags: AudioTags): Promise<Buffer>` Writes metadata to an audio buffer and returns the modified buffer. **Parameters:** - `buffer` (Buffer): Audio data buffer - `tags` (AudioTags): Metadata to write **Returns:** Promise<Buffer> **Example:** ```javascript const audioBuffer = fs.readFileSync('./music/song.mp3'); const modifiedBuffer = await writeTagsToBuffer(audioBuffer, { title: 'New Title', artist: 'New Artist' }); fs.writeFileSync('./music/modified-song.mp3', modifiedBuffer); ``` ### Cover Art #### `readCoverImage(buffer: Buffer): Promise<Buffer | null>` Reads cover art from an audio buffer. **Parameters:** - `buffer` (Buffer): Audio data buffer **Returns:** Promise<Buffer | null> **Example:** ```javascript const audioBuffer = fs.readFileSync('./music/song.mp3'); const coverImage = await readCoverImage(audioBuffer); if (coverImage) { fs.writeFileSync('./cover.jpg', coverImage); } ``` #### `writeCoverImage(buffer: Buffer, imageData: Buffer): Promise<Buffer>` Writes cover art to an audio buffer and returns the modified buffer. **Parameters:** - `buffer` (Buffer): Audio data buffer - `imageData` (Buffer): Image data (JPEG, PNG, GIF, BMP, TIFF) **Returns:** Promise<Buffer> **Example:** ```javascript const audioBuffer = fs.readFileSync('./music/song.mp3'); const imageBuffer = fs.readFileSync('./cover.jpg'); const modifiedAudio = await writeCoverImage(audioBuffer, imageBuffer); fs.writeFileSync('./music/song-with-cover.mp3', modifiedAudio); ``` ## Types ### AudioTags ```typescript interface AudioTags { title?: string; artist?: string; album?: string; year?: number; genre?: string; track?: number; trackTotal?: number; albumArtist?: string; comment?: string; disc?: number; discTotal?: number; } ``` ## Examples ### Basic Usage ```javascript const { readTags, writeTags } = require('tagpilot-lib'); async function updateSongMetadata() { // Read existing metadata const tags = await readTags('./music/song.mp3'); console.log('Current title:', tags.title); // Update metadata await writeTags('./music/song.mp3', { ...tags, title: 'Updated Title', year: 2024 }); console.log('Metadata updated successfully!'); } ``` ### Cover Art Management ```javascript const { readCoverImage, writeCoverImage } = require('tagpilot-lib'); const fs = require('fs'); async function manageCoverArt() { const audioBuffer = fs.readFileSync('./music/song.mp3'); // Read existing cover art const existingCover = await readCoverImage(audioBuffer); if (existingCover) { console.log('Existing cover art found:', existingCover.length, 'bytes'); fs.writeFileSync('./existing-cover.jpg', existingCover); } // Add new cover art const newCover = fs.readFileSync('./new-cover.jpg'); const modifiedAudio = await writeCoverImage(audioBuffer, newCover); fs.writeFileSync('./music/song-with-new-cover.mp3', modifiedAudio); } ``` ### Batch Processing ```javascript const { readTags, writeTags } = require('tagpilot-lib'); const fs = require('fs'); const path = require('path'); async function batchUpdateMetadata() { const musicDir = './music'; const files = fs.readdirSync(musicDir).filter(f => f.endsWith('.mp3')); for (const file of files) { const filePath = path.join(musicDir, file); const tags = await readTags(filePath); // Update all files with a common album name await writeTags(filePath, { ...tags, album: 'My Greatest Hits', year: 2024 }); console.log(`Updated: ${file}`); } } ``` ### Data URL Generation ```javascript const { readCoverImage } = require('tagpilot-lib'); const fs = require('fs'); function bufferToDataURL(buffer, mimeType = 'image/jpeg') { const base64 = buffer.toString('base64'); return `data:${mimeType};base64,${base64}`; } async function getCoverAsDataURL() { const audioBuffer = fs.readFileSync('./music/song.mp3'); const coverImage = await readCoverImage(audioBuffer); if (coverImage) { const dataURL = bufferToDataURL(coverImage, 'image/jpeg'); console.log('Cover art data URL:', dataURL.substring(0, 100) + '...'); return dataURL; } return null; } ``` ## Supported Formats ### Audio Formats - MP3 (ID3v1, ID3v2) - FLAC - M4A (MPEG-4 Audio) - OGG (Vorbis) - WAV - WAVPACK - AAC - AIFF - OPUS - Speex ### Image Formats - JPEG - PNG - GIF - BMP - TIFF ## Performance tagpilot-lib is built with Rust and NAPI-RS for maximum performance: - **Fast**: Native implementation with minimal overhead - **Memory Efficient**: Direct buffer operations without temporary files - **Scalable**: Handles large audio files efficiently - **Concurrent**: Async operations for better throughput ## Development ### Prerequisites - Node.js 16+ - Rust toolchain - npm or yarn ### Building from Source ```bash git clone https://github.com/yortyrh/tagpilot-lib.git cd tagpilot-lib npm install npm run build ``` ### Running Tests ```bash npm test ``` ### Running Examples ```bash # Read tags example node examples/read-tags-example.js ./music/song.mp3 # Write tags example node examples/write-tags-example.js ./music/song.mp3 # Cover image example node examples/cover-image-buffer-example.js ./music/song.mp3 ./cover.jpg # Read cover image as data URL node examples/read-cover-image-example.js ./music/song.mp3 ``` ## Contributing 1. Fork the repository 2. Create a feature branch 3. Make your changes 4. Add tests for new functionality 5. Run the test suite 6. Submit a pull request ## License MIT License - see [LICENSE](LICENSE) file for details. ## Changelog ### v1.0.0 - Initial release - Audio metadata reading and writing - Cover art extraction and embedding - Buffer-based operations - TypeScript support - Comprehensive examples ## Support - **Issues**: [GitHub Issues](https://github.com/yortyrh/tagpilot-lib/issues) - **Documentation**: [GitHub Wiki](https://github.com/yortyrh/tagpilot-lib/wiki) - **Discussions**: [GitHub Discussions](https://github.com/yortyrh/tagpilot-lib/discussions) ## Acknowledgments - Built with [NAPI-RS](https://napi.rs/) for Node.js native addons - Audio metadata handling powered by [lofty](https://github.com/Serial-ATA/lofty) - Image format detection using [infer](https://github.com/bojand/infer)