最新消息
2.0.9 - 2026-08-23
更新通知以您的語言開啟其頁面
內容: 更新通知中的「最新消息」和「下載」連結現在會以 MusicBee 自己的語言(而非英文)開啟外掛程式頁面。
原因: 這兩個連結在將語言傳送到網站之前,會將語言限制為四種語言之一(英文、法文、西班牙文或德文),因此即使存在翻譯,其他所有人都會收到英文頁面。現在,它們會將 MusicBee 的語言原封不動地傳遞,並讓網站決定要提供什麼,這正是旁邊的「說明」按鈕一直以來的做法。
2.0.8 - 2026-08-22
外掛程式現在能正確地向在您的網路上探索到它的應用程式和裝置介紹自己,且「裝置設定檔」設定再次對齊。
您的裝置顯示正確的製造商、型號和版本
內容: 當控制應用程式、手機或電視在您的網路上找到 MusicBee 時,它現在會將此外掛程式顯示為由 yaiol 製作,指向外掛程式自己的網站,將其描述為涵蓋所有三個角色 — 伺服器、播放器和渲染器 — 並報告您實際安裝的版本。
原因: 每個 UPnP 裝置都會宣告其製造商和用途,而控制應用程式會將其顯示為裝置的身份。此外掛程式仍然宣告其分叉自的原始外掛程式的詳細資訊:另一位作者的姓名、MusicBee 的網站而非其自己的網站,以及自首次發布以來一直凍結在「1.0」的型號。從您的手機上,無法判斷您正在與哪個外掛程式通訊,更不用說它的哪個版本了。這些詳細資訊現在來自外掛程式本身,因此裝置旁邊顯示的版本在每次更新後都保持正確。
裝置設定檔設定再次對齊
內容: 在「裝置設定檔」標籤上,標籤及其方塊共用一個左邊緣並以均勻的間距排列,並且取樣率範圍顯示為單行。
原因: 隨著時間的推移,選項被添加到標籤中,欄位已經分開,並且取樣率範圍的「到」標籤最終位於其旁邊的方塊上方 — 一旦您知道它說什麼,就可以讀懂,但第一次看時會感到困惑。
2.0.7 - 2026-08-08
較大的音軌會保留其標題和位置滑桿
內容: 從手機傳送的較大音軌 — 長 FLAC、高解析度或 DSD 檔案 — 現在會顯示其正確標題,並且可以像較小的音軌一樣移動。以前,其中一些音軌會透過網路播放,標題位置會顯示網址,而滑桿則不起作用。
原因: 外掛程式會在開始前等待其本機副本,但它過去會根據檔案大小「預先」判斷檔案是否值得等待。這實際上是在猜測您的網路速度,而它無法得知:一個 65 MB 的音軌被判斷為太大,然後在一秒後完成下載 — 輕鬆地在它已經放棄的等待時間內。它現在只是監控下載。當它仍在傳輸時,外掛程式會繼續等待,無論需要多長時間;它只會在傳輸真正停滯時才放棄,而它現在比舊的固定延遲更快地注意到這一點。
2.0.6 - 2026-08-03
從手機傳送的專輯現在可以無間斷地播放,網路電台也被識別為電台,而不是被視為一首異常長的歌曲。
專輯無間斷播放
內容: 當您從手機傳送整張專輯時,MusicBee 現在會從一首曲目無縫播放到下一首,沒有任何停頓 — 因此現場錄音、DJ 混音和連續的古典作品都能保持連貫性。
原因: 標準允許控制應用程式說「這是接下來的內容」,這使得無縫連接成為可能。該指令之前完全不被接受,因此應用程式沒有地方放置即將播放的曲目,並將其宣布為當前曲目 — 這是在上一個版本中修復的專輯問題的原因。現在它已被正確接受:下一首曲目在當前曲目仍在播放時被獲取,MusicBee 自己的播放器會跨越邊界。
網路電台被識別為電台
內容: 傳送到 MusicBee 的直播電台會以串流形式播放,絕不會下載。
原因: 下載廣播沒有意義 — 它沒有終點,也沒有什麼可以跳過 — 但插件之前無法區分它和音樂文件,因此它開始獲取並在下載超過固定大小後停止。控制應用程式會說明它正在傳送的是兩者中的哪一個,現在這會被直接讀取。無需設定,無需猜測。
長高解析度曲目保留其副本
內容: DSD 文件和長的 24 位元錄音現在可以像任何其他曲目一樣移動。
原因: 它們受到上述大小限制的影響 — 一個 20 分鐘的高解析度樂章或一個 10 分鐘的 DSD 曲目都超過了它 — 因此它們的副本被放棄,並且位置滑塊停止工作,而這正是最有可能值得仔細聆聽的內容。由於電台已正確識別,因此不再需要大小限制。
2.0.5 - 2026-08-03
從手機遙控 MusicBee 的兩項修正:音量現在在兩端都表示相同,並且播放整個專輯在第一首曲目之後仍能繼續播放。
手機上的音量與 MusicBee 中的音量相符
內容: 將手機音量調到最大,現在 MusicBee 中的音量也會達到最大,並且 MusicBee 自己的設定在手機上也能正確讀取。
原因: 外掛程式從未告知控制應用程式其最高音量是多少,因此每個應用程式都必須猜測。其中一個應用程式設定為 69,這表示其 100% 在 MusicBee 中只能達到 69%,而 MusicBee 的 100% 在手機上則顯示為 144% — 且手機的音量按鈕始終無法達到最大。現在渲染器明確說明了範圍,因此兩端都在談論相同的音量刻度。
播放專輯時保留其標題和位置滑桿
內容: 從手機傳送的專輯中,現在每首曲目都會顯示其正確的標題,並且可以移動,而不僅僅是第一首。
原因: 控制應用程式會在當前曲目播放後幾分之一秒宣布下一首曲目,而該宣布會取消正在為即將播放的曲目獲取的副本 — 因此大多數曲目會悄悄地退回到透過網路播放,這會失去標題和跳轉的能力。現在,多首曲目的副本會並排保存,因此宣布不再能取消正在使用的副本。
2.0.4 - 2026-08-02
從手機或其他伺服器傳送到 MusicBee 的音樂現在表現得像真正的音軌:您可以移動播放進度,並且它會立即顯示正確的標題。此外,還修復了將自己呈現為一個組合裝置的 Hi-Fi 串流播放器問題。
移動從其他地方傳送的音軌
內容: 拖曳位置滑桿現在適用於從手機、NAS 或其他媒體伺服器傳送的音軌。為了實現這一點,MusicBee 會在開始播放時將音軌的副本下載到臨時資料夾,並播放該副本。這在家庭網路中大約需要一秒鐘,一旦您傳送另一個音軌,該副本就會被刪除,並且任何殘留物都會在 MusicBee 下次啟動時清除。
原因: MusicBee 可以啟動和停止它在網路上收聽的內容,但它無法移動播放進度 — 因此滑桿似乎會跳動,然後立即滑回原位,沒有任何解釋。播放自己磁碟上的普通檔案完全消除了限制,而不是繞過它。
從第一個音符開始顯示正確的標題和長度
內容: 由未給予檔案普通副檔名的應用程式傳送的音軌,現在會在開始播放時立即顯示其真實標題和長度,而不是顯示為一個長長的網址。
原因: MusicBee 從副檔名識別音軌 — 並找到其標籤 — 而某些播放器會發出完全沒有副檔名的位址。本地副本始終帶有正確的副檔名,因此無論傳送應用程式如何稱呼它,音軌都會被識別。
無法進行的跳轉現在會顯示
內容: 如果渲染器確實無法移動到請求的點,則會通知控制應用程式,並由其報告。
原因: 它以前無論如何都回答「完成」,因此滑桿在一秒鐘後滑回,沒有任何解釋。誠實的拒絕比無聲的拒絕更容易處理。
組合式 Hi-Fi 串流播放器讀取正確
內容: 當 MusicBee 播放到將自己呈現為一個組合單元(例如 Marantz 或 Denon 串流播放器,其中播放器與媒體伺服器一起位於製造商包裝內)的裝置時,外掛程式現在會讀取播放器自己的詳細資訊,而不是媒體伺服器的詳細資訊。
原因: 它以前一直在詢問裝置的錯誤部分可以處理哪些音訊格式,沒有得到可用的答案,並且在沒有檢查的情況下繼續進行 — 這正是最需要正確處理格式的硬體。裝置的型號描述現在也納入考量,以便將其與裝置設定檔匹配;它以前是從錯誤的位置讀取並丟棄的。
2.0.3 - 2026-08-02
播放目標角色成長:MusicBee 現在可以播放不在其媒體庫中的音樂(手機、NAS 或其他伺服器上的檔案),而不僅限於其已擁有的曲目。
播放從手機傳送的音樂,而不僅限於您自己的媒體庫
內容: 當您使用 Symfonium 或 BubbleUPnP 等控制應用程式將音樂傳送至 MusicBee 時,該曲目不再需要來自 MusicBee 自己的媒體庫。儲存在手機本身、NAS 或其他媒體伺服器上的檔案現在可以播放。標題和長度來自傳送它的應用程式,因此即使 MusicBee 從未見過該檔案,該曲目也能正確顯示。
原因: 播放目標角色是為您從手機瀏覽此電腦的媒體庫並點擊歌曲的情況而建置的——該曲目已經在電腦上,因此 MusicBee 只是播放它自己的檔案。任何來自其他地方的內容都會被悄悄丟棄,這使得該功能對於同樣自然的將音樂從手機推送到優質揚聲器的情況變得無用。
無法播放的曲目會顯示提示
內容: 如果渲染器確實無法播放傳送給它的內容,它現在會將此報告回傳給傳送它的應用程式。
原因: 它以前對任何內容都回答「已收到」,因此控制應用程式繼續按下播放。由於沒有實際載入任何內容,MusicBee 重新啟動了之前留下的任何曲目——如果該檔案已消失,則會抱怨其來源無法找到。錯誤會命名一個不相關的曲目,並且與實際問題相去甚遠。
在暫停時傳送新曲目現在會播放該曲目
內容: 如果 MusicBee 處於暫停狀態,並且您的控制應用程式傳送了新內容,則新曲目會開始播放。
原因: 恢復優先於載入,因此暫停的曲目會從上次停止的地方繼續播放,而您剛剛選擇的曲目則會被默默丟棄。
2.0.2 - 2026-08-01
搜尋是本次更新的主題:它現在能依藝術家搜尋、傳回正確的結果,並且在大資料庫上速度飛快。此外,還新增了一種指定控制應用程式隨機播放目標的方式,並修復了三個無效按鈕的問題。
依藝術家搜尋現在確實有效
內容: 從您的控制應用程式搜尋藝術家現在會傳回該藝術家的音樂。搜尋會同時比對曲目的「藝術家」和專輯的「專輯藝術家」,因此無論您輸入表演者的姓名還是專輯的歸檔名稱,都能找到合輯。
原因: 藝術家搜尋以前被誤認為是「給我所有這種類型的內容」——您輸入的藝術家名稱被丟棄,整個資料庫都被傳回,因此一個應該只比對幾百首曲目的搜尋卻傳回了數萬首。如果只比對兩個藝術家欄位中的一個,會悄悄地遺失一半的結果,因此兩者都會被檢查。
搜尋結果頁面正常運作
內容: 捲動長長的搜尋結果清單現在會正常移動。您捲動到的每一頁都是您實際看到的頁面。
原因: 伺服器以前會對每個請求都傳回「第一批」結果,同時報告完整的比對數量,因此應用程式捲動以獲取更多結果時,會不斷收到相同的項目,永遠無法到達結尾。
在大型資料庫上搜尋速度快得多
內容: 搜尋現在會對整個結果集執行單一查詢,並且只讀取您正在查看的頁面的標籤。
原因: 以前,每一頁都會重新對資料庫執行查詢,然後載入每個比對項的標籤——數千個——只為了顯示十幾個。在大型收藏中,這會導致每次捲動都暫停。當資料庫重新整理時,結果也會被丟棄,因此編輯永遠不會傳回過時的內容。
將隨機播放目標設定為篩選器
內容: 在「資料庫選項」分頁上新增了「從以下來源隨機播放」設定。將其保留為「所有音樂」,您的控制應用程式的「隨機曲目」/「隨機專輯」資料夾將像以前一樣運作;選擇您的 MusicBee 篩選器之一,所有隨機請求都將從該篩選器中提取。隱藏的篩選器也會提供。
原因: 隨機播放資料夾要求的是「所有內容」的一部分,這是唯一一個沒有任何線索表明您意圖的請求——因此它總是從整個資料庫中提取,包括口語內容。這是您定義「所有內容」含義的地方。從裝置上篩選器「內部」打開隨機播放資料夾仍然會隨機播放該篩選器:您在瀏覽時做出的選擇優先於設定。
說明、GitHub 和更新檢查會導向實際頁面
內容: 設定對話方塊中的「說明」和「GitHub」按鈕,以及自動檢查新版本的功能,現在會打開它們所命名的頁面。
原因: 這三個功能以前都是根據外掛程式名稱的縮寫形式建立的,而該縮寫形式從未存在過任何頁面,因此每個功能都靜默失敗——按鈕似乎沒有任何作用,更新檢查也從未報告任何內容,無論新版本發布了多久。
2.0.1 - 2026-07-26
一輪播放和瀏覽修復,重點放在播客和驅動播放與隨機播放的控制器應用程式(例如 BubbleUPnP)。
播客立即開始播放,沒有暫停
內容: 下載的播客節目現在會預先顯示其真實長度和檔案大小,直接從磁碟上的檔案傳輸到媒體中繼資料。
原因: 如果沒有明確的持續時間,像 BubbleUPnP 這樣的控制器每次按下播放時都會重新掃描整個音訊串流,只是為了計算節目有多長 — 因此播放會在明顯的暫停後才開始。現在有了廣告的長度,它就能順暢地開始。
瀏覽藝術家時會顯示播客
內容: 播客的節目名稱現在歸檔為其藝術家 — 鏡像到「藝術家」和「專輯藝術家」欄位(及其排序變體),就像它已經填寫「專輯」欄位一樣。
原因: 以前,按藝術家欄位分組的瀏覽路徑會發現每個播客的藝術家都是空白的,並在空層級上終止。現在將節目本身視為藝術家 — 與將其視為專輯一致 — 意味著這些路徑現在可以到達節目而不是空無一物。
重新啟動後「最近播放」和投射功能正常
內容: 直接播放播客、有聲書、收件匣或電台曲目 — 無需先瀏覽到它,就像 BubbleUPnP 的「最近播放」列表和投射目標所做的那樣 — 不再失敗。外掛程式現在會在按 ID 請求時按需載入曲目。
原因: 這些列表在 MusicBee 重新啟動後立即請求曲目,在任何內容被瀏覽之前,因此外掛程式從未見過 ID 並回答「無效 ID」。它現在會在第一次直接請求時強制載入相關來源並找到曲目。
「隨機曲目」和「隨機專輯」返回結果
內容: BubbleUPnP 的「隨機曲目」和「隨機專輯」隨機播放資料夾,這些資料夾請求整個媒體庫的隨機片段,現在會返回填充的結果。
原因: 這些是沒有標題可匹配的搜尋,外掛程式以前從空的內部列表回答它們,因此它們總是顯示空無一物。它們現在從其餘瀏覽使用的相同按需查詢路徑提供 — 正確分頁。
標籤空白的專輯會列出其曲目
內容: 打開一個按空值分組的專輯 — 例如沒有年份的收件匣曲目 — 現在會顯示其曲目。
原因: 收集專輯曲目的匹配將「此標籤為空」視為「不匹配」,因此任何由空白欄位形成的專輯都會瀏覽到空無一物。現在,空組值會正確匹配共享它的曲目。
2.0.0 - 2026-07-22
This is the first public release of the open-source yaiol fork of the MusicBee UPnP plugin. It is presented in two parts: everything that is new to this fork, then the fixes and improvements made to the original plugin. Each item keeps the What / Why form of the project's internal feature catalog so the reasoning behind every change is on the page, not just the change.
此分支的新功能
MediaRenderer - 播放至 MusicBee
N01 - MusicBee 作為播放目標渲染器
內容: 通常此插件以單向運作:手機或其他裝置瀏覽 MusicBee 的音樂庫並在自身播放音樂。此功能新增了反向操作 - 讓 MusicBee 成為播放器。您可以從手機上的控制器應用程式(例如 BubbleUPnP)選擇您的桌面 MusicBee 作為播放裝置,然後從您的手掌控制它:播放、暫停、停止、向前或向後跳過、跳到曲目中的某個點,以及更改音量或靜音。
原因: 它將您的手機變成您 PC 上已有音樂的遙控器。坐在沙發上,在手機上瀏覽您的音樂庫,點擊一首曲目,它就會從連接到您桌面的揚聲器中播放出來 - 並可從您所在的位置進行完全控制。原始插件從未將此功能作為一個可運作的功能發布。
開啟方式: 此功能預設為關閉,因為開啟它會讓您家庭網路上的任何裝置都能在您的 PC 上開始播放。您可以在設定對話框的「一般」選項卡上勾選一個方塊來啟用它。該插件的三個角色各自在那裡有一個勾選方塊 - 分享我的音樂庫(伺服器)、讓其他人播放到我(渲染器)和播放到其他裝置(控制點) - 並且對話框只顯示您開啟的角色實際需要的設定選項卡,因此您永遠不會遇到不適用於您的選項。
區分您的機器: 您可以為渲染器指定任何您喜歡的名稱(它最初是「MusicBee (yaiol)」)。該名稱會出現在您手機的播放目標列表中,因此當有多台 PC 運行 MusicBee 時,您可以區分它們。名稱更改會立即生效,無需重新啟動。
播放到自身時的最佳音質: 當您從手機瀏覽 MusicBee 自己的音樂庫並將曲目發送回 同一台 MusicBee 時,插件會識別出它被要求播放自己的文件之一,並直接從您的磁碟播放。結果是精確且即時的 - 位元完美,並應用了 MusicBee 自己的等化器和音量平衡 - 而不是無意義地將音訊推送到網路然後再直接返回自身。
私密運行: 這三個角色獨立運作,因此您可以在關閉音樂庫分享的情況下開啟渲染器。在這種「僅渲染器」設定中,您的音樂庫對網路完全隱藏 - 只會宣布播放目標 - 並且 MusicBee 永遠不會提供播放到自身。
播放行為
N02 - 5.1 FLAC 不會自動降混
內容: MediaServerDevice.GetEncodedFile 中的聲道數限制為 If StereoOnly OrElse Not isPcmData Then channelCount = 2。Not isPcmData 子句會靜默地將每個非 PCM 轉碼(FLAC、MP3、AAC、Ogg)降混為立體聲,無論源聲道數如何,從而使 5.1 FLAC 源的 5.1 渲染器失效。現在第二個子句排除了 FLAC:Not isPcmData AndAlso encoder.Codec <> FileCodec.Flac。FLAC 5.1 會通過;MP3/AAC/Ogg 仍然強制立體聲,因為 MusicBee 針對這些格式的命令列編碼器需要 2 聲道輸入。
原因: 將 5.1 FLAC 源轉碼為 FLAC 輸出的全部意義在於保留多聲道混音。靜默降混使得 FLAC 轉碼選項對於環繞聲聆聽毫無用處。有了 N02,它就能做正確的事情。
架構
使此分支在大型音樂庫上可行的結構性變更 - 原始插件中沒有。
N03 - 延遲(按需)瀏覽樹
內容: 原始插件在 MusicBee 啟動時構建整個瀏覽樹 - 枚舉每個曲目,每個文件進行完整的 Library_GetFileTags,組裝整個容器層次結構 - 然後才打開 HTTP 端口。在真實的音樂庫(5 萬多首曲目,5400 集播客,數百個電台)上,這需要數分鐘的冷啟動,並且樹會永遠保留在 RAM 中,包括客戶端從未打開的分支。此分支不會預先構建任何內容:根目錄為每個端點公開一個 L: 前綴的佔位符(L:music、L:podcast、L:filter:…);每個級別僅在客戶端瀏覽到它時才計算(LazyBrowse → EnsureLazyEndpointInMemory → 每級別緩存),並且音樂庫更改通知會清除緩存(SetLibraryDirty)。
原因: 冷啟動基本上是即時的 - MusicBee 完成插件初始化時 HTTP 端口已打開 - 並且記憶體與已瀏覽的內容成比例,而不是與音樂庫大小成比例。權衡:首次瀏覽到端點會支付其載入成本;重新進入會被緩存,直到下一次音樂庫更改。這是其他一切的基礎。完整說明:FIXES.md。
網路與穩健性
強化 HTTP 伺服器的綁定路徑。原始插件在其端口不可用時會靜默死亡。
N04 - 自我修復 HTTP 端口綁定
內容: 插件的 HTTP 伺服器在其配置的端口不可用時不再死亡。三個相關的變更:
- 綁定失敗時自動回退。
HttpServer.Start嘗試配置的端口,並在SocketException時向上掃描最多 20 個端口以尋找第一個空閒端口。實際綁定的端口記錄在新的Plugin.boundServerPort中,並且所有宣傳伺服器的內容 - SSDPLOCATIONURL(NOTIFY + M-SEARCH 響應)、裝置 URL(PrimaryHostUrl)、路由器端口轉發以及 SSDP/控制點自過濾器 - 現在都讀取boundServerPort而不是Settings.ServerPort。UPnP 客戶端通過 SSDP 發現真實端口,因此移動的端口對渲染器是透明的。 - 使用者通知。 當發生回退時(保存的端口不是正在使用的端口),本地化的
MessageBox(WarnPortInUse)會告訴使用者實際提供服務的端口以及裝置仍然會找到它 - 因為插件無頭運行,對話框中的訊息只會被已經懷疑有問題的人看到。 - 重新啟動恢復。
RestartServer(設定保存重新啟動路徑)過去會盲目地取消引用Plugin.controller/Plugin.server。如果初始Initialise在創建它們之前拋出(正是綁定失敗導致的),下一次設定保存會遇到NullReferenceException- 留下一個半死的插件。它現在在Nothing時重新創建並啟動它們,因此保存一個可用的端口可以在不完全重新啟動 MusicBee 的情況下恢復插件。
原因: 觸發因素是一個真實的使用者事件。舊的預設端口 49382 位於 Windows 動態範圍(49152-65535)內,其中 Hyper-V/WSL2/Docker/WinNAT 保留了大量區塊,這些區塊在每次啟動時都會移動 - 因此在一個已經運行了數月的機器上,綁定失敗並出現 WSAEACCES(「訪問被禁止」)。將預設值更改為空閒端口然後與 Serviio(一個單獨的 DLNA 伺服器已佔用新端口)衝突,導致 WSAEADDRINUSE 失敗。每次失敗都被 Initialise 吞噬,導致插件靜默死亡,然後在下一次設定保存時出現 NRE。在 N04 之後,端口衝突會自我修復 - 伺服器繼續在下一個空閒端口上運行,使用者會收到通知,並且客戶端會重新發現它 - 而不是使整個插件崩潰。
實作:
- 預設端口從
49382移至9779(低於動態範圍,因此 Windows 永遠不會自動保留它;不是已知的媒體伺服器預設值),在所有三個ServerPort宣告 + 設定解析失敗回退中。 Plugin.boundServerPort(新的共享欄位)保存實時監聽端口;activeServerPort保持配置的快照,因此「需要重新啟動」徽章邏輯不會在回退時錯誤觸發。HttpServer.PortScanRange = 20;掃描在第一次成功的TcpListener.Start()時停止,並且只有在所有嘗試都失敗時才拋出最後一個異常。- 新的 EN 資源鍵
WarnPortInUse(翻譯將在發布時的地區設定通過後進行)。
N05 - 透過多播群組(VPN / 點對點)進行 SSDP 廣播
內容: SSDP 廣播發送到 UPnP 多播群組(239.255.255.250),而不是 IP 廣播地址。當 SSDP 搜索響應與伺服器重新啟動競爭時,記錄的無害「無法訪問已處置的對象」錯誤也被抑制。
原因: 在點對點 / VPN 網路適配器上,IP 廣播不適用 - 舊的廣播發送失敗並出現「無效參數」,並且廣播被遺漏,因此插件對這些鏈接上的客戶端是不可見的。廣播到正確的多播群組可以修復這些適配器上的發現問題。
音樂庫導航
這些功能已在此分支中發布,原始插件中沒有。它們來自實際使用真實 UPnP 客戶端瀏覽插件自己的輸出。
N06 - 基於篩選器的音樂庫公開
內容: MusicBee 的篩選器選項卡(使用者 MusicBee 資料夾中的 .xautopf 文件)在插件的音樂庫中成為 UPnP 根容器。然後,每個篩選器的曲目都可以在 AlbumArtistSort → Album → Tracks 的層次結構中瀏覽。
原因:: 擁有精心策劃的 MusicBee 篩選器(例如「5 星級曲目」、「最近新增」、「古典 → 巴洛克」)的使用者希望在從 UPnP 客戶端瀏覽插件時找到它們。原始插件只公開了原始音樂庫樹。
N07 - SortAlbumArtist 欄位連接
內容: 插件現在讀取 MusicBee 的 MetaDataType 165(排序專輯藝術家)並使用它在瀏覽視圖中對藝術家進行分組/排序。
原因: 高傳真瀏覽器和發燒友使用排序藝術家名稱(「Beethoven, Ludwig van」而不是「Ludwig van Beethoven」)來組織音樂庫。這是認真聆聽者的標準期望。兩個上游都缺少此功能。
N08 - 多值專輯藝術家處理
內容: 當專輯的 AlbumArtist 欄位包含多個藝術家,並以 "; " 分隔(例如 "yaiol; Ars Ricercata")時,曲目現在會出現在瀏覽視圖中每個藝術家下方,而不是單個結合了這些名稱的 Frankenstein 藝術家下方。
原因: 合作專輯和合輯需要出現在每個合作者下方。沒有這個,尋找專輯的一半搜索路徑都會中斷。
N09 - 專輯容器封面 (upnp:albumArtURI)
內容: DIDL 瀏覽響應中的專輯容器節點現在包含一個 upnp:albumArtURI 元素,指向專輯封面。
原因: 沒有這個,UPnP 客戶端瀏覽視圖中的每個專輯都會顯示一個通用圖標,而不是專輯封面。這是導航的視覺提示;每個現代高傳真瀏覽器都期望如此。
N10 - 篩選器專輯內的曲目排序
內容: 篩選器公開專輯內的曲目現在按碟號排序,然後按曲目號排序。
原因: 標準專輯順序。如果沒有明確排序,曲目會以篩選器碰巧返回的任何順序返回 - 通常看起來是隨機的。
N11 - 播放列表資料夾樹修復
內容: LoadLibraryPlaylists 函數(最初由 Steven Mayall 於 2014 年左右編寫)未能進入新創建的播放列表資料夾。每個資料夾中的第一個播放列表,以及任何子資料夾,最終都孤立在根級別。
原因: 在原始插件中存在了十一年。在打開 BubbleUPnP 並點擊播放列表後 30 秒內即可看到。在 yaiol 中通過在樹構建期間正確遞歸到新創建的資料夾來修復。
N12 - XML 非法控制字元淨化
內容: 任何包含 C0 控制字元(例如來自錯誤編碼傳遞的 0x19 - UTF-8 → Latin-1 → 回溯截斷 0x99 為 0x19)的標籤的曲目,一旦進入分頁批次,就會導致整個瀏覽響應失敗並顯示 Action Failed。
原因: XML 1.0 禁止大多數 C0 控制字元,並且 XmlWriter 在被要求寫入任何字元時會拋出異常。存在於原始插件中。通過在每個 Library_GetFileTags 退出點通過 XmlConvert.IsXmlChar 剝離無效字元來修復。
N13 - 分頁瀏覽中電台列表的確定性
內容: 電台容器的瀏覽落入通用文件列表分支,該分支在每次調用時都調用 files.Sort(AlbumFileComparer)。電台條目具有空的專輯/碟片/曲目標籤,因此每次比較都返回 0 - List(Of T).Sort 不穩定,每次調用都會產生不同的順序。UPnP 控制點進行分頁(BubbleUPnP 獲取 0..15,然後是 16..end);在兩次調用之間,列表會重新洗牌,因此有些電台會出現在兩個頁面中(重複),有些則都沒有(缺失) - 每次刷新都看起來是隨機的。
原因: 存在於原始插件中(其作者從未通過 UPnP 瀏覽電台)。此處通過 Browse 中專用的 ContainerCategory.Radio 分支修復,沒有每次調用排序;radioFiles 在載入時按標題排序一次(穩定)。分頁瀏覽現在看到確定性順序;頁面 1 和頁面 2 是不相交的。
N14 - UPnP 搜尋專輯類別會傳回專輯容器
內容: UPnP 搜尋專輯類別查詢(upnp:class = "object.container.album.musicAlbum",例如 BubbleUPnP 的「隨機專輯」)會傳回完整的曲目列表而不是專輯容器,因此客戶端顯示零專輯。原始處理器只解析括號內的條件,然後無論請求的類別如何都傾倒所有曲目。
原因: 此處已修復 - 專輯類別查詢現在會枚舉不同的專輯(按專輯藝術家+專輯分組)並將每個專輯作為一個適當的 musicAlbum 容器發出,帶有封面藝術,可透過 Salb<idx> 虛擬 ID 空間尋址,以便客戶端可以深入結果並播放它。
N15 - 可運作、範圍感知且可點擊的 UPnP 搜尋
內容: 原始版本沒有宣傳任何搜尋功能(GetSearchCapabilities 返回空),因此客戶端甚至拒絕發送搜尋請求;舊的後端從 musicFiles 讀取,在延遲樹時代永久為空。此分支宣傳真實可搜尋的屬性,針對延遲音樂庫實現按標題搜尋曲目和按標題搜尋專輯(HandleLazySearch),當發送真實容器 ID 時將查詢範圍限定在客戶端的當前分支(否則替換為 L:music,以便頂部欄搜尋不會引入播客/電台/有聲書的雜訊),並透過合成的 Ssrch_alb_* ID 使專輯結果可點擊,Browse 的早期分支會將這些 ID 映射回專輯的曲目。(專輯類別結果作為容器的部分是 N14。)
原因: BubbleUPnP 中的搜尋從「音樂庫不支持搜尋」變為返回有用、範圍限定、可播放的結果。完整設計 + 拒絕的方法:SEARCH.md。
N16 - UPnP 快取失效 (SystemUpdateID)
內容: 原始版本返回一個常數 SystemUpdateID=0 - UPnP ContentDirectory 快取失效合約 - 因此符合規範的客戶端 (BubbleUPnP) 將音樂庫視為永不變更:過時的瀏覽結果、URL 方案變更後的 404 縮圖,以及「重新啟動 MusicBee 兩次才能看到變更」的麻煩。此分支在載入時從紀元秒數種子 SystemUpdateID (因此每次重新啟動都嚴格領先於上次),並在每次音樂庫變更和設定變更時遞增它 (SetLibraryDirty / ResetCache → BumpSystemUpdateId)。
原因: 客戶端在下次瀏覽時可靠地獲取編輯、新文件和設定變更。已知限制:訂閱的客戶端不會透過 GENA 主動重新推送新值 (作為未來工作擱置);他們仍然會在下次瀏覽時看到它。
N17 - 播客訂閱封面
內容: 播客磁磚沒有顯示圖片 - 每個 /PodcastThumbnail/ 請求都返回 404。兩個堆疊的錯誤:解析鏈從未檢查 MusicBee 的實際封面快取(%LocalAppData%\MusicBee\InternalCache\Subscriptions\<name>.jpg,桌面 UI 從中載入),並且 HTTP 層的 unescape+lowercase 將 feed-URL 路由鍵損壞到其最後一個路徑段。此分支從 MB 的 InternalCache 解析封面,並通過一個在 HTTP 層保持完整的 URL 安全 slug 路由查找(PodcastSlug / podcastSubIdBySlug)。
原因: 訂閱封面現在在瀏覽視圖中呈現(所有 22 個以前返回 404 的請求都已解決)。
N18 - 階層式(分隔符號)標籤瀏覽
內容: 任何欄位都可以在「音樂庫選項」選項卡上標記為階層式,並給予一個單字元分隔符號(一個欄位選擇器 + 分隔符號框,帶有新增/移除功能,並儲存在插件設定中)。將分組設定為 /,並將值設定為 Jazz/Cool Jazz,然後瀏覽時會顯示為 Jazz › Cool Jazz,而不是一個扁平的條目。精確標記在分支上的曲目(僅 Jazz)會獲得自己的 [Jazz] 節點,因此沒有任何內容被隱藏,只有一個子項的分支會自行折疊,並且 ; 被拒絕作為分隔符號,因為它是 MusicBee 自己的多值分隔符號。
原因: 使用者已經在單一欄位中編碼的深度標籤分類(流派樹、情緒層次結構、「古典/巴洛克/協奏曲」)最終會像標籤描述的樹一樣瀏覽,而不是使用者必須從頭到尾閱讀的扁平斜線分隔字串牆。
N19 - 以其分組欄位標記的單一根路徑
內容: 根目錄下的單一瀏覽路徑會以其分組欄位(例如「流派」)而不是其完整的短路徑來標記,這與合併的第一個欄位組的命名方式相符。
原因: 瀏覽樹的讀取方式一致 - 無論根條目是獨立存在還是與同級合併(N20),都採用相同的命名規則 - 而不是單獨的根條目顯示冗長的內部路徑,而其合併的鄰居則顯示清晰的欄位名稱。
N20 - 合併共享第一個欄位的瀏覽路徑
內容: 兩個共享相同第一個欄位的瀏覽路徑 - 「流派 / 排序專輯藝術家」和「流派 / 播客人物」 - 會合併為一個單一的流派根資料夾,該資料夾首先列出流派值,然後再分為兩個視圖,而不是在根目錄並排顯示兩個幾乎重複的「流派 / …」條目。
原因:: 擁有幾個相關視圖嵌套在一個共同欄位下的使用者會看到根目錄被幾乎相同的頂層條目弄得雜亂無章。合併它們可以保持根目錄的可讀性,並將相關視圖分組到它們所屬的位置 - 在它們的共享欄位下。
N21 - 類別型瀏覽路徑(標準 / 電台 / 播客)
內容: 每個瀏覽路徑都按類別分類 - 標準、電台或播客。模板列表分為這三個部分,每個模板的欄位選擇器只提供該類別資料實際可以提供的欄位,並且模板只能應用於視圖樹中匹配的節點(不兼容的節點會變灰且無法選中)。保留的電台和播客模板無法刪除,因此它們的類別部分永遠不會消失。
原因: 如果沒有類型化,使用者可能會建立一個靜默為空的佈局 - 電台沒有「專輯」,播客節目沒有「專輯藝術家」 - 並且只有在從 UPnP 客戶端瀏覽到一個死資料夾時才會發現。將欄位選單和應用目標限制在類別的真實資料上,可以使空的佈局無法建立。
N22 - 按發布年份分組播客
內容: 每個播客節目的發布日期都會被讀取,因此具有年份級別的播客瀏覽路徑會按年份對節目進行分組,而不是將它們折疊在單個「未知」下。
原因: 大型播客訂閱可以像音樂庫的其餘部分一樣按年份導航,而不是因為插件從未查看每個節目的發布日期而導致每個節目都落在一個未註明日期的堆中。
N23 - 折疊單一結果分組層級
內容: 解析為單一值的分組層級 - 例如,對於只製作 LP 的藝術家,顯示「LP」的唱片類型層級,或者只有一個字母的字母層級 - 會自動跳過,直接將使用者帶入其內容。
原因: 瀏覽一個只包含一個資料夾的資料夾純粹是摩擦。折疊單一選擇層級可以消除不必要的點擊,而不會改變使用者可以到達的內容。
N24 - 針對 MusicBee 日期欄位的年份分組/搜尋
內容: 年份條件不再使用單純的四位數值查詢 MusicBee 的完整日期「年份」欄位,並且硬編碼的年份欄位別名已移除,因此每個分組欄位現在都從路徑定義中通用解析。
原因: 對於其年份標籤包含完整日期的音樂庫,以前按年份分組或搜尋會返回空 - 四位數查詢從未匹配完整日期欄位。查詢正確的欄位會使年份分組和搜尋再次找到曲目。
N25 - 分開的「年份」和「年份 (yyyy)」分組欄位
內容: 專輯分組和瀏覽路徑現在公開 MusicBee 自己的兩個年份欄位 - 年份(完整日期標籤)和年份 (yyyy)(僅四位數年份) - 因此使用者在定義專輯分組或瀏覽路徑時可以選擇其中一個。
原因: 這兩個欄位在 MusicBee 中有不同的含義,將它們合併會失去這種區別。同時顯示兩者讓使用者可以將一年的所有發行版聚集在一起 (yyyy) 或保持精確的日期順序 (完整年份標籤),以符合他們的意圖。
N32 - 釘選的篩選器和播放列表按類型分組在根目錄
內容: 釘選的篩選器現在直接顯示在瀏覽根目錄的篩選器資料夾下方,釘選的播放列表直接顯示在播放列表資料夾下方,而不是所有釘選都聚集在根目錄末尾的一個團塊中。每個釘選的捷徑都與其自己的類型放在一起。
原因: 隨著使用者釘選更多捷徑,單一的混合篩選器和播放列表的尾隨團塊變得更難掃描,並將每個捷徑與其所屬的資料夾分開。將釘選的項目分組到其自己的類別下可以保持根目錄的可讀性,並使每個捷徑與其所屬的項目保持在一起。
設定對話框與封裝
N26 - 分區設定對話框
內容: 「偏好設定」頁面採用了左側導航佈局,分為幾個部分:一般 / 播放 / 音樂庫 / 裝置設定檔 / 診斷。
原因: 原始版本是一個單一的、高大的、扁平的設定列表 - 對於開發者來說很好,但對其他人來說卻令人困惑。分區將相關選項分組,使對話框感覺更像現代應用程式設定。
組件 + 插件重新命名(無 F-id - 封裝說明)
內容: 編譯後的 DLL 命名為 mb_UPnP_yaiol.dll,插件本身報告為「MusicBee UPnP (yaiol)」。與原始的 mb_Upnp.dll 不同。
原因: 使用者可以將 yaiol 與原始插件並排安裝,並並排比較行為。
徽章系統 - 顯示運行時狀態(F40 背後的機制)
內容: 一種通用的 UI 模式,用於將重要的運行時條件顯示為設定對話框中可見的彩色徽章。當前實例:
- ⚠ 最大連接數 (N04) - 當 MusicBee 啟動以來至少達到一次最大連接數限制時觸發。粘性會話標誌
Plugin.MaxConnectionsHit。在沒有可用插槽時在WaitOnSendBarrier內部設定。 - ⚠ 需要重新啟動 - 當保存的設定需要重新啟動 MusicBee 才能生效時觸發。粘性會話標誌
Plugin.RestartRequired。在對話框的保存處理程式中設定,當新的持久值與運行時快照 (Plugin.activeMaxConnections、Plugin.activeServerPort、Plugin.activeIpAddress) 不同時。需要重新啟動的設定僅限於那些真正無法熱重載的設定 - HTTP 伺服器綁定參數和在 Initialise 時構建的 SemaphoreSlim。
原因: 插件的日誌文件對於技術使用者進行調試來說很好,但非技術使用者面對「裝置聲音不對」或「播放緩慢」時永遠不會打開 診斷 → 查看日誌。徽章捕捉了使用者需要知道發生了什麼的情況,並在他們下次打開插件時顯示出來 - 無需閱讀任何內容即可發現。
未來可重複使用:
- 偵測到設定檔不匹配(裝置使用者代理從未匹配任何設定檔,回退到通用設定檔)。
- 觸發 NextURI 退避(F13 - 對於不穩定的裝置,會話期間禁用無縫播放)。
- 音樂庫掃描失敗 / 部分失敗。
- 會話中途渲染器連接丟失。
- 任何其他「發生過一次,使用者應該知道」比「在 1000 行其他日誌中靜默記錄」更好的情況。
實作慣例:
- 徽章標籤位於對話框層級(不在任何面板內),因此無論使用者在哪個部分,它們都可見。
- 放置在底部行,靠近「儲存/取消」(目前:y=410 水平堆疊)。
- 每個徽章在
Plugin中都有一個對應的粘性會話標誌,當條件發生時翻轉為 True,並且僅在 MusicBee 重新啟動時重置。 - 資源:
<Condition>Badge(標籤文字,前綴為 ⚠)+<Condition>BadgeTip(解釋原因 + 補救措施的工具提示)。 - 對於「已儲存設定需要重新啟動」徽章,在
Plugin.Initialise()時獲取運行時快照,並在對話框儲存處理程式中Settings.SaveSettings()後與Settings.*進行比較。
N27 - 取消會捨棄路徑/模板編輯
內容: 在設定對話框中對路徑和模板所做的編輯,現在在使用者點擊「取消」時會被捨棄,而不是悄悄地保持應用,並且在會話期間移除的任何保留模板都會重新建立。(模板在編輯時會即時儲存 - 「路徑」選項卡上沒有單獨的「儲存」按鈕。)
原因: 「取消」應該意味著取消。以前,使用者嘗試路徑/模板更改並退出後,發現更改已經提交,除了手動重新操作每個更改之外,沒有辦法撤銷它們。
N28 - 欄位選擇器選單中的字面安培號
內容: 名稱中包含「&」的欄位 - 例如「Mood & Context」 - 在欄位選擇器選單中會字面呈現安培號,而不是將其吞噬為 Alt 助記符前綴。
原因: 帶有安培號的欄位名稱顯示錯誤(字元消失,下一個字母變成加速器),使得選單條目難以識別。
N29 - 穩定、未翻譯的設定視窗標題
內容: 設定視窗標題固定為品牌字串「MusicBee UPnP Plugin」,不再隨介面語言而變化;每個地區設定包中都移除了每個語言的 DialogTitle 字串。
原因: 隨語言變化的視窗標題是一個可翻譯的介面,但沒有任何好處 - 標題是一個品牌標誌。固定它使其在任何地方都穩定且一致。
本地化
原始插件僅支援英文。此分支完全可本地化 - 每個面向使用者的字串都通過資源包流動,並且插件會自動偵測 MusicBee 自己的 UI 語言。
N30 - 多語言 UI(翻譯待定)
內容: 本地化機制已完成並發布。Localisation.vb 從 MusicBee3Settings.ini 讀取 MusicBee 選定的語言(<SystemLanguage> 內名)並將匹配的 .NET 文化應用於執行緒,因此 My.Resources.Resources.* 返回本地化字串。每個面向使用者的標籤/按鈕/訊息都連接到一個資源鍵(設計器控制項通過 ApplyDesignerExtras + sync-en-locale.js;運行時字串如 WarnPortInUse 手動添加)。尚未完成的是實際翻譯:只有英文源包(Resources.resx)存在 - 其他語言的衛星包將在插件功能完整時一次性生成(在字串仍在變動時零碎翻譯會浪費精力)。
目標語言(MusicBee 本身提供的集合,由 endonymToCulture 1:1 匹配,因此插件會自動遵循 MusicBee 的語言):
阿拉伯語 (ar) |
捷克語 (cs) |
德語 (de) |
希臘語 (el) |
西班牙語 (es) |
法語 (fr) |
匈牙利語 (hu) |
義大利語 (it) |
韓語 (ko) |
荷蘭語 (nl) |
挪威語 (nb) |
波蘭語 (pl) |
葡萄牙語 BR (pt-BR) |
葡萄牙語 PT (pt-PT) |
瑞典語 (sv) |
土耳其語 (tr) |
烏克蘭語 (uk) |
俄語 (ru) |
日語 (ja) |
簡體中文 (zh-CN) |
繁體中文 (zh-TW) |
英文 (en, 源語言) |
變體策略(根據工作區地區設定規則):PT 和 ZH 分為不同的捆綁包,因為詞彙/腳本確實存在差異(pt-BR/pt-PT、zh-CN/zh-TW)。EN 是一個單一捆綁包 - MusicBee 的「English(US)」(en-US) 通過 .NET 的文化鏈回退到 en,因此不會生成單獨的美國捆綁包。ES 和 FR 也同樣是單一地區設定。
原因: UPnP 插件的設定(「不使用原始 PCM」、「強制小端 PCM」、端口回退警告)在母語中已經夠神秘了。遵循 MusicBee 自己的 UI 語言 - 而不是強制使用英文 - 是非英文使用者能否配置工具的關鍵。兩個上游都沒有嘗試過這個。
N31 - 說明連結以完整的介面語言開啟
內容: 從插件開啟「說明」連結時,會尊重使用者完整的介面語言(例如 pt-BR、zh-CN),而不是縮減為基本語言,並發送更清晰的更新檢查識別碼。
原因: 運行 MusicBee 的區域變體(巴西葡萄牙語、簡體中文)的使用者會被導向基本語言的說明頁面。攜帶完整的文化會將他們導向他們使用的確切語言的說明頁面。
原始插件的修復和改進
核心協議與播放
F01 - 更新預設 DLNA 裝置設定檔
內容: 隨附 PlayStation 4、Xbox 360/One 和現代 BubbleUPnP 的全新預設設定檔,其功能標誌(取樣率、位元深度、編解碼器)反映了這些裝置目前實際支援的功能。
原因: 原始插件的預設值凍結於 2014 年左右。PS4/Xbox/BubbleUPnP 此後已獲得高解析度音訊支援。開箱即用,新安裝在這些裝置上可提供最佳播放效果,無需使用者觸摸裝置設定檔設定。
F02 - 控制宣傳 MediaRenderer:3 的裝置
內容: 插件會探測渲染器的 UPnP 服務描述,以決定 MusicBee 是否可以驅動它。原始版本只匹配 urn:schemas-upnp-org:device:MediaRenderer:1。現代裝置宣傳 :2 或 :3。F02 擴大了匹配範圍。
原因: 沒有這個,最近的 Sonos / WiiM / Eversolo 裝置根本不會出現在 MusicBee 的「播放到」裝置列表中 - 即使它們使用相同的協議。一個簡單的字串前綴匹配修復解鎖了整個現代裝置世代。
F03 - 「強制原生串流」每個設定檔選項(預設開啟)
內容: 勾選後,插件會將原始文件位元組發送到裝置,不進行轉碼、不進行 DSP、不進行 ReplayGain 處理。只是使用者選擇的原始文件,逐位元組(除了 HTTP 幀)。
原因: 根據論壇證詞,這是最大的播放品質提升。高傳真使用者購買昂貴的渲染器明確希望位元完美輸出;任何 DSP 觸摸都會破壞其目的。預設開啟,因為大多數現代裝置都能處理使用者扔給它們的任何編解碼器,並且 ReplayGain/EQ 應該是可選的。它是每個設定檔的,因此您可以為舊的 Xbox 保持轉碼,同時將原生串流發送到高傳真 DAC。
F04 - 「強制轉碼」每個設定檔
內容: 每個設定檔覆蓋,強制將每個串流通過轉碼器傳輸到此裝置,無論原生編解碼器支援如何。與 F03(強制原生串流)相反。與 F03 互斥 - 當其中一個開啟時,UI 會自動取消勾選另一個。
原因: 單一的全域開關會與每個設定檔的強制原生串流 (F03) 產生矛盾。實際案例:裝置 A 是一個高傳真 DAC,需要位元完美的原生串流;裝置 B 是一個舊的 AV 接收器,無法處理 FLAC。如果使用全域開關,使用者必須選擇 - 以犧牲另一個裝置為代價。使用每個設定檔,每個裝置都能得到正確的答案。
實作:
StreamingProfile.ForceTranscoding As Boolean = False。- 持久化架構版本升級至 v9。v9 之前的文件會載入舊版全域值一次,並將其複製到所有設定檔中,在升級後保留舊版行為。
- UI:從診斷面板中移除,新增至裝置設定檔部分,位於強制原生串流旁邊。雙向互斥處理程式(每個
CheckedChanged在翻轉之前取消訂閱另一個,以避免無限循環)。 - 決策點:
Settings.ForceTranscoding→streamingProfile.ForceTranscoding在WriteAudioFileDIDL中。
F05 - 「強制小端 PCM」每個設定檔
內容: PCM 串流(L16/L24 mime 類型)根據規範是大端。有些裝置錯誤地期望小端,並在收到正確的大端資料時播放白噪音。F05 會為每個設定檔切換位元組順序。
原因: 沒有這個,某些裝置會輸出靜態牆。症狀是戲劇性的,如果沒有 PCM 編碼知識,原因就看不見 - 這個開關為使用者提供了一個猜測和檢查的修復方法。
F06 - 「不使用原始 PCM」每個設定檔
內容: 當裝置聲稱支援原始 PCM 時,插件會使用它。有些裝置會說謊 - 它們接受 SOAP 握手,但會破壞實際的原始 PCM 資料,同時正確處理包裝在 WAVE 容器中的 PCM。F06 會強制使用 PCM-over-Wave,無論裝置宣傳什麼。
原因: 某些 Marantz 型號特別如此 - 它們宣傳原始 PCM,但只有 WAVE 有效。沒有這個,原始 PCM 串流會失真,沒有錯誤訊息可指出。
F07 - 「內容長度」每個設定檔
內容: 在 HTTP Content-Length 標頭中發送什麼值。四個選項:
- 預設 - 已知時為實際位元組數,未知時省略。
- 無 - 從不發送標頭(僅分塊編碼)。
- 僅 PCM - 僅發送原始 PCM;其他所有內容都省略。
- 固定 - 發送
UInt32.MaxValue - 8192(「巨大未知長度」的哨兵值)。
原因: UPnP/DLNA 裝置對 Content-Length 的反應差異很大。有些需要精確的數字,有些不喜歡串流上的它,有些需要一個哨兵「非常大」的值來保持緩衝。這後來從僅 PCM 擴展到所有輸出格式,因為相同的問題出現在轉碼的 MP3/AAC 串流中。
F08 - 「不清除 NextURI」每個設定檔
內容: 通常,當佇列清空時,插件會清除裝置的佇列 NextURI(發送帶有空白 URL 的 SetNextAVTransportURI)。有些裝置(尤其是 Denon)會將空白 NextURI 解釋為「停止所有內容」並立即停止播放。F08 會阻止插件清除它。
原因: 沒有這個,Denon 用戶會遇到設備在隊列清空時中途切斷曲目的情況。勾選 F08 後,設備會將過時的 NextURI 保留在記憶體中(無害 - 它只會在下次有內容排隊時被覆蓋)。
F09 - FLAC 作為轉碼輸出格式
內容: 裝置設定檔中的轉碼格式下拉選單現在除了 PCM 16/24、MP3、AAC、Ogg 之外還提供 FLAC。選擇它會將 BASS 編碼器通過 MusicBee 的標準 FLAC 轉換命令列(與 MP3/AAC/Ogg 已經使用的機制相同)。
原因: 對於能很好處理 FLAC 但無法解碼源編解碼器(例如 Eversolo 接收 MusicBee 的 WMA 音樂庫並轉換為 FLAC)的裝置,這可以在 MP3/AAC 會丟失音訊資料的情況下保留無損品質。解鎖 N02(5.1 降混控制),如果沒有無損轉碼選項,就無法解決。
實作: 在 Encoder.StartEncode 的 Select Case Codec 區塊中新增一行 - FLAC 加入 MP3/AAC/Ogg 的命令列驅動分支。UI 下拉選單中新增「FLAC」作為第 6 個選項。SettingsDialog 中的載入/儲存映射擴展以識別 FileCodec.Flac ↔ SelectedIndex = 5。Mime、DLNA 類型和編碼功能已在 ItemManager.GetMimes / GetDlnaType / GetEncodeFeature 中通過早期工作(F21、F26)連接。
無縫播放 (SetNextAVTransportURI)
F10 - SetNextAVTransportURI / NextURI 核心
內容: 真正的無縫播放。當裝置在其 UPnP 服務描述中宣傳支援 SetNextAVTransportURI 時,插件會在當前曲目結束前預先將下一曲目排入裝置佇列。裝置會在內部轉換,曲目之間沒有可聽見的間隙 - 就像您在 CD 播放器上聽到的那樣。這不是「連續串流」技巧(它將所有內容連接成一個長串流並丟失每曲目中繼資料)。
原因: 旗艦級 Tier-2 功能。以連續現場表演錄製的專輯(現場錄音、古典樂章、DJ 套組)在曲目之間有半秒的靜音時聽起來不對勁。正確解決這個問題是一個旗艦功能,現在已在此分支中。
備註: 佇列中的音訊通過插件的 HTTP 伺服器使用 streamHandle=0(音樂庫獲取模式)提供服務,這意味著 MusicBee 的音訊引擎不參與佇列中的曲目。權衡:ReplayGain/DSP/EQ 效果不適用於下一曲目。當「強制原生串流」開啟時(預設),這是可以接受的。
F11 - 「禁用 NextURI 支援」每個設定檔
內容: 即使裝置宣傳 SetNextAVTransportURI,此核取方塊也會強制插件忽略該宣傳並回退到一次播放一首曲目。
原因: 某些裝置宣傳 NextURI 但實作有錯誤(崩潰、半轉換、掛起)。與其逆向工程每個損壞的裝置,不如讓使用者獲得一個「在這裡關閉它」的開關。
F12 - 播放清單上的 NextURI 生命週期
內容: 當 MusicBee 觸發 NowPlayingListChanged 時,插件會重新評估應為無縫轉換排隊的內容。它會通過 NowPlayingList_GetNextIndex(1) + NowPlayingList_GetListFileUrl 向 MusicBee 請求新的「下一首」曲目,與裝置上當前排隊的內容進行比較(通過新的 nextPlaySourceUrl 欄位追蹤),如果發生變化則重新排隊(如果 MusicBee 說沒有下一首曲目,則清除佇列)。
原因: 沒有 F12,當使用者移除/重新排序佇列中的曲目時,裝置會繼續播放過時的 NextURI。這在歷史上經過了多次迭代,因為每次列表變動都需要不同的處理 - 我們通過信任 NowPlayingList_GetNextIndex(它已經尊重隨機播放和重複播放所有環繞)來簡化,因此所有變體都通過相同的比較。
實作:
- 新的
nextPlaySourceUrl欄位儲存已排隊的 MusicBee 音樂庫 URL(帶有句柄後綴的串流 URL 無法與音樂庫路徑進行比較)。 MediaRendererDevice上新的Public Sub RefreshQueuedNextUri()。三種結果:沒有 NextURI 排隊 → 無操作;排隊的與新的「下一首」匹配 → 無操作;排隊的不同 → 調用QueueNext並帶有新的 URL(或QueueNext("")以清除 - 這尊重 F08 DoNotClearNextUri)。- 在
Plugin.ReceiveNotification下的NotificationType.NowPlayingListChanged中連接。
F13 - NextURI 失敗退避
內容: 在同一裝置上連續 4 次 SetNextAVTransportURI 失敗後,插件會禁用該裝置的無縫播放,直到 MusicBee 重新啟動。
原因: 如果裝置的 NextURI 確實損壞(間歇性 SOAP 錯誤、網路故障),否則插件會在每首曲目上不斷重試。F13 會停止噪音並靜默回退到一次播放一首曲目。
F14 - 重複模式 + NextURI 整合
內容: F14 分為兩種情況,由 OnAvTransportStatusCheck 中的 F15 轉換偵測器處理:
- 重複播放所有:MusicBee 將正確的「環繞」URL(列表末尾的曲目 1)傳遞給
Plugin.QueueNext本身。無需特殊的插件邏輯 - 裝置會轉換到它,F15 偵測器會像往常一樣調用Player_PlayNextTrack,這會將 MusicBee 的 NPL 索引環繞回 0。 - 重複播放單曲:MusicBee 將相同的曲目 URL 傳遞給
Plugin.QueueNext。裝置會轉換到它(新的串流句柄,相同的來源)。F15 偵測器現在查詢Player_GetRepeat()- 如果是RepeatMode.One,它會跳過Player_PlayNextTrack調用,這樣 MusicBee 就不會將 NPL 索引從循環曲目中推進。
原因: 如果沒有重複播放單曲跳過,在無縫轉換時調用 Player_PlayNextTrack 會將 MusicBee 推進到列表中的下一首曲目(重複播放單曲只影響播放器 UI 中曲目結束自動推進行為 - 下一首曲目 總是向前移動),這與重複播放單曲的含義相矛盾。
播放次數注意事項: 在重複播放單曲模式下,播放次數的增加取決於 MusicBee 3.7.9563+ 是否注意到循環播放。較舊的 MusicBee 版本會正確播放無縫重複,但會錯過播放次數的增加。已記錄;不阻礙。
F15 - 曲目轉換偵測狀態機
內容: 當裝置內部從當前曲目轉換到 NextURI 時,插件需要注意並告知 MusicBee 推進其正在播放的索引。否則 MusicBee 會認為它仍在播放上一曲目,並且播放次數/UI/scrobbling 會不同步。
實作: 在每個狀態計時器滴答時輪詢 GetPositionInfo.TrackURI。當報告的 URI 與我們通過 NextURI 排隊的 URI 匹配時,我們會在 MusicBee 上調用 Player_PlayNextTrack 並設定 suppressNextSoapCall,這樣產生的 PlayToDevice 就不會重新發送 SetAVTransportURI(這會中斷無縫播放)。
原因: 沒有 F15,裝置會播放下一首曲目,但 MusicBee 的 UI 會顯示它仍在播放上一首。這會令人困惑,破壞 scrobbling,破壞播放次數追蹤。曲目轉換偵測需要長時間、每個渲染器迭代,因為每個渲染器品牌在何時報告 URI 更改方面都有其自身的怪癖(有些首先報告 TRANSITIONING,有些直接跳到 PLAYING 並帶有新 URI,有些在兩者之間有短暫的 STOPPED)。
備註: 我們的第一個版本適用於 BubbleUPnP 渲染器。每個裝置的邊緣情況保留在 B6 中。
F16 - 無縫轉換時的爆音修復
內容: 當佇列中曲目的來源格式(取樣率 / 聲道 / 編解碼器)與當前播放曲目不同時,會發生爆音,這會迫使裝置的 DAC 在轉換時重新鎖定。F16 新增了一個 NextUri:FormatChange 診斷,只要格式不同,就會在佇列時觸發,並命名兩側 - 這樣聽到爆音的使用者就可以關聯起來。
診斷還指出了緩解措施:勾選裝置設定檔上的強制轉碼。這會將每首曲目同質化為單一轉碼編解碼器/取樣率/位元深度,完全消除來源格式差異。
為何延遲實際的轉碼匹配修復: 結構性修復(將佇列中的曲目轉碼以匹配正在播放曲目的格式)需要更改插件的 HTTP 伺服器 URL 方案 - 目前 /encode/{id}0.{ext} 以原生方式提供佇列文件。F16 的未來 v2 將新增每個格式的 /encode/{id}0_{rate}_{depth}.{ext} 路由並通過編碼器連接它們。這是一個更大的架構更改,如果實際裝置在強制轉碼不足以解決問題後仍然出現爆音,則值得進行。
今日實作:
lastSourceUrl欄位追蹤當前播放的來源 URL。QueueNext讀取當前和佇列中曲目的FilePropertyType.SampleRate/Channels/Kind,並在不匹配時記錄NextUri:FormatChange。
F17 - 搜尋後進度條重新同步
內容: Seek() 函數在成功的 Seek SOAP 後已經調用 GetPlayPositionInformation(),這解決了「完全沒有重新同步」的情況。F17 解決了 UPnP 1 秒 RelTime 量化導致的剩餘高達 1 秒的漂移:當裝置報告的位置四捨五入到使用者請求目標的 1 秒內時,插件現在信任使用者亞秒級精確的值,而不是裝置的截斷。只有當裝置報告的內容顯著不同(>1 秒偏差)時,我們才使用其值(搜尋落在與請求不同的位置,例如某些編解碼器上的關鍵幀對齊)。
原因: 沒有這個,搜尋到 2:30.500 並以裝置的「2:30」報告為基準,會使進度條顯示比實際慢約 500 毫秒。在 F17 之後,進度條與使用者在常見的曲目內拖曳情況下的意圖相符,並且仍然尊重裝置對關鍵幀對齊異常情況的報告。
F18 - 連續串流 / NextURI 互鎖
內容: 現在有兩個互鎖:
- 運行時: 當
Settings.ContinuousOutput開啟時,QueueNext會提前返回False。連續串流是其自身的無縫機制(一個長的串聯串流);在其之上發送SetNextAVTransportURI會使裝置混淆每個曲目是獨立的 URI 還是連續流的一部分。 - UI: 當使用者勾選全域連續串流核取方塊時,當前顯示的設定檔的
forceNativeStream會自動取消勾選。連續串流總是轉碼,因此強制原生串流在組合中是無意義的。
原因: 防止使用者同時啟用兩種相互衝突的無縫機制。沒有 F18,裝置將同時接收連續串流 URI 和每個後續曲目的 NextURI,其行為取決於渲染器而未定義。
F19 - 忽略空白 NextURI 錯誤
內容: 當 SetNextAVTransportURI 以空白 URL 調用時(例如列表中的最後一首曲目),某些裝置會返回 SOAP 錯誤。F19 會靜默吞噬這些錯誤 - 記錄但不作為錯誤傳播。
原因: 「沒有下一首曲目」是正常情況,而不是錯誤。將其視為致命錯誤會污染日誌,並(在某些流程中)觸發重試風暴。
Mime 類型與 DLNA 中繼資料
F20 - MP3 mime → audio/mpeg
內容: 符合標準的 MP3 mime 類型是 audio/mpeg,而不是 audio/mp3。後者是常見的錯誤名稱,大多數裝置都能容忍,但更嚴格的渲染器會拒絕它。
原因: 靜默修復了遵循標準的更嚴格裝置上的播放問題。yaiol 代碼庫已經正確處理了這個問題;無需更改。
F21 - Mime 類型順序:非 x- 變體優先
內容: 當裝置同時宣傳 audio/flac 和 audio/x-flac 時,插件會優先返回非 x- 變體。任何同時具有標準和實驗性 mime 的編解碼器也一樣。
原因: x- 前綴標記實驗性/非官方 mime。有些渲染器在標準形式下表現更好。微小的重新排序,實際影響。
F22 - Opus mime 類型支援
內容: 識別 Opus 為可串流音訊編解碼器;在提供 Opus 曲目時發送 audio/opus mime。
原因: Opus 現在很常見(現代語音/音樂折衷編解碼器)。沒有 F22,插件將拒絕串流 Opus 文件,即使是支援它們的裝置。
F23 - Monkey Audio (APE) 來源檔案支援
內容: 識別 .ape 檔案為串流/轉碼的有效來源編解碼器。
原因: APE 是一種無損格式,擁有小眾但忠實的使用者群。新增它成本低廉,並為這些使用者解鎖了音樂庫。
F24 - AAC / ALAC mime 回退
內容: 如果裝置支援 AAC 或 ALAC 但未在其 UPnP 服務描述中明確宣傳,插件仍會將其作為回退選項提供。
原因: 許多能夠正常處理 AAC 的裝置忘記在其功能 XML 中列出它。沒有 F24,插件甚至不會嘗試,強制轉碼。有了 F24,插件會嘗試並讓裝置在能夠處理的情況下原生處理。
F25 - 原生 + 編碼 WAV 串流的 DLNA 類型標誌
內容: DLNA 類型標誌(例如 LPCM、WAVE、MP3 等設定檔識別碼)需要與裝置接收的內容匹配。F25 確保原生串流和編碼 WAV 串流被正確標記。
原因: DLNA 類型不匹配會導致某些裝置完全拒絕播放或應用錯誤的解碼器。
F26 - FLAC 檔案的 DLNA 標頭
內容: FLAC 串流在其標頭中獲得正確的 DLNA 設定檔識別碼。
原因: 沒有它,某些支援 FLAC 的裝置無法將串流識別為 FLAC。
F27 - 中繼資料中的位元率計算修正
內容: 連續串流 res@bitrate 以前計算為 (sampleRate * channels * bitsPerSample) / 1000 - kbps,與 UPnP DIDL 規範定義的屬性為每秒位元組數相差約 125 倍。現在除以 8 而不是 1000。
原因: 裝置上顯示的位元率錯誤 - 在大多數渲染器上是外觀問題,但有些會根據該值分配串流緩衝區,並在看起來比實際小約 125 倍的串流上出現卡頓。非連續源文件路徑已經正確處理了這個問題((bitrate_kbps * 1000) \ 8 = 每秒位元組數);只有連續串流路徑是錯誤的。
F28 - 中繼資料時間格式修正 (Marantz)
內容: DIDL 中的 res@duration 格式為 H:MM:SS(例如 0:03:42)。UPnP DIDL 規範將格式定義為 H+:MM:SS[.F+] - 嚴格來說,小數秒是可選但建議的;某些 Marantz 裝置將裸露形式視為無效,並將其持續時間顯示留空。現在格式化為 H:MM:SS.fff(例如 0:03:42.000)。
原因: 特定於品牌的顯示問題;符合 ISO 風格的帶有小數秒的格式可以解決此問題,而不會影響任何其他裝置。應用於兩個 DIDL 發射點(WriteAudioFileDIDL 中的源文件路徑 + 編碼串流路徑)。
同一批次的額外修復: pv:addedTime 和 pv:lastPlayedTime 在其 DateTime 格式字串中使用了 hh(12 小時制)而不是 HH(24 小時制)。任何在 13:00 到 23:59 之間添加或播放的曲目都會在顯示該欄位的裝置上以錯誤的小時呈現(例如 17:42 →「05:42」)。現在使用 HH。
F29 - 編碼 MP3 搜尋支援 (CBR)
內容: 轉碼後的 MP3 串流現在宣傳 DLNA.ORG_OP=11(位元組和時間搜尋),而不是 DLNA.ORG_OP=10(僅位元組)。以前拒絕轉碼 MP3 時間搜尋的裝置現在可以正常驅動其進度條/搜尋 UI。
原因: MusicBee 的轉碼器以 HighQuality 預設產生恆定位元率 MP3,因此位元組 ↔ 時間映射是線性的 - 裝置可以將時間搜尋請求轉換為 HTTP Range 位元組搜尋,而無需任何編碼器端支援。宣傳 OP=11 解鎖了裝置上的該 UI。沒有 F29,使用者在轉碼 MP3 內搜尋時,要麼搜尋被靜默忽略,要麼被丟到曲目開頭。
實作: 重構 ItemManager.vb 中的 GetEncodeFeature,將內聯 If 分解為可讀的 If/ElseIf/Else 鏈。MP3 明確獲得 OP=11;其他非 PCM 編解碼器保留 OP=10。AAC/FLAC 等沒有變化 - 這些需要編解碼器特定的 CBR 驗證,而 MusicBee 不保證。
F30 - 處理 .mpeg 檔案副檔名
內容: 帶有 .mpeg(甚至更罕見的 .mpe)副檔名的檔案現在在 GetCodec 中被識別為 FileCodec.Mp3。在 F30 之前,它們返回 FileCodec.Unknown 並被靜默地從音樂庫中拒絕 / 無法作為轉碼來源。
原因: 舊的 MPEG-1 Layer 3 檔案有時使用 .mpeg 而不是 .mp3(規範允許兩者)。在 30 萬個檔案的音樂庫中,少數幾個檔案足以讓人感覺「MusicBee 顯示它們,但插件不顯示」 - 這讓使用者感到困惑。
播放行為
F31 - 電台串流自動使用連續模式
內容: WriteAudioFileDIDL 現在通過 Library_GetFileProperty 探測來源 URL 的 Kind 屬性,並將任何 Kind 以「Stream」結尾的文件(MusicBee 報告電台為「MP3 Stream」、「Internet Stream」等)視為連續串流,無論全域 Settings.ContinuousOutput 開關如何。使用連續串流 DIDL 分支(標題:「Continuous Stream」,id="continuousstream",固定 PCM/Wave 輸出);裝置會看到一個單一的無限式串流。
原因: 電台串流沒有曲目邊界,沒有固定長度,沒有搜尋。在 DIDL 中將它們視為獨立文件會導致插件宣傳不存在的位元組範圍和持續時間。當 MusicBee 已經告訴我們「這是一個串流」時自動切換,可以消除使用者不應該考慮的自找麻煩。
範圍: 僅適用於 MusicBee 驅動播放時(musicBeePlayToMode)。音樂庫獲取路徑(UPnP 客戶端瀏覽)保持不變 - 那裡的電台 URL 很少見,並且在沒有明確測試的情況下,面向使用者的行為不應改變。
F32 - 編解碼器廣告回退
內容: 如果裝置沒有宣傳某些編解碼器(或插件無法解析裝置的功能 XML),插件不會立即拒絕串流。相反,它會嘗試提供串流,並讓裝置決定。
原因: 許多裝置的功能 XML 不完整或無法讀取,但實際上可以正常處理編解碼器。F32 以一個小的「最佳猜測並嘗試」來換取徹底拒絕。
F33 - 進度條同步改進
內容: 輪詢之間的進度位置已經從單一錨點 (currentPlayStartTicks) 進行時鐘外推,因此進度條以亞秒級速率平滑更新。剩餘的抖動源是新啟動曲目的初始錨點:以前的代碼假設在狀態計時器首次注意到狀態變為播放時 position=0,但那時裝置可能已經播放了 100-500 毫秒(一個輪詢間隔)。MusicBee 的進度條會從 0 開始,然後在現實趕上時向前跳躍。
F33 修復:當在新曲目上首次轉換到播放狀態時 (currentPlayStartTimeEstimated=True),調用 GetPlayPositionInformation() 以獲取裝置的實際當前位置,然後以此為錨點。UPnP 只報告 1 秒解析度,因此錨點仍然是量化的,但它比假設為 0 更接近真實。
原因: 更流暢 + 更準確的進度顯示,尤其是在曲目變更後。無法繞過 1 秒 UPnP 報告解析度本身 - 這是規範。
F34 - 曲目變更後進度條抖動
內容: 當為新曲目調用 PlayToDevice 時,插件過去會將 currentPlayPositionMs 和 currentPlayStartTicks 保留在其上一曲目值,持續約 100 毫秒,這是在 SOAP-Play 和第一個狀態計時器輪詢檢測到新播放狀態之間的時間窗口。MusicBee 的進度條會短暫顯示上一曲目的結尾,然後跳回 0,然後再爬升。F34 在 PlayToDevice 進入時將兩者歸零 - 在我們知道曲目變更正在發生之前,在任何 SOAP 工作之前。
原因: 快速跳轉使用案例(手動下一首或無縫轉換)的視覺故障。現在 MusicBee 的第一個 PlayPositionMs 查詢在 Play 後乾淨地返回 0,然後 F33 的 GetPlayPositionInformation 在第一個狀態變更滴答時將其精確到裝置的實際位置。
實作: PlayToDevice 頂部的四行,與 F33 的轉換時間精確錨定配對。
F35 - 「強制轉碼」錯誤
內容: 強制轉碼在某些組合下仍然可以跳過轉碼。在 F04 每個設定檔重做之後,兩個特定的漏洞被堵塞:
- 與 ForceNativeStream 的優先順序。 當兩者都為 True 時(這可能發生在架構遷移或部分設定文件上),ForceTranscoding 現在完全勝出(
If streamingProfile.ForceTranscoding Then forceEncode = True ElseIf streamingProfile.ForceNativeStream Then forceEncode = False)。UI 互斥防止使用者同時勾選兩者,但運行時保護處理從磁碟載入不一致的任何狀態。 - bypassTranscodeDecision 邏輯。 以前:
streamingProfile.ForceNativeStream AndAlso Not Settings.ForceTranscoding。現在:streamingProfile.ForceNativeStream AndAlso Not streamingProfile.ForceTranscoding- 相同的優先順序規則,但在相同的每個設定檔範圍內。
原因: 「強制」應該意味著強制。如果使用者明確為裝置啟用了強制轉碼,插件絕不能靜默地回退到原生串流,無論其他標誌如何組合。
F36 - 渲染器關閉異常
內容: 將 Plugin.ReceiveNotification 包裹在頂層 Try/Catch 中,該 Try/Catch 會記錄任何未捕獲的異常,而不是讓它傳播回 MusicBee 的通知泵。
原因: 來自 MusicBee 的通知(PlayStateChanged、VolumeMuteChanged 等)會分派到 ControlPointManager,後者通過 SOAP 與渲染器通信。單獨的調用站點已經在其 SOAP 調用周圍設置了 Try/Catch,但足夠奇怪的時序情況(例如,渲染器在同一通知處理程式中的兩個 SOAP 調用之間死亡)仍然可能逃脫。頂層包裝器是最終的安全網,因此使用者永遠不會看到來自 MusicBee 的通用「TargetInvocationException」彈出視窗。
實作: 將現有主體重新命名為 ReceiveNotificationInternal,並新增一個薄包裝器 ReceiveNotification,該包裝器執行 Try { ReceiveNotificationInternal(...) } Catch { LogError(...) }。ControlPointManager 內部預先存在的每個方法 Try/Catch 基礎設施(圍繞每個 PostSoapRequest 調用)保持不變 - F36 是雙重保險。
F37 - 長曲目搜尋觸發錯誤轉換
內容: 在某些渲染器上,在長曲目中搜尋可能會產生短暫的停止→播放循環。如果沒有區分,ProcessNewPlayState.Stopped 會將其視為曲目自然結束並調用 Player_PlayNextTrack,在使用者只想拖曳時推進 MusicBee。F37 在 Seek() 中標記 lastUserInitiatedSeek,並在停止處理程式中新增一個 5 秒的保護(鏡像現有的 lastUserInitiatedStop 窗口)。
原因: 在搜尋期間靜默跳到下一首曲目是那種沒有人能猜到原因的錯誤 - 使用者會想「奇怪,我試圖向前拖曳,現在它正在播放下一首歌」。修復是機械的:與已經存在的用戶停止區分模式相同。
F38 - 針對易崩潰編解碼器的改進搜尋處理
內容: BubbleUPnP 在 MP3 搜尋時崩潰是典型的症狀。經過審核,目前的 yaiol 搜尋代碼已經做了正確的事情 - 原生路徑正確處理 HTTP Range (206, Content-Range, AcceptRanges),編碼路徑宣傳 X-AvailableSeekRange 並解析傳入的 timeSeekRange.dlna.org / npt 標頭,DLNA.ORG_OP 標誌反映實際串流功能(對於有問題的 Platinum 裝置,可選擇禁用 DisablePcmTimeSeek)。在當前 BubbleUPnP 4.6.4 上進行了使用者測試:未觀察到崩潰。
原因: BubbleUPnP MP3 搜尋崩潰報告於 2024 年左右,此應用程式自那時起已進行了約 16 個月的修復。F29(編碼 MP3 OP=11)是可能重新暴露它的新變數;在測試版本上沒有。
如果崩潰再次發生: 修復形式將是每個設定檔的「有限搜尋」開關,強制對標記的編解碼器使用 DLNA.ORG_OP=10(僅位元組) - 鏡像 DisablePcmTimeSeek 對 PCM 的工作方式。屆時再添加,而不是預防性地添加。
UI 與日誌
F39 - 「新增」按鈕選取新的設定檔
內容: 點擊裝置設定檔列表中的「新增」會建立一個新的設定檔並自動選取它,以便使用者可以立即編輯欄位。我們的分區對話框重構已經這樣做了 - 直接新增路徑和從模板新增路徑都以 Me.activeStreamingProfiles.SelectedIndex = Me.activeStreamingProfiles.Items.Count - 1 結束。檢查確認我們的分支已經處理了這個問題 - 無需更改。
原因: 一個小的 UX 痛點,結果在這裡已經不是痛點了。
F40 - 更大的最大連接數 + 警告日誌
內容: 插件的並發串流上限(SemaphoreSlim 圍繞 Sockets_Stream_File / Sockets_Encoder_Start)硬編碼為 4。F40 將其設為使用者可在「一般」設定頁面配置(預設 16,範圍 1-256),在請求必須等待插槽時新增 MaxConnections 日誌行,並且如果 MusicBee 啟動以來至少達到一次上限,則在設定對話框的左下角顯示紅色 ⚠ 最大連接數徽章。
原因: 當裝置發出並行請求時(某些 Marantz/Linn 在封面掃描期間,BubbleUPnP 的中繼資料探測與活動播放同時進行),額外的請求會靜默地被信號量阻塞 - 使用者看到「裝置緩慢」而沒有可見原因。日誌行對於技術調試很有用,但非技術使用者從不閱讀日誌。設定對話框中可見的徽章使任何打開插件偏好設定的人都能發現達到上限的情況。
實作:
- 將等待集中在
MusicBeeUpnp.vb中的WaitOnSendBarrier(logTag);兩個調用站點(MediaServerDevice.GetFile、Encoder.StartEncode)都使用它。 Settings.MaxConnections持久化在設定架構的 v8 中。Plugin.MaxConnectionsHit是一個粘性會話標誌,在WaitOnSendBarrier內部設定;僅在 MusicBee 重新啟動時重置。SettingsDialog.maxConnectionsBadge是一個位於(16, 410)的紅色粗體標籤,僅在Plugin.MaxConnectionsHit為 True 時顯示。帶有解釋原因和補救措施的工具提示。- 信號量在類型載入時初始化一次,因此更改設定需要重新啟動 MusicBee(在欄位標籤中註明)。
F41 - 記錄「由於 ReplayGain/DSP 進行編碼」
內容: 不再是單獨的「為 RG 編碼」/「為 DSP 編碼」日誌行,F42 中的單一 StreamDecision 行包含 MB-DSP/EQ、MB-ReplayGain、Profile-DSP/EQ、Profile-ReplayGain 作為累積原因。診斷價值相同,噪音更少。
原因: 使用者可以在一行日誌中看到給定曲目發生轉碼的所有原因,而不是分散的。有關詳細資訊,請參閱 F42。
F42 - 記錄「渲染器不支援來源編解碼器」
內容: 為每個播放到裝置的曲目新增一條 StreamDecision 日誌行,說明是「原生 CODEC」還是「轉碼 CODEC→CODEC reason=…」。原因欄位累積了觸發轉碼的每個條件:MB-DSP/EQ、MB-ReplayGain、Profile-DSP/EQ、Profile-ReplayGain、WebFile、VirtualFile、ForceTranscoding(global)、SampleRate<min/SampleRate>max、DownmixToStereo、DeviceLacksCodec(X)、BandwidthConstrained。
原因: 使用者對他們預期原生串流的文件上意外的 CPU 峰值感到困惑。每首曲目的一行日誌會告訴他們確切是哪個條件導致了轉碼 - 如果該欄位顯示 DeviceLacksCodec(Flac),他們會立即知道裝置的協議資訊不完整,並且可能希望 F32 的回退機制啟動。
實作: 單一累積字串在決策鏈中逐步構建;在結束時記錄一次。受 Settings.LogDebugInfo 控制,以避免在生產環境中產生日誌噪音。
F43 - SetNextAVTransport 日誌顯示來源 URL
內容: QueueNext 日誌條目現在包含 source=<MusicBee 音樂庫路徑> 以及 stream=<HTTP 串流 URL>。相同的更改應用於成功路徑和失敗路徑(QueueNext:Failed)。
原因: 在調試佇列曲目問題時,串流 URL(/encode/aabbccdd0.flac)本身是不透明的 - 每首曲目都相同。來源 URL 是人類可 grep 的音樂庫路徑,它會告訴您 MusicBee 嘗試佇列的確切文件。
F44 - 更好的 mime 類型錯誤日誌記錄
內容: Activate 期間新增兩個日誌條目:
Activate:MimeUnverified- 針對裝置GetProtocolInfo響應中每個格式錯誤的條目觸發,命名無法解析的條目(因此使用者可以看到例如「Marantz 為某些編解碼器返回http-get:*::*- 功能未驗證,F32 的回退將猜測」)。Activate:NoSinkInfo- 如果裝置完全沒有返回<Sink>元素,則觸發一次。這意味著SupportedMimeTypes保持 Nothing,並且IsCodecSupported降級為「假設一切正常」 - 當稍後出現「裝置拒絕串流」錯誤時,這是有用的上下文。
原因: 在 F44 之前,這些靜默的功能回退讓使用者猜測為什麼他們的曲目要麼被轉碼超出預期,要麼被裝置拒絕。現在,對 Activate: 的單一 grep 會顯示裝置的功能資訊是否可用。
F45 - 更好的中繼資料錯誤日誌記錄
內容: ContentDirectoryService.vb 中的 Browse 異常日誌已在早期 yaiol 工作(Alia Vox 錯誤會話)中豐富了 ObjectID 和堆疊追蹤。F45 進一步擴展了它,包括 BrowseFlag(中繼資料與子項)、Filter(客戶端請求的屬性)、sortCriteria 和 partialResultLength(失敗前產生了多少位元組的 DIDL - 指示壞曲目在批次中的位置)。
原因: 當 DIDL 中途出現問題時,部分長度值會告訴您失敗是在批次的第一首曲目(partial=0)還是中途(partial=N) - 結合批次的 startingIndex,您可以識別出有問題的曲目索引。Filter 和 BrowseFlag 解釋了客戶端想要的哪種類型的瀏覽;有時,僅中繼資料瀏覽失敗,而相同 ID 的子項瀏覽成功。
網路
F46 - 自動模式僅在真實網路適配器上宣傳
內容: 在自動介面模式下,插件過去會在每個運作中的 IPv4 適配器上宣傳自己 (SSDP)。在同時運行 VPN 隧道 (NordLynx) 或虛擬交換機 (Hyper-V / WSL / Docker) 的機器上,相同的音樂庫也會在這些適配器上宣傳,因此您投射的控制點會發現伺服器兩三次,並將音樂庫列為重複副本。自動模式現在只保留具有真實 IPv4 預設閘道 (HasIPv4Gateway) 的適配器 - 隧道和虛擬交換機適配器沒有 - 因此這些適配器會從宣傳列表中刪除。使用者釘選的地址仍然完全優先 (僅在該介面上宣傳),如果沒有適配器報告閘道,選擇器會回退到所有適配器,因此宣傳的地址列表永遠不會為空,插件也不會變得不可見。
原因: 重複不是由「在 VPN 上」引起的 - 它是由於同時在 LAN 適配器和隧道/虛擬適配器上宣傳引起的,因此一個控制點在兩個地址上看到相同的伺服器。消費級 VPN (NordVPN/NordLynx) 僅隧道網際網路流量;DLNA 渲染器位於 LAN 上,本地子網流量繞過隧道,因此隧道適配器無論如何都無法到達渲染器 - 刪除它會刪除一個虛擬副本,而不是一個可用的路徑。閘道測試是區分真實 LAN/Wi-Fi 適配器與隧道或虛擬交換機的廉價、可靠信號。補充 N05 (它修復了在此類鏈路上如何發送廣播 - 多播而不是廣播);F46 管理哪些適配器會被宣傳。
已知限制: 網狀 / 遠端存取 VPN (Tailscale, ZeroTier, WireGuard-to-home) 的渲染器確實位於隧道中,通常會呈現一個沒有預設閘道的適配器,因此自動模式也會將其刪除。這些使用者會釘選 VPN 地址,該地址優先於閘道過濾器。