使用本机Chrome Javascript/FileReader/DataView读取id3 v2.4标记
根据的答案,可以通过以下方式读取id3v1标签: Chrome和其他浏览器现在已经实现了DataView(我只对Chrome感兴趣)。我很好奇是否有人知道如何:使用本机Chrome Javascript/FileReader/DataView读取id3 v2.4标记,javascript,filereader,dataview,id3,Javascript,Filereader,Dataview,Id3,根据的答案,可以通过以下方式读取id3v1标签: Chrome和其他浏览器现在已经实现了DataView(我只对Chrome感兴趣)。我很好奇是否有人知道如何: 使用本机数据视图读取标记 读取id3 v2.4标记(包括APIC图像“coverart”) 关键是我没有使用二进制文件的经验,完全不知道如何跳转到正确的标记位置,也不知道little endian和long endian(或其他什么)是什么。我只需要一个标签的例子,比如标题,TIT2标签,我希望它能帮助我理解如何跳转到正确的位置,以及如
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);
这是你的电话号码
更新
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(b-c<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);
}
我添加了getString
,以便读取第一行并检查它是否包含ID3。
现在我需要找到第一个标记(TIT2)的位置和该字符串的“可变”长度&还要检查它是否是版本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
可能的外部来源: 我现在正在使用PHP getid3库 您可以尝试使用 包括后,您只需在代码中执行以下操作:
function readFile(){
id3(this.files[0], function(err, tags) {
console.log(tags);
})
}
document.getElementsByTagName('input')[0].addEventListener('change',readFile,false);
下面是由id3
创建的标记
对象:
{
"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"
}
}
希望这有帮助 使用我在这里找到的代码:,我在这里将其翻译成Javascript: 下面是我在那里写的代码:
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);
}
DataView.prototype.getChar=函数(开始){
返回字符串.fromCharCode(this.getUint8(start));
};
DataView.prototype.getString=函数(开始,长度){
对于(var i=0,v='';i部分正确答案(正确读取utf8格式的id3v2.4.0,包括封面)
我在问题中提出的问题现在可能有效了
我想要一个非常粗糙的最小函数集,只处理id3v2.4.0&并解析附加的图像
在@Siderite Zackwehdex的帮助下,答案被标记为正确,我理解了代码中缺失的重要部分
由于我有一些时间来使用它,我对代码进行了各种修改
首先,对于压缩后的脚本,我感到很抱歉,但我对整个代码有了更好的了解。这对我来说更容易。如果您对代码有一些问题,请提问
无论如何,我删除了uses\u synch
…很难找到使用synch的文件。同样的扩展了\u hdr
。我还删除了对id3v2.0.0到id3v2.2.0的支持。我添加了一个版本检查,该检查适用于所有id3v2子版本
主函数输出包含一个包含所有标记的数组,在里面你也可以找到id3v2版本。最后,但我想这对扩展很有用,我添加了一个自定义框架对象,其中包含除textFrames之外的其他框架的自定义函数。现在唯一的内部函数将image/cover/APIC转换为易于使用的base64字符串。这样,数组可以存储为JSON字符串
对你们中的一些人来说,兼容性很重要,但上面提到的exended头或sync实际上是最小的问题
问题
编码需要是UTF-8,否则会出现奇怪的文本填充和
有些图像只被部分解析,基本上被破坏了
我想避免使用外部库,甚至是一个非常大的函数…需要一些智能的简单解决方案来正确处理编码。
ISO-8859-1,UTF-8,UTF-16..大端…不管什么…#00对#00
如果做到这一点,支持能力将得到指数级的提高
我希望你们中的一些人能找到解决办法
代码
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(b-c<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);
}
DataView.prototype.str=函数(a,b,c,d){//开始,长度,占位符,占位符
b=b | | 1;c=0;d='';代表(;C能否提供此帮助?我正在搜索本机函数…反物质使用了大量的多边形填充…这使得它非常缓慢和不稳定。它在许多浏览器上都很好用…但我只需要chrome就可以了。我只想在ajax filereader dataview等重要函数中使用最新的js 1.7+。反物质的代码适用于一个文件…但是如果你需要处理多个文件这不是一个好主意。另外,使用chrome,你可以使用持久性存储,因此可以处理大文件。wich,有了更多的知识,还可以让你将这些标记写入文件。想想mp4格式。我在Node.js中使用了它,并取得了很大成功,也许你可以通过browserify?运行它,因为它期望一个可读的流,你必须找到一种方法将数据打包成一个可读的流。你可以使用XHR获取mp3,并将responseType设置为“arraybuffer”以处理原始字节。这方面的好处是它可以在客户端运行…如果客户端可以这样做,为什么要使用服务器上的节点进行计算???我希望我们可以改进我希望我们能进一步改进这个极小函数