UTF-8/CP932 両対応 mt-daapd の作成とインストール

ちょっと話は前後するのですが、LANDISK に Debian がなんとかはいったので、とりあえず samba やらをいれてファイルサーバにしてみました。その後ついでなので、mt-daapd をいれて iTunes サーバにしてみようと。apt で問題なくインストールされて設定もでき、いよいよノート PC の HDD を圧迫していた MP3 ファイル群をコピーして、iTunes から覗いてみよう…と思ったら、文字化け発生!
mt-daapd は ID3 タグを UTF-8 として解釈するのですが、iTunes 以外で取り込んだファイルでは Shift-JIS で作成されたタグが ISO-8859-1 のふりをして混じっているためにこーゆーことになるそうです。


Shift-JIS と UTF-8 両対応のパッチがこちらにあったので、ちょっとバージョンが下がりますがソースをとってきてあてました。
ただ、私の使った mt-daapd 0.2.4.2 では若干調整が必要だったのと、このパッチはファイルの名前やプレイリストなどのファイルが Shift-JIS で作成されている前提に立っており、少なくともうちの環境では必要ない(samba でファイルをコピーすれば、自動的に UTF-8 のファイル名でコピーされているようです)ので、いくらかさらに手を入れてます。

パッチをあてる

mt-daapd は現在 firefly media server と名前が変わっています。http://www.fireflymediaserver.org/ からたどって、0.2.4 系 stable release の最新版(0.2.4.2)をとってきます。UTF-8/CP932 両対応パッチは上記サイトの最新版が 0.2.4 用なので、そちらをダウンロードしておきます。
パッケージを展開して、パッチをあてます。バージョン違いのため、parser.c で一部失敗がでます。

$ tar xvzf mt-daapd-0.2.4.2.tar.gz
...

$ cd mt-daapd-0.2.4.2
$ patch -p1 <../mt-daapd-0.2.4-cp932.patch
patching file src/db-gdbm.c
patching file src/mp3-scanner.c
patching file src/parser.c
Hunk #1 FAILED at 36.
Hunk #2 succeeded at 180 (offset 100 lines).
Hunk #3 succeeded at 1846 with fuzz 2 (offset 697 lines).
Hunk #4 succeeded at 1922 (offset 697 lines).
1 out of 4 hunks FAILED -- saving rejects to file src/parser.c.rej

src/parser.c は実は bison が生成したファイルで、ちょっと文法規則などがバージョン間で差異があるために、コンテクストがうまくマッチできずにパッチが失敗しているのです。
なので、本当はパッチをあてるなら src/parser.y にあてるべきです。ここでは src/parser.c はほっといて、src/parser.y を修正しましょう。

*** mt-daapd-0.2.4.2/src/parser.y    Sun Apr 20 04:17:23 2008
--- mt-daapd-0.2.4.2_work/src/parser.y      Sat Jan 24 01:52:40 2009
***************
*** 28,33 ****
--- 28,49 ----
  #include <string.h>
  #include "playlist.h"

+ #define USE_CP932
+
+ #ifdef USE_CP932
+   #define UNKNOWN_STR    "UNKNOWN"
+   #ifndef FILESYSTEM_CES
+     #define FILESYSTEM_CES "CP932"
+ /*
+     #define FILESYSTEM_CES "UTF-8"
+     #define FILESYSTEM_CES "EUC-JP"
+ */
+   #endif
+
+   #include <iconv.h>
+   #include <errno.h>
+ #endif
+
  #define YYERROR_VERBOSE 1

  extern int yyerror(char *msg);
***************
*** 172,177 ****
--- 188,230 ----
  ;

  %%
+ #ifdef USE_CP932
+ #define MAX_ICONV_BUF 1024
+
+ typedef enum {
+   ICONV_OK,
+   ICONV_TRYNEXT,
+   ICONV_FATAL
+ } iconv_result;
+
+ static iconv_result do_convert(const char* to_ces, const char* from_ces,
+                              char *inbuf,  size_t inbytesleft,
+                              char *outbuf_orig, size_t outbytesleft_orig) {
+   size_t rc;
+   iconv_result ret = ICONV_OK;
+
+   size_t outbytesleft = outbytesleft_orig - 1;
+   char* outbuf = outbuf_orig;
+
+   iconv_t cd  = iconv_open(to_ces, from_ces);
+   if (cd == (iconv_t)-1) {
+     return ICONV_FATAL;
+   }
+   rc = iconv(cd, &inbuf, &inbytesleft, &outbuf, &outbytesleft);
+   if (rc == (size_t)-1) {
+     if (errno == E2BIG) {
+       ret = ICONV_FATAL;
+     } else {
+       ret = ICONV_TRYNEXT;
+       memset(outbuf_orig, '\0', outbytesleft_orig);
+     }
+   }
+   iconv_close(cd);
+
+   return ret;
+ }
+ #endif
+
  PL_NODE *pl_newintpredicate(int tag, int op, int value) {
      PL_NODE *pnew;

***************
*** 210,216 ****
--- 263,287 ----
      pnew->op=op;
      pnew->type=T_STR;
      pnew->arg1.ival=tag;
+ #ifdef USE_CP932
+     if (!strcasecmp(FILESYSTEM_CES, "UTF-8")) {
+     pnew->arg2.cval=value;
+       } else {
+       char* iconv_buf = (char*)calloc(MAX_ICONV_BUF, sizeof(char));
+       if (iconv_buf) {
+         iconv_result rc = do_convert("UTF-8", FILESYSTEM_CES, value, strlen(value),
+                                      iconv_buf, MAX_ICONV_BUF);
+         if(rc == ICONV_OK) {
+           pnew->arg2.cval = iconv_buf;
+         } else {
+           pnew->arg2.cval = strdup(UNKNOWN_STR);
+           free(iconv_buf);
+         }
+       }
+       } // if utf-8
+ #else
      pnew->arg2.cval=value;
+ #endif
      return pnew;
  }

これであとは make すれば、parser.y から勝手に parser.c が作られます。ソースコードのほうは CP932 の文字コードに対応しました。

もうちょっとソースをいじる

さて、ところでそれぞれのファイルへのパッチは次のような変換をやっています。

src/db-gdbm.c
プレイリスト名(おそらくはファイル名)の文字コード変換。CP932 の入力を仮定。
src/mp3-scanner.c
曲名などの文字コード変換。CP932/UTF-8 両対応。ID3 タグがない場合ファイル名を使うが、そちらは CP932 の入力を仮定。
src/parser.y (src/parser.c)
プレイリストの記述における文字コード変換。CP932 の入力を仮定。

最初に書いたとおり、samba はファイルをコピーするとファイル名は UTF-8 に変換してくれるようですので、ファイル名変換は必要ありません。(というか、やると文字化けが起こります。) また、プレイリストも(現在使っていませんが)書くとしたらおそらく UTF-8 で書くでしょうから、内部の文字コード変換は必要なさそうです。
そこで、ファイルシステム文字コードをあわせてやります。このへんを制御しているのが、各ファイルにある FILESYSTEM_CES という #define 行で、ここが "CP932" になっているのを 3 ファイルとも "UTF-8" に書き換えれば OK です。

ビルド、インストール

configure, make, make install でもいいですし、Debian の場合は dpkg-buildpackage でもビルドできます。
パッケージになっていたほうが管理が楽なので、私は後者を選びました。

$ dpkg-buildpackage -rfakeroot
$ cd ..
$ sudo dpkg -i mt-daapd_0.2.5-1_arm.deb
(Reading database ... 15501 files and directories currently installed.)
Preparing to replace mt-daapd 0.2.5-1 (using mt-daapd_0.2.5-1_arm.deb) ...
Stopping mt-daapd: mt-daapd.
Unpacking replacement mt-daapd ...
Setting up mt-daapd (0.2.5-1) ...

$