Windows iTunes で管理してる MP3 ファイルのタグを Web アプリから更新する?「Neo's Music Library」を作った

自分は iPod nano 7G (16GB) を未だに使っている。2012-10-09 発表、2017-07-27 に発売終了を迎えた製品。自分が購入したのは 2016-10-14 のことで、もう10年前のことになる。商品在庫の都合で、池袋のビックカメラの本館と別館をたらい回しにされたなぁ…。w

ちなみに、普段使っているスマホ iPhone 17 Pro Max にも同じプレイリストを同期だけはしてあって、イヤホンさえ挿せればスマホでも音楽を聴けるんだけど、Type-C → 3.5mm イヤホンジャックへの変換コネクタをしばらく紛失していたので、外出時に聴くには向かなかった。最近ようやく変換コネクタを買い直したので、一応聴けるようにはなった。

ちなみにちなみに、イヤホンは先日 Audio-Technica Solid Bass ATH-CKS550i という型番のモノを Amazon 欲しいものリストから送っていただいたので、コレを利用している。このイヤホンは昔使っていたのと全く同じ型番。

オーテクの Solid Bass シリーズは元々好きで何回か買い替えて使ってきた。さらにこの ATH-CKS550i についてはイヤホンジャック部分がストレートなのが、個人的に使いやすくて好きだったのだ。それ以外の Solid Bass の型番や、2021-03-04 から使ってきた Sony MDR-EX155 など、多くのイヤホンはジャック部分にかかる負荷を減らすためにあえて L 字型になっていることが多いのだが、個人的にはポケット内で収まりが悪いのでストレート型のイヤホンはないものかと思ってずっと探していた。結局、低音もしっかり出るストレートジャックのイヤホンは、この型番しかなかった。w Sony MDR-EX155 は今も使えはするが、さすがに黄ばみがスゴいので、新しいイヤホンが手に入って良かった。ATH-CKS550i には iPod の操作用ボタンがケーブルに付いているのだが、iPhone 17 Pro Max に対しても音楽の再生・一時停止だけは動作してくれた (曲送り・戻るボタンは効かなかった)。

……えーと、何の話だっけ?w


あーそうそう、自分は iPod nano に音楽を入れているものの、同期する母艦 PC は Windows で、Windows iTunes を未だに使っている。そんな中、

とか、

みたいな、曲ごとに感じる複雑な情景や「効能」があったりする。

コレを管理したくなったのです。

単に「ヒップホップだからテンションが上がる」「バラードだから泣ける」ではない。音楽的ジャンルに関係なく、「自分の感じ方」を基準にした分類がしたくなった次第。


真っ先に思いつく手段としては「そういうテーマごとにプレイリストを作る」だが、前述のとおり自分はデスクトップの Windows マシンで iTunes 管理をしているので、外出先では iTunes の操作は難しい。

っていうか、この「自分がどう感じたか」を書く欄って、iTunes にあるの…?

と思ったら、「コメント」という項目があったので、ココに書いた内容を元に「スマートプレイリスト」機能を使って、「コメント欄に『モチベーションアップ』の文字列を含む楽曲リスト」みたいなのを自動的に組み上げることは出来そうだな、と。

じゃああとは外出先から iTunes ライブラリの「コメント」欄に何かを書き込むにはどうしたらいいのか、という話で、解決策として、Web アプリを作りました。その名も「Neo's Music Library」。


今回も Cloudflare Workers にデプロイしている。ウェブ画面は React Router SPA + Hono バックエンドといういつもの構成。

ウェブ画面で出来ることとしては、楽曲一覧を表示し、「コメント」欄だけ編集可能になっている。ついでに、「その曲をギターでコピーしたことがあるか」とか「自分がカラオケで歌える曲か」みたいな「レパートリー表」としても機能するように入力欄を用意してある。

ウェブ画面で入力された「コメント」情報は、その時点では D1 データベースに書き込まれるだけ。ココから母艦マシン上で実行するローカルスクリプトが別で用意してあり、以下のような作業を行っている。

  1. iTunes ライブラリから楽曲一覧を出力する
  2. Cloudflare D1 データベースに登録されている楽曲一覧 (ウェブ画面で編集されたコメント情報を含む) を取得する
  3. 両者のデータから「コメント」欄の変更内容を照合して、「(ウェブ画面で入力があったので) iTunes ライブラリへの反映が必要なコメント」と「(iTunes 側で先にコメントを編集していたので) D1 のレコードを UPDATE すべきモノ」とを判定する
  4. 「iTunes への反映が必要なコメント」については、MP3 ファイルの ID3 タグを書き換えて、MP3 ファイル自体にコメントを書く。その上で iTunes アプリが MP3 ファイルの変更を検知するように再読み込み処理を行う
  5. MP3 ファイルの書き換えを行った結果と「D1 の UPDATE が必要なモノ」とを合わせて、D1 に対して UPDATE をかけて状態を最新に保つ

…ということで、さすがにウェブ画面で編集した内容がリアルタイムに MP3 ファイルへと届くワケではないものの、母艦マシンでスクリプトを実行することで、ローカルでの変更とウェブ画面での変更とをうまくマージして処理するようにした。

iTunes ライブラリの情報取得や MP3 ファイルの変更反映に際しては、iTunes COM を使って iTunes アプリを操作している。winax という npm パッケージがあり、Node.js スクリプトから WSH 的なコードが書ける。コレのおかげで一部に VBScript を使う、みたいに開発言語がばらけずに済んだ。

Cloudflare D1 のデータの読み書きは Cloudflare SDK (cloudflare という npm パッケージ) を通じて API コールで実現している。D1 の操作といっても Wrangler CLI は使っておらず、API キーを発行している。

MP3 ファイルの ID3 タグとして書き込む部分は、iTunes が GUI 上で「コメント」欄をどう処理しているのかを調査したうえで、node-id3 という npm パッケージを使って MP3 ファイルへの書き込みを行っている。

ID3 タグの comment フィールドには language と text のプロパティがあって、言語ごとに異なるコメント文字列を複数持てる仕組みになっていた。

手元で動かしてみたところ、Windows エクスプローラ上で「ファイルのプロパティ」からコメント欄を書くと language: jpn (日本語) に書き込まれるのだが、iTunes アプリでコメントを書くと文字列の内容に関わらず language: eng (英語) に書き込まれるという仕様だった。つまり iTunes が認識できるコメントは language: eng のモノだけ、ということだ。

こうした仕様をチェックしたうえで iTunes COM の操作を行っているので、ウェブ上になかなか情報がなかったのだが、ChatGPT くんのおかげで調査も捗った。

この辺を使っている。


現在、iTunes ライブラリに入れている楽曲は8000曲程度。普段聴かない曲はライブラリから外したりしてだいぶ減らしたが、ウェブ画面で使う項目だけに絞っても、楽曲のメタデータを取り出すと 2MB 超の JSON ファイルになる。

当然、これをまるっと入れてある D1 データベースも、楽曲一覧のテーブルが8000レコード、1曲ずつに付随する「レパートリー」の表現のために別テーブルも存在して、ウェブ画面が最初にリクエストするデータ量も、2MB をちょっと超えることになる。

そのため、ウェブアプリ部分に関しては LocalStorage ではなく IndexedDB でローカルキャッシュを持つようにした。LocalStorage だと 5MB 上限が基本なので、将来的な容量増加を見越しての選択だ。Zustand ストアと idb-keyval を使ったラッパーを噛ませてある感じ。

やっていることの複雑さ、面倒臭さでは、母艦マシンで動かすローカル同期スクリプトの方が大変だったが、単純なコード行数だけ見るとなぜかウェブアプリ部分の方が多い、という結果に落ち着いた。今回も総コード行数は2万行程度。

Node.js で ActiveX・COM 操作を行うとか、MP3 ファイルのタグを書き換える、とかは今までほとんどやってこなかったのでなかなか作っていて楽しかった。


ココ最近、React Router SPA、TailwindCSS、daisyUI、ky、Zustand、Zod あたりをフロントエンドに使い、Hono でバックエンドを組み、オレオレルールを含む ESLint でフォーマットする、という構成が板についてきたので、AGENTS.md を含めた AI エージェント利用を前提としたドキュメンテーションも含めてボイラープレートプロジェクトを作った。

ローカル開発時に Hono と React Router と Cloudflare Workers をうまく統合して開発サーバを立てるところで苦労して、react-router-hono-server というパッケージを利用するに至ったので、この辺も含めて誰かの参考になれば幸い。