ひびのログ

日々ではないけどログを出力していくブログ

mp3 ファイルのメタ情報に入っているタイトルを CLI で一括変更

iTunes で複数の関連する CD を取り込んだのだが、曲のタイトルが統一されていなかった。 具体的には、「[CD 番号]-[トラック番号]」みたいになってほしかったのに、1 つの CD だけ「トラックxx」みたいな形式でインポートされてしまった。

ファイル名を変えても曲タイトルは変わらないので、メタ情報を変更する必要があると判断した。 ただ、CLI からサクッとメタ情報を触る方法がなさそうだったので、バイナリをいじって変更してみようと思いたった。

ベターな解決策

フリーソフト入れたらよかったんじゃない?

目次

本編

環境

$ zsh --version
zsh 5.8 (x86_64-ubuntu-linux-gnu)

やったことまとめ

  • mp3 のメタ情報について調べる
  • Vim でバイナリファイルを開く方法を調べる
  • 実際のファイルがどうなっているか調べる
  • 書き換えてみる
  • CLI で一括変更する

mp3 のメタ情報について調べる

超参考になったのはこちらの記事。

zenn.dev

どうやら ID3 タグというものが mp3 のメタ情報らしい。

じゃあ実際のファイルでどうなってるんだろうか。

Vim でバイナリファイルを開く方法を調べる

ググったらサクッと出てきた。

qiita.com

-b を付けてファイルを開き、:%!xxd を実行すればいいらしい。 編集が終わったら :%!xxd -r して保存。

実際のファイルがどうなっているか調べる

ということで中身を確認。

00000000: 4944 3302 0000 0000 107f 5454 3200 0011  ID3.......TT2...
00000010: 01ff fec8 30e9 30c3 30af 3030 0032 0000  ....0.0.0.00.2..
00000020: 0054 524b 0000 0600 322f 3532 0043 4f4d  .TRK....2/52.COM

さっきの記事などに照らし合わせて読んでみる。 ちなみに「長さ」はバイト数なので、例えば 4944 は長さ 2 になる。

最初の 3 文字が ID3、その次が 0200 なっているので、ID3v2.2 であることが分かる。

ID3v2 ヘッダ

ID3v2 ヘッダは以下のように定義されている(Wikipedia より)

オフセット 長さ 説明
0 3 “ID3” のマジックナンバー3文字
3 2 バージョン
5 1 フラグ
6 4 サイズ

バージョンまでは読んだのでそこから。

次の 1 バイトがフラグで、00 なので未使用。

次の 4 バイトがサイズで、0000 107f となっている。
Syncsafe Integer というやつらしいので、いい感じに読んでみると以下の通り。
107f0001 0000 0111 111101000011111112175
合っているかは確認してない。

この後に拡張ヘッダが続くこともあるらしいが、v2.2 では存在しないので関係ない。
(ちなみに v2.3 以降で拡張ヘッダがある場合は、フラグで明示される)

フレーム

要するにデータの塊。 これが繰り返し指定される。

データの形式は以下(Wikipedia より)

オフセット 長さ 記述
0 3 フレームID
3 3 フレームサイズ
6 可変長 フレームデータ

はい見ていきましょう。

5454 32 で、右側に TT2 って出ている。 Wikipedia でフレーム ID の一覧を見ると、「Title/songname/content description」とのこと。 今回の目的であるタイトルはここに記述されているようだ。

その次にはフレームサイズがある。 00 0011 なので 3……3? 17 だった気がするんだけどなぁ…… (ここはよくわからない、記事を書く前に試したときは 17 だったんだけどなんでだろう)

ということでスルーしてフレームデータへ。 3 行目に TRK が見えているので、この前までだろう。 17 だよなぁ……?

01ff fec8 30e9 30c3 30af 3030 0032 0000 00

最初が 01 なので UTF-16。 fffe はリトルエンディアンの BOM。 ということは、これ以降は 2 バイトごとに区切ってひっくり返して読む必要がある。 直したものがこちら。

30c8 30e9 30c3 30af 3000 3032 0000 00

ここで 30c8 unicode でググると、「ト」に対応することが分かる。 全部対応付けると、「トラック02」となる。 実際にエクスプローラーに表示されているものと同じ。

ここまでで、どこにどんなふうにタイトル情報が入ってきているのかわかった。

書き換えてみる

最初の c8 30c7 30 に書き換えたら、1 文字目の「ト」が別の文字に変わるだろうと予想。

実際に書き換えてエクスプローラーで見てみると、「デラック02」になっていた。 Unicode は濁点付きの文字はなしの文字の直後に入っているんだな。

CLI で一括変更する

ここからが実際にやりたかったこと。

バイナリを一括置換してタイトルを変えていく。 今回は「トラックxx」を「1-xx」というものに変える。

xx 部分はそのままに、前半をサクッと置き換える。 全体の文字長が違うので、余った部分を 0 パディングする必要があるので、フレーム全体を置換する。

sed でバイナリを扱えないか調べたけど、一回 xxd コマンドでバイナリにしてから置き換える方法が書いてあった。

nodachisoft.com

で、ここで -c オプションに 68719476736 = 64 GB を指定しているが、自分の環境では指定できなかった。 10000000 = 10 MB なら問題なさそうなのでこちらに変更して実行。

xxd -p -c 10000000 "$filename" \
  | sed -r -e 's/c830e930c330af30(....)(..)000/31002d00\1\200000000000/' \
  | xxd -p -r \
  > "_new-$filename"

今気付いたけど 0 足りなかったかも、支障はないけど。 あと数字の位置指定がいまいちだなぁ(両方 4 文字取るべきだった)。

ファイルサイズがでかいなら、-c での指定をそこそこにして、最後に改行を消して書き込みすればよさそう。 sed -r -e 's/\r?\n//g' みたいな。

実行すると、_new-ファイル名.mp3 みたいなやつが吐き出されるので、「タイトルが書き換わっているか」「再生できるか」を確認する。 問題なさそうなので全体に適用していく。

ls -1 \
  | while read -r line; do \
    xxd -p -c 10000000 "$line" \
      | sed -r -e 's/c830e930c330af30(....)(..)000/31002d00\1\200000000000/' \
      | xxd -p -r \
      > "_new_$line";
  done

これで全部のファイルに対して実行が完了したので、置換前のファイルを消して、置換後のファイルの名前を変える。 自分はエクスプローラーで古いファイルを削除、mmv コマンドで Vim の矩形選択でよきにした。 CLI でやるならこうやったかなぁ(未検証)

# 消す
ls -1 | grep ".mp3" | grep -v "_new_" | rm
# リネーム
ls -1 | grep ".mp3" | xargs -i mv {} "$(echo {} | sed -r -e 's/_new_//g')"

おわりに

mp3 のメタ情報について詳しくなった。 詳しくなる必要があったかはわからないけど、多分なかった。

とはいえ、シェルでバイナリファイルを触ったことがあまりなかったから、いい経験になった。 xxd を使えばこれまでの文字列とほぼ同じように扱えた。 ただ自動的に改行が入ってしまって、それをまたぐと sed が効かなかったりしたので、若干のハマりどころがあるっぽい。