JavaScript library for writing ID3 tag to MP3 files in browsers and Node.js
JavaScript library for writing ID3 (v2.3) tag to MP3 files in browsers and Node.js. It can't read the tag so use another lib to do it.
Note: the library removes existing ID3 tag (v2.2, v2.3 and v2.4).
Here is an online demonstration: egoroof.github.io/browser-id3-writer/
Find the changelog in CHANGELOG.md
Take latest version here or with npm:
npm install browser-id3-writer --save
The library is only deployed in native JS modules, so in browsers you have to use script
with type module
:
<script type="module">
import { ID3Writer } from 'https://your-host/browser-id3-writer.mjs';
// your code here..
</script>
Or bundle the library to your code.
In Nodejs it imports easily:
import { ID3Writer } from 'browser-id3-writer';
In browsers you should first get ArrayBuffer of the song you would like to add ID3 tag.
For example you can create file input and use FileReader:
<input type="file" id="file" accept="audio/mpeg" />
<script type="module">
import { ID3Writer } from 'https://your-host/browser-id3-writer.mjs';
document.getElementById('file').addEventListener('change', function () {
if (this.files.length === 0) {
return;
}
const reader = new FileReader();
reader.onload = function () {
const arrayBuffer = reader.result;
// go next
};
reader.onerror = function () {
// handle error
console.error('Reader error', reader.error);
};
reader.readAsArrayBuffer(this.files[0]);
});
</script>
To get arrayBuffer from a remote server you can use Fetch:
const request = await fetch(urlToSongFile);
if (!request.ok) {
// handle error
console.error(`Unable to fetch ${urlToSongFile}`);
}
const arrayBuffer = await request.arrayBuffer();
// go next
Create a new ID3Writer
instance with arrayBuffer of your song, set frames and add a tag:
// arrayBuffer of song or empty arrayBuffer if you just want only id3 tag without song
const writer = new ID3Writer(arrayBuffer);
writer
.setFrame('TIT2', 'Home')
.setFrame('TPE1', ['Eminem', '50 Cent'])
.setFrame('TALB', 'Friday Night Lights')
.setFrame('TYER', 2004)
.setFrame('TRCK', '6/8')
.setFrame('TCON', ['Soundtrack'])
.setFrame('TBPM', 128)
.setFrame('WPAY', 'https://google.com')
.setFrame('TKEY', 'Fbm')
.setFrame('APIC', {
type: 3,
data: coverArrayBuffer,
description: 'Super picture',
});
writer.addTag();
Now you can save it to file as you want:
const taggedSongBuffer = writer.arrayBuffer;
const blob = writer.getBlob();
const url = writer.getURL();
For example you can save file using FileSaver.js:
saveAs(blob, 'song with tags.mp3');
If you are writing chromium extension you can save file using Downloads API:
chrome.downloads.download({
url: url,
filename: 'song with tags.mp3',
});
When you generate URLs via writer.getURL()
you should know
that whole file is kept in memory until you close the page or move to another one.
So if you generate lots of URLs in a single page you should manually free memory
after you finish downloading file:
URL.revokeObjectURL(url); // if you know url or
writer.revokeURL(); // if you have access to writer
Simple example with blocking IO:
import { ID3Writer } from 'browser-id3-writer';
import { readFileSync, writeFileSync } from 'fs';
const songBuffer = readFileSync('path_to_song.mp3');
const coverBuffer = readFileSync('path_to_cover.jpg');
const writer = new ID3Writer(songBuffer);
writer
.setFrame('TIT2', 'Home')
.setFrame('TPE1', ['Eminem', '50 Cent'])
.setFrame('TALB', 'Friday Night Lights')
.setFrame('TYER', 2004)
.setFrame('APIC', {
type: 3,
data: coverBuffer,
description: 'Super picture',
});
writer.addTag();
const taggedSongBuffer = Buffer.from(writer.arrayBuffer);
writeFileSync('song_with_tags.mp3', taggedSongBuffer);
You can also create only ID3 tag without song and use it as you want:
const writer = new ID3Writer(Buffer.alloc(0));
writer.padding = 0; // default 4096
writer.setFrame('TIT2', 'Home');
writer.addTag();
const id3Buffer = Buffer.from(writer.arrayBuffer);
array of strings:
string
integer
object
writer.setFrame('COMM', {
description: 'description here',
text: 'text here',
language: 'eng',
});
writer.setFrame('USLT', {
description: 'description here',
lyrics: 'lyrics here',
language: 'eng',
});
writer.setFrame('IPLS', [
['role', 'name'],
['role', 'name'],
// ...
]);
writer.setFrame('SYLT', {
type: 1,
text: [
['lyrics here', 0],
['lyrics here', 3500],
// ...
],
timestampFormat: 2,
language: 'eng',
description: 'description',
});
text
is an array of arrays of string and integer.
writer.setFrame('TXXX', {
description: 'description here',
value: 'value here',
});
writer.setFrame('PRIV', {
id: 'identifier',
data: dataArrayBuffer,
});
writer.setFrame('APIC', {
type: 3,
data: coverArrayBuffer,
description: 'description here',
useUnicodeEncoding: false,
});
useUnicodeEncoding
should only be true
when description contains non-Western characters.
When it's set to true
some program might not be able to read the picture correctly.
See #42.
Type | Name |
---|---|
0 | Other |
1 | 32x32 pixels 'file icon' (PNG only) |
2 | Other file icon |
3 | Cover (front) |
4 | Cover (back) |
5 | Leaflet page |
6 | Media (e.g. label side of CD) |
7 | Lead artist/lead performer/soloist |
8 | Artist/performer |
9 | Conductor |
10 | Band/Orchestra |
11 | Composer |
12 | Lyricist/text writer |
13 | Recording location |
14 | During recording |
15 | During performance |
16 | Movie/video screen capture |
17 | A bright coloured fish |
18 | Illustration |
19 | Band/artist logotype |
20 | Publisher/Studio logotype |
Type | Name |
---|---|
0 | Other |
1 | Lyrics |
2 | Text transcription |
3 | Movement/part name (e.g. "Adagio") |
4 | Events (e.g. "Don Quijote enters the stage") |
5 | Chord (e.g. "Bb F Fsus") |
6 | Trivia/'pop up' information |
Type | Name |
---|---|
1 | Absolute time, 32 bit sized, using MPEG frames as unit |
2 | Absolute time, 32 bit sized, using milliseconds as unit |