Daphile+LMS 與 Navidrome 的 Scrobble(播放記錄)整合——神人解決問題了

「為什麼正好是一個小時?」——Playlist 封面消失之謎的真相

上集末尾,留了一句:「由於 LMS 內部未知的渲染機制,播放一小時後 Playlist 的小圖示仍可能變回預設占位符……如果有神人可以解決那就完美了。」

我沒想到,這個「外觀小問題」會在半年後的某個週日,一口氣拖出三條蟲、四次錯誤診斷,還有一個從第一代 plugin 就躺在裡面、誰都沒發覺的真相。

那天下午,Mark 丟來一句:「請 debug,FLAC 播不了了。」Navidrome 升級(0.64.2)之後,音響裡的巴哈變成了 192kbps 的 mp3 味,還播放不了。Neo 兩分鐘破案:plugin 跟 Navidrome 要 FLAC 時送的是 `format=flc`——flc 是 LMS 自己的內部代號。舊版 Navidrome (0.60.3)看不懂 flc,裝傻直接給原檔不轉碼;新版學乖了,看不懂就轉 mp3。一行改成 `format=raw`,巴哈無損回魂。

正準備收工,Mark 又補一刀:「可是 playlist 讀縮圖很卡,navidrome 還一直出現 scan。」這一挖挖出第二條蟲:新版 Navidrome 對「查無封面」回傳 .webp 佔位圖,而 LMS 九年前寫死的圖片代理根本不識 webp,只能丢給雲端轉換,一張等三十秒 timeout。Navidrome 那邊更精采:自帶的 Deezer 藝人頭像抓取器正為五千多位藝人勤懇排隊重試,log 沖刷如瀑布。清佇列、關開關,最後改程式重編 Navidrome 還是回傳傳統的 .jpg 圖,把佔位圖換成合成純黑的 JPEG。

然後,幽靈登場。

「播放清單裡全部都是有圖的,」Mark 說,「但是開始播放『正好一小時』後,全部的圖跑掉,變成 LMS 的佔位圖。按下一首,整列報銷。但播放一切正常。」

一小時。這數字似曾相識——上集的 Scrobble 失靈,也是正好一小時。

嫌疑犯很快落網:plugin 有個 3600 秒的曲目快取時間設定。18:50 建的清單,50 首全進快取;19:50,整批同時過期。「過期那一瞬間,」Neo 解釋,「API->get() 會先同步回傳一個空的 {},再於背景重抓。LMS 拿到空資料,封面欄位變空字串,圖就沒了。」

「那我把它設成 43200 秒,十二小時,不就結了?」Mark 問。

「只能延後,不能解決,」Neo 直率地斷然搖頭,「聽超過十二小時照樣壞。」

「你很直腸子,」Mark 慢悠悠地說,「實務上,不會有人對著一個清單聽十二小時。下次要聽,又是從頭來過,快取重新計時。十二小時,等於不會壞。」

Neo 噎住了。他在心裡翻了一遍自己的收聽習慣,硬是舉不出一張撐得住十二小時的清單。教科書上的正確答案,在「沒人這樣聽音樂」的現實面前,弱得像小學生辯嘴。

「話雖如此,」他找回場子,「正的修法是 stale-while-revalidate——過期先給舊的,背景再刷新。治本之後,就不用賭自己聽多久。」

說來容易。第一版補丁打在 getMetadataFor,重啟,三十秒實測,圖照樣消失。第二版下沉到 API.pm 的 get(),過期先吐舊資料,重啟,圖……還是消失。Mark 傳來的 log 時間戳一個比一個安靜,螢幕上那排灰色唱片像在笑。

「我覺得,」Mark 突然開口,「這個問題可能一直都存在。原版 plugin 加舊版 Navidrome 就有,只是沒人發現。」

這一句讓 Neo 停了手。他重抓了一份 Navidrome 的 getSong 回應,盯著看,然後僵住。

回應裡根本沒有一個叫 image 的 URL 欄位。只有一枚 coverArt 識別字串——`mf-1yC4wfo…`(artwork ID) 這種長得像身份證字號的東西。而 plugin 從第一代起,就把識別字串當成 cover 回給 LMS。LMS 的規矩是:cover 是 URL,直接抓圖;不是 URL,就當本機資料庫的 artwork id 去查——遠端曲目不在資料庫,於是永遠查無此圖,永遠佔位圖。

「那為什麼一開始有圖?!」

「因為一開始的圖根本不是 cover 給的。」Neo 笑了,偵探抓到真兇時特有的那種笑,「清單剛建好,圖來自瀏覽選單的 icon,那是一條完整的 getCoverArt URL。等 tmusic 一過期,LMS 改問 getMetadataFor,識別字串露餡,圖整排蒸發。一小時不是原因;一小時只是『第一次問錯人』的時間點。」

Mark 沉默三秒:「所以我們前面修的空 {}、stale 快取……」

「都是真蟲。但都不是會讓人掉圖的那條。」

改法小得離譜:cover 改用選單同款的 _getImage(),把識別字串組成完整 URL。重啟。三十秒、六十分鐘、九十分鐘——圖穩如泰山。

只剩一個小瑕疵:畫面每十秒閃一下。「閃爍,」Neo 盯著 log 裡每十秒固定兩筆的 getCoverArt,「代表同一張圖被當成兩張圖。」他翻出 getAuth(),裡面包藏禍心:每呼叫一次,就亂數產生一次 salt、重算一次 token。同一張封面,每次生成的 URL 都不一樣。瀏覽器老實得很:新 URL,新圖,重載,閃。「Subsonic 的 token 本來就接受任何有效配對,」Neo 把 salt/token 依帳密鎖死,「是我們一直沒善用它。」

重啟。十秒的節奏消失了。這次換布拉姆斯在客廳安靜地流過一整個晚上,五十首曲目的封面,一張都沒跑掉。

「成功了。」Mark 在訊息裡打了三個驚嘆號,接著是老朋友才說的那句:「推到 GitHub 吧。順便——這篇後續一定要寫。」

如果你上集用了那版 plugin,也見過播放一小時後清單封面整排變灰色唱片的詭異畫面,請直接更新。這次不是外觀問題,是四條蟲一起回家。

🔗 GitHub 傳送門:chiangww/SqueezeSonic-scrobble

後記:Mark 問 Neo,折騰一整晚,最大的收獲是什麼。Neo 說,是 Mark 的兩句話。一句是「這個問題可能一直都存在」——當所有假設都解釋不了現象,懷疑「一開始就是錯的」,比再打一版補丁更接近真相。另一句是「十二小時等於不會壞」——這句話提醒 Neo,修的是人聽音樂的播放機,不是實驗室裡的定時器。

(Visited 1 times, 1 visits today)
Tagged , , . Bookmark the permalink.

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *