• tag_cache の構造について

    個人的な理由になりますが、MPDでライブラリの更新を行った場合でもRADIOなど並び順が気になる場合がありまして、
    ググった限りではあまり情報がヒットしなかったのでスレッド立てしてみました。

    とりあえず、「エディタで編集してMPDに戻しこむ」のをまずは最初の目標にしたいと思います。

    ちなみに今日までの解析結果のヒントはこちら
    から。
    ざっくりとrubyのスクリプト見た限りではそのまま楽曲のタグ情報を改行だけで書き出しているように見える。

    でもダウンロードしたタグはバイナリ形式でそのままは中身が見えない。
    よくよくファイルをCinnamon(私は普段使いはlinux mintなもので)で見るとアーカイブのアイコンになっていました。
    というわけで、ファイル拡張子を色々替えて試した結果、tag_cache.zipに変更して解凍すると、中からtag_cacheが出てきまして、
    中身を下にちょっとだけ貼り付けると以下のように記述されていました。

    info_begin
    format: 2
    mpd_version: 0.22~git
    fs_charset: UTF-8
    tag: Artist
    tag: ArtistSort
    tag: Album
    tag: AlbumSort
    tag: AlbumArtist
    tag: AlbumArtistSort
    tag: Title
    tag: Track
    tag: Genre
    tag: Date
    tag: Composer
    tag: Performer
    tag: Disc
    tag: Label
    info_end
    directory: NAS
    mtime: 1622358726
    begin: NAS
    directory: 2 Unlimited
    mtime: 1588470282
    begin: NAS/2 Unlimited
    directory: Get Ready!
    mtime: 1609336652
    begin: NAS/2 Unlimited/Get Ready!
    song_begin: 01 Get Ready For This [Orchestral Mix].mp3
    Time: 327.894000
    Artist: 2 Unlimited
    Album: Get Ready!
    Title: Get Ready For This [Orchestral Mix]
    Track: 1
    Genre: Club Dance
    Date: 1992
    AlbumArtist: 2 Unlimited
    Format: 44100:f:2
    mtime: 1544356199
    song_end
    

    なんとなく見た通り意味が読み取れそうな記述だが、果たして「mtime」って何?
    推測した結果、UNIX時間らしいことがわかりました。
    たとえば、上のリンクに「1544356199」を入れてみると「2018年12月09日 20:49:59」と出てきました。
    DB作成時点で当該ファイル・ディレクトリのタイムスタンプを記録しているのでしょうか(ココはまだ未確認です)

    とりあえず今日はここまでです。

  • まずは昨日のmtimeの推測結果の回答について。
    やはりタイムスタンプでした。
    ディレクトリのmtimeはディレクトリの、楽曲のmtimeは楽曲ファイルのタイムスタンプです(実際のファイル・ディレクトリのタイムスタンプと一致しました)。

    tag_cacheの記述をもう少し見てみます。
    先頭のinfo_beginとinfo_endで囲まれた領域でMPDの環境設定が並ぶようです。
    また基本的には1行で":"の左右で2つの意味を分けており、左側が「キー」右側が「値」となっています。この辺りはjsonに似ていますね。
    info_begin句にある各キーの説明を表にまとめると以下の通り。

    キー 説明
    format 今のところ不明
    mpd_version mpdで使用しているバージョン表記(0.22-git)
    fs_charset mpd.conf の filesystem_charset に指定した文字コード(UTF-8)
    tag mpd.conf の metadata_to_use に指定したタグ(アルファベット順で表記)

    次に begin: NAS からNAS内部の各ディレクトリ・楽曲が並びます。
    ディレクトリ表記の有効範囲は begin: NAS から end: NAS(ディレクトリ名がNASの場合)までになります。
    階層ディレクトリの場合は以下の通りの表記になります(仮にA > B > C の順の場合とします)。

    begin: A
    begin: B
    begin: C
    (C直下の楽曲プレイリストの情報が記述される)
    end: C
    (B直下の楽曲・プレイリストがある場合、ココに記述される)
    end: B
    (A直下の楽曲・プレイリストがある場合、ココに記述される)
    end: A
    

    NAS内部についてはアルファベット順のため基本的には変更が必要なさそうです。

    mp3・flac等楽曲の場合は以下の通りタグが埋め込まれています。
    実は現状のwebuiでは使用していませんが、楽曲に直接タグが埋め込まれていない場合でも mpd を経由してタグを編集出来、
    その結果は tag_cache に保存されているようです。

    ディレクトリ(アルバム)内の楽曲のタグ分類を例示します。

    song_begin: 01 - 鷺巣 詩郎_ Shiro SAGISU - paris.flac
    Time: 232.653000
    Track: 1
    Artist: 鷺巣 詩郎
    AlbumArtist: 鷺巣 詩郎
    Album: Music from "SHIN EVANGELION" Evangelion:3.0+1.0_ 2021.3.17 release <KICA 2586>
    Title: paris
    Genre: Anime
    Date: 2021
    Disc: 1
    Format: 44100:16:2
    mtime: 1618142465
    song_end
    

    ディレクトリの場合と同じく、楽曲も構造化されており、song_beginからsong_endまでが1つの楽曲のタグ情報集合の一区切りになります。
    楽曲で見慣れないキーとしてはTimeですが、これは見たとおり楽曲の長さを示します。

    次にラジオ局などのプレイリストを例示します

    playlist_begin: BBC-RADIO-1.pls
    mtime: 1623476708
    playlist_end
    

    楽曲のタグと打って変わって、プレイリスト名とそのファイルのタイムスタンプ(mtime)のみです。

    という訳で、手動でソートする場合はbeginのキー部分からendまでを選択する必要があるようだと言うのは読み取れました。

    手動でソート後にzipで再圧縮し、元の tag_cache と差替えて動作するか確認した結果は明日報告します。

  • さて、手動書換後のtag_cacheですが、結論から言うと「成功しました」。
    ただし、2点ほど注意点がありましたので報告します。

    • 記載順番がある
      先の投稿で示したルールではちょっと説明が足りなかったので、再度説明します。
      ディレクトリ表記の場合
    directory: a
    begin: a
    mtime: 〜
    (ココに当該ディレクトリ内のアイテムを記述する)
    end: a
    

    ()内部に指定可能なアイテムはディレクトリ(directory〜end)、楽曲(song_begin〜song_end)、プレイリスト(playlist_begin〜playlist_end)です。
    必ず、ディレクトリ→楽曲→プレイリストの順になります。
    本順番を誤って手動編集した場合、
    mpd起動時に正規のtag_cacheとみなされずにライブラリーの再検索がかかって編集したtag_cacheが上書きされました。
    (私の場合、RADIOの編集時にbeginとmtimeの間にプレイリストを入れてしまったところ、mpdの起動時に再検索がかかりました。)
    またディレクトリ階層表示(構造がa>b>cと仮定した場合)は以下のとおりになります

    directory: a
    begin
    mtime: 〜(ディレクトリaのタイムスタンプ)
    directory: a/b
    begin
    mtime:〜(ディレクトリbのタイムスタンプ)
    directory: a/b/c
    begin
    mtime:〜(ディレクトリcのタイムスタンプ)
    (ココにはcディレクトリ内部のアイテム)
    end: a/b/c
    (ココにはbディレクトリ内部のアイテム)
    end: a/b
    (ココにはaディレクトリ内部のアイテム)
    end: a
    
    • tag_cacheの圧縮方法について
      linux mintのCinnamon右クリックで表示される「Compress」で圧縮した場合、
      認識できませんでした。
      ググって調査したところ、tag_cacheはgzipで圧縮されているとの記述があり、記載通りにgzip圧縮した結果正常に認識されました。

    という訳で、ライブラリの規模にもよりますが、どうしてもtag_cacheのソートがしたい場合は手動で実施することは出来ますが、
    「事前に現在のtag_cacheのバックアップを必ず取ること」、「tag_cacheの表記方法を遵守すること」、
    「圧縮時はgzipを使用すること」
    の3点を遵守すると幸せになると思います。

  • @sunatomo さん
    こんにちはご無沙汰です。 kkumaxです。
    個人的な思いつきなので
    外しているかも知れませんが
    今回のtag_cache の構造解析が無事終わったら
    ls sed awk等のコマンドを組み合わせたスクリプトで
    tag_cache ファイルが作れる様にすると便利になる様に思います。

  • @kkumax さま
    ご意見有り難く頂戴します。
    ただ以下の2点悩ましい問題があります。
    1.tag_cacheの編集に耐えられる品質のソフトが作れるか
    私は曲数がまだ少ないのでtag_cacheを解凍しても2.3MBしかありませんでしたが、
    ここにおられるかたは重量級のライブラリをお持ちの方が多いと想定されるため、
    これに応えることのできるバグのないツールはちょっと難しそうです。

    2.mpd本体のソースコードを直したほうが得策な気もするため
    ソースコードを見てみたところ、tag_cache生成部でライブラリフォルダ内部ではプレイリストのソートをしないような記述に見えました。
    mpd本体を直せばツールを作るより根本的に対策出来そうですが、
    smpdのpで使用しているmpdは @パパリウス さまが専用チューニングまでしているためパッチの提供が何時になるかはちょっと見通せません。
    mpd公式のgithubでのpull requestのやり取りみましたが開発者の方で考えている思想に反していると受け取って貰えないように見えます。
    (「プレイリストはmpd.confに記載のフォルダに入れない場合はソートしない」と言う思想の場合)

    取り敢えずArch Linux AoE の mpd で実証はしてみようと思います。

  • @sunatomo さん
    思いつきで勝手な事を書いてすいません。m(_ _)m
    どんな感じなのか?ちょっとだけ触ってみました(笑)

    UNIX時間って知りませんでした....
    $ echo 1415925575 | awk '{print strftime("%c",$1)}'
    2014年11月14日 09時39分35秒

    なるほど!!
    それともう判明している思いますがFormat: 44100:f:2 のfは
    ファイルのビット数みたいですですね。
    「f」って16進数で16ですよね。
    私のデスクトップはArchlinuxなのですが
    tag_cache.zipにして解凍すると

    directory: A-ha
    mtime: 1506610846
    begin: A-ha
    song_begin: take_on_me.flac
    Time: 231.956000
    Album: Hunting High And Low
    Artist: a-ha
    Title: take on me
    Track: 1
    Format: 96000:24:2
    mtime: 1415925575
    song_end
    end: A-ha

    こんな感じで出て来ますね。
    Mediainfoをインストールしてcliで使えば
    変数として情報を色々と取り込めそうな気がします^^;

  • @kkumax さま

    思いつきで勝手な事を書いてすいません。m(_ _)m

    いやー、その気になれば何とかなるかもしれませんが、
    「巷にそういうものが無い」ということはあまり不便に感じていないのかもしれませんね...

    Format: 44100:f:2 のfは
    ファイルのビット数みたいですですね。
    「f」って16進数で16ですよね。

    ここらへんはちょっと謎なんです。mp3の私の例示では"44100:f:2"なのに、flacの場合は"44100:16:2"になっているんですよね。
    ちなみに16進数でfは15になります。(10進数の9以上を16進数で並べると、 9,a,b,c,e,f)。

    ちなみにinfo_begin部分のtagキーに列挙したタグ以外を書いたとしても認識しないかもしれません(ココはまだ未確認ですが)


    さて、MPDのビルドですが、pacmanでパッケージ情報を取得してからaspとmakepkgを実行してみたのですが、実行の途中で失敗します。

    この調子だと、かなり時間がかかりそう...

  • さて、私の普段動作しているArch LinuxはRaspberryPi3のため、
    どうやらメモリが足りないというのがビルドできなかった理由ぽいです。

    PCの方で試した結果ビルド出来なかったのはOSが古すぎた(Linux Mint 19.3)ためで、Mesonがエラーを起こしていました。
    という訳で、週末を使ってLinux Mint 20.1 に入替を行ったところ、ビルドは通りました。

    あとは、C++のソースコードをちゃんと読めるようにならないと...

  • さて、ちょっと間が空いてしまいましたが、理由としては普段使っていないC++でMPDは記述されているため、これに合わせて自分のバージョンアップを少しづつする必要があるためでした。
    まだまだ、実際に改変・ビルドすることはできませんが、
    本日時点でわかった範囲をちょっと記載してみます。

    mpdのソースコードを見ていて気づいたことがありました。
    下にsrc/db/plugin/simple/Directory.hxx の当該部のみ貼ります。

    struct Directory {
    	List children;
    	SongList songs;
    	PlaylistVector playlists;
    

    ディレクトリ構造体の中にchildren(子ディレクトリ)・songs(楽曲)・playlistsというデータが格納されていることが想像出来ます。

    Directory.cxxを見ると

    void
    Directory::Sort() noexcept
    {
    	assert(holding_db_lock());
    
    	children.sort(directory_cmp);
    	song_list_sort(songs);
    
    	for (auto &child : children)
    		child.Sort();
    }
    

    という関数があることが分かりました。
    という訳で、子ディレクトリの並びと楽曲以外はソートされない仕様です。
    PlaylistVector.cxx にソート関数を装備し、Directory::Sort() 関数でプレイリストのソートをさせないと恐らくRADIOはソート出来ないでしょう。

    ついでに言うと、私が調べた範囲では/etc/mpd.conf に指定した playlist_directory 内部のファイルもソートされない仕様のようです(これはソースコードベースではなく、webuiでアルバム単位でプレイリストを保存した場合の動作でわかった範囲での話ですが)。

    ちなみに、今回ソースコードを調査した範囲はsmpd v.1.0系で採用したmpd v0.22-git(githubからその当時にダウンロードしたソースコード)の内容に基きます。
    現状のメインストリームの最新版であるv0.22-8ではどの様に記述されているかもちょっと見てみます。