您的位置:首页 > 编程语言 > Python开发

用python给MP3加封面图片,修改作者,专辑等信息

2013-05-12 21:55 429 查看
如何给MP3加封面呢,当然用iturns,千千静听当然是可以的,但是如果用程序来自动加封面呢,研究linux的ffmpeg,发现用这个加专辑信息还是容易的,但是封面始终加不上,无法,去研究mp3文件的编码吧

用来描述MP3信息的head有两个大版本,分别是idv2和idv3,idv2放在文件尾部,只能描述一些简单的信息,idv3就厉害了,可以添加图片,和其它各种信息,包括自定义信息

我们只研究 idv3,它放在文件头

idv3也有几个常用的版本,一个是idv3.3一个是idv3.4两者区别不大,就是frame中的长度idv3.4 改成了sync safe integer ,其它相同,但是idv3.4只有iturns和一些比较先进的播放器能识别出来,比如windows 8 的mediaplay 就无法识别出来,这样在windows的文件夹中,那张封面图就木有了,所以,我们主要研究idv3.3

idv3.3分 header和frame ,header描述 整个idv3.3的长度啊,和一些常见信息 共10bytes,frame可以有多个,比如专辑名,作者名,就是两个frame

每个frame也有它的头,也是10个字节,下面具体描述

header 共10个字节

1-3  字节 字符串

ID3

4     字节  整数 

表示版本号 正常是03 或者 04,03就是idv3.3 ,04就是idv3.4

5    字节 整数

小版本号 不管它

6    一个flags 

不管它 用0即可

7-10 字节 一个无符号整数

表示整个id3头的长度,这里的长度是个synchsafe integer,具体这个是啥,你可以去百度搜,我这儿只提供个算法,将该数字转义成真正的长度(不包括这个头的长度)

def decode(x):  #如果按照正常算法得到的是synchsafe integer,解析成 真正的整数大小
a = x & 0xff;
b = (x >> 8) & 0xff;
c = (x >> 16) & 0xff;
d = (x >> 24) & 0xff;
x_final = 0x0;
x_final = x_final | a;
x_final = x_final | (b << 7);
x_final = x_final | (c << 14);
x_final = x_final | (d << 21);
return x_final

def encode(x): #和上边相反
a = x & 0x7f;
b = (x >> 7) & 0x7f;
c = (x >> 14) & 0x7f;
d = (x >> 21) & 0x7f;

x_final = 0x0;
x_final = x_final | a;
x_final = x_final | (b << 8);
x_final = x_final | (c << 16);
x_final = x_final | (d << 24);
return x_final


首先将那个4个字节的无符号整形转成整数n,这个整数并不是真正的长度,然后调decode(n)

如果你要将一个整数转化成syncsafe integer 那么调encode()函数即可

一个header的例子    ID3| 0x03| 0x00 | 0x00 | 0x00000013

那么意思是idv3的版本,decode(0x00000013)的长度,全部是大端编码 big-endian

到这儿id3的头就说完啦

下面讲frame,每个frame也有一个固定的格式,每个frame 都有一个头

也是10个字节

1-4 字节 字符串

TPE1 ,TIT2 ,TALB 具体去查http://id3.org/id3v2.3.0
5-8字节 一个无符号整形 大端编码
表示这个frame的长度,不包括这个10个字节的头(v3.4的版本这儿也是sync safe integer 需要decode,v3.3就不用啦)
9-10字节  两个 0 不管他
每个frame还有一个体,体也是有格式滴复杂的咱不说,只说最常用的,第一个字节表示编码,0就是普通编码,在win上就是gbk,在linux系列就是utf8
所以如果是在win上的能正确解析的到 linux上就是乱码咧,如果你默认用utf8,win上是解析不出来滴,
 那如果我们选择1呢,1就是unicode,unicode是啥编码,说是ucs-2 这个是神马东东,其实就是utf16,所以,第一个字节,咱们用1,然后内容用utf16编码,两个平台就兼容啦
例子  
TPE1|0x00000012|0x0000
0x01|content 
长度12的content编码是utf16

普通的frame是这个样子,还有我们的关键 frame,图片
图片的的frame头和上边一样,也是10个字节 ,但是体 稍微不同
第一个字节还是编码,选0就成,然后是 mime type 就是图片格式比如 image/jpeg 或者是image/png 然后跟一个0x00 表示格式结束
然后再来个一个字节表示图片用途,比如封面是03,但是用03 有问题,不知道为啥,所以都是用0
然后一个是描述,没用 用0就行
然后就是图片数据开始啦,将图片打开,然后read数据到这儿就成了
例子
APIC|0x00001234|0x0000   头
0x00|image/jpeg0x00|0x00|0x00 content(比如一个jpg的图片 是0xFFD8打头)
噢了,下边是我写的一个读写mp3 idv3信息的小python代码

# -*- coding: utf8 -*-
import struct

def decode(x):  #如果按照正常算法得到的synchsafe integer,解析成 真正的整数大小
a = x & 0xff;
b = (x >> 8) & 0xff;
c = (x >> 16) & 0xff;
d = (x >> 24) & 0xff;
x_final = 0x0;
x_final = x_final | a;
x_final = x_final | (b << 7);
x_final = x_final | (c << 14);
x_final = x_final | (d << 21);
return x_final

def encode(x): #和上边相反
a = x & 0x7f;
b = (x >> 7) & 0x7f;
c = (x >> 14) & 0x7f;
d = (x >> 21) & 0x7f;

x_final = 0x0;
x_final = x_final | a;
x_final = x_final | (b << 8);
x_final = x_final | (c << 16);
x_final = x_final | (d << 24);
return x_final

class MP3:
def __init__(self,path):
self.path = path
pass
def getInfo(self):
fp = open( self.path,'rb');
head = fp.read(10)
id3,ver,revision,flag,length  = struct.unpack("!3sBBBI",head);
length = decode(length)
data = []
while True:
frame = fp.read(10)
fid,size,flag,flag2 = struct.unpack("!4sI2B",frame)
if size==0: #有时候会留1024的白 不知道为啥
break
if ver==4:   #就是这一点 4和3的不同之处,4的这儿也采用synchsafe integer 了,注意啊
size = decode(size)
content = fp.read(size)
data.append((fid,content))
length-= (size+10)
print length
if length<=0:
break
fp.close()
return data
def buildItem(self,flag,content):
content = content.decode('utf8').encode("utf16")
content = struct.pack('!B',1)+content
length = len(content)
head = struct.pack('!4sI2B',flag,length,0,0);
return head + content

def addImage(self,image,data):
fp = open( self.path,'rb');
head = fp.read(10)
try:
id3,ver,revision,flag,length  = struct.unpack("!3sBBBI",head);
except:
return False;
if id3 != 'ID3':
return False
#新建立个文件
fpNew = open(self.path+'.bak',"wb");
fpImage = open(image,"rb")
imageData = fpImage.read() #待用
originLength = decode(length) #真实长度
length = 0

imageDataPre = struct.pack("!B10s2BB",0,'image/jpeg',0,0,0)
imageData = imageDataPre+imageData
apicLen = len(imageData)  #图片数据区域长度
imageDataHead = struct.pack("!4sI2B",'APIC',apicLen,0,0)
imageData  = imageDataHead+imageData

TPE1 =  self.buildItem('TPE1', data[u'Artist'].encode("utf8"))
TIT2 = self.buildItem('TIT2', data[u'Title'].encode("utf8"))
TALB = self.buildItem('TALB', data[u'Album'].encode("utf8"))

#新长度
length += len(imageData)
length += len(TPE1)
length += len(TIT2)
length += len(TALB)

header = head[0:3]
header += struct.pack('!B',3)
header += struct.pack('!H',0)
#1字节留白
header += struct.pack("!I",encode(length+1))

fpNew.write(header)
fpNew.write(TPE1)
fpNew.write(TIT2)
fpNew.write(TALB)
fpNew.write(imageData)
fpNew.write(struct.pack('!B',0))

fp.seek(originLength,1) #跳
fpNew.write(fp.read())
fpNew.close()
fp.close()
fpImage.close()
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Python 图片 mp3