Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
A simple ID3v2.3.0 Tag Reader written for node.js
var fs = require('fs');
var Buffer = require('buffer').Buffer;
var id3Reader = new (function(){
// Credits to esailja //
var self = this;
function id3Size( buffer ) {
var integer = ( ( buffer[0] & 0x7F ) << 21 ) |
( ( buffer[1] & 0x7F ) << 14 ) |
( ( buffer[2] & 0x7F ) << 7 ) |
( buffer[3] & 0x7F );
return integer;
}
var PIC_TYPE = ["Other","32x32 pixels 'file icon' (PNG only)","Other file icon","Cover (front)","Cover (back)","Leaflet page","Media (e.g. lable side of CD)","Lead artist/lead performer/soloist","Artist/performer","Conductor","Movie/video screen capture","A bright coloured fish","Illustration","Band/artist logotype","Publisher/Studio logotype","Band/Orchestra","Composer","Lyricist/text writer","Recording Location","During recording","During performance"];
var GENRES = ["Blues","Classic Rock","Country","Dance","Disco","Funk","Grunge","Hip-Hop","Jazz","Metal","New Age","Oldies","Other","Pop","R&B","Rap","Reggae","Rock","Techno","Industrial","Alternative","Ska","Death Metal","Pranks","Soundtrack","Euro-Techno","Ambient","Trip-Hop","Vocal","Jazz+Funk","Fusion","Trance","Classical","Instrumental","Acid","House","Game","Sound Clip","Gospel","Noise","AlternRock","Bass","Soul","Punk","Space","Meditative","Instrumental Pop","Instrumental Rock","Ethnic","Gothic","Darkwave","Techno-Industrial","Electronic","Pop-Folk","Eurodance","Dream","Southern Rock","Comedy","Cult","Gangsta","Top 40","Christian Rap","Pop/Funk","Jungle","Native American","Cabaret","New Wave","Psychadelic","Rave","Showtunes","Trailer","Lo-Fi","Tribal","Acid Punk","Acid Jazz","Polka","Retro","Musical","Rock & Roll","Hard Rock","Folk","Folk-Rock","National Folk","Swing","Fast Fusion","Bebob","Latin","Revival","Celtic","Bluegrass","Avantgarde","Gothic Rock","Progressive Rock","Psychedelic Rock","Symphonic Rock","Slow Rock","Big Band","Chorus","Easy Listening","Acoustic","Humour","Speech","Chanson","Opera","Chamber Music","Sonata","Symphony","Booty Bass","Primus","Porn Groove","Satire","Slow Jam","Club","Tango","Samba","Folklore","Ballad","Power Ballad","Rhythmic Soul","Freestyle","Duet","Punk Rock","Drum Solo","A capella","Euro-House","Dance Hall"];
var TAGS = {
"AENC": "Audio encryption",
"APIC": "Attached picture",
"COMM": "Comments",
"COMR": "Commercial frame",
"ENCR": "Encryption method registration",
"EQUA": "Equalization",
"ETCO": "Event timing codes",
"GEOB": "General encapsulated object",
"GRID": "Group identification registration",
"IPLS": "Involved people list",
"LINK": "Linked information",
"MCDI": "Music CD identifier",
"MLLT": "MPEG location lookup table",
"OWNE": "Ownership frame",
"PRIV": "Private frame",
"PCNT": "Play counter",
"POPM": "Popularimeter",
"POSS": "Position synchronisation frame",
"RBUF": "Recommended buffer size",
"RVAD": "Relative volume adjustment",
"RVRB": "Reverb",
"SYLT": "Synchronized lyric/text",
"SYTC": "Synchronized tempo codes",
"TALB": "Album",
"TBPM": "BPM",
"TCOM": "Composer",
"TCON": "Genre",
"TCOP": "Copyright message",
"TDAT": "Date",
"TDLY": "Playlist delay",
"TENC": "Encoded by",
"TEXT": "Lyricist",
"TFLT": "File type",
"TIME": "Time",
"TIT1": "Content group description",
"TIT2": "Title",
"TIT3": "Subtitle",
"TKEY": "Initial key",
"TLAN": "Language(s)",
"TLEN": "Length",
"TMED": "Media type",
"TOAL": "Original album",
"TOFN": "Original filename",
"TOLY": "Original lyricist",
"TOPE": "Original artist",
"TORY": "Original release year",
"TOWN": "File owner",
"TPE1": "Artist",
"TPE2": "Band",
"TPE3": "Conductor",
"TPE4": "Interpreted, remixed, or otherwise modified by",
"TPOS": "Part of a set",
"TPUB": "Publisher",
"TRCK": "Track number",
"TRDA": "Recording dates",
"TRSN": "Internet radio station name",
"TRSO": "Internet radio station owner",
"TSIZ": "Size",
"TSRC": "ISRC (international standard recording code)",
"TSSE": "Software/Hardware and settings used for encoding",
"TYER": "Year",
"TXXX": "User defined text information frame",
"UFID": "Unique file identifier",
"USER": "Terms of use",
"USLT": "Unsychronized lyric/text transcription",
"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",
"WXXX": "User defined URL link frame"
};
var special_tags = {
'APIC':function(raw){
var frame = {
txt_enc : raw.readUInt8(0)
}
var pos = raw.toString('ascii',1,(raw.length < 24)?raw.length:24).indexOf('\0');
frame.mime = raw.toString('ascii',1,pos+1);
pos += 2;
frame.type = PIC_TYPE[raw.readUInt8(pos++)] || 'unknown';
var desc = raw.toString('ascii',pos,pos+64); // Max 64 char comment
var desc_pos = desc.indexOf('\0');
frame.desc = desc.substr(0,desc_pos);
pos += desc_pos + 1 ;// /0 is the last character which wont be counted xP
frame.img = fs.writeFileSync('art2.'+frame.mime.split('/')[1],raw.slice(pos,raw.length),'binary'); // Replace the art with unique ID .
return frame;
},
'TRCK':function(raw){
return raw.toString('ascii').replace(/\u0000/g,'') * 1;
},
'TYER':function(raw){
return raw.toString('ascii').replace(/\u0000/g,'') *1;
}
}
function parseTags (raw_tags,callback){
var max = raw_tags.length;
var pos = 0;
var parsed_tags = [];
while( pos < max-10){
var TAG = {
NAME : raw_tags.toString('ascii',pos,pos+4),
SIZE : raw_tags.readUInt32BE(pos+4)
};
if( special_tags[TAG.NAME] !== undefined){
TAG.content = special_tags[TAG.NAME](raw_tags.slice(pos+10,pos+10+TAG.SIZE)) || 'FUCK IN COMPLETE THE FUCKING FUNCTION';
}else{
TAG.content = raw_tags.toString('utf8',pos+10,pos+10+TAG.SIZE).replace(/\u0000/g,'');
}
if( TAGS[TAG.NAME] !== undefined && TAG.NAME !== 'PRIV'){
parsed_tags.push(TAG);
}
pos += (10+TAG.SIZE);
}
callback(parsed_tags);
};
/*
* @API - PUBLIC
*/
self.read = function (file,_callback){
var callback = _callback;
fs.open(file,'r',function(err,fd){
if(err){
console.dir(err);
return;
}
var id3 = {};
var header = new Buffer(10);
fs.read(fd,header,0,10,0,function(err,bytesRead,buff){
if( buff.toString('ascii',0,3) != 'ID3'){
console.log("Not an id3v2 ");
return;
}
id3.head = {
size:id3Size(buff.slice(6,10)),
ver:'2.'+buff.readUInt8(3)+'.'+buff.readUInt8(4)
};
var raw_tags = new Buffer(id3.head.size);
fs.read(fd,raw_tags,0,id3.head.size,null,function(){
fs.close(fd,function(){
parseTags( raw_tags,function(parsed_tags){
id3.tags = parsed_tags;
callback(id3);
});
});
});
});
});
}
})();
// */
@the-eater

This comment has been minimized.

Copy link

@the-eater the-eater commented Jun 13, 2013

'FUCK IN COMPLETE THE FUCKING FUNCTION', sure.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.