您的位置:首页 > 其它

原创Pascal程序:批量导出mp3内嵌专辑封面图片

2007-05-11 22:01 375 查看
英文发布页: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.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: