英文发布页:http://www.matrix67.com/blog/article.asp?id=259
0.11版发布,主要更新如下:
- 使用二进制方式打开文件,加快程序运行速度(处理我的600多个mp3文件不到半分钟)
- 自动检测重复专辑,同一专辑只写一次文件
- 修正当ID3v1中有发行年份、歌曲类型等信息时发生的错误
- 修正找不到ID3v1时歌曲名出现乱码的错误
- 修正没有专辑图片时错误输出文件的问题
- 修正查找文件时漏掉部分文件的错误
- 识别更多的图片类型
- 其它一些小的修改
点击此处下载
转移系统平台后我试用了一大柄的音乐播放软件,最后还是选择了RhythmBox。对于我来说,RhythmBox的唯一缺点就是不支持mp3内部的专辑图片,而我的mp3文件全部都是标签嵌入的图片。RhythmBox可以通过读取~/.gnome2/rhythmbox/covers下“歌手 - 专辑名”格式的文件名来显示图片,于是我想到找个软件来批量导出专辑图片。没想到我把google翻了个底朝天都没找到这样的程序,一狠心打算自己写一个。
这个程序估计会有需要用的人,因此作为0.1 beta版发布在这里。现在已经发布了0.11版。我对这个小程序的开发比较感兴趣,任何觉得有需求的人可以在下面留言。发现Bug请帮忙报告一下(这句话说了N多次都没人回应)。
程序是在Windows下编写的。和上次一样,目前暂时不打算用Delphi(除非我要接着做下去)。C语言的文件操作还不怎么过关,只好又拿Free Pascal写了。
使用方法(请仔细阅读,发生任何意外我不负责!)
由于这是一个测试版,请先备份好你的mp3文件以防不测(其实一般不会发生问题,程序不写mp3文件)。下载该rar文件并解压,你会看到一个.exe文件。 将这个文件拷贝至你的mp3目录下,运行该文件后程序将扫描该目录下的所有mp3文件并寻找可能的内嵌图片,以ID3v1信息来命名图片文件,文件名格式为“歌手 - 专辑名”。相同文件名自动覆盖(这样的话你的专辑图片就是唯一的)。之所以不用ID3v2是因为ID3v2的编码不确定,我暂时不想处理Unicode。如果你的mp3里没有ID3v1标签,你可以随便找一个工具把ID3v2转为ID3v1,推荐用ID3-TagIt。建议你先复制少许mp3文件到新的文件夹试用一下。
以下情况可能导致错误:
1. mp3文件里嵌入多个图片或有图片说明(如果你是iTunes用户应该没问题);
2. ID3v1里有Windows不允许作为文件名的字符;
3. 不存在ID3v1或需要的ID3v1信息不全。
发生后两种情况时,文件名将用FilenameError加数字编号代替。
Matrix67原创
转贴请注明出处
如果有人感兴趣的话,附程序代码:program pic_extractor;
// -------------------------
// Current Version : 0.11
// Last Update : 2007-05-31
// Author : Matrix67.com
// -------------------------
// ----------------------------------------------------
// This program is a batch extractor which exports album
// covers embedded in mp3 ID3v2 tags. Compiled version can
// be found at http://www.matrix67.com/blog/article.asp?id=240
// Version 0.11 tested under Windows XP SP2, Free Pascal 2.0
// Next version will be written in Delphi.
// ----------------------------------------------------
uses dos,sysutils;
var
PictureType: string; // .png|.jpg|.bmp|.gif
FailCount : longint=0; // Number of Invalid Filenames
AlbumCount : longint=0; // Number of Albums
FileHandle : longint;
PicBuffer : array[1..1100000]of char;
InfoBuffer : array[1..210]of char;
FileNameDone: array[1..1100]of string[70];
// FileNameDone[AlbumCount]:=OutputFileName
function SameAlbum(OutputFileName:string):boolean;
var
i:longint;
begin
for i:=1 to AlbumCount do
if FileNameDone[i] = OutputFileName then exit(true);
inc(AlbumCount);
FileNameDone[ AlbumCount ] :=OutputFileName;
exit(false);
end;
function FindAPIC:longint;
// --------------------------------------------------------------
// This function will search the entire tag for APIC tag frame.
// Error may occur if there happens to be another 'APIC' string.
// Bugs will be fixed in next version.
// --------------------------------------------------------------
var
Scanner:string=' ';
FrameLength : longint=0;
Id3Length : longint=0;
identifier : integer;
procedure ReadFrame(var ch:char);
begin
FileRead( FileHandle , ch , SizeOf(ch) );
dec(FrameLength);
end;
var
i:longint;
ch:char;
begin
// Check if ID3 Tag Exists
FileRead( FileHandle, identifier, 2);
if identifier<>$4449 then exit(-1);
// Get Size of ID3 Tag
FileSeek( FileHandle, 4, fsFromCurrent);
for i:=1 to 4 do
begin
FileRead( FileHandle , ch , SizeOf(ch) );
Id3Length:=Id3Length shl 7 + ord(ch);
end;
// Search for APIC header
repeat
FileRead( FileHandle , ch , SizeOf(ch) );
dec(Id3Length);
delete(Scanner, 1, 1);
Scanner:=Scanner+ch;
if Scanner='APIC' then break;
until Id3Length=0;
if Scanner<>'APIC' then exit(-1);
// Calculate size of APIC frame
for i:=1 to 4 do
begin
FileRead( FileHandle , ch , SizeOf(ch) );
FrameLength:=FrameLength shl 8 + ord(ch);
end;
for i:=1 to 2 do FileRead( FileHandle , ch , SizeOf(ch) );
// Filename Extension
repeat ReadFrame(ch) until ch='/';
ReadFrame(ch);
if ch='j' then PictureType:='jpg'
else if ch='p' then PictureType:='png'
else if ch='b' then PictureType:='bmp'
else if ch='g' then PictureType:='gif'
else PictureType:='ukn'; // Unknown
// Drop some useless bits.
// Error may occur if any comment or type description exists.
repeat ReadFrame(ch) until ch=#0;
for i:=1 to 2 do ReadFrame(ch);
FileRead( FileHandle , PicBuffer , FrameLength );
exit(FrameLength);
end;
function FindOutputFileName:string;
// ------------------------------------------------------------------
// This function reads the very end of file for getting ID3v1 tag.
// Filename will be generated using "Artist - Album" format. We use ID3v1
// instead of ID3v2 because the encoding of ID3v2 may vary. This program
// can only handle GBK encodings. Files without ID3v1 lead to invalid
// filenames.
// ------------------------------------------------------------------
var
FindResult:string='';
i:longint;
begin
FileSeek( FileHandle , -128 , fsFromEnd );
FileRead( FileHandle , InfoBuffer , 128 );
// Identifier of ID3v1
// If no ID3v1 found, exit invalid characters for filename.
if InfoBuffer[1]+InfoBuffer[2]+InfoBuffer[3]<>'TAG' then exit('? - ?');
// Name of Album
i:=34;
while InfoBuffer[i]<>#0 do
begin
FindResult:=FindResult+InfoBuffer[i];
inc(i);
end;
// Filename Format : Artist - Album
// This format fits Rhythmbox well
// Format can be customized in further
FindResult:=FindResult + ' - ';
// Name of Artist
i:=64;
while InfoBuffer[i]<>#0 do
begin
FindResult:=FindResult+InfoBuffer[i];
inc(i);
end;
exit(FindResult);
end;
procedure SavePic( var OutputFileName:string; FrameLength:longint );
// ------------------------------------------------------
// Save the extracted picture with the filename provided
// by FindOutputFileName Function. Output directory and
// overwrite option will be added in GUI version.
// ------------------------------------------------------
var
FailedNum:string;
begin
FileHandle:=FileCreate(OutputFileName + '.' + PictureType);
if FileHandle=-1 then
begin
// Error caused by invalid characters, Changing Filename
inc(FailCount);
str(FailCount,FailedNum);
OutputFileName:='FilenameError' + FailedNum;
FileHandle:=FileCreate(OutputFileName + '.' + PictureType);
end;
FileWrite( FileHandle , PicBuffer , FrameLength );
FileClose( FileHandle );
end;
procedure main;
var
FrameLength : longint;
OutputFileName : string;
LastFileName : string;
dir:TSearchRec;
begin
FindFirst( '*.mp3', faAnyFile and not (faVolumeID or faDirectory), Dir);
while (DosError=0) do
begin
LastFileName:=Dir.name;
FileHandle:=FileOpen(Dir.name,fmOpenRead);
if FileHandle=-1 then halt;
write(Dir.name + ' --> ');
FrameLength := FindAPIC;
OutputFileName := FindOutputFileName;
FileClose( FileHandle );
if SameAlbum(OutputFileName) then
writeln('Same Album Cover Already Extracted.')
else if FrameLength=-1 then
writeln('Album Cover Not Found.')
else begin
SavePic(OutputFileName , FrameLength);
write('Found Album Cover, Saved as "');
writeln(OutputFileName + '.' + PictureType + '"');
end;
FindNext(Dir);
if Dir.name=LastFileName then halt;
end;
FindClose(Dir);
end;
begin
main;
end.