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); | |
}); | |
}); | |
}); | |
}); | |
}); | |
} | |
})(); | |
// */ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This comment has been minimized.
'FUCK IN COMPLETE THE FUCKING FUNCTION', sure.