iPhone で YouTube Music を聴いた履歴を Last.fm へ送りたかったが、iPhone 版の Last.fm アプリは YouTube Music に対応していなかった。Spotify のように、YouTube Music 自体から直接 scrobble することもできないようだった。
そこで、Reddit で見つけた youtube-music-scrobbler を使い、聴いた曲を定期的に scrobble することにした。Incus コンテナ上で 1 時間ごとに動かしている。
初 回は約 139 曲を送れたものの、しばらくすると「Retrieved 0 songs from history」になった。最終的には、シークレットウィンドウの Network タブから認証済みリクエストの Cookie ヘッダーを取り直した。2026 年 9 月 10 日時点では履歴を取得できており、そのまま様子を見ている。
実行環境
| 項目 | 内容 |
|---|---|
| ホスト側 | Incus |
| コンテナ | Ubuntu 24.04 |
| 実装 | Python、youtube-music-scrobbler |
| 実行ユーザー | system user ytmscrob |
| 定期実行 | systemd timer、1 時間ごと |
| YouTube Music 認証 | 認証済みリクエストの Cookie |
| Last.fm 認証 | Web Authentication の session key |
Incus コンテナへ導入する
Ubuntu 24.04 のコンテナを作成して中に入った。
incus launch images:ubuntu/24.04 youtube-music-scrobbler
incus exec youtube-music-scrobbler -- bash必要なパッケージをインストールし、リポジトリを /opt/youtube-music-scrobbler へ配置した。
apt update
apt install git python3 python3-venv python3-pip
git clone https://github.com/luisignaciocc/youtube-music-scrobbler.git /opt/youtube-music-scrobbler
cd /opt/youtube-music-scrobbler
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txtpysqlite3 を標準の sqlite3 へ変更した
そのままでは pysqlite3 のインストールに失敗した。このコンテナでは Python 標準の sqlite3 で動いたため、次の 2 点だけ変更した。
- requirements.txt から pysqlite3 を削除する。
- Python コード内で pysqlite3 を読み込んでいる箇所の import を変更する。
-import pysqlite3 as sqlite3
+import sqlite3変更後に pip install -r requirements.txt をやり直した。これは上流リポジトリの標準手順ではなく、今回の Ubuntu 24.04 コンテナで行った変更である。
Last.fm の session key を取得する
Last.fm の Web Authentication では、アプリケーションを許可したあとに返される token を auth.getSession へ渡して session key と交換する。
Incus コンテナ上の処理へコールバックを戻す構成にしづらかったので、ブラウザーに返された token をコピーし、次のスクリプトで交換した。
import os
import hashlib
import requests
from dotenv import load_dotenv
load_dotenv()
api_key = os.getenv("LAST_FM_API") or os.getenv("LASTFM_API_KEY") or os.getenv("LAST_FM_API_KEY")
api_secret = os.getenv("LAST_FM_API_SECRET") or os.getenv("LASTFM_API_SECRET") or os.getenv("LAST_FM_SECRET")
if not api_key or not api_secret:
raise SystemExit("API key/secret が .env から読めません")
token = input("Paste token: ").strip()
params = {"method": "auth.getSession", "api_key": api_key, "token": token}
sig_base = "".join(f"{k}{params[k]}" for k in sorted(params)) + api_secret
params["api_sig"] = hashlib.md5(sig_base.encode()).hexdigest()
params["format"] = "json"
r = requests.post("https://ws.audioscrobbler.com/2.0/", data=params, timeout=20)
print(r.text).env には Last.fm の API key と shared secret を保存する。実行時は認可後の URL に含まれる token を入力する。
実行する場合は、上のコードを /opt/youtube-music-scrobbler/get_lastfm_session.py として保存し、同じディレクトリの .env を使う。必要なライブラリを仮想環境へ入れてから実行する。
cd /opt/youtube-music-scrobbler
.venv/bin/python -m pip install requests python-dotenv
.venv/bin/python get_lastfm_session.pyレスポンスには session key が含まれるため、この出力をそのまま共有すると認証情報も渡してしまう。API key や shared secret、後述する Cookie と同じく、取り扱いに気をつけたい。
専用ユーザーと systemd timer で動かす
定期実行を root で動かす必要はないため、ytmscrob ユーザーを作成した。
useradd --system --home /opt/youtube-music-scrobbler --shell /usr/sbin/nologin ytmscrob
chown -R ytmscrob:ytmscrob /opt/youtube-music-scrobblersystemd service の User と Group にもこのユーザーを指定し、ExecStart には仮想環境内の Python と実際のエントリーポイントを絶対パスで設定した。完全な unit は記録に残っていないので、ここでは確認に使ったコマンドだけを載せる。
systemctl daemon-reload
systemctl start youtube-music-scrobbler.service
journalctl -u youtube-music-scrobbler.serviceservice を 1 時間ごとに呼ぶ timer は次のようにした。
[Unit]
Description=Run YouTube Music scrobbler hourly
[Timer]
OnCalendar=hourly
Persistent=true
[Install]
WantedBy=timers.targetsystemctl enable --now youtube-music-scrobbler.timer
systemctl list-timers youtube-music-scrobbler.timertimer は予定どおり起動し、初回は YouTube Music から約 139 曲を取得して Last.fm へ送れた。
途中から履歴が 0 件になった
その後の実行では「Retrieved 0 songs from history」と出るようになった。
- 初回は同じ Cookie で約 139 曲を取得できた
- timer は予定どおり実行されていた
- Last.fm へ送る前の、YouTube Music から履歴を取る段階で 0 件になった
- 別端末で YouTube Music を再生したあとに発生した
別端末で再生したあとだったため、この時点では Google 側のセッションが更新されたのではないかと考えた。ただし、再生が直接の原因だったかは確認できていない。
シークレットウィンドウから Cookie を取り直す
Music Assistant の資料には、開いたままのブラウザータブでは Cookie が更新されるため、シークレットウィンドウを使い、Network タブの認証済みリクエストから Cookie を取得する方法が記載されていた。今回はこれを参考にした。
- シークレットウィンドウで https://music.youtube.com/ へログインする。
- 開発者ツールの Network タブを開く。
- /browse で絞り込み、ライブラリなど認証が必要なページを開く。
- /browse リクエストの Headers を開く。
- Request Headers にある Cookie の値を、前後へ余分な文字を付けずにコピーする。
- scrobbler の Cookie を置き換え、シークレットウィンドウを閉じる。
Application タブの Cookie 一覧から個々の値を組み立てるのではなく、実際に送信されたリクエストの Cookie ヘッダーをそのまま使うのがポイントだった。
ytmusicapi の Browser Authentication にも、Network タブの認証済み POST リクエストからヘッダーを取得する手順がある。
9 月 10 日時点では動作を継続している
シークレットウィンドウから取り直したセッションは、2026 年 9 月 10 日時点でも履歴を取得できている。以前の Cookie より長く利用できているため、この環境ではこの方法を採用して様子を見ることにした。
ただし、観察を始めた日を記録していなかったので「何日間有効だった」とは言えない。また、長持ちした理由も切り分けられていない。考えている候補は次の 3 つである。
- シークレットウィンドウでは関係のない Cookie が少なかった
- ログイン、コピー、ウィンドウを閉じるという流れで、その後 Cookie が更新されにくかった
- Application タブではなく、認証済みリクエストが実際に送った Cookie ヘッダーを丸ごとコピーしたことが効いた
特に 3 つ目の影響が大 きいのではないかと考えている。Application タブから手作業で集めると、domain や path による送信先の条件に合う「そのリクエストで実際に送られる Cookie」と一致しない可能性があるためである。
いずれも仮説であり、どれが有効期間の差につながったかは未検証である。次に履歴が 0 件になったら、取得日と失効日を記録して確認する。
まとめ
Incus コンテナ上へ youtube-music-scrobbler を置き、専用ユーザーと systemd timer で 1 時間ごとに動かせた。途中で履歴が 0 件になったが、シークレットウィンドウの Network タブから、認証済み /browse リクエストの Cookie ヘッダーを取り直すと復旧した。
2026 年 9 月 10 日時点では動作を継続している。シークレットウィンドウ由来のセッションは以前より長く使えているが、有効期間と理由はまだ確定していないので、次に切れた時点まで記録を続ける。
おしまい
参考
関連書籍
Incus の環境構築に関心があれば、『コンテナ管理ツール Incus で作る Linux 学習環境 ―環境構築編―』も関連書籍として挙げておく。







