GithubHelp home page GithubHelp logo

id3's Introduction

id3.js - Javascript ID3 tag parser

id3.js is a JavaScript library for reading and parsing ID3 tags of MP3 files.

It can parse both ID3v1 and ID3v2 tags within a browser or within Node.

Files can be read from the local disk (Node only), same-origin URLs and File instances (HTML5 File API).

Usage

Install:

$ npm i -S id3js

AJAX

You may parse ID3 tags of a remote MP3 by URL:

<script type="module">
import * as id3 from '//unpkg.com/id3js@^2/lib/id3.js';

id3.fromUrl('/audio/track.mp3').then((tags) => {
  // tags now contains v1, v2 and merged tags
});
</script>

This works by sending a HEAD request for the file and, based on the response, sending subsequent Range requests for the ID3 tags.

This is rather efficient as there is no need for the entire file to be downloaded.

Local Files

You may parse ID3 tags of a local file in Node:

import * as id3 from 'id3js';

id3.fromPath('./test.mp3').then((tags) => {
  // tags now contains v1, v2 and merged tags
});

Keep in mind, Node must be run with --experimental-modules for this to be imported and it cannot be used with require.

File inputs (HTML5)

You may parse ID3 tags of a file input:

<input type="file">

<script type="module">
import * as id3 from '//unpkg.com/id3js@^2/lib/id3.js';

document
  .querySelector('input[type="file"]')
  .addEventListener('change', async (e) => {
    const tags = await id3.fromFile(e.currentTarget.files[0]);
    // tags now contains v1, v2 and merged tags
  });
</script>

This will read the data from the File instance using slices, so the entire file is not loaded into memory but rather only the tags.

Images

An MP3 may have images embedded in the ID3 tags. If this is the case, they can be accessed through the tag.images property and will look like so:

{
  "type": "cover-front",
  "mime": "image/jpeg",
  "description": null,
  "data": ArrayBuffer
}

As you can see, the data is provided as an ArrayBuffer. To access it, you may use a DataView or typed array such as Uint8Array.

License

MIT

id3's People

Contributors

43081j avatar alecgorge 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  avatar  avatar  avatar  avatar  avatar

id3's Issues

Implement correct string encoding

Currently strings are read as-is, byte by byte using a Uint8Array. The downside of this is that characters in high UTF-16/UTF-8 ranges will likely break or be malformed.

I'm not too sure about this but I'll assume it is an issue anyhow.

See here.

Nodered in Docker run issue

I need id3js in Nodered inside Docker container.
I installed id3 running npm i -S id3js in /data directory. According to Nodered manual (https://nodered.org/docs/user-guide/writing-functions#loading-additional-modules)
I have added id3js:require('id3js') to functionGlobalContext section. After container retsart I have this on logs:

> [email protected] start /usr/src/node-red
> node $NODE_OPTIONS node_modules/node-red/red.js $FLOWS "--userDir" "/data"

Error loading settings file: /data/settings.js
/data/node_modules/id3js/lib/id3.js:1
import { parse } from './id3Tag.js';
       ^

SyntaxError: Unexpected token {
    at Module._compile (internal/modules/cjs/loader.js:723:23)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:789:10)
    at Module.load (internal/modules/cjs/loader.js:653:32)
    at tryModuleLoad (internal/modules/cjs/loader.js:593:12)
    at Function.Module._load (internal/modules/cjs/loader.js:585:3)
    at Module.require (internal/modules/cjs/loader.js:692:17)
    at require (internal/modules/cjs/helpers.js:25:18)
    at Object.<anonymous> (/data/settings.js:220:8)
    at Module._compile (internal/modules/cjs/loader.js:778:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:789:10)

Extract generic Reader and consolidate Dataview extensions

Hi there,

I just found your nice ID3 Parser and started a similar project for parsing MP4 Tags using your DataView extensions and the Reader interface.

It would be awesome if we could extract these two to a generic module both id3js and mp4js could depend on (or the other way around, like grunt-plugins via npm's peer dependencies) so one did not have to maintain the same code twice.

I had to make minor changes to your DataView#getString method as the values extracted from MP4 in nodejs could not be decoded via decodeURIComponent (Malformed URI errors). I hope I can shed some light on the why, so there are no differences in neither dataview.js nor reader.js.

Most of all I wanted to start writing a parser for FLAC and OGG Metadata and again I'd be needing your Reader for it, as the browser compatibility is really nice!

All in all this looks like a project like taglib, that could provide a generic interface for reading tags from different file types. Tell me what you think about it.

Kindly,

Lennart

P.S.: mp4js is here

Integration id3js and meteor

Hello,
I'm trying to integrate this lib to a Meteor project.
To do this I added meteorhacks package :
meteor add meteorhacks:npm
Then in the packages.json I wrote :
{ "id3js": "1.1.3" }
And then I used id3js like that :

var id3js = Meteor.npmRequire('id3js');
id3js({ 
    file: url, 
    type: id3js.OPEN_URI 
}, function(err, tags) {
    if(err) console.log(err);
    console.log(tags);
});

And it crash like that :

I20160129-14:45:21.813(1)? Exception while invoking method 'podcastAdd' ReferenceError: XMLHttpRequest is not defined
I20160129-14:45:21.814(1)?     at [object Object].Reader.ajax (/Users/ogre/Work/6102/ogre/from.brussels/.meteor/local/isopacks/npm-container/npm/node_modules/id3js/dist/id3.js:137:17)
I20160129-14:45:21.814(1)?     at [object Object].Reader.open (/Users/ogre/Work/6102/ogre/from.brussels/.meteor/local/isopacks/npm-container/npm/node_modules/id3js/dist/id3.js:50:10)
I20160129-14:45:21.814(1)?     at id3 (/Users/ogre/Work/6102/ogre/from.brussels/.meteor/local/isopacks/npm-container/npm/node_modules/id3js/dist/id3.js:961:10)
I20160129-14:45:21.814(1)?     at [object Object].Meteor.methods.podcastAdd (lib/methods/podcast.js:14:4)
I20160129-14:45:21.814(1)?     at maybeAuditArgumentChecks (livedata_server.js:1698:12)
I20160129-14:45:21.815(1)?     at livedata_server.js:708:19
I20160129-14:45:21.815(1)?     at [object Object]._.extend.withValue (packages/meteor/dynamics_nodejs.js:56:1)
I20160129-14:45:21.815(1)?     at livedata_server.js:706:40
I20160129-14:45:21.815(1)?     at [object Object]._.extend.withValue (packages/meteor/dynamics_nodejs.js:56:1)
I20160129-14:45:21.815(1)?     at livedata_server.js:704

Any idea to solve it properly ?

Years are returned as null

This library seems to read most tags fine, but always returns a year: null field, whether or not the song has a year in its tags.

Support promises instead of callbacks

Interested in making this library support promises to allow for using async/await. Wanted to start a discussion over whether it's worth the effort support both callbacks and Promises, or whether to do a semver major version change and support only promises going forward?

Thought?

Range Error

d:\Code\mp3\node_modules\id3js\dist\id3.js:928
                                                        frameBit = dv.getUint8(p
osition + i);
                                                                      ^
RangeError: Offset is outside the bounds of the DataView
    at DataView.getUint8 (native)
    at d:\Code\mp3\node_modules\id3js\dist\id3.js:928:22
    at d:\Code\mp3\node_modules\id3js\dist\id3.js:118:4
    at FSReqWrap.wrapper [as oncomplete] (fs.js:529:17)

DeprecationWarning: Calling an asynchronous function without callback is deprecated

i got problem with id3js when i try to get tags
i ask the question on stack you can check it here
and i solve it by changing thsose lines

fs.open(self.file, 'r', function(err, fd) {
						if(err) {
							return callback(err);
						}
						self.fd = fd;
						callback();
					}); 

to

fs.openSync(self.file, 'r', function(err, fd) {
						if(err) {
							return callback(err);
						}
						self.fd = fd;
						callback();
					});

Index out of range [when parsing images]

The parser freaks out on some embedded images, it seems completely random as to what images do and don't trip it:

Uncaught node.js Error 

Error: Index out of range.
    at Object.ID3Frame.parse (/home/damon/projects/tunes/node_modules/id3js/dist/id3.js:696:13)
    at eval (/home/damon/projects/tunes/node_modules/id3js/dist/id3.js:943:24)
    at eval (/home/damon/projects/tunes/node_modules/id3js/dist/id3.js:118:4)
    at Object.wrapper [as oncomplete] (fs.js:454:17)

An example of one it's breaking on: http://pastebin.com/1RRDqknW

As you can see it kinda just cuts off part way through parsing the image.

If I delete the image from the ID3 tag, it parses that file fine and then breaks on a later one.

The only thing the breaking images seem to have had in common is that they're mostly fairly high res (both this and the previous one were 800x800) though there have been ones smaller such as 400x360 which have broken in the same way.

There should really be an option to skip parsing images so those getting this issue can just bypass the image parsing and so this module will still be usable.

Usage in a web browser with a bundler

Hi.
The readme provides an example of using this library in a web browser via a <script/> tag:

<input type="file">

<script type="module">
import * as id3 from '//unpkg.com/id3js@^2/lib/id3.js';

document
  .querySelector('input[type="file"]')
  .addEventListener('change', async (e) => {
    const tags = await id3.fromFile(e.currentTarget.files[0]);
    // tags now contains v1, v2 and merged tags
  });
</script>

That's not the most conventional way of using packages in a web browser.
The most conventional one is using a "bundler" like Webpack and importing directly from npm packages.

Example:

import { fromFile } from 'id3js'

const tags = await fromFile(file)
// tags now contains v1, v2 and merged tags

But it throws an error:

Module not found: Can't resolve 'fs' in '...\node_modules\id3js\lib'
node_modules/id3js/lib/localReader.js

So the main file of the library looks like this:
https://unpkg.com/browse/[email protected]/lib/id3.js

{
  "name": "id3js",
  "version": "2.1.1",
  "main": "./lib/id3.js",
  "types": "./lib/id3.d.ts",
  "type": "module",
  ...
}
...

export async function fromPath(path) {
  const mod = await import('./localReader.js');
  return fromReader(new mod.LocalReader(path));
}

...

For some reason, it seems to execute that await import() statement even though I didn't call that specific function called fromPath().
The error happens before calling any function, already at the import level:

import * as Id3 from 'id3js'

I've fixed that in my fork by introducing two separate exports: /browser and /node.

See the updated README:
https://github.com/catamphetamine/id3

It now works in my setup (Webpack).

I've also added a function for getting an album cover image data URL.

PRIV size not SynchSafeInt

I have mp3s where ID3 is not finding the embedded image. Win Explorer, TagScanner, Quintessential, all find the artwork.
I see that part of the problem, is that when ID3 finds the PRIV tag, it treats it as SynchSafe, when it shouldn't. The APIC tag comes right after the PRIV tag. The size calculation is correct if not treated as SynchSafe.

UTF-8 support

Edit: This was not a UTF-8 issue, it was an issue ID3v2.4 tracks not being read correctly

need to close Reader after parsing is finished

id3() creates a new Reader and calls its .open method but never closes it. If you don't cycle through lots of files quickly, this does not seem to be a problem (presumably because the Reader eventually gets destroyed and the file handle along with it), but if you churn through a lot of files very quickly, this can cause you to run out of file handles and id3 eventually starts failing with "Could not open specified file." The fix is simply to put a call to handle.close() inside the ID3Tag.parse callback.

image tag does not contain the image that other software uses to display album art

Hi,

When I was trying to use this library I noticed that the image tag contains a different image than I expected to see. Correct me if I'm wrong, but what I think is happening is that each APIC or PIC record is getting read but only the last one is saved. I think this is because there is only one image property on the tags object.

// Code snippet from id3.js
} else if(header.id === 'APIC') {
  var encoding = dv.getUint8(10),
    image = {
      type: null,
      mime: null,
      description: null,
     data: null
    };
  var variableStart = 11, variableLength = 0;
  for(var i = variableStart;;i++) {
    if(dv.getUint8(i) === 0x00) {
      variableLength = i - variableStart;
      break;
    }
  }
  image.mime = dv.getString(variableLength, variableStart);
  image.type = ID3Frame.imageTypes[dv.getUint8(variableStart + variableLength + 1)] || 'other';
  variableStart += variableLength + 2;
  variableLength = 0;
  for(var i = variableStart;; i++) {
    if(dv.getUint8(i) === 0x00) {
      variableLength = i - variableStart;
      break;
    }
  }
  image.description = (variableLength === 0 ? null : dv.getString(variableLength, variableStart));
  image.data = buffer.slice(variableStart + 1);
  result.value = image;
}

Anyone have a workaround for this?

dv.getUint8(i) Index out of range

node_modules/id3js/dist/id3.js:696

if(dv.getUint8(i) === 0x00) {
      ^
Error: Index out of range.
    at Object.ID3Frame.parse (./node_modules/id3js/dist/id3.js:696:13)

I can repeat this with one mp3 I have. I'm currently trying to figure out what in the mp3 is failing.

Documentation need for using the module in. and html page

If I have a number of script files, normally I include them as:

<script src="js/foo.js"></script>
<script src="js/bar.js"></script>

but if one of those files needs to use the module, I can't figure out how to import everything so it works.

If I do it this way:

<script type="module">
    import * as id3 from './js/vendor/id3js/id3.js';
 </script> 
<script src="js/foo.js"></script>
<script src="js/bar.js"></script>

id3 isn't visible in the script files
while if I include the script files in the module:

  <script type="module">
    import * as id3 from './js/vendor/id3js/id3.js';
    import './js/foo.js';
    import './js/bar.js';
  </script>

I can't use any of their code (I suspect I would have to rewrite everything to be a module)
Is there a way to use this in non-modular javascript?

Support ID3v2.2 tags and earlier

Tags with a major version of 2 of less use a slightly different format for frame headers and such.

As the >2.3 docs state for a frame header:

Frame ID      $xx xx xx xx  (four characters)
Size          4 * %0xxxxxxx
Flags         $xx xx

Whereas a <=2.2 frame will be like so:

Frame ID    $xx xx xx
Size        3 * %0xxxxxxx

How to create a valid image url from ArrayBuffer

Thanks for you wonderful library, I've already tested this and this before, but yours is way faster.

I've got a question about how to use the returned image data. I'm currently doing it that way:

var arrayBufferView = new Uint8Array(tags.v2.image.data);
tags.v2.image.mime = tags.v2.image.mime || 'image/jpeg';
var blob = new Blob([arrayBufferView], {type: tags.v2.image.mime});
var urlCreator = window.URL || window.webkitURL;
var imageUrl = urlCreator.createObjectURL(blob);

This works sometimes, however most of the time it fails. Sometimes it just doesn't create a valid file URL, most of the time it creates only parts of the picture and rarely it works perfectly:

All of these mp3 covers are shown correctly in my windows explorer.
The ones that have a nice placeholder image don't have a tags.v2.image attribute. (which is alright, better have nothing then something wrong...)
The last one somehow created an invalid URL, the others show only a part of the image.
Interestingly the Beatles one doesn't show up in my explorer, but your library parses it correctly.

Am I doing this wrong?

'null' tags returned for correctly tagged file... ?

Hi,

I'm trying to create a small app, using webpack, with an audio file located in my project folder. Here is my code:

import fs from 'fs'
import id3 from 'id3js'

tagReader: () => {
  id3('./../data/test.ogg', function(err, tags) {
    console.log('tags: ', tags)
  })
}

I checked the file which is correctly tagged (it is an .ogg), but for some reason I can't understand, all properties remains 'null'... Someone having the same issue, and/or able to give me some help? Thnak you in advance!

Genre Failure

Hello,
First off I want to complement you on a very good library however there is one rather serious error.

In a ID3V2 tag you can have any genre, not just the ones you listed in the array. If you read a file that has a genre that is not in your array then the app crashses.

/id3js/dist/id3.js:231
var bomInt = this.getUint16(offset);

Is there any plans to extract the actual id3v2 genre string instead of converting it to a number?

Write support

Thanks for the great project. Are there any plans for write support?

Get the encoding of the ID3

Hi,

I am trying to get the text encoding of the ID3 and also the text encoding of each tag.
How do I do that?

Thanks

Metadata from a file stored in GridFS

Hi,

Can i used this library to somehow to read a file stored in gridfs and give me the tags?

I tried giving a stream in both mode

  1. OPEN_LOCAL -> it give TypeError: path must be a string error.
  2. OPEN_FILE -> Browser does not have support for the File API and/or ArrayBuffers

So any other way to do this?

Error with Node 11.5 [10.0.0+]"Callback must be a function"

TypeError [ERR_INVALID_CALLBACK]: Callback must be a function
at makeCallback (fs.js:143:11)
at Object.close (fs.js:401:20)
at Reader.close (/server/node_modules/id3js/dist/id3.js:69:7)
at /server/node_modules/id3js/dist/id3.js:967:12
at process (/server/node_modules/id3js/dist/id3.js:837:7)
at /server/node_modules/id3js/dist/id3.js:950:6
at /server/node_modules/id3js/dist/id3.js:118:4
at FSReqCallback.wrapper [as oncomplete] (fs.js:480:5)

Running against Node 11.5, but according to fs docs, a callback was required since 10.
To test, I inserted a noop function and it worked fine.

Double-callbacks

In rare cases the callback is called twice due to the fact that v1 and v2 tags are processed asynchronously, in parallel essentially.

If parsing of v1 tags fails and v2 tags have been processed, it should call the callback with an error, otherwise it should silently fail.

This way the v1 tag will be empty and the v2 tag can still parse, or not.

getUint16 - Index out of range

Processing files having a long name, the following error appears:

/home/pi/nodejs/snjsp/node_modules/id3js/dist/id3.js:231
                        var bomInt = this.getUint16(offset);
                                          ^
Error: Index out of range.
    at DataView.getStringUtf16 (/home/pi/nodejs/snjsp/node_modules/id3js/dist/id3.js:231:22)
    at Object.ID3Frame.parse (/home/pi/nodejs/snjsp/node_modules/id3js/dist/id3.js:668:24)
    at /home/pi/nodejs/snjsp/node_modules/id3js/dist/id3.js:943:24
    at /home/pi/nodejs/snjsp/node_modules/id3js/dist/id3.js:118:4
    at Object.wrapper [as oncomplete] (fs.js:454:17)

By example, this error occurs while reading tags for file in:
/home/pi/music/dd/Aphrodites Child/1968 - End of the World/01 - End Of The World.mp3

Renaming the file of the folder with a shorter name solves the issue.

Regards,

kvalium

DataView.getStringUtf16 mangles strings when useBuffer is true

This issue looks like it's fairly simple: getStringUtf16 builds an array of 16-bit character values in an array and then attempts to create an output string using new Buffer(str). Unfortunately that expects an array of octets and discards the high byte in each of the two-byte values given. This means the method works if every character is actually ASCII, but fails if any UTF-16 pairs are found.

The simple solution I went with was:

// str is an array of two-byte pairs
var buf = new Buffer(str.length*2);
for (var i = 0; i < str.length; i++) {
    buf.writeUInt16LE(str[i], i*2);
}
return buf.toString('utf16le');

And that solved my problem.

ID3 2.0 Release

I have been gone a long time and have had very little time to maintain this repo (I still do lack time, in fact). However, I am going to give 2.0 a go.

This won't be a release of any new functionality but rather a major cleanup/refactor and improvement of build process, along with any possible bug fixes brought up since I was last around.

TODO

  • Add gulp along with necessary tasks
  • Use browserify instead of manually trying commonjs/amd/globals
  • Move dependencies to NPM instead (readerjs)
  • Move dataview-extras to a separate class rather than extending the native DataView
  • Fix any lint issues
  • Fix major bugs
    • #6, add a guard or figure some nicer way
    • Take a look at the (hacky) decodeURIComponent(escape(...)) calls

Notes

The current branch being used for this is feature/browserify-cleanup, not so appropriately named but it'll do for now.

The browserify output needs testing. I have tried it quickly with an mp3 in my browser and it worked fine.

Please do help if you can, this is not such a simple task and I do not have the capacity to do it alone in an acceptable time frame

J

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.