MusicBee UPnP Plugin ヘルプ

新機能

2.0.2 - 2026-07-20

  • テンプレートに追従するノードがある場合、テンプレートは削除できなくなりました。削除ボタンは無効のままになるため、ノードが孤立することはありません。各テンプレート行には、追従するノードのライブカウント (n) が表示され、削除が無効になっている理由が一目でわかります。また、未使用のテンプレートを削除すると、次回読み込み時に出荷時のデフォルトが静かに再表示されるのではなく、削除が維持されるようになりました。
  • ビューツリーの横にある新しいファネルボタンは、どのノードがテンプレートに追従しているかを正確に表示します。選択したテンプレートの追従者のみにツリーをフィルタリングし、他のテンプレートを選択すると再フィルタリングされ、オフに切り替えるとツリー全体が復元されます。
  • ラジオポッドキャストノードは、カテゴリのテンプレートと永続的にペアになりました。パスタブでテンプレートを再形成すると、ノードは自動的に追従します。適用するものがないため、適用ボタンは無効になっています。テンプレートリストには、標準(すべての新しいテンプレートが作成される場所)と予約済み**(ラジオ + ポッドキャスト)の2つのバンドでこれが反映されています。

2.0.1 - 2026-07-20

  • パステンプレートが、それを使用するノードにライブリンクされるようになりました。テンプレートを適用すると、ノードはそれに従います。後でテンプレートを編集すると、それに従うすべてのノードはすぐに再形成されます。ノードを一つずつ探し出して再適用する必要はありません。ビューツリーには、各ノードがどのテンプレートに従っているかが名前の直後に表示され、名前の変更はそこに即座に反映されます。テンプレートを削除する際には、まずそれに従っているノードの数が表示されます(それらのノードは現在のレイアウトを維持し、何も従わなくなります)。
  • 非表示のノードにテンプレートを適用すると、そのノードは再び表示されるようになります。適用は「これをこのように表示する」というジェスチャーであり、非表示は「表示」チェックボックスで行います。これに置き換わる予約済みの「非表示」テンプレートはなくなりました。
  • ビューツリーで場所を見失うことがなくなりました。チェック、展開されたフォルダー、スクロール位置はすべて、テンプレートの適用やその他の更新後も維持されます。

2.0.0 - 2026-06-16

これは、MusicBee UPnP プラグインのオープンソース yaiol フォークの最初の公開リリースです。このフォークで新しく追加された機能と、元のプラグインに加えられた修正と改善の2つのパートに分けて紹介します。各項目は、プロジェクトの内部機能カタログの What / Why 形式を維持しており、変更だけでなく、すべての変更の背後にある理由もページに記載されています。

このフォークの新機能

MediaRenderer - MusicBeeで再生

N01 - MusicBeeを再生先レンダラーとして使用

内容: 通常、このプラグインは一方向で動作します。電話や他のデバイスがMusicBeeのライブラリを閲覧し、そのデバイス自体で音楽を再生します。この機能は逆方向の機能を追加します。MusicBeeをプレーヤーとして機能させることができます。電話のコントローラーアプリ(BubbleUPnPなど)から、デスクトップのMusicBeeを再生するものとして選択し、手元から操作できます。再生、一時停止、停止、早送りまたは巻き戻し、トラック内の特定の位置へのジャンプ、音量変更、ミュートが可能です。

理由: これにより、電話がPC上の音楽のリモコンになります。ソファに座って、電話でライブラリを閲覧し、トラックをタップすると、デスクトップに接続されたスピーカーから音楽が流れ、座っている場所から完全にコントロールできます。元のプラグインでは、これは動作する機能として提供されていませんでした。

有効にする方法: ホームネットワーク上のあらゆるものがPCで再生を開始できるため、この機能はデフォルトでオフになっています。設定ダイアログの「全般」タブにあるチェックボックスで有効にできます。プラグインの3つの役割にはそれぞれ独自のチェックボックスがあります。ライブラリを共有する (サーバー)、他のユーザーに自分に再生させる (レンダラー)、他のデバイスに再生する (コントロールポイント)。ダイアログには、有効にした役割に必要な設定タブのみが表示されるため、自分に適用されないオプションに直面することはありません。

マシンを区別する: レンダラーには好きな名前を付けることができます(最初は「MusicBee (yaiol)」です)。この名前は、電話の再生先リストに表示されるため、複数のPCでMusicBeeが実行されている場合に、どれがどれであるかを区別できます。名前の変更はすぐに有効になり、再起動は不要です。

自分自身に再生するときの最高の音質: 電話からMusicBee自身のライブラリを閲覧し、その同じMusicBeeにトラックを送り戻すと、プラグインはそれが自身のファイルの一つを再生するように要求されていることを認識し、ディスクから直接再生します。その結果は正確かつ即座であり、MusicBee自身のイコライザーと音量レベルが適用されたビットパーフェクトな音質が得られます。オーディオを無意味にネットワークに出力してすぐに自分自身に戻すことはありません。

プライベートで実行する: 3つの役割は独立して機能するため、ライブラリ共有をオフにしたままレンダラーをオンにすることができます。この「レンダラーのみ」のセットアップでは、ライブラリはネットワークから完全に隠されたままになり、再生先のみがアナウンスされ、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対応レンダラーの機能を無効にしていました。現在、2番目の句はFLACを除外しています: Not isPcmData AndAlso encoder.Codec <> FileCodec.Flac。FLAC 5.1はそのまま通過します。MP3/AAC/Oggは、MusicBeeのこれらの形式用のコマンドラインエンコーダーが2チャンネル入力を期待するため、引き続き強制的にステレオになります。

理由: 5.1 FLACソースをFLAC出力にトランスコードする目的は、マルチチャンネルミックスを維持することです。サイレントダウンミックスは、サラウンドリスニングにとってFLACトランスコードオプションを無用にしていました。N02により、それが正しく機能するようになりました。


アーキテクチャ

大規模なライブラリでフォークを機能させるための構造変更 - 元のプラグインにはありませんでした。

N03 - 遅延 (オンデマンド) ブラウズツリー

内容: 元のプラグインは、HTTPポートを開く前に、MusicBeeの起動時にブラウズツリー全体を構築していました。つまり、すべてのトラックを列挙し、ファイルごとに完全な Library_GetFileTags を実行し、コンテナ階層全体を組み立てていました。実際のライブラリ (5万以上のトラック、5400のエピソードのポッドキャスト、数百のステーション) では、コールドスタートに数分かかり、クライアントが一度も開かないブランチを含め、ツリーはRAMに永遠に残っていました。このフォークは事前に何も構築しません。ルートはエンドポイントごとに1つの L: プレフィックス付きプレースホルダー (L:musicL:podcastL:filter:…) を公開します。各レベルは、クライアントがそのレベルをブラウズしたときにのみ計算され (LazyBrowseEnsureLazyEndpointInMemory → レベルごとのキャッシュ)、ライブラリ変更通知はキャッシュをクリアします (SetLibraryDirty)。

理由: コールドスタートは実質的に瞬時です。MusicBeeがプラグインの初期化を完了する頃にはHTTPポートが開いています。メモリはライブラリのサイズではなく、ブラウズされた内容に比例して維持されます。トレードオフ: エンドポイントへの最初のブラウズにはロードコストがかかります。再エントリは次のライブラリ変更までキャッシュされます。これは他のすべてが依存する基盤です。詳細なメモ: FIXES.md


ネットワークと堅牢性

HTTPサーバーのバインドパスの強化。元のプラグインは、ポートが利用できない場合にサイレントに停止します。

N04 - 自己修復型HTTPポートバインディング

内容: プラグインのHTTPサーバーは、設定されたポートが利用できない場合でも停止しなくなりました。3つの関連する変更点があります。

  1. バインド失敗時の自動フォールバック。 HttpServer.Start は設定されたポートを試行し、SocketException が発生した場合は、最大20ポートを上にスキャンして最初の空きポートを見つけます。実際にバインドされたポートは新しい Plugin.boundServerPort に記録され、サーバーをアドバタイズするすべてのもの (SSDP LOCATION URL (NOTIFY + M-SEARCH応答)、デバイスURL (PrimaryHostUrl)、ルーターポートフォワード、SSDP/コントロールポイント自己フィルター) は、Settings.ServerPort の代わりに boundServerPort を読み取るようになりました。UPnPクライアントはSSDP経由で実際のポートを検出するため、移動したポートはレンダラーにとって透過的です。
  2. ユーザー通知。 フォールバックが発生した場合 (保存されたポートが使用中のポートではない場合)、ローカライズされた MessageBox (WarnPortInUse) が、実際にサービスを提供しているポートと、デバイスが引き続きそれを見つけることをユーザーに通知します。これは、プラグインがヘッドレスで実行され、ダイアログ内のメッセージは問題があることをすでに疑っている人にしか見られないためです。
  3. 再起動による回復。 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の後、ポート衝突は自己修復します。サーバーは次の空きポートで実行を続け、ユーザーに通知され、クライアントはそれを再検出します。プラグイン全体が停止することはありません。

実装:

  • デフォルトポートは、3つの ServerPort 宣言と設定解析失敗時のフォールバックで 493829779 (動的範囲より下なので、Windowsが自動予約することはありません。既知のメディアサーバーのデフォルトではありません) に移動しました。
  • Plugin.boundServerPort (新しい共有フィールド) はライブリスニングポートを保持します。activeServerPort設定されたスナップショットのままなので、「再起動が必要です」バッジロジックがフォールバックで誤って起動することはありません。
  • HttpServer.PortScanRange = 20。スキャンは最初の TcpListener.Start() の成功で停止し、すべての試行が失敗した場合にのみ最後の例外をスローします。
  • 新しいENリソースキー WarnPortInUse (翻訳は公開時のロケールパスに従います)。

N05 - マルチキャストグループ経由のSSDPアナウンス (VPN / ポイントツーポイント)

内容: SSDPアナウンスは、IPブロードキャストアドレスではなく、UPnPマルチキャストグループ (239.255.255.250) に送信されます。SSDP検索応答がサーバーの再起動と競合したときにログに記録される無害な「破棄されたオブジェクトにアクセスできません」エラーも抑制されます。

理由: ポイントツーポイント/VPNネットワークアダプターでは、IPブロードキャストは適用されません。古いブロードキャスト送信は「無効な引数」で失敗し、アナウンスが失われたため、プラグインはそれらのリンク上のクライアントから見えませんでした。適切なマルチキャストグループにアナウンスすることで、まさにそれらのアダプターでの検出が修正されます。

ライブラリナビゲーション

これらはこのフォークで出荷され、元のプラグインにはありません。これらは、実際のUPnPクライアントからプラグイン自身の出力を実際に閲覧することから生まれました。

N06 - フィルターベースのライブラリ公開

内容: MusicBeeのフィルタータブ (ユーザーのMusicBeeフォルダー内の .xautopf ファイル) が、プラグインのライブラリでUPnPルートコンテナになります。各フィルターのトラックは、AlbumArtistSort → Album → Tracks の階層で閲覧できます。

理由: キュレーションされたMusicBeeフィルター (例: 「5つ星トラック」、「最近追加されたもの」、「クラシック → バロック」) を持つユーザーは、UPnPクライアントからプラグインを閲覧するときにそれらを見つけることを期待します。元のプラグインは生のライブラリツリーしか公開していませんでした。


N07 - SortAlbumArtist フィールドの配線

内容: プラグインはMusicBeeの MetaDataType 165 (アルバムアーティストのソート) を読み取り、それを使用してブラウズビューでアーティストをグループ化/ソートするようになりました。

理由: ハイファイブラウザやオーディオ愛好家は、ライブラリを整理するためにソートアーティスト名 (「Ludwig van Beethoven」ではなく「Beethoven, Ludwig van」) を使用します。真剣なリスナーにとっては標準的な期待です。どちらのアップストリームにも欠けていました。


N08 - 複数値のアルバムアーティスト処理

内容: アルバムの AlbumArtist フィールドに "; " で区切られた複数のアーティスト (例: "yaiol; Ars Ricercata") が含まれている場合、トラックは名前を結合した単一のフランケンシュタインアーティストの下ではなく、ブラウズビューでアーティストの下に表示されるようになりました。

理由: コラボレーションアルバムやコンピレーションは、すべてのコラボレーターの下に表示される必要があります。これがなければ、アルバムを見つけるための検索パスの半分が壊れてしまいます。


N09 - アルバムコンテナのアートワーク (upnp:albumArtURI)

内容: DIDL Browse 応答のアルバムコンテナノードに、アルバムカバーを指す upnp:albumArtURI 要素が含まれるようになりました。

理由: これがないと、UPnPクライアントのブラウズビューのすべてのアルバムが、アルバムカバーではなく汎用アイコンを表示していました。ナビゲーションの視覚的な手がかりであり、すべての最新のハイファイブラウザで期待されています。


N10 - フィルターアルバム内のトラック順序

内容: フィルターで公開されたアルバム内のトラックは、ディスク番号、次にトラック番号でソートされるようになりました。

理由: 標準的なアルバム順序です。明示的なソートがないと、トラックはフィルターが返した順序で返され、通常はランダムに見えました。


N11 - プレイリストフォルダーツリーの修正

内容: LoadLibraryPlaylists 関数 (元はSteven Mayall氏による、2014年頃) は、新しく作成されたプレイリストフォルダーに降りていくことができませんでした。各フォルダーの最初のプレイリストとサブフォルダーは、ルートレベルで孤立していました。

理由: 元のプラグインに11年間存在していました。BubbleUPnPを開いてプレイリストをクリックすると30秒以内に表示されました。yaiolでは、ツリー構築中に新しく作成されたフォルダーに正しく再帰することで修正されました。


N12 - XML不正制御文字のサニタイズ

内容: C0制御文字 (例: 不良なエンコーディングパスによる 0x19 - UTF-8 → Latin-1 → 0x990x19 に切り詰める) を含むタグを持つトラックは、不良なトラックがページ分割されたバッチに入ると、Browse応答全体が Action Failed で失敗していました。

理由: XML 1.0はほとんどのC0制御文字を禁止しており、XmlWriter はそれらを書き込むように要求されると例外をスローします。元のプラグインに存在していました。XmlConvert.IsXmlChar を介して、すべての Library_GetFileTags 終了点で無効な文字を削除することで修正されました。


N13 - ページ分割されたBrowse全体でラジオリストが決定論的

内容: ラジオコンテナのBrowseは汎用ファイルリストブランチに分類され、すべての呼び出しで files.Sort(AlbumFileComparer) を呼び出していました。ラジオエントリはアルバム/ディスク/トラックタグが空であるため、すべての比較が0を返していました。List(Of T).Sort は不安定であり、呼び出しごとに異なる順序を生成していました。UPnPコントロールポイントはページ分割 (BubbleUPnPは0..15、次に16..最後までフェッチ) します。2つの呼び出しの間でリストが再シャッフルされたため、一部のステーションは両方のページに表示され (重複)、一部はどちらにも表示されず (欠落)、更新ごとにランダムに見えました。

理由: 元のプラグインに存在していました (その作者はUPnP経由でラジオを閲覧することはありません)。ここでは、Browse 内の専用の ContainerCategory.Radio ブランチで修正され、呼び出しごとのソートはありません。radioFiles はロード時にタイトルで一度ソートされます (安定)。ページ分割されたブラウズは決定論的な順序で表示されるようになりました。ページ1とページ2は互いに排他的です。


N14 - UPnP検索アルバムクラスがアルバムコンテナを返す

内容: アルバムクラスクエリ (upnp:class = "object.container.album.musicAlbum", 例: BubbleUPnPの「ランダムアルバム」) のUPnP検索は、アルバムコンテナではなく完全なトラックリストを返していたため、クライアントはアルバムをゼロと表示していました。元のハンドラーは括弧で囲まれた条件のみを解析し、要求されたクラスに関係なくすべてのトラックをダンプしていました。

理由: ここで修正されました。アルバムクラスクエリは、個別のアルバム (アルバムアーティスト+アルバムでグループ化) を列挙し、それぞれをカバーアート付きの適切な musicAlbum コンテナとして発行するようになりました。これは Salb<idx> 仮想ID空間を介してアドレス指定できるため、クライアントは結果をドリルダウンして再生できます。


N15 - クリックスルー付きの、動作するスコープ対応UPnP検索

内容: 元のプラグインは検索機能がないと宣伝していたため (GetSearchCapabilities は空を返していた)、クライアントは検索を送信することさえ拒否していました。また、古いバックエンドは musicFiles から読み取っていましたが、これは遅延ツリーの時代には常に空でした。このフォークは、実際に検索可能なプロパティを宣伝し、遅延ライブラリに対してタイトルによるトラック検索とタイトルによるアルバム検索を実装し (HandleLazySearch)、実際のコンテナIDが送信された場合はクライアントの現在のブランチにクエリのスコープを設定し (それ以外の場合は L:music を代用して、トップバー検索がポッドキャスト/ラジオ/オーディオブックのノイズを引き込まないようにします)、アルバム結果を合成 Ssrch_alb_* IDを介してクリック可能にします。このIDは、Browse の早期ブランチがアルバムのトラックにマッピングし直します。(アルバムクラスの結果をコンテナとして扱う部分はN14です。)

理由: BubbleUPnPでの検索は、「ライブラリは検索をサポートしていません」から、有用でスコープが設定され、再生可能な結果を返すようになりました。完全な設計と却下されたアプローチ: SEARCH.md


N16 - UPnPキャッシュ無効化 (SystemUpdateID)

内容: 元のプラグインは定数 SystemUpdateID=0 を返していました。これはUPnP ContentDirectoryのキャッシュ無効化契約であり、仕様に準拠したクライアント (BubbleUPnP) はライブラリを変化しないものとして扱っていました。古いブラウズ結果、URLスキーム変更後の404サムネイル、「変更を見るにはMusicBeeを2回再起動する」という手間がありました。このフォークは、ロード時にエポック秒から SystemUpdateID をシードし (したがって、すべての再起動は厳密に前回より進んでいます)、すべてのライブラリ変更と設定変更時にそれをインクリメントします (SetLibraryDirty / ResetCacheBumpSystemUpdateId)。

理由: クライアントは、次のブラウズで編集、新しいファイル、設定変更を確実に取得します。既知の制限: サブスクライブされたクライアントはGENA経由で新しい値を積極的に再プッシュされません (将来の作業として保留中)。彼らは次のブラウズでそれを見ます。


N17 - ポッドキャスト購読アートワーク

内容: ポッドキャストタイルに画像が表示されず、すべての /PodcastThumbnail/ リクエストが404エラーになっていました。2つのバグが重なっていました。解決チェーンがMusicBeeの実際のアートワークキャッシュ (%LocalAppData%\MusicBee\InternalCache\Subscriptions\<name>.jpg、デスクトップUIが読み込む場所) をチェックせず、HTTPレイヤーのアンエスケープ+小文字化によってフィードURLルートキーが最後のパスセグメントまで破損していました。このフォークは、MBのInternalCacheからアートワークを解決し、HTTPレイヤーを無傷で通過するURLセーフなスラッグ (PodcastSlug / podcastSubIdBySlug) を介してルックアップをルーティングします。

理由: 購読アートワークがブラウズビューでレンダリングされるようになりました (以前404エラーだった22件のすべてのリクエストが解決されます)。


N18 - 階層型 (区切り文字付き) タグブラウジング

内容: 任意のフィールドをライブラリオプションタブで階層型としてマークし、1文字の区切り文字 (追加/削除機能付きのフィールドピッカーと区切り文字ボックスで、プラグイン設定に永続化されます) を指定できるようになりました。グループ化/ に設定し、Jazz/Cool Jazz のような値を設定すると、フラットなエントリではなく Jazz › Cool Jazz としてブラウズされます。ブランチで正確にタグ付けされたトラック (単に Jazz) は独自の [Jazz] ノードを取得するため、何も隠されず、単一の子を持つブランチは自動的に折りたたまれ、; はMusicBee自身の複数値セパレーターであるため、区切り文字として拒否されます。

理由: ユーザーがすでに単一のフィールドにエンコードしている深いタグ分類 (ジャンルツリー、ムード階層、「クラシック/バロック/協奏曲」) が、ユーザーが端から端まで読む必要のあるスラッシュ区切りの文字列のフラットな壁ではなく、タグが記述するツリーとして最終的にブラウズされるようになりました。


N19 - グループ化フィールドでラベル付けされた単一のルートパス

内容: ルートにある単一のブラウズパスは、マージされた最初のフィールドグループの命名方法に合わせて、完全な短いパスではなく、そのグループ化フィールド (例: 「ジャンル」) でラベル付けされるようになりました。

理由: ブラウズツリーは一貫して読み取れます。ルートエントリが単独で存在する場合でも、兄弟 (N20) とマージされた場合でも、同じ命名規則が適用されます。これにより、単独のルートエントリが冗長な内部パスを表示し、マージされた隣接エントリがクリーンなフィールド名を表示するという不整合がなくなります。


N20 - 最初のフィールドを共有するブラウズパスをマージ

内容: 最初のフィールドを共有する2つのブラウズパス (「ジャンル / ソートアルバムアーティスト」と「ジャンル / ポッドキャスト人物」) は、単一のジャンルルートフォルダーにまとめられ、最初にジャンル値をリストし、次に2つのビューに分割されます。これにより、ルートにほぼ重複する2つの「ジャンル / …」エントリが並んで表示されることがなくなります。

理由: 共通のフィールドの下にいくつかの関連ビューをネストしているユーザーは、ルートがほとんど同じトップレベルエントリで散らかっているのを見ていました。それらをマージすることで、ルートが読みやすくなり、関連するビューが共有フィールドの下に適切にグループ化されます。


N21 - カテゴリ型ブラウズパス (標準 / ラジオ / ポッドキャスト)

内容: すべてのブラウズパスは、標準ラジオポッドキャストのカテゴリで型付けされます。テンプレートリストはこれら3つのセクションにグループ化され、各テンプレートのフィールドピッカーは、そのカテゴリのデータが実際に提供できるフィールドのみを提供し、テンプレートはビューツリー内の対応するノードにのみ適用できます (互換性のないノードはグレー表示され、チェックできません)。予約済みのラジオとポッドキャストのテンプレートは削除できないため、そのカテゴリセクションが消えることはありません。

理由: 型付けがないと、ユーザーはサイレントに空になるレイアウトを作成する可能性があります。ラジオ局には「アルバム」がなく、ポッドキャストエピソードには「アルバムアーティスト」がありません。そして、UPnPクライアントからデッドフォルダーをブラウズして初めてそれに気づくことになります。フィールドメニューと適用ターゲットをカテゴリの実際のデータに制約することで、空のレイアウトを作成できなくなります。


N22 - ポッドキャストを公開年でグループ化

内容: 各ポッドキャストエピソードの公開日が読み込まれるため、年レベルを持つポッドキャストブラウズパスは、エピソードを単一の「不明」の下にまとめるのではなく、年ごとにバケット化します。

理由: 大規模なポッドキャスト購読は、プラグインがエピソードごとの公開日を一度も見ていなかったために、すべてのエピソードが日付のない一つの山にまとめられるのではなく、ライブラリの他の部分と同様に年ごとにナビゲートできるようになります。


N23 - 単一結果のグループ化レベルを折りたたむ

内容: 単一の値に解決されるグループ化レベル (LPしか作成していないアーティストの「LP」のみを表示するレコードタイプレベル、または単一の文字を持つ文字レベル) は自動的にスキップされ、ユーザーは直接その内容に移動します。

理由: ちょうど1つのフォルダーを含むフォルダーをブラウズするのは純粋な摩擦です。単一選択レベルを折りたたむことで、ユーザーが到達できるものを変更することなく、無駄なクリックを排除します。


N24 - MusicBeeの日付フィールドに対する年グループ化/検索

内容: 年条件は、MusicBeeの完全な日付「年」フィールドを裸の4桁の値でクエリしなくなり、ハードコードされた年フィールドのエイリアシングがなくなったため、すべてのグループ化フィールドがパス定義から汎用的に解決されるようになりました。

理由: 年タグが完全な日付を保持しているライブラリの場合、以前は年でグループ化または検索しても何も返されませんでした。4桁のクエリが完全な日付フィールドと一致しなかったためです。正しいフィールドをクエリすることで、年グループ化と検索が再びトラックを見つけるようになります。


N25 - 「年」と「年 (yyyy)」のグループ化フィールドを分離

内容: アルバムのグループ化とブラウズパスは、MusicBee自身の2つの年フィールド、 (完全な日付タグ) と 年 (yyyy) (4桁の年のみ) の両方を公開するようになりました。これにより、ユーザーはアルバムのグループ化やブラウズパスを定義する際にどちらかを選択できます。

理由: MusicBeeでは2つのフィールドが異なる意味を持ち、それらをまとめることでその区別が失われていました。両方を表示することで、ユーザーは意図に応じて、ある年のすべてのリリースをまとめる (yyyy) か、正確な日付順序を維持する (完全な年タグ) ことができます。


N32 - ピン留めされたフィルターとプレイリストをルートで種類ごとにグループ化

内容: ピン留めしたフィルターはブラウズルートのフィルターフォルダーの直下に、ピン留めしたプレイリストはプレイリストフォルダーの直下に表示されるようになりました。すべてのピンがルートの末尾に一塊で集まることはなくなり、ピン留めした各ショートカットはそれぞれの種類と並びます。

理由: ピン留めするショートカットが増えるほど、フィルターとプレイリストが混在した末尾の一塊はスキャンしづらくなり、各ショートカットが本来属するフォルダーから切り離されてしまいます。ピン留めした項目をそれぞれのカテゴリーの下にグループ化することで、ルートは読みやすいまま保たれ、各ショートカットは関連するものの隣に並びます。

設定ダイアログとパッケージング

N26 - セクション化された設定ダイアログ

内容: 環境設定ページが、左ナビゲーションレイアウトでセクション化されました: 全般 / 再生 / ライブラリ / デバイスプロファイル / 診断。

理由: 元のものは、すべての設定が単一の長いフラットリストになっていました。開発者にとっては問題ありませんでしたが、他のすべての人にとっては混乱を招くものでした。セクション化により、関連するオプションがグループ化され、ダイアログがより現代的なアプリ設定のように感じられます。


アセンブリ + プラグイン名の変更 (F-idなし - パッケージングに関する注意)

内容: コンパイルされたDLLは mb_UPnP_yaiol.dll と命名され、プラグインは自身を「MusicBee UPnP (yaiol)」と報告します。元の mb_Upnp.dll とは異なります。

理由: ユーザーはyaiolを元のプラグインと並行してインストールし、動作を並べて比較できます。


バッジシステム - ランタイム状態の表示 (F40の背後にあるメカニズム)

内容: 重要なランタイム条件を、設定ダイアログで目に見える色付きのバッジとして表示するための汎用UIパターン。現在のインスタンス:

  • ⚠ 最大接続数 (N04) - MusicBeeの起動以降、最大接続数制限に少なくとも1回達した場合に発生します。スティッキーセッションフラグ Plugin.MaxConnectionsHit。空きスロットがない場合に WaitOnSendBarrier 内で設定されます。
  • ⚠ 再起動が必要です - 保存された設定を有効にするためにMusicBeeの再起動が必要な場合に発生します。スティッキーセッションフラグ Plugin.RestartRequired。新しい永続化された値がランタイムスナップショット (Plugin.activeMaxConnectionsPlugin.activeServerPortPlugin.activeIpAddress) と異なる場合に、ダイアログの保存ハンドラーで設定されます。再起動が必要な設定は、HTTPサーバーのバインドパラメーターやInitialiseで一度構築されるSemaphoreSlimなど、真にホットリロードできないものに限定されます。

理由: プラグインのログファイルは、技術ユーザーがデバッグするのには適していますが、「デバイスの音が変だ」とか「再生が遅い」といった問題に直面している非技術ユーザーは、診断 → ログを表示 を開くことはありません。バッジは、ユーザーが何かが起こったことを知る必要があるケースを捉え、次にプラグインを開いたときにそれを表示します。何も読まずに発見できます。

将来の再利用性:

  • プロファイル不一致が検出されました (デバイスのユーザーエージェントがどのプロファイルとも一致せず、汎用にフォールバックしました)。
  • NextURIバックオフがトリガーされました (F13 - 不安定なデバイスでセッションのギャップレスが無効になりました)。
  • ライブラリスキャンが失敗した / 部分的。
  • セッション中にレンダラー接続が失われました。
  • 「一度発生した、ユーザーは知るべき」が「他の1000行の中にサイレントにログに記録された」よりも優れているその他の条件。

実装規則:

  • バッジラベルはダイアログレベル (どのパネル内でもない) に配置されるため、ユーザーがどのセクションにいるかに関係なく表示されます。
  • 保存/キャンセルボタンの近くの下段に配置されます (現在: y=410、水平に積み重ねられています)。
  • 各バッジには、条件が発生したときにTrueになり、MusicBeeの再起動時にのみリセットされる、Plugin 内の対応するスティッキーセッションフラグがあります。
  • リソース: <Condition>Badge (ラベルテキスト、⚠をプレフィックス) + <Condition>BadgeTip (原因と解決策を説明するツールチップ)。
  • 「保存された設定に再起動が必要」バッジの場合、Plugin.Initialise() でランタイムスナップショットを取得し、ダイアログの保存ハンドラーで Settings.SaveSettings() の後に Settings.* と比較します。

N27 - キャンセルでパス/テンプレートの編集を破棄

内容: 設定ダイアログ内でパスとテンプレートに対して行われた編集は、ユーザーがキャンセルをクリックすると破棄されるようになりました。以前は静かに適用されたままでした。また、セッション中に削除された予約済みテンプレートは再作成されます。(テンプレートは、編集されるとライブで保存されます。パス]タブには別の保存ボタンはありません。)

理由: キャンセルはキャンセルを意味するべきです。以前は、パス/テンプレートの変更を試して元に戻したユーザーは、変更がすでにコミットされており、手動でやり直す以外に元に戻す方法がないことに気づきました。


N28 - フィールドピッカーメニュー内のリテラルアンパサンド

内容: 名前に「&」を含むフィールド (例: 「Mood & Context」) は、フィールドピッカーメニューでアンパサンドをAlt-ニーモニックプレフィックスとして飲み込むのではなく、リテラルにレンダリングするようになりました。

理由: アンパサンドを含むフィールド名が正しく表示されず (文字が消え、次の文字がアクセラレーターになった)、メニューエントリを認識しにくくしていました。


N29 - 安定した、翻訳されない設定ウィンドウタイトル

内容: 設定ウィンドウのタイトルはブランド文字列「MusicBee UPnP Plugin」に固定され、インターフェース言語によって変化しなくなりました。言語ごとのDialogTitle文字列はすべてのロケールバンドルから削除されました。

理由: 言語ごとに文言が変わるウィンドウタイトルは、メリットのない翻訳可能な表面でした。タイトルはブランドマークです。それを固定することで、どこでも安定して一貫性が保たれます。

ローカライズ

元のプラグインは英語のみです。このフォークは完全にローカライズ可能であり、すべてのユーザー向け文字列はリソースバンドルを介して流れ、プラグインはMusicBee自身のUI言語を自動検出します。

N30 - 多言語UI (翻訳は保留中)

内容: ローカライズ機構は完成し、出荷されています。Localisation.vbMusicBee3Settings.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, ソース)

バリアントポリシー (ワークスペースロケールルールによる): PTZH は、語彙/スクリプトが実際に異なるため (pt-BR/pt-PTzh-CN/zh-TW)、個別のバンドルに分割されます。EN は単一のバンドルです。MusicBeeの「English(US)」 (en-US) は、.NETのカルチャチェーンを介して en にフォールバックするため、個別のUSバンドルは生成されません。ESとFRも同様に単一ロケールです。

理由: UPnPプラグインの設定 (「生のPCMを使用しない」、「リトルエンディアンPCMを強制する」、「ポートフォールバック警告」) は、母国語でも十分に難解です。英語を強制するのではなく、MusicBee自身のUI言語に従うことは、非英語ユーザーが設定できるツールとできないツールの違いです。どちらのアップストリームもこれを試みませんでした。


N31 - ヘルプリンクが完全なインターフェース言語で開く

内容: プラグインからヘルプリンクを開くと、基本言語に折りたたまれるのではなく、ユーザーの完全なインターフェース言語 (例: pt-BRzh-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 - プロファイルごとの「ネイティブストリームを強制」オプション (デフォルトON)

内容: チェックを入れると、プラグインは元のファイルバイトを、トランスコーディング、DSP、ReplayGain処理を適用せずにデバイスに送信します。ユーザーが選択した生のファイルが、バイト単位で (HTTPフレーミングを除く) 送信されます。

理由: フォーラムの証言によると、これは再生品質において最大の改善点です。高音質を求めるユーザーは高価なレンダラーを購入し、ビットパーフェクトな出力を明示的に求めています。DSPに触れることはその目的を損ないます。ほとんどの最新デバイスはユーザーが提供するあらゆるコーデックを処理でき、ReplayGain/EQはオプトインであるべきなので、デフォルトはONです。これはプロファイルごとであるため、古いXboxにはトランスコーディングを維持しつつ、高音質DACにはネイティブストリームを送信できます。


F04 - プロファイルごとの「トランスコーディングを強制」

内容: ネイティブコーデックのサポートに関係なく、このデバイスへのすべてのストリームをトランスコーダー経由で強制するプロファイルごとのオーバーライド。F03 (ForceNativeStream) の逆です。F03とは相互排他的です。どちらかがオンに切り替えられると、UIは自動的にもう一方のチェックを外します。

理由: グローバルな単一のトグルでは、プロファイルごとのForceNativeStream (F03) と矛盾します。実際のケース: デバイスAはビットパーフェクトなネイティブストリームを必要とするハイファイDACです。デバイスBはFLACで詰まる古いAVレシーバーです。グローバルなトグルでは、ユーザーは選択しなければならず、他のデバイスを犠牲にします。プロファイルごとであれば、各デバイスは正しい答えを得られます。

実装:

  • StreamingProfile.ForceTranscoding As Boolean = False
  • 永続化スキーマがv9に更新されました。v9より前のファイルは、レガシーグローバル値を一度ロードし、それをすべてのプロファイルにコピーして、アップグレード後も古い動作を維持します。
  • UI: 診断パネルから削除され、デバイスプロファイルセクションのForceNativeStreamの横に追加されました。双方向の相互排他ハンドラー (それぞれの CheckedChanged は、無限ループを避けるために、切り替える前にもう一方を購読解除します)。
  • 決定箇所: Settings.ForceTranscodingstreamingProfile.ForceTranscoding (in WriteAudioFileDIDL)。

F05 - プロファイルごとの「リトルエンディアンPCMを強制」

内容: PCMストリーム (L16/L24 MIMEタイプ) は仕様上ビッグエンディアンです。一部のデバイスは誤ってリトルエンディアンを期待し、適切なビッグエンディアンデータが渡されるとホワイトノイズを再生します。F05はプロファイルごとにバイトオーダーを切り替えます。

理由: これがないと、特定のデバイスは静的な音を出力します。症状は劇的で、PCMエンコーディングの知識がなければ原因は不明です。このトグルは、ユーザーに推測と確認による修正を提供します。


F06 - プロファイルごとの「Raw PCMを使用しない」

内容: デバイスがRaw PCMをサポートすると主張する場合、プラグインはそれを使用します。一部のデバイスは嘘をつきます。SOAPハンドシェイクは受け入れますが、実際のRaw PCMデータを破損させ、WAVEコンテナにラップされたPCMは適切に処理します。F06は、デバイスが何をアドバタイズするかに関係なく、PCM-over-Waveを強制します。

理由: 特定のマランツモデル、特にRaw PCMをアドバタイズするがWAVEのみが機能するモデルです。これがないと、Raw PCMストリームは歪んで出力され、エラーメッセージも表示されません。


F07 - プロファイルごとの「コンテンツ長」

内容: HTTP Content-Length ヘッダーに送信する値。4つのオプション:

  • デフォルト - 既知の場合は実際のバイト数、不明な場合は省略。
  • なし - ヘッダーを送信しない (チャンクエンコーディングのみ)。
  • PCMのみ - Raw 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をうまく処理できるがソースコーデックをデコードできないデバイス (例: MusicBeeのWMAライブラリをFLACに変換して受け取るEversolo) の場合、MP3/AACがオーディオデータを破棄するのに対し、これはロスレス品質を維持します。ロスレストランスコードオプションなしでは対処できなかったN02 (5.1ダウンミックス制御) のブロックを解除します。

実装: Encoder.StartEncodeSelect Case Codec ブロックに1行追加。FLACがMP3/AAC/Oggに加わり、コマンドライン駆動ブランチに入ります。UIドロップダウンには6番目のオプションとして「FLAC」が追加されます。SettingsDialog のロード/保存マッピングは FileCodec.FlacSelectedIndex = 5 を認識するように拡張されます。Mime、DLNAタイプ、およびエンコード機能は、以前の作業 (F21、F26) から ItemManager.GetMimes / GetDlnaType / GetEncodeFeature で既に配線されていました。


ギャップレス (SetNextAVTransportURI)

F10 - SetNextAVTransportURI / NextURIコア

内容: 真のギャップレス再生。デバイスがUPnPサービス記述で SetNextAVTransportURI のサポートをアドバタイズする場合、プラグインは現在のトラックが終了する前に次のトラックをデバイスにプリキューします。デバイスはトラック間に聞こえるギャップなしで内部的に移行します。これはCDプレーヤーで聞くものと同じです。これは「連続ストリーム」ハック (すべてを1つの長いストリームに連結し、トラックごとのメタデータを失う) ではありません。

理由: ティア2の主要機能です。連続したライブパフォーマンスとして録音されたアルバム (ライブレコード、クラシックの楽章、DJセット) は、トラック間に0.5秒の無音があると間違った音に聞こえます。これを適切に解決することは主要機能であり、このフォークで実現されました。

注記: キューに入れられたオーディオは、プラグインのHTTPサーバーを介して streamHandle=0 (ライブラリフェッチモード) を使用して提供されます。これは、MusicBeeのオーディオエンジンがキューに入れられたトラックのループに含まれないことを意味します。トレードオフ: ReplayGain/DSP/EQ効果は次のトラックには適用されません。「ネイティブストリームを強制」がオンの場合 (デフォルト) は許容されます。


F11 - プロファイルごとの「NextURIサポートを無効にする」

内容: デバイスが SetNextAVTransportURI をアドバタイズしている場合でも、このチェックボックスはプラグインにそのアドバタイズを無視させ、1トラックずつの再生にフォールバックさせます。

理由: 一部のデバイスはNextURIをアドバタイズしますが、バグのある実装 (クラッシュ、半遷移、ハング) を持っています。壊れたデバイスをそれぞれリバースエンジニアリングするのではなく、ユーザーは「ここでオフにするだけ」のトグルを得られます。


F12 - 再生リストでのNextURIライフサイクル

内容: MusicBeeが NowPlayingListChanged を発生させると、プラグインはギャップレス遷移のためにキューに入れるべきものを再評価します。MusicBeeに NowPlayingList_GetNextIndex(1) + NowPlayingList_GetListFileUrl を介して新しい「次」のトラックを要求し、デバイスで現在キューに入れられているもの (新しい nextPlaySourceUrl フィールドで追跡) と比較し、変更された場合は再キューします (または、MusicBeeが次のトラックがないと言った場合はキューをクリアします)。

理由: F12がないと、ユーザーがキューに入れられたトラックを削除/並べ替えたときに、デバイスは古いNextURIを再生し続けました。これは、すべてのリスト変更が異なる処理を必要とするため、歴史的にいくつかの反復を要しました。我々NowPlayingList_GetNextIndex (シャッフルとリピートオールラップアラウンドをすでに尊重している) を信頼することで簡素化し、すべてのバリアントが同じ比較を通過するようにしました。

実装:

  • 新しい nextPlaySourceUrl フィールドは、キューに入れられたもののMusicBeeライブラリURLを保存します (ハンドルサフィックス付きのストリーミングURLはライブラリパスと比較できません)。
  • MediaRendererDevice に新しい Public Sub RefreshQueuedNextUri()。3つの結果: NextURIがキューに入れられていない → 何もしない。キューに入れられたものが新しい「次」と一致する → 何もしない。キューに入れられたものが異なる → 新しいURLで QueueNext を呼び出す (またはクリアするために QueueNext("") を呼び出す - これはF08 DoNotClearNextUriを尊重します)。
  • Plugin.ReceiveNotificationNotificationType.NowPlayingListChanged の下で配線されています。

F13 - NextURI失敗時のバックオフ

内容: 同じデバイスで SetNextAVTransportURI が4回連続で失敗した後、プラグインはそのデバイスのギャップレス再生をMusicBeeが再起動するまで無効にします。

理由: デバイスがNextURIで本当に壊れている場合 (断続的なSOAPエラー、ネットワークグリッチ)、プラグインはそうでなければすべてのトラックで再試行し続けます。F13はノイズを停止し、サイレントに1トラックずつの再生にフォールバックします。


F14 - リピートモード + NextURI統合

内容: F14は、OnAvTransportStatusCheck のF15遷移検出器で処理される2つのケースに分かれます。

  • リピートオール: 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/スクロブリングが同期からずれてしまいます。

実装: 各ステータスタイマーティックで GetPositionInfo.TrackURI をポーリングします。報告されたURIがNextURI経由でキューに入れたものと一致する場合、MusicBeeで Player_PlayNextTrack を呼び出し、suppressNextSoapCall を設定して、結果として生じる PlayToDeviceSetAVTransportURI を再送信しないようにします (これによりギャップレス再生が中断されます)。

理由: F15がないと、デバイスは次のトラックを再生しますが、MusicBeeのUIはまだ前のトラックにあると表示します。これは混乱を招き、スクロブリングや再生回数の追跡を妨げます。トラック遷移検出は、各レンダラーブランドがURI変更を報告するタイミングに独自の癖があるため (TRANSITIONINGを最初に報告するもの、新しいURIでPLAYINGに直接スキップするもの、その間に短いSTOPPEDがあるもの)、レンダラーごとに長い反復が必要です。

注記: 最初のカットはBubbleUPnPレンダラーで動作します。デバイスごとのエッジケースはB6に残ります。


F16 - ギャップレス遷移時のポップ音の修正

内容: ポップ音は、キューに入れられたトラックのソース形式 (サンプルレート / チャンネル / コーデック) が現在再生中のトラックと異なり、デバイスのDACが遷移時に再ロックを強制されるときに発生します。F16は、形式が異なるたびにキューイング時に発生する NextUri:FormatChange 診断を追加し、両側を命名します。これにより、ポップ音を聞いたユーザーは相関関係を把握できます。

この診断は、軽減策も示しています。デバイスプロファイルでForceTranscodingにチェックを入れます。これにより、すべてのトラックが単一のトランスコードコーデック/サンプルレート/ビット深度に均一化され、ソース形式の違いが完全に排除されます。

実際のトランスコード一致修正のために延期された理由: 構造的な修正 (キューに入れられたトラックを再生中のトラックの形式に一致するようにトランスコードする) には、プラグインのHTTPサーバーURLスキームの変更が必要です。現在、/encode/{id}0.{ext} はキューに入れられたファイルをネイティブで提供します。将来のF16 v2では、形式ごとの /encode/{id}0_{rate}_{depth}.{ext} ルートを追加し、エンコーダーを介してそれらを配線します。これは、ForceTranscodingで十分でない場合に実際のデバイスがポップ音を示す場合に、より大きなアーキテクチャ変更を行う価値があります。

現在の実装:

  • lastSourceUrl フィールドは、現在再生中のソースURLを追跡します。
  • QueueNext は、現在およびキューに入れられたトラックの両方について FilePropertyType.SampleRate/Channels/Kind を読み取り、不一致の場合に NextUri:FormatChange をログに記録します。

F17 - シーク後のプログレスバー再同期

内容: Seek() 関数は、シークSOAPが成功した後、すでに GetPlayPositionInformation() を呼び出しており、「まったく再同期しない」ケースを修正していました。F17は、UPnPの1秒 RelTime 量子化によって引き起こされる最大1秒の残りのずれを解消します。デバイスが報告する位置がユーザーが要求したターゲットの1秒以内に丸められる場合、プラグインはデバイスの切り捨てではなく、ユーザーのサブ秒精度の値を信頼するようになりました。デバイスが劇的に異なるもの (>1秒ずれている) を報告した場合にのみ、その値を使用します (シークが要求された場所とは異なる場所に到達した場合、例: 一部のコーデックでのキーフレームへのスナップ)。

理由: これがないと、デバイスの「2:30」という報告に対して2:30.500にシークすると、プログレスバーは実際より約500ms遅れて表示されていました。F17の後、バーは一般的なトラック内スクラブケースでのユーザーの意図と一致し、キーフレームへのスナップという例外的なケースではデバイスの報告を尊重します。


F18 - 連続ストリーム / NextURIインターロック

内容: 2つのインターロックが現在導入されています。

  1. ランタイム: Settings.ContinuousOutput がオンの場合、QueueNext は早期に False を返します。連続ストリームは独自のギャップレスメカニズム (1つの長い連結ストリーム) であり、その上に SetNextAVTransportURI を送信すると、各トラックが個別のURIであるか、連続フローの一部であるかについてデバイスが混乱します。
  2. UI: ユーザーがグローバルな連続ストリームチェックボックスをオンにすると、現在表示されているプロファイルの forceNativeStream が自動的にオフになります。連続ストリームは常にトランスコードされるため、強制ネイティブは組み合わせると意味がありません。

理由: ユーザーが2つの競合するギャップレスメカニズムを同時に有効にするのを防ぎます。F18がないと、デバイスは連続ストリームURIと、その後の各トラックのNextURIの両方を受け取り、レンダラーに応じて未定義の動作が発生します。


F19 - 空のNextURIエラーは無視される

内容: 空のURLで SetNextAVTransportURI が呼び出された場合 (例: リストの最後のトラック)、一部のデバイスはSOAPエラーを返します。F19はこれらをサイレントに飲み込みます。ログには記録されますが、エラーとして伝播されません。

理由: 「次のトラックがない」という状態は正常であり、エラーではありません。これを致命的として扱うと、ログが汚染され、(一部のフローでは) 再試行の嵐がトリガーされます。


MIMEタイプとDLNAメタデータ

F20 - MP3 mime → audio/mpeg

内容: 標準に準拠したMP3 MIMEタイプは audio/mpeg であり、audio/mp3 ではありません。後者はほとんどのデバイスが許容する一般的な誤称ですが、より厳格なレンダラーはそれを拒否します。

理由: 標準に従うより厳格なデバイスでの再生をサイレントに修正します。yaiolコードベースはすでにこれを正しく処理していました。変更は不要です。


F21 - MIMEタイプの順序: 非 x- バリアントが最初

内容: デバイスが audio/flacaudio/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倍ずれていました。現在は1000ではなく8で割っています。

理由: デバイスでのビットレート表示が間違っていました。ほとんどのレンダラーでは見た目の問題ですが、一部のレンダラーは値からストリームバッファを割り当て、実際よりも約125倍小さく見えるストリームで途切れていました。非連続のソースファイルパスはすでにこれを正しく処理していました ((bitrate_kbps * 1000) \ 8 = バイト/秒)。連続ストリームパスのみが間違っていました。


F28 - メタデータ時間形式の修正 (Marantz)

内容: DIDLの res@durationH:MM:SS (例: 0:03:42) としてフォーマットされていました。UPnP DIDL仕様では、フォーマットを H+:MM:SS[.F+] と定義しており、厳密には小数秒はオプションですが推奨されています。一部のマランツデバイスは、裸の形式を無効とみなし、持続時間表示を空白のままにします。現在は H:MM:SS.fff (例: 0:03:42.000) としてフォーマットされています。

理由: ブランド固有の表示問題であり、小数秒を含むISOスタイルの準拠フォーマットで修正され、他のデバイスには影響しません。DIDL出力サイト (ソースファイルパス + WriteAudioFileDIDL のエンコードストリームパス) の両方に適用されました。

同じパスでのボーナス修正: pv:addedTimepv:lastPlayedTime は、DateTimeフォーマット文字列で HH (24時間時計) の代わりに hh (12時間時計) を使用していました。13:00から23:59の間に追加または再生されたトラックは、フィールドを表示するデバイスで間違った時間 (例: 17:42 → "05:42") でレンダリングされていました。現在は HH を使用しています。


F29 - エンコードされたMP3シークサポート (CBR)

内容: トランスコードされたMP3ストリームは、DLNA.ORG_OP=10 (バイトのみ) の代わりに DLNA.ORG_OP=11 (バイトと時間の両方のシーク) をアドバタイズするようになりました。以前はトランスコードされたMP3での時間シークを拒否していたデバイスは、プログレスバー/シークUIを通常通り操作できるようになりました。

理由: MusicBeeのトランスコーダーはHighQualityプリセットで定ビットレートMP3を生成するため、バイト ↔ 時間マッピングは線形です。デバイスは、エンコーダー側のサポートなしに、時間シーク要求をHTTP Rangeバイトシークに変換できます。OP=11をアドバタイズすることで、デバイス上のそのUIが利用可能になります。F29がないと、トランスコードされたMP3内でシークするユーザーは、シークがサイレントに無視されるか、トラックの開始にドロップされていました。

実装: ItemManager.vbGetEncodeFeature を再構築し、インライン If を読みやすい If/ElseIf/Else チェーンに分割しました。MP3は明示的に OP=11 を取得します。他の非PCMコーデックは OP=10 を維持します。AAC/FLACなどについては変更なし。これらはMusicBeeが保証しないCBR性のコーデック固有の検証が必要になります。


F30 - .mpeg ファイル拡張子の処理

内容: .mpeg (およびさらに珍しい .mpe) 拡張子を持つファイルは、GetCodecFileCodec.Mp3 として認識されるようになりました。F30以前は FileCodec.Unknown を返し、ライブラリからサイレントに拒否されたり、トランスコードソースとして使用できませんでした。

理由: 古いMPEG-1 Layer 3アーカイブでは、.mp3 の代わりに .mpeg が使用されることがありました (仕様では両方許可されています)。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は、 outright refusal の代わりに、小さな「最善の推測と試行」をトレードオフします。


F33 - プログレスバー同期の改善

内容: ポーリング間の位置は、単一のアンカー (currentPlayStartTicks) からすでにウォールクロックで外挿されているため、プログレスバーはサブ秒レートでスムーズに更新されます。残りのジッターの原因は、新しく開始されたトラックの初期アンカーでした。以前のコードは、ステータスタイマーが最初に状態が再生中になったことを認識した時点で position=0 と仮定していましたが、その時点までにデバイスは100〜500ms (1ポーリング間隔) 再生していた可能性があります。MusicBeeのプログレスバーは0から始まり、現実が追いつくと前方にジャンプしていました。

F33の修正: 新しいトラックで初めて再生中に遷移するとき (currentPlayStartTimeEstimated=True)、GetPlayPositionInformation() を呼び出してデバイスの実際の現在位置を取得し、それをアンカーとして使用します。UPnPは1秒分解能しか報告しないため、アンカーはまだ量子化されていますが、0と仮定するよりもはるかに真実に近いです。

理由: 特にトラック変更直後の、よりスムーズで正確なプログレス表示。1秒のUPnP報告分解能自体を回避する方法はありません。それは仕様です。


F34 - トラック変更後のプログレスバーのジッター

内容: 新しいトラックに対して PlayToDevice が呼び出されると、プラグインは以前、SOAP-Playと新しい再生状態を検出する最初のステータスタイマーポーリングの間の約100msのウィンドウで、currentPlayPositionMscurrentPlayStartTicks を前のトラックの値のままにしていました。MusicBeeのプログレスバーは、前のトラックの終わりを briefly 表示し、その後0に戻り、上昇していました。F34は、SOAP作業の前に、トラック変更が発生していることを知った瞬間に、両方を PlayToDevice エントリでゼロにします。

理由: 高速スキップのユースケース (手動での次への移動またはギャップレス遷移) での視覚的なグリッチ。これで、MusicBeeのPlayPositionMsの最初のクエリはPlay後にきれいに0を返し、その後F33のGetPlayPositionInformationが最初の状態変更ティックでデバイスの実際の位置にそれを洗練します。

実装: PlayToDevice の上部に4行、F33の遷移時間の正確なアンカーと対になっています。


F35 - 「トランスコーディングを強制」バグ

内容: トランスコーディングを強制しても、特定の組み合わせではトランスコーディングがスキップされる可能性がありました。F04のプロファイルごとの再構築後、2つの特定のギャップが解消されました。

  1. ForceNativeStreamとの優先順位。 両方がTrueの場合 (スキーマ移行または部分的な設定ファイルで発生する可能性があります)、ForceTranscodingが完全に優先されます (If streamingProfile.ForceTranscoding Then forceEncode = True ElseIf streamingProfile.ForceNativeStream Then forceEncode = False)。UIの相互排他はユーザーが両方をチェックするのを防ぎますが、ランタイムガードはディスクから矛盾した状態でロードされた状態を処理します。
  2. bypassTranscodeDecisionロジック。 以前: streamingProfile.ForceNativeStream AndAlso Not Settings.ForceTranscoding。現在: streamingProfile.ForceNativeStream AndAlso Not streamingProfile.ForceTranscoding - 同じ優先順位ルールですが、同じプロファイルごとのスコープです。

理由: 「強制」は強制を意味するべきです。ユーザーがデバイスに対してForceTranscodingを明示的に有効にした場合、他のフラグがどのように組み合わされても、プラグインはサイレントにネイティブストリーミングにフォールスルーしてはなりません。


F36 - レンダラークローズ例外

内容: Plugin.ReceiveNotification をトップレベルの Try/Catch でラップし、MusicBeeの通知ポンプに伝播させるのではなく、キャッチされない例外をログに記録するようにしました。

理由: MusicBeeからの通知 (PlayStateChanged、VolumeMuteChangedなど) は ControlPointManager にディスパッチされ、SOAPを介してレンダラーと通信します。個々の呼び出しサイトにはすでにSOAP呼び出しの周りに Try/Catch がありましたが、十分に奇妙なタイミングのケース (例: 同じ通知ハンドラー内の2つのSOAP呼び出しの間でレンダラーが停止する) はまだエスケープする可能性がありました。トップレベルのラッパーは最終的な安全ネットであり、ユーザーがMusicBeeから一般的な「TargetInvocationException」ポップアップを見ることはありません。

実装: 既存の本体を ReceiveNotificationInternal に名前変更し、Try { ReceiveNotificationInternal(...) } Catch { LogError(...) } を行う薄いラッパー ReceiveNotification を追加しました。ControlPointManager内の既存のメソッドごとのTry/Catchインフラストラクチャ (すべてのPostSoapRequest呼び出しの周り) はそのままです。F36はベルトとサスペンダーです。


F37 - 長いトラックのシークが誤った遷移をトリガーする

内容: 長いトラック内でシークすると、一部のレンダラーで一時的な停止→再生サイクルが発生する可能性があります。区別しないと、ProcessNewPlayState.Stopped はそれをトラックの自然な終了とみなし、Player_PlayNextTrack を呼び出して、ユーザーがスクラブしたいだけなのにMusicBeeを進めてしまいます。F37は Seek()lastUserInitiatedSeek をスタンプし、停止ハンドラーに5秒のガードを追加します (既存の lastUserInitiatedStop ウィンドウをミラーリング)。

理由: シーク中のサイレントな次のトラックへのスキップは、誰も原因を推測できないバグの1つです。ユーザーは「変だな、早送りしようとしたら次の曲が再生されている」と思います。修正は機械的です。すでに導入されているユーザー停止の区別と同じパターンです。


F38 - クラッシュしやすいコーデックに対するシーク処理の改善

内容: MP3シーク時のBubbleUPnPのクラッシュが典型的な症状でした。監査の結果、現在の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 around Sockets_Stream_File / Sockets_Encoder_Start) は、ハードコードされた4でした。F40では、これを一般設定ページでユーザーが設定できるようにし (デフォルト16、範囲1-256)、リクエストがスロットを待つ必要がある場合に MaxConnections ログ行を追加し、MusicBeeの起動以降に制限に少なくとも1回達した場合に、設定ダイアログの左下に赤い ⚠ 最大接続数バッジを表示します。

理由: デバイスが並行リクエストを発行する場合 (アートワークスキャン中のMarantz/Linnの一部、アクティブな再生と並行したBubbleUPnPのメタデータプローブ)、追加のリクエストはセマフォの背後でサイレントにブロックされていました。ユーザーは「デバイスが遅い」と感じても、目に見える原因はありませんでした。ログ行は技術的なデバッグには適していますが、非技術ユーザーはログを読みません。設定ダイアログの目に見えるバッジは、プラグインの環境設定を開く人なら誰でも、制限に達した状態を発見できるようにします。

実装:

  • MusicBeeUpnp.vbWaitOnSendBarrier(logTag) に待機を集中化しました。両方の呼び出しサイト (MediaServerDevice.GetFileEncoder.StartEncode) がそれを使用します。
  • Settings.MaxConnections は設定スキーマのv8で永続化されます。
  • Plugin.MaxConnectionsHit は、条件が発生したときに設定され、MusicBeeの再起動時にのみリセットされるスティッキーセッションフラグです。
  • SettingsDialog.maxConnectionsBadge は、Plugin.MaxConnectionsHit がTrueの場合にのみ表示される (16, 410) の赤い太字ラベルです。原因と解決策を説明するツールチップがあります。
  • セマフォはタイプロード時に一度初期化されるため、設定を変更するにはMusicBeeの再起動が必要です (フィールドラベルに記載)。

F41 - 「ReplayGain/DSPによるエンコード」をログに記録

内容: 「RGによるエンコード」/「DSPによるエンコード」という個別のログ行ではなく、F42からの単一の StreamDecision 行に、MB-DSP/EQMB-ReplayGainProfile-DSP/EQProfile-ReplayGain が累積された理由として含まれるようになりました。診断値は同じで、ノイズが少なくなります。

理由: ユーザーは、特定のトラックでトランスコーディングが発生しているすべての理由を1つのログ行で確認でき、散らばっていません。詳細についてはF42を参照してください。


F42 - 「レンダラーがソースコーデックをサポートしていません」をログに記録

内容: 再生デバイスごとのトラックごとに、「ネイティブ CODEC」または「トランスコード CODEC→CODEC reason=…」という StreamDecision ログ行が追加されました。理由フィールドには、トランスコーディングをトリガーしたすべての条件が累積されます: MB-DSP/EQMB-ReplayGainProfile-DSP/EQProfile-ReplayGainWebFileVirtualFileForceTranscoding(global)SampleRate<min/SampleRate>maxDownmixToStereoDeviceLacksCodec(X)BandwidthConstrained

理由: ユーザーは、ネイティブでストリーミングされると予想していたファイルで予期しないCPUスパイクが発生することに混乱していました。トラックごとの1つのログ行は、トランスコーディングを引き起こした条件を正確に伝えます。フィールドに DeviceLacksCodec(Flac) と表示された場合、ユーザーはデバイスのプロトコル情報が不完全であり、F32のフォールバックが機能することを望むかもしれないことをすぐに知ることができます。

実装: 決定チェーンを通じて段階的に構築される単一の累積文字列。最後に一度ログに記録されます。本番環境でのログノイズを避けるため、Settings.LogDebugInfo でゲートされています。


F43 - SetNextAVTransportログにソースURLを表示

内容: QueueNext ログエントリに、stream=<HTTPストリーミングURL> とともに source=<MusicBeeライブラリパス> が含まれるようになりました。成功パスと失敗パス (QueueNext:Failed) にも同じ変更が適用されました。

理由: キューに入れられたトラックの問題をデバッグする場合、ストリーミングURL (/encode/aabbccdd0.flac) はそれ自体では不透明であり、すべてのトラックで同じです。ソースURLは、MusicBeeがキューに入れようとしたファイルを正確に伝える、人間がgrep可能なライブラリパスです。


F44 - MIMEタイプエラーのロギングの改善

内容: Activate 中に2つの新しいログエントリが追加されました。

  • Activate:MimeUnverified - デバイスの GetProtocolInfo 応答内の不正なエントリごとに発生し、解析できなかったエントリの名前を付けます (これにより、ユーザーは「Marantzが特定のコーデックに対して http-get:*::* を返した。機能は未検証であり、F32のフォールバックが推測するだろう」などと確認できます)。
  • Activate:NoSinkInfo - デバイスが <Sink> 要素をまったく返さなかった場合に一度発生します。これは SupportedMimeTypes がNothingのままであり、IsCodecSupported が「すべてが機能すると仮定する」に劣化することを意味します。後で「デバイスがストリームを拒否した」エラーが表示された場合に役立つコンテキストです。

理由: F44以前は、これらのサイレントな機能フォールスルーにより、ユーザーはトラックが期待に反してトランスコードされたり、デバイスによって拒否されたりする理由を推測するしかありませんでした。これで、Activate: を一度grepするだけで、デバイスの機能情報が使用可能であったかどうかがわかります。


F45 - メタデータエラーのロギングの改善

内容: ContentDirectoryService.vbBrowse 例外ログは、以前のyaiol作業 (Alia Voxバグセッション) で ObjectID とスタックトレースで既に強化されていました。F45は、BrowseFlag (メタデータ vs 子)、Filter (クライアントが要求した属性)、sortCriteria、および partialResultLength (失敗するまでに生成されたDIDLのバイト数 - バッチ内の不良トラックがどこにあるかを示す) でさらに拡張します。

理由: DIDLの途中で何かがうまくいかない場合、部分長の値は、失敗がバッチの最初のトラックで発生したのか (partial=0)、途中で発生したのか (partial=N) を示します。バッチの startingIndex と組み合わせることで、問題のあるトラックインデックスを特定できます。FilterとBrowseFlagは、クライアントがどのような種類のブラウズを望んでいたかを説明します。同じIDの子ブラウズが成功するのに、メタデータのみのブラウズが失敗することがあります。


ネットワーク

F46 - 自動モードは実ネットワークのアダプターでのみ通知

内容: インターフェイスの自動モードでは、プラグインは以前、動作中のすべてのIPv4アダプターで自身を通知(SSDP)していました。VPNトンネル(NordLynx)や仮想スイッチ(Hyper-V / WSL / Docker)も動いているマシンでは、同じライブラリがそれらのアダプターでも通知されていたため、キャスト元のコントロールポイントはサーバーを2回、3回と発見し、ライブラリを重複コピーとして一覧表示していました。自動モードは、実際のIPv4デフォルトゲートウェイを持つアダプター(HasIPv4Gateway)のみを保持するようになりました - トンネルや仮想スイッチのアダプターにはこれがないため、通知リストから除外されます。ユーザーが固定したアドレスは引き続き最優先され(そのインターフェイスでのみ通知)、どのアダプターもゲートウェイを報告しない場合、セレクターはすべてのアダプターにフォールバックするため、通知されるアドレスのリストが空になることはなく、プラグインが見えなくなることはありません。

理由: 重複の原因は「VPNを使っていること」ではありません - LANアダプターとトンネル/仮想アダプターの両方で同時に通知することが原因で、1つのコントロールポイントが同じサーバーを2つのアドレスで見てしまいます。コンシューマー向けVPN(NordVPN/NordLynx)がトンネルに通すのはインターネット宛のトラフィックだけです。DLNAレンダラーはLAN上にあり、ローカルサブネットのトラフィックはトンネルを迂回するため、トンネルのアダプターがレンダラーに到達することはそもそもありません - これを除外すると幻のコピーが消えるだけで、動作する経路が失われることはありません。ゲートウェイの有無は、実際のLAN/Wi-Fiアダプターをトンネルや仮想スイッチから区別する、安価で信頼できるシグナルです。N05(このようなリンクで通知がどのように送られるかを修正 - ブロードキャストの代わりにマルチキャスト)を補完し、F46はそもそもどのアダプターで通知するかを決めます。

既知の制限: メッシュ/リモートアクセス型のVPN(Tailscale、ZeroTier、自宅へのWireGuard)で、レンダラーが本当にトンネルの向こう側にある場合、そのアダプターは通常デフォルトゲートウェイを持たないため、自動モードはそれも除外してしまいます。そのようなユーザーは代わりにVPNアドレスを固定してください。固定アドレスはゲートウェイフィルターより優先されます。