Giter VIP home page Giter VIP logo

browser-id3-writer's Introduction

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

browser-id3-writer's People

Contributors

altearius avatar dependabot[bot] avatar egoroof avatar eidoriantan avatar greenkeeperio-bot avatar jamsinclair avatar npmcdn-to-unpkg-bot avatar tbhartman avatar timdream avatar tognee avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

browser-id3-writer's Issues

MP4s?

MP4 videos seems to fail. MP3's worked fine.

stream demo browser support

I've tried https://egoroof.ru/browser-id3-writer/stream out of interest for respondWith API...

  • chrome works
  • firefox kinda works (downloads .part file, fails to rename? or download is incomplete?)
  • firefox nightly doesn't work (page says feature not supported)
  • safari doesn't work (downloads html instead)
  • safari TP doesn't work (same)

Tested on macOS 11.1 (intel).
I understand safari issues, as the API is not supported per caniuse.com, but firefox breakage is odd...

TIT 1 is not supported

TIT1 is not supported

Uncaught Error: Unsupported frame TIT1 at t.setFrame (browser-id3-writer.js:1:4487) at FileReader.reader.onload (track-uploader.js:18:30)

writer.setFrame('TIT1', 'MP3 TAGGED SONG');

TypeScript types?

Hi there, thanks for this package, very useful!

Would you be open to publishing TypeScript types with this?

Workaround: Switch to node-id3, which is written in TypeScript

Podcast tag support?

This ID3 Editor (http://www.pa-software.com/id3editor/) allows control over some custom tags use by Apple and Google for podcasting called "feed" and "description". Any chance this tool could support those?

This article recommends that I use them: https://prestopod.com/how-to-add-id3-tags-to-your-podcast-mp3-file/

When I added those tags, iTunes picked them up instantly and categorized the mp3 files under podcasts and subscribed me to the show.

The help information from the ID3 Editor CLI tool has this about the podcast tags:

Podcast information:
    -pcon                Marks the track as a podcast
    -pcof                Removes the podcast mark from the track
    -pcid  utext         Set the podcast ID
    -pcfd  text          Set the podcast feed
    -pcds  utext         Set the podcast description

WXXX frame for links

My player (AIMP) don't want to recognize WOAS frame. Only WXXX for link.
Can you implement this frame in you script, please?

Document Edit

Hi,

Sorry to disturb and I am quite new to NodeJS, I was recently using this library to write ID3 tags. I noticed that in the documents regarding the library it says an ArrayBuffer is expected as the initial value for the Writer and I was not aware that a normal Buffer could be also provided until I had a look at the code in the ID3Writer.js file.

Would it be possible to change it to say that Buffer is also allowed as well as ArrayBuffer? Reading other articles and pages it states that this is the standard now? I don't know if this is the case, if it is not sorry for this issue.

Thank you for your work! :)

Adding base64 image data to APIC tag

A few months ago, as an experiment, I was attempting to embed a generated QR code (using javascript) upon form submission into an APIC ID3 tag. The QR code was generated base64 image data. I was not successful dealing with arrayBuffer conversions or other issues and had to move on to other projects. I attached (partial) where the code was left before I gave up.

I'll review the latest version of the library and revisit this again soon but posting here in case it is of interest to others as suggested by the developer.

base64-arrayBuffer-test-code-snippet.txt

Album cover failed to be written into the file

I am testing with a file like this.

The picture will be encoded into the file but with a few bytes missing from the beginning, therefore it will not be decoded correctly (this is a conclusion I found from extracting the encoded bytes with ID3 Reader).

This can be reproduced with the online demo. I couldn't see obvious issues by looking at the code for 10 min, so I decided to file an issue first. Will look into it later.

Thanks for the great work!

Add TEXT tag

"TEXT [#TEXT Lyricist/Text writer]"
Add the option for TEXT tagging

Any supports for frame COMM?

Hi, I tried to write a comment into the tag but receive this error.

browser-id3-writer.js:283
	                        throw new Error('Unsupported frame ' + frameName);
	                        ^

Error: Unsupported frame COMM

I know COMM isn't in the supported frame, but will there any supports in the future?

How do I add empty id3 buffer to my mp3 buffer

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

How do I add empty id3 buffer to my mp3 buffer?

removing TextEncoder support for UTF-16

TextEncoder support for UTF-16 is already removed from Encoding Standard (whatwg/encoding#18) and only supports utf8. Chromium is going to remove utf16 soon https://bugs.chromium.org/p/chromium/issues/detail?id=595351. This is sad because this library use utf16 to encode most frames data and can't use utf8 because id3v2.3 doesn't support it.

We can't use id3v2.4 (which supports utf8) because Windows Explorer and Windows Media Player doesn't support it even in windows 10. Also it isn't supported by some other players and devices (as I know).

The only way I see is to use a polyfill like https://github.com/inexorabletash/text-encoding (it's about + 100KB of size, omg) but it now only supports utf16 with a non-standard behavior so it won't work if we're using the polyfill in a browser that natively supports the TextEncoder API, since the polyfill won't be used. So we'll need to force the polyfill to be used by refuse browser native API. This may have some cons.

Or we can adapt that polyfill to us: change it's namespace. This won't result any conflicts.

But anyway using a polyfill will increase size of this library.

writing tags increases file size much larger than needed

if I write 7 tags into the smallest possible mp3 (72 bytes), the output file is > 4k. If I write the same tags using other libraries, the file is ~362 bytes. Attached are two files, one written by your library, and one written by kid3. Looking at both files in a hex editor, your library adds 4096 null bytes between the ID3 header and the first Mp3 frame.

I see at: https://github.com/egoroof/browser-id3-writer/blob/master/src/ID3Writer.js#L109 that a 4k buffer is added to the ID3 header when being written.

tracks.zip

USLT and COMM frames don't allow entry of langage codes other than English

Text fields such as USLT and COMM currently save a three digit language code, as per the ID3 spec, but this is hardcoded to eng:

const langEng = [0x65, 0x6e, 0x67];

writeBytes = writeBytes.concat(langEng); // language

Can support please be added to allow this value to be passed though along with the other parameters?
Thanks.

SYLT language error

I'm trying to set SYLT (synchronised lyrics) with language = 'eng' and the lyrics shows wrong:

image

If I don't add a language property all the lyrics shows correctly:

image

Support nodejs

As of today, there aren't any pure JS IDv3 writers for nodejs. It would be very great if it worked for nodejs.

This would probably require a support for Buffer instead of ArrayBuffer and a polyfill for TextEncoder.

[Feature Request] Support for AAC or M4A files?

The current library works correctly on mp3 files. But fails on AAC or M4A files that have their headers differently.
Currently there is no library in JS that works with AAC or M4A files.

Any plans on adding this feature?

How to load a song and add id3 tag in nodejs ?

The song is downloaded successfully and exists in the specified file path but the buffer is never written. Any thoughts?

    request
    .get(url)
    .pipe(fs.createWriteStream(fileName), ((fileName) => {
      console.log(`${fileName} Downloaded!`)

      let songBuffer = fs.readFileSync('./' + fileName);
      let coverBuffer = fs.readFileSync('./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();

      let taggedSongBuffer = Buffer.from(writer.arrayBuffer);
      fs.writeFileSync('./' + fileName, taggedSongBuffer);
      
    })(fileName));

Destroys m4a files

Works fine on mp3 files, but m4a files are no longer playable after writing, any ideas?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.