Don't like ads? PRO users don't see any ads ;-)
Guest

JS id3v2 Parser

By: a guest on Jun 16th, 2012  |  syntax: JavaScript  |  size: 7.51 KB  |  hits: 38  |  expires: Never
download  |  raw  |  embed  |  report abuse  |  print
Text below is selected. Please press Ctrl+C to copy to your clipboard. (⌘+C on Mac)
  1. // Mp3Id3Reader
  2. // Supports Id3v2.3.0 fully
  3. // TODO: Add id3v2.2.0
  4. // TODO: Add id3v2.4.0
  5. var id3Reader = function(){
  6.   var self = this;
  7.   function id3Size( buffer ) {
  8.         var integer = ( ( buffer[0] & 0x7F ) << 21 ) |
  9.                           ( ( buffer[1] & 0x7F ) << 14 ) |
  10.                           ( ( buffer[2] & 0x7F ) << 7 ) |
  11.                           ( buffer[3] & 0x7F );
  12.     return integer;
  13.   }
  14.   var callback = null;
  15.   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",
  16.   "A bright coloured fish", //<--- Wait what the f ?
  17.   "Illustration","Band/artist logotype","Publisher/Studio logotype","Band/Orchestra","Composer","Lyricist/text writer","Recording Location","During recording","During performance"];
  18.   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"];
  19.   var TAGS = {
  20.     "AENC": "Audio encryption",
  21.     "APIC": "Attached picture",
  22.     "COMM": "Comments",
  23.     "COMR": "Commercial frame",
  24.     "ENCR": "Encryption method registration",
  25.     "EQUA": "Equalization",
  26.     "ETCO": "Event timing codes",
  27.     "GEOB": "General encapsulated object",
  28.     "GRID": "Group identification registration",
  29.     "IPLS": "Involved people list",
  30.     "LINK": "Linked information",
  31.     "MCDI": "Music CD identifier",
  32.     "MLLT": "MPEG location lookup table",
  33.     "OWNE": "Ownership frame",
  34.     "PRIV": "Private frame",
  35.     "PCNT": "Play counter",
  36.     "POPM": "Popularimeter",
  37.     "POSS": "Position synchronisation frame",
  38.     "RBUF": "Recommended buffer size",
  39.     "RVAD": "Relative volume adjustment",
  40.     "RVRB": "Reverb",
  41.     "SYLT": "Synchronized lyric/text",
  42.     "SYTC": "Synchronized tempo codes",
  43.     "TALB": "Album",
  44.     "TBPM": "BPM",
  45.     "TCOM": "Composer",
  46.     "TCON": "Genre",
  47.     "TCOP": "Copyright message",
  48.     "TDAT": "Date",
  49.     "TDLY": "Playlist delay",
  50.     "TENC": "Encoded by",
  51.     "TEXT": "Lyricist",
  52.     "TFLT": "File type",
  53.     "TIME": "Time",
  54.     "TIT1": "Content group description",
  55.     "TIT2": "Title",
  56.     "TIT3": "Subtitle",
  57.     "TKEY": "Initial key",
  58.     "TLAN": "Language(s)",
  59.     "TLEN": "Length",
  60.     "TMED": "Media type",
  61.     "TOAL": "Original album",
  62.     "TOFN": "Original filename",
  63.     "TOLY": "Original lyricist",
  64.     "TOPE": "Original artist",
  65.     "TORY": "Original release year",
  66.     "TOWN": "File owner",
  67.     "TPE1": "Artist",
  68.     "TPE2": "Band",
  69.     "TPE3": "Conductor",
  70.     "TPE4": "Interpreted, remixed, or otherwise modified by",
  71.     "TPOS": "Part of a set",
  72.     "TPUB": "Publisher",
  73.     "TRCK": "Track number",
  74.     "TRDA": "Recording dates",
  75.     "TRSN": "Internet radio station name",
  76.     "TRSO": "Internet radio station owner",
  77.     "TSIZ": "Size",
  78.     "TSRC": "ISRC (international standard recording code)",
  79.     "TSSE": "Software/Hardware and settings used for encoding",
  80.     "TYER": "Year",
  81.     "TXXX": "User defined text information frame",
  82.     "UFID": "Unique file identifier",
  83.     "USER": "Terms of use",
  84.     "USLT": "Unsychronized lyric/text transcription",
  85.     "WCOM": "Commercial information",
  86.     "WCOP": "Copyright/Legal information",
  87.     "WOAF": "Official audio file webpage",
  88.     "WOAR": "Official artist/performer webpage",
  89.     "WOAS": "Official audio source webpage",
  90.     "WORS": "Official internet radio station homepage",
  91.     "WPAY": "Payment",
  92.     "WPUB": "Publishers official webpage",
  93.     "WXXX": "User defined URL link frame"
  94.   };
  95.   var special_tags = {
  96.         'APIC':function(raw){
  97.                 var frame = {
  98.                         txt_enc : raw.readUInt8(0)
  99.                 }
  100.                 var pos = raw.toString('ascii',1,(raw.length < 24)?raw.length:24).indexOf('\0');
  101.                 frame.mime = raw.toString('ascii',1,pos+1);
  102.                 pos += 2;
  103.                 frame.type = PIC_TYPE[raw.readUInt8(pos++)] || 'unknown';
  104.                 var desc = raw.toString('ascii',pos,pos+64); // Max 64 char comment
  105.         var desc_pos = desc.indexOf('\0');
  106.                 frame.desc = desc.substr(0,desc_pos);
  107.                 pos +=  desc_pos + 1 ;// /0 is the last character which wont be counted xP
  108.                 frame.img = fs.writeFileSync('art2.'+frame.mime.split('/')[1],raw.slice(pos,raw.length),'binary'); // Replace the art with unique ID .
  109.                 return frame;
  110.         },
  111.         'TRCK':function(raw){
  112.                 return raw.toString('ascii').replace(/\u0000/g,'') * 1;
  113.         },
  114.         'TYER':function(raw){
  115.                 return raw.toString('ascii').replace(/\u0000/g,'') *1;
  116.         }
  117.   }
  118.   function parseTags (raw_tags,callback){
  119.         var max = raw_tags.length;
  120.         var pos = 0;
  121.         var parsed_tags = [];
  122.     while( pos < max-10){
  123.         var TAG = {
  124.                 NAME : raw_tags.toString('ascii',pos,pos+4),
  125.                 SIZE : raw_tags.readUInt32BE(pos+4)
  126.         };
  127.         if( special_tags[TAG.NAME] !== undefined){
  128.             TAG.content = special_tags[TAG.NAME](raw_tags.slice(pos+10,pos+10+TAG.SIZE)) || 'FUCK IN COMPLETE THE FUCKING FUNCTION';
  129.         }else{
  130.                 TAG.content = raw_tags.toString('utf8',pos+10,pos+10+TAG.SIZE).replace(/\u0000/g,'');
  131.         }
  132.         if( TAGS[TAG.NAME] !== undefined && TAG.NAME !== 'PRIV'){
  133.                 parsed_tags.push(TAG);
  134.         }
  135.         pos += (10+TAG.SIZE);
  136.     }
  137.     callback(parsed_tags);
  138.   };
  139.   function beginRead(err,fd,extern){
  140.                 if(err){
  141.                         console.dir(err);
  142.                         return;
  143.                 }
  144.                 var id3 = {};
  145.                 var _ext = extern;
  146.                 var header = new Buffer(10);
  147.                 fs.read(fd,header,0,10,0,function(err,bytesRead,buff){
  148.                         if( buff.toString('ascii',0,3) != 'ID3'){
  149.                                 console.log("Not an id3v2 ");
  150.                                 return;
  151.                         }
  152.                         id3.head = {
  153.                                 size:id3Size(buff.slice(6,10)),
  154.                                 ver:'2.'+buff.readUInt8(3)+'.'+buff.readUInt8(4)
  155.                         };
  156.                         var raw_tags = new Buffer(id3.head.size);
  157.                         fs.read(fd,raw_tags,0,id3.head.size,null,function(){
  158.                                 if( _ext === false ){
  159.                                         fs.close(fd,function(){
  160.                                                 parseTags( raw_tags,function(parsed_tags){
  161.                                                      id3.tags = parsed_tags;
  162.                                                      callback(id3);
  163.                                                 });
  164.                                         });
  165.                                 }else{
  166.                                                 parseTags( raw_tags , function( parsed_tags ){
  167.                                                      id3.tags = parsed_tags;
  168.                                                      callback(id3);
  169.                                                 });
  170.                                 }
  171.                         });
  172.                 });
  173.   }
  174.  
  175.   /*
  176.    * @API - PUBLIC
  177.    */
  178.   self.read = function (file,_callback,fd){
  179.     callback = _callback;
  180.         if(fd === undefined){
  181.                 fs.open(file,'r',beginRead);
  182.     }else{
  183.         beginRead(null,fd,true);
  184.     }
  185.   }
  186. };