Skip to content

Instantly share code, notes, and snippets.

@darkyen
Created April 24, 2012 17:23
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save darkyen/2481696 to your computer and use it in GitHub Desktop.
Save darkyen/2481696 to your computer and use it in GitHub Desktop.
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
Copy link

'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