May 31

Current Version : 0.11
Last Update : 2007-05-31
Author : Matrix67.com

Click here to download (Do not directly link to this file)

This program is a batch extractor which exports album covers embedded in mp3 ID3v2 tags. Run pic_extractor.exe under Windows, it will search all mp3 files in the directory, export pictures in ID3v2 tags. Output filenames are formatted as "artist - album". Note that the artist and album info is taken from ID3v1, because the encoding of ID3v2 may vary. If there's no ID3v1 tag contained in your mp3 file, you may need some other powerful ID3 tools (ID3-TagIT fits me well) so you can transfer ID3v2 tags to ID3v1 tags. If there's any invalid character for filename (like '?', '\', '*') or ID3v1 tag doesn't exist, filename changes to "FilenameError", followed by a sequence number.

If you have any suggestion, advice or bug report, feel free to mail me at gs.matrix67@gmail.com, or you can just leave a message below.

Source code can be found here.

May 11

英文发布页: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.

Jan 17



一共有150多张专辑,全部手工添加的图片
累死我了,搞得手都酸了,小红帽被折磨得都想奶奶了
这下爽了,iPod播放时还可以显示专辑图片

Jan 16

    我从来不承认我是一个完美主义者,不过昨天不得不承认了。我花了3个小时的时间重新整理了我的F:\mp3\*.mp3所有近600个文件的ID3标签。

    在我的播放列表里添加一个文件非常的麻烦。首先下载歌曲,然后重新压成96Kbps或128Kbps的mp3文件,然后把文件名改成“歌手+空格+歌名”的样子。然后用Mp3 Gain把音量统一为89dB。然后,下载.lrc歌词文件,去掉歌词文件里的广告和制作人信息,可能还要重新编辑一下时间轴。最后把mp3文件放在F:\mp3目录下面,把歌词文件放在F:\mp3\lrc目录下面。添加进Winamp的播放列表后,有时会看见歌曲名带有网址的,那是ID3标签里的。如果把这个ID3标签留着的话,MSN上的正在收听就不好看了。为此,我干脆去掉所有mp3文件的ID3标签。这样,不管是什么地方,只要文件没有ID3,歌曲显示的就是我的文件名,而我的文件名肯定都是正确的。
    昨天去买了iPod nano,二代,2G。iPod的价格都是打算吃人的,尤其是配件。不说Hi-Fi和inMotion了,就连漫步者的某个iPod音响就是600多银子。买个套套都要花很多钱,幸好它那里送了一个。
    同步到iPod后我才发现ID3标签有多重要。iPod(当然也包括一些其它的mp3)用ID3的标签对音乐进行分类。你可以按音乐人和专辑来搜索歌曲。其它很多功能也需要完备的ID3标签。这下好了,我的ID3标签全部空白。早知道的话,不删除那些ID3标签该多好啊,至少现在我想重新完善ID3标签也有一些参考信息嘛。不然的话,难道重新添加ID3标签只能上网一个个搜索每首歌对应的专辑和曲目号?我600首歌呢,整死你。
    后来我想到好办法了。我不是有歌词文件吗?歌词文件里不是有[ti:]、[ar:]和[al:]吗?我的歌词文件名和mp3一一对应,方便文件操作。但网上从来没找到过类似的程序可以提取.lrc文件的信息加入ID3标签。于是我打算自己编一个。这个程序很简单,我也就直接用的FP了,没用Delphi。
    我花了一些时间研究了一下ID3的格式。用WinHex看几个试试,ID3 v1比v2的格式更简单,直接添加在Mp3的最末尾,你用Winamp改几个标签然后用WinHex一看就明白了。.lrc的格式更不说了,白痴都看得懂。
    以下程序代码可以自动搜索程序所在目录下的mp3文件,使用lrc子目录下的相应歌词文件中的歌曲名、音乐人和专辑信息实现自动添加ID3信息。该程序在FPC 2.0下测试。通篇代码没有一个注释,结构比较乱。毕竟是给我自己看的,当时没想过要发布上来。
program id3rename;

uses dos;

type
   info=record
           title,artist,album:string[26];
        end;
var
   Mp3:array[1..20000000]of char;

function GetInfo(FileName:string):info;
var
   ch:char;
   LrcInfo:info;
   Lines:integer;
   Content:boolean;
begin
   Lines:=1;
   LrcInfo.title:='';
   LrcInfo.artist:='';
   LrcInfo.album:='';
   {$I-}
   assign(input,FileName);
   reset(input);
   {$I+}
   if ioresult<>0 then exit(LrcInfo);

   repeat
      read(ch);
      if ord(ch)=13 then inc(Lines)
      else if ch=']' then Content:=false
      else if ch='[' then Content:=true;

      if Content then
        if Lines=1 then LrcInfo.title:=LrcInfo.title+ch
        else if Lines=2 then LrcInfo.artist:=LrcInfo.artist+ch
        else if Lines=3 then LrcInfo.album:=LrcInfo.album+ch;
   until Lines=4;
   delete(LrcInfo.title,1,4);
   delete(LrcInfo.artist,1,4);
   delete(LrcInfo.album,1,4);
   close(input);
   exit(LrcInfo);
end;

procedure SaveTag(LrcInfo:info;FileName:string;Total:longint);
var
   FileSize:longint;

   procedure InsertText(a:string;l:longint);
   var
      i:longint;
   begin
      for i:=1 to l do
         if i<=length(a) then Mp3[FileSize+i]:=a[i]
         else Mp3[FileSize+i]:=chr(0);
      FileSize:=FileSize+l;
   end;

var
   i:longint;
begin
   assign(input,FileName);
   reset(input);
   FileSize:=0;
   for FileSize:=1 to total do
      read(Mp3[FileSize]);
   close(input);

   InsertText('TAG',3);
   InsertText(LrcInfo.title,30);
   InsertText(LrcInfo.artist,30);
   InsertText(LrcInfo.album,65);
   Mp3[FileSize]:=chr(255);

   assign(output,FileName);
   rewrite(output);
   for i:=1 to FileSize do write(Mp3[i]);
   close(output);
end;

procedure main;
var
   dir:SearchRec;
   LrcInfo:info;
begin
   FindFirst('*.mp3',archive,Dir);
   while (DosError=0) do
   begin
      FindNext(Dir);
      delete(Dir.name,length(Dir.name)-2,3);
      LrcInfo:=GetInfo('lrc\'+Dir.name+'lrc');
      SaveTag(LrcInfo,Dir.name+'mp3',Dir.size);
      assign(input,'');
      reset(input);
      assign(output,'');
      rewrite(output);
      writeln(Dir.name,' Finished!');
      //readln;
   end;
   FindClose(Dir);
end;

begin
   main;
end.

    郑重声明:如果你读不懂这个代码,千万别去玩火。而且这是批量的玩火。搞坏了你的mp3文件的话我概不负责,到时候看你找谁哭去。如果你真的需要这个程序的话,在下面留言,我会考虑搞个发布版。我还有一些想法,要是这个程序能自动上网找曲目号、发行年份和专辑封面之类的就好了。国外有类似的软件,因为美国佬的网上有这一类的开放的数据库。国内呢?如果大家有好的数据库的话也欢迎在下面留言提供一下。

    接下来,使用个人认为最好的标签编辑软件ID3-TagIT 3。批量把ID3 v1转换为v2,然后手工改一些不完美的信息。比如,调整同样的东西不同的名称,例如把she、SHE、S.H.E、S.H.E(女朋友)、S.H.E(女朋友)之类的全部改成S.H.E,这样才能在按音乐人分类时准确地把同一个人的歌搞在一块。这个软件十分强大,允许你选择多个文件统一编辑某项信息。当然,它也允许按指定的格式从文件名中提取信息。有人会问,那我为什么不从文件名提取信息呢?笨蛋,前面我说过我是用空格来分隔歌手和歌名的。鬼知道“Avirl Lavigne Complicated.mp3”是不是指Avirl唱的Lavigne Complicated。
    一切搞完了,还要Save一次,软件才会真正的改动文件。哈哈,这下就爽了。当然,这样的ID3还不完善,我就等着大家推荐一些在线歌曲数据了。这下Perfect了,同步到iPod,爽。

做人要厚道
转贴请注明出处