使用本地Chrome浏览器Javascript / FileReader / DataView阅读id3 v2.4标签

基于ebidel的答案,可以使用jDataView来读取id3v1标签:

document.querySelector('input[type="file"]').onchange = function (e) { var reader = new FileReader(); reader.onload = function (e) { var dv = new jDataView(this.result); // "TAG" starts at byte -128 from EOF. // See http://en.wikipedia.org/wiki/ID3 if (dv.getString(3, dv.byteLength - 128) == 'TAG') { var title = dv.getString(30, dv.tell()); var artist = dv.getString(30, dv.tell()); var album = dv.getString(30, dv.tell()); var year = dv.getString(4, dv.tell()); } else { // no ID3v1 data found. } }; reader.readAsArrayBuffer(this.files[0]); }; 

Chrome和其他浏览器现在已经实现了DataView(我只对Chrome感兴趣)。 我很好奇,如果有人知道如何:

  1. 使用本机DataView读取标签
  2. 读取id3 v2.4标签(包括APIC图像“coverart”)

关键是我没有使用二进制文件的经验,完全不知道如何跳到正确的标签位置,或者是什么小端和长端(或其他什么)。 我只需要一个标签的例子 – 让我们说标题, TIT2标签,我希望能帮助我理解如何跳到正确的位置,并阅读其他标签:

 function readID3() { //https://developer.mozilla.org/en-US/docs/Web/API/DataView //and the position //http://id3.org/id3v2.4.0-frames //var id3={}; //id3.TIT2=new DataView(this.result,?offset?,?length?) /* ? var a=new DataView(this.result); console.dir(String.fromCharCode(a.getUint8(0))); ? */ } function readFile() { var a = new FileReader(); a.onload = readID3; a.readAsArrayBuffer(this.files[0]); } fileBox.addEventListener('change', readFile, false); 

这里是JSFiddle 。


UPDATE

http://jsfiddle.net/s492L/3/

我添加了getString所以我可以读取第一行,并检查它是否包含ID3。 现在我需要find第一个标签(TIT2)的位置和该string的“可变”长度,并检查它是否是版本2.4。

 //Header //ID3v2/file identifier "ID3" //ID3v2 version $04 00 //ID3v2 flags (%ab000000 in v2.2, %abc00000 in v2.3, %abcd0000 in v2.4.x) //ID3v2 size 4 * %0xxxxxxx 

可能的外部来源:

https://developer.mozilla.org/en-US/docs/Web/API/DataView

http://id3.org/id3v2.4.0-frames

http://id3.org/id3v2.4.0-structure

http://blog.nihilogic.dk/2008/08/reading-id3-tags-with-javascript.html

https://ericbidelman.tumblr.com/post/8343485440/reading-mp3-id3-tags-in-javascript

https://github.com/aadsm/JavaScript-ID3-Reader

目前我正在使用PHP getid3 lib …

http://getid3.sourceforge.net/

http://getid3.sourceforge.net/source2/module.tag.id3v2.phps

使用我在这里find的代码: http : //www.ulduzsoft.com/2012/07/parsing-id3v2-tags-in-the-mp3-files/ ,我在这里将它翻译成Javascript: http : //jsfiddle.net / eb7rrbw4 /

这里是我在那里写的代码:

 DataView.prototype.getChar=function(start) { return String.fromCharCode(this.getUint8(start)); }; DataView.prototype.getString=function(start,length) { for(var i=0,v='';i<length;++i) { v+=this.getChar(start+i); } return v; }; DataView.prototype.getInt=function(start) { return (this.getUint8(start) << 21) | (this.getUint8(start+1) << 14) | (this.getUint8(start+2) << 7) | this.getUint8(start+3); }; function readID3(){ var a=new DataView(this.result); // Parse it quickly if ( a.getString(0,3)!="ID3" ) { return false; } // True if the tag is pre-V3 tag (shorter headers) var TagVersion = a.getUint8(3); // Check the version if ( TagVersion < 0 || TagVersion > 4 ) { return false; } // Get the ID3 tag size and flags; see 3.1 var tagsize = a.getInt(6)+10; //(a.getUint8(9) & 0xFF) | ((a.getUint8(8) & 0xFF) << 7 ) | ((a.getUint8(7) & 0xFF) << 14 ) | ((a.getUint8(6) & 0xFF) << 21 ) + 10; var uses_synch = (a.getUint8(5) & 0x80) != 0 ? true : false; var has_extended_hdr = (a.getUint8(5) & 0x40) != 0 ? true : false; var headersize=0; // Read the extended header length and skip it if ( has_extended_hdr ) { var headersize = a.getInt(10); //(a.getUint8(10) << 21) | (a.getUint8(11) << 14) | (a.getUint8(12) << 7) | a.getUint8(13); } // Read the whole tag var buffer=new DataView(a.buffer.slice(10+headersize,tagsize)); // Prepare to parse the tag var length = buffer.byteLength; // Recreate the tag if desynchronization is used inside; we need to replace 0xFF 0x00 with 0xFF if ( uses_synch ) { var newpos = 0; var newbuffer = new DataView(new ArrayBuffer(tagsize)); for ( var i = 0; i < tagsize; i++ ) { if ( i < tagsize - 1 && (buffer.getUint8(i) & 0xFF) == 0xFF && buffer.getUint8(i+1) == 0 ) { newbuffer.setUint8(newpos++,0xFF); i++; continue; } newbuffer.setUint8(newpos++,buffer.getUint8(i)); } length = newpos; buffer = newbuffer; } // Set some params var pos = 0; var ID3FrameSize = TagVersion < 3 ? 6 : 10; var m_title; var m_artist; // Parse the tags while ( true ) { var rembytes = length - pos; // Do we have the frame header? if ( rembytes < ID3FrameSize ) break; // Is there a frame? if ( buffer.getChar(pos) < 'A' || buffer.getChar(pos) > 'Z' ) break; // Frame name is 3 chars in pre-ID3v3 and 4 chars after var framename; var framesize; if ( TagVersion < 3 ) { framename = buffer.getString(pos,3); framesize = ((buffer.getUint8(pos+5) & 0xFF) << 8 ) | ((buffer.getUint8(pos+4) & 0xFF) << 16 ) | ((buffer.getUint8(pos+3) & 0xFF) << 24 ); } else { framename = buffer.getString(pos,4); framesize = buffer.getInt(pos+4); //(buffer.getUint8(pos+7) & 0xFF) | ((buffer.getUint8(pos+6) & 0xFF) << 8 ) | ((buffer.getUint8(pos+5) & 0xFF) << 16 ) | ((buffer.getUint8(pos+4) & 0xFF) << 24 ); } if ( pos + framesize > length ) break; if ( framename== "TPE1" || framename== "TPE2" || framename== "TPE3" || framename== "TPE" ) { if ( m_artist == null ) m_artist = parseTextField( buffer, pos + ID3FrameSize, framesize ); } if ( framename== "TIT2" || framename== "TIT" ) { if ( m_title == null ) m_title = parseTextField( buffer, pos + ID3FrameSize, framesize ); } pos += framesize + ID3FrameSize; continue; } console.log(m_title,m_artist); return m_title != null || m_artist != null; } function parseTextField( buffer, pos, size ) { if ( size < 2 ) return null; var charcode = buffer.getUint8(pos); //TODO string decoding /*if ( charcode == 0 ) charset = Charset.forName( "ISO-8859-1" ); else if ( charcode == 3 ) charset = Charset.forName( "UTF-8" ); else charset = Charset.forName( "UTF-16" ); return charset.decode( ByteBuffer.wrap( buffer, pos + 1, size - 1) ).toString();*/ return buffer.getString(pos+1,size-1); } 

您应该在控制台日志中看到标题和作者。 看看parsing文本函数,但是,编码决定了读取string的方式。 (searchTODO)。 此外,我还没有testing它与扩展头或uses_synch true或标签版本3。

你可以尝试在github上使用id3parsing器 。

这是您更新的小提琴在控制台中logging标签对象

随着id3.js包括,所有你需要在你的代码是这样的:

 function readFile(){ id3(this.files[0], function(err, tags) { console.log(tags); }) } document.getElementsByTagName('input')[0].addEventListener('change',readFile,false); 

这里是由id3创build的tags对象:

 { "title": "Stairway To Heaven", "album": "Stairway To Heaven", "artist": "Led Zeppelin", "year": "1999", "v1": { "title": "Stairway To Heaven", "artist": "Led Zeppelin", "album": "Stairway To Heaven", "year": "1999", "comment": "Classic Rock", "track": 13, "version": 1.1, "genre": "Other" }, "v2": { "version": [3, 0], "title": "Stairway To Heaven", "album": "Stairway To Heaven", "comments": "Classic Rock", "publisher": "Virgin Records" } } 

希望这可以帮助!

部分正确答案 (正确读取utf8格式的id3v2.4.0包括封面)

我在问题中提出的问题现在可能正在工作。

我想要一个非常粗略的最小函数集来处理只id3v2.4.0&并parsing附加的图像。

在@Siderite Zackwehdex的帮助下,我的答案被标记为正确的,我明白了代码中缺less的重要部分。

由于我有一段时间玩它,我对代码做了各种修改。

首先抱歉的压缩脚本,但我有一个更好的整体代码的概述。 对我来说比较容易 如果你有关于代码的一些问题,请问。

无论如何,我删除了uses_synch …很难find一个使用同步的文件。 同样为has_extended_hdr 。我也删除了id3v2.0.0到id3v2.2.0的支持。 我添加了一个版本检查,可以使用所有的id3v2颠覆。

主函数输出包含一个包含所有标签的数组,在里面你也可以findid3v2 Version.Last,但我猜有用展开,我添加了一个自定义的FRAME对象,其中包含了除了textFrames之外的FRAMES的自定义函数。 现在唯一的function是将image / cover / APIC转换成一个易于使用的base64string。 这样做可以将数组存储为JSONstring。

虽然对于你们中的一些人来说,兼容性是重要的,但是上面提到的exended header或sync实际上是最小的问题。

问题

编码需要是UTF-8,否则你会得到奇怪的文本填充,一些图像只能被部分parsing。 基本上坏了。

我想避免使用外部库,甚至是一个真正的大function… …需要一些聪明的简单的解决scheme来正确处理编码。 ISO-8859-1,UTF-8,UTF-16 ..大端…任何…#00和#00 00 ..

如果这样做,支持可以成倍地改善。

我希望你们中的一些人有这个解决scheme。

 DataView.prototype.str=function(a,b,c,d){//start,length,placeholder,placeholder b=b||1;c=0;d='';for(;c<b;)d+=String.fromCharCode(this.getUint8(a+c++));return d } DataView.prototype.int=function(a){//start return (this.getUint8(a)<<21)|(this.getUint8(a+1)<<14)| (this.getUint8(a+2)<<7)|this.getUint8(a+3) } var frID3={ 'APIC':function(x,y,z,q){ var b=0,c=['',0,''],d=1,e,b64; while(b<3)e=x.getUint8(y+z+d++),c[b]+=String.fromCharCode(e), e!=0||(b+=b==0?(c[1]=x.getUint8(y+z+d),2):1); b64='data:'+c[0]+';base64,'+ btoa(String.fromCharCode.apply(null,new Uint8Array(x.buffer.slice(y+z+++d,q)))); return {mime:c[0],description:c[2],type:c[1],base64:b64} } } function readID3(a,b,c,d,e,f,g,h){ if(!(a=new DataView(this.result))||a.str(0,3)!='ID3')return; g={Version:'ID3v2.'+a.getUint8(3)+'.'+a.getUint8(4)}; a=new DataView(a.buffer.slice(10+((a.getUint8(5)&0x40)!=0?a.int(10):0),a.int(6)+10)); b=a.byteLength;c=0;d=10; while(true){ f=a.str(c);e=a.int(c+4); if(bc<d||(f<'A'||f>'Z')||c+e>b)break; g[h=a.str(c,4)]=frID3[h]?frID3[h](a,c,d,e):a.str(c+d,e); c+=e+d; } console.log(g); } 

DEMO

https://jsfiddle.net/2awq6pz7/

Interesting Posts