Important: This documentation covers Yarn 1 (Classic).
For Yarn 2+ docs and migration guide, see yarnpkg.com.

Package detail

browser-id3-writer

egoroof26.1kMIT6.1.0TypeScript support: included

JavaScript library for writing ID3 tag to MP3 files in browsers and Node.js

browser, nodejs, writer, id3, mp3, audio, tag, library

readme

Browser ID3 Writer

npm package

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

Table of Contents

Installation

Take latest version here or with npm:

npm install browser-id3-writer --save

JS modules

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';

Usage

Browser

Get ArrayBuffer of song

In browsers you should first get ArrayBuffer of the song you would like to add ID3 tag.

FileReader

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>
Fetch

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

Add a tag

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();

Save file

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',
});

Memory control

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

Node.js

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);

Supported frames

array of strings:

  • TPE1 (song artists)
  • TCOM (song composers)
  • TCON (song genres)

string

  • TLAN (language)
  • TIT1 (content group description)
  • TIT2 (song title)
  • TIT3 (song subtitle)
  • TALB (album title)
  • TPE2 (album artist)
  • TPE3 (conductor/performer refinement)
  • TPE4 (interpreted, remixed, or otherwise modified by)
  • TRCK (song number in album): '5' or '5/10'
  • TPOS (album disc number): '1' or '1/3'
  • TPUB (label name)
  • TKEY (initial key)
  • TMED (media type)
  • TDAT (album release date expressed as 'DDMM')
  • TSRC (isrc - international standard recording code)
  • TCOP (copyright message)
  • TEXT (lyricist / text writer)
  • WCOM (commercial information)
  • WCOP (copyright/Legal information)
  • WOAF (official audio file webpage)
  • WOAR (official artist/performer webpage)
  • WOAS (official audio source webpage)
  • WORS (official internet radio station homepage)
  • WPAY (payment)
  • WPUB (publishers official webpage)

integer

  • TLEN (song duration in milliseconds)
  • TYER (album release year)
  • TBPM (beats per minute)

object

  • COMM (comments):
writer.setFrame('COMM', {
  description: 'description here',
  text: 'text here',
  language: 'eng',
});
  • USLT (unsychronised lyrics):
writer.setFrame('USLT', {
  description: 'description here',
  lyrics: 'lyrics here',
  language: 'eng',
});
  • IPLS (involved people list):
writer.setFrame('IPLS', [
  ['role', 'name'],
  ['role', 'name'],
  // ...
]);
  • SYLT (synchronised lyrics):
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.

  • TXXX (user defined text):
writer.setFrame('TXXX', {
  description: 'description here',
  value: 'value here',
});
  • PRIV (private frame):
writer.setFrame('PRIV', {
  id: 'identifier',
  data: dataArrayBuffer,
});
  • APIC (attached picture):
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.

APIC picture types

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

SYLT content types

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

SYLT timestamp formats

Type Name
1 Absolute time, 32 bit sized, using MPEG frames as unit
2 Absolute time, 32 bit sized, using milliseconds as unit

changelog

Changelog

v6.1.0

  • Add TypeScript declaration file

v6.0.0

  • Breaking: Now this library exports only as JS native module (not UMD) and use named export (not default export)

Migration on Nodejs:

// v5 common js
const ID3Writer = require('browser-id3-writer');

// v5 esm interop
import ID3Writer from 'browser-id3-writer';

// v6
import { ID3Writer } from 'browser-id3-writer';

Migration on browsers:

<!-- v5 -->
<script src="browser-id3-writer.js"></script>
<script>
  // your code using ID3Writer
</script>

<!-- v6 -->
<script type="module">
  import { ID3Writer } from 'browser-id3-writer.mjs';
  // your code
</script>

v5.0.0

  • Breaking: Change TDAT frame type from number to string as some values is not possible to represent as number in JS (like 0212), so this change fixes ability to properly encode this frame in some situations:
// v4
writer.setFrame('TDAT', 1234);

// v5
writer.setFrame('TDAT', '1234');
  • Breaking: Drop Babel, so now this library requires ES6 native support (IE isn't supported anymore)
  • Add IPLS and SYLT frames support

v4.4.0

  • Add language support for COMM and USLT frames:
writer.setFrame('USLT', {
  language: 'jpn',
  description: '例えば',
  lyrics: 'サマータイム',
});

v4.3.0

  • Add TLAN, TIT1, TIT3 frames

v4.2.0

  • Remove TKEY frame validation
  • Support TEXT and PRIV frames

v4.1.0

  • Add support for TCOP, TSRC and TDAT frames

v4.0.0

  • Breaking: Now description of APIC frame is encoded in Western encoding by-default. That's because of a problem with iTunes and Finder on macOS. You can still encode it in Unicode encoding by specifying it:
// v3
writer.setFrame('APIC', {
  type: 3,
  data: coverArrayBuffer,
  description: 'Продам гараж',
});

// v4
writer.setFrame('APIC', {
  type: 3,
  data: coverArrayBuffer,
  description: 'Продам гараж',
  useUnicodeEncoding: true, // that's dangerous
});

v3.0.3

  • Decrease library size from 8.68 kB to 7.3 kB in result of using rollup instead of webpack

v3.0.2

  • Now this library works in IE10. Just replaced ArrayBuffer.prototype.slice to TypedArray.prototype.subarray.

v3.0.1

  • No new features / bug fixes, but now readme in both Github and npm will contain exact library version and integrity to include it from CDN.

v3.0.0

  • Breaking: now only minified version of the lib is distributed and without maps. If you are using v2 browser-id3-writer.js from CDN update the link:
<!-- v2 -->
<script src="//unpkg.com/browser-id3-writer@^2.0.0/dist/browser-id3-writer.js"></script>
<script src="//unpkg.com/browser-id3-writer@^2.0.0/dist/browser-id3-writer.min.js"></script>

<!-- v3 -->
<script src="https://unpkg.com/browser-id3-writer@3.0.0"></script>
  • Breaking: no more "Unknown Artist" is added when you set TPE1 or TCOM frames with empty array
  • Breaking: USLT frame now accepts an object with keys description and lyrics:
// v2
writer.setFrame('USLT', 'This is unsychronised lyrics');

// v3
writer.setFrame('USLT', {
  description: '',
  lyrics: 'This is unsychronised lyrics',
});
  • Breaking: APIC frame now accepts an object with keys type (see APIC picture types), data and description:
// v2
writer.setFrame('APIC', coverArrayBuffer);

// v3
writer.setFrame('APIC', {
  type: 3,
  data: coverArrayBuffer,
  description: '',
});
  • Add support for next frames: COMM, TXXX, WCOM, WCOP, WOAF, WOAR, WOAS, WORS, WPAY, WPUB, TKEY, TMED, TPE4, TPE3 and TBPM. See readme for usage info.