MusicBee UPnP Plugin ヘルプ

新機能

2.0.9 - 2026-08-23

更新通知があなたの言語でページを開くようになりました

内容: 更新通知内の「新機能」および「ダウンロード」リンクが、プラグインのページを英語ではなくMusicBee自身の言語で開くようになりました。

理由: これら2つのリンクは、ウェブサイトに送信する前に言語を英語、フランス語、スペイン語、ドイツ語のいずれかに絞り込んでいたため、翻訳が存在する場合でも、それ以外のすべてのユーザーには英語のページが表示されていました。現在はMusicBeeの言語をそのまま渡し、ウェブサイトに表示する言語を決定させるようになりました。これは、それらの隣にあるヘルプボタンが常に実行していたことです。

2.0.8 - 2026-08-22

このプラグインは、ネットワーク上で検出するアプリやデバイスに正しく自己紹介するようになり、デバイスプロファイルの設定が再び整列するようになりました。

デバイスに正しいメーカー、モデル、バージョンが表示されるようになりました

内容: 制御アプリ、電話、またはテレビがネットワーク上でMusicBeeを検出すると、このプラグインはyaiol製として表示され、プラグイン自身のサイトを指し、サーバー、プレーヤー、レンダラーの3つの役割すべてをカバーしていると説明され、実際にインストールされているバージョンが報告されるようになりました。

理由: すべてのUPnPデバイスは、誰がそれを作成し、それが何であるかをアナウンスし、制御アプリはそれをデバイスのIDとして表示します。このプラグインは、フォーク元の元のプラグインの詳細(別の作者の名前、自身のウェブサイトではなくMusicBeeのウェブサイト、最初のリリース以来「1.0」で固定されたモデル番号)をまだアナウンスしていました。お使いの電話からは、どのプラグインと通信しているのか、ましてやそのどのバージョンなのかを判断する方法がありませんでした。これらの詳細はプラグイン自体から取得されるようになったため、デバイスの横に表示されるバージョンは更新ごとに正しく保たれます。

デバイスプロファイルの設定が再び整列するようになりました

内容: デバイスプロファイルタブでは、ラベルとそのボックスが同じ左端を共有し、均等な間隔で配置され、サンプルレートの範囲が1行で表示されるようになりました。

理由: オプションがタブに追加されるにつれて、フィールドがずれてしまい、サンプルレート範囲の「to」ラベルがその横のボックスの上に重なって表示されていました。何が書かれているかを知っていれば読めますが、初めて見たときは戸惑うものでした。

2.0.7 - 2026-08-08

大きいトラックでもタイトルと再生位置スライダーが表示されるようになりました

内容: スマートフォンから送信された大きいトラック(長いFLAC、高解像度ファイル、DSDファイルなど)でも、小さいトラックと同様に、正しいタイトルが表示され、再生位置を移動できるようになりました。以前は、一部のトラックがネットワーク経由で再生され、タイトルがウェブアドレスに置き換わり、スライダーが機能しないことがありました。

理由: プラグインはローカルコピーが完了するまで待機しますが、以前はファイルのサイズに基づいて、待機する価値があるかどうかを事前に判断していました。これはネットワークの速度を推測するものでしたが、プラグインにはそれを知る方法がありませんでした。例えば、65MBのトラックは大きすぎると判断され、その1秒後にダウンロードが完了するということがありました。これは、プラグインがすでに諦めていた待機時間内に快適に完了していたことになります。現在は、ダウンロードを単純に監視するようになりました。ダウンロードが完了するまで、プラグインはどれだけ時間がかかっても待機し続けます。転送が実際に停止した場合にのみ諦めますが、その停止を以前の固定遅延よりも早く検知できるようになりました。

2.0.6 - 2026-08-03

電話から送信されたアルバムは、トラック間にギャップなく再生されるようになり、インターネットラジオは異常に長い曲として扱われる代わりにラジオとして認識されるようになりました。

アルバムがギャップなく再生される

内容: スマートフォンからアルバム全体を送信すると、MusicBeeは一時停止することなく次のトラックへと再生を続けます。これにより、ライブ録音、DJセット、連続したクラシック作品が途切れることなく楽しめます。

理由: 標準では、制御アプリが「次に何が来るか」を伝えることができ、これによりシームレスな接続が可能になります。以前はこの指示が全く受け入れられなかったため、アプリは次のトラックをどこに置くべきか分からず、現在のトラックであるかのようにアナウンスしていました。これが前回のリリースで修正されたアルバムの問題の原因でした。現在は適切に受け入れられ、現在のトラックが再生されている間に次のトラックがフェッチされ、MusicBee自身のプレーヤーが境界を越えます。

インターネットラジオがラジオとして認識される

内容: MusicBeeに送信されたライブステーションはストリームとして再生され、ダウンロードされることはありません。

理由: 放送をダウンロードすることは意味がありません。終わりがなく、スキップする箇所もありません。しかし、以前のプラグインには音楽ファイルと区別する方法がなかったため、ダウンロードを開始し、ダウンロードが固定サイズを超えると停止していました。制御アプリはどちらを送信しているかを明示しており、それが直接読み取られるようになりました。設定も推測も不要です。

長い高解像度トラックのコピーが保持される

内容: DSDファイルや長い24ビット録音も、他のトラックと同様に移動できるようになりました。

理由: これらは上記のサイズ制限に引っかかっていました。20分の高解像度楽章や10分のDSDトラックはどちらも制限を超えていたため、コピーが破棄され、最もスクラブする価値のある素材で位置スライダーが機能しなくなっていました。ラジオが適切に識別されるようになったため、サイズ制限は不要になりました。

2.0.5 - 2026-08-03

電話からMusicBeeを操作する際の2つの修正点:音量が両端で同じ意味を持つようになり、アルバム全体をキャストしても最初のトラック以降も動作し続けるようになりました。

電話の音量がMusicBeeの音量と一致するようになりました

内容: 電話の音量を最大にするとMusicBeeでも最大になり、MusicBee自体の設定も電話で正しく読み取れるようになりました。

理由: プラグインが制御アプリに最大音量を伝えていなかったため、各アプリが推測する必要がありました。そのうちの1つが69に設定したため、電話の100%はMusicBeeの69%にしか到達せず、MusicBeeの100%は電話で144%として表示され、電話の音量ボタンでは最大に到達できませんでした。レンダラーが範囲を明確に伝えるようになったため、両端で同じスケールについて話すようになりました。

アルバムをキャストすると、タイトルと再生位置スライダーが保持されるようになりました

内容: 電話から送信されたアルバムのすべてのトラックで、最初のトラックだけでなく、適切なタイトルが表示され、移動できるようになりました。

理由: 制御アプリは現在のトラックの数分の1秒後に次のトラックをアナウンスし、そのアナウンスが再生しようとしているトラックのために取得されていたコピーをキャンセルしていました。そのため、ほとんどのトラックは静かにネットワーク経由での再生に戻り、タイトルとジャンプ機能の両方が失われていました。複数のトラックのコピーが並行して保持されるようになったため、アナウンスが使用中のコピーをキャンセルすることはなくなりました。

2.0.4 - 2026-08-02

電話や別のサーバーからMusicBeeに送信された音楽は、本物のトラックのように動作するようになりました。トラック内を移動でき、適切なタイトルがすぐに表示されます。さらに、1つの結合されたデバイスとして表示されるハイファイストリーマーの修正も行われました。

他の場所から送信されたトラック内を移動する

内容: 電話、NAS、または別のメディアサーバーから送信されたトラックで、位置スライダーをドラッグできるようになりました。これを可能にするために、MusicBeeは再生を開始すると同時にトラックのコピーを一時フォルダーにダウンロードし、そのコピーを再生します。ホームネットワークでは約1秒かかり、別のトラックを送信するとすぐにコピーは削除され、MusicBeeが次に起動したときに残ったファイルはすべてクリアされます。

理由: MusicBeeはネットワーク経由で聴いているものを開始および停止できますが、その中を移動することはできません。そのため、スライダーはジャンプしたように見え、説明なしにすぐに元の位置に戻っていました。自分のディスク上の通常のファイルを再生することで、この制限を回避するのではなく、完全に解消します。

最初の音から正しいタイトルと長さ

内容: ファイルに通常のファイル拡張子を付けないアプリによって送信されたトラックは、長いウェブアドレスとして表示される代わりに、開始するとすぐに実際のタイトルと長さが表示されるようになりました。

理由: MusicBeeはファイル拡張子からトラックを識別し、そのタグを見つけます。一部のプレーヤーは拡張子のないアドレスを渡します。ローカルコピーは常に正しい拡張子を持っているため、送信元のアプリが何と呼んでいてもトラックは認識されます。

実行できないジャンプは、その旨を伝えるようになりました

内容: レンダラーが要求されたポイントに実際に移動できない場合、制御アプリに通知され、それが報告されます。

理由: 以前は「完了」と答えていたため、スライダーは1秒後に何の説明もなく元に戻っていました。正直な拒否は、沈黙の拒否よりも対処しやすいです。

結合されたハイファイストリーマーが正しく読み取られるようになりました

内容: MusicBeeが、MarantzやDenonのストリーマーのように、プレーヤーがメディアサーバーと並んでメーカーのラッパー内に収まっている、1つの結合されたユニットとして表示されるデバイスに再生する場合、プラグインはメディアサーバーの詳細ではなく、プレーヤー自身の詳細を読み取るようになりました。

理由: 以前は、デバイスの誤った半分にどのオーディオ形式を処理できるかを尋ねており、使用可能な回答を得られず、確認せずに処理を続けていました。これは、形式処理が最も正確である必要があるハードウェアでまさに問題でした。デバイスのモデル記述も、デバイスプロファイルと照合する際に考慮されるようになりました。以前は誤った場所から読み取られ、破棄されていました。

2.0.3 - 2026-08-02

再生先としての役割が成長しました: MusicBeeは、すでにライブラリにない音楽(携帯電話、NAS、別のサーバー上のファイルなど)を送信できるようになりました。これまでは、MusicBeeがすでに所有しているトラックのみでした。

自分のライブラリだけでなく、携帯電話から送信された音楽を再生する

内容: SymfoniumやBubbleUPnPなどのコントロールアプリを使用してMusicBeeに音楽を送信する場合、トラックはMusicBee自身のライブラリからである必要がなくなりました。携帯電話自体、NAS、または別のメディアサーバーに保存されているファイルが再生されるようになりました。タイトルと長さは送信元のアプリから取得されるため、MusicBeeがそのファイルを一度も見たことがなくても、トラックは適切に表示されます。

理由: 再生先としての役割は、携帯電話からこのPCのライブラリを閲覧し、曲をタップするケースのために構築されました。トラックはすでにPC上にあったため、MusicBeeは単に自身のファイルを再生しました。他の場所から到着したものはすべて静かに破棄され、携帯電話から良いスピーカーに音楽をプッシュするという、同様に自然なケースではこの機能は役に立ちませんでした。

再生できないトラックはそう表示される

内容: レンダラーが送信されたものを本当に再生できない場合、その旨を送信元のアプリに報告するようになりました。

理由: 以前は、何に対しても「受け取った」と応答していたため、コントロールアプリはそのまま再生を開始しました。実際には何もロードされていないため、MusicBeeは以前から残っていたトラックを再開しました。そして、そのファイルがなくなっていた場合、そのソースが見つからないと不平を言いました。エラーは無関係なトラックを指し、実際の問題とはかけ離れた場所を指していました。

一時停止中に新しいトラックを送信すると、そのトラックが再生される

内容: MusicBeeが一時停止中に、コントロールアプリが新しいものを送信すると、新しいトラックが開始されます。

理由: 再開がロードよりも優先されたため、一時停止していたトラックは中断したところから再開され、選択したばかりのトラックは何も言わずに破棄されました。

2.0.2 - 2026-08-01

検索がテーマです。アーティストによる検索が機能するようになり、適切な結果が返され、大規模なライブラリでも高速になりました。さらに、コントロールアプリのシャッフルを目的とする新しい方法と、どこにもつながらなかった3つのボタンの修正が含まれています。

アーティストによる検索が実際に機能するようになりました

内容: コントロールアプリからアーティストを検索すると、そのアーティストの音楽が返されるようになりました。検索はトラックのArtistとアルバムのAlbum Artistの両方に一致するため、コンピレーションは演奏者の名前を入力しても、アルバムが登録されている名前を入力しても見つかります。

理由: 以前は、アーティスト検索は「この種類のものをすべてください」と誤解されていました。入力したアーティストは破棄され、ライブラリ全体が返されたため、数百のトラックに一致するはずの検索が数万のトラックを返していました。2つのアーティストフィールドのいずれか一方のみに一致させると、結果の半分が静かに失われるため、両方がチェックされます。

検索結果ページが適切に表示されるようになりました

内容: 長い検索結果リストをスクロールすると、その中を移動できるようになりました。スクロールする各ページは、取得するページです。

理由: 以前は、サーバーはすべてのリクエストに対して最初の少数の結果を返し、完全な一致数を報告していました。そのため、さらにスクロールするアプリは同じアイテムを受け取り続け、最後まで到達することはありませんでした。

大規模なライブラリでの検索が大幅に高速化されました

内容: 検索は、結果セット全体に対して単一のクエリを実行し、表示しているページのタグのみを読み込むようになりました。

理由: 以前は、各ページでライブラリに対してクエリが再実行され、一致するすべてのタグ(数千)が読み込まれて、数十個が表示されていました。大規模なコレクションでは、スクロールするたびに一時停止が発生していました。ライブラリが更新されると結果も破棄されるため、編集されたものが古い状態で提供されることはありません。

シャッフルをフィルターに合わせる

内容: ライブラリオプションタブに新しいRandom plays from設定が追加されました。これをAll Musicのままにしておくと、コントロールアプリのRandom Tracks / Random Albumsフォルダーは以前と同じように動作します。MusicBeeフィルターのいずれかを選択すると、すべてのランダムリクエストがそのフィルターから取得されます。非表示のフィルターも提供されます。

理由: シャッフルフォルダーは「すべて」の一部を要求しますが、それは何を意味するのかの手がかりがない唯一のリクエストであるため、常にライブラリ全体(話し言葉なども含む)から取得していました。これは、「すべて」が何を意味するかを伝える場所です。デバイス上のフィルターからシャッフルフォルダーを開くと、そのフィルターがシャッフルされます。閲覧中に選択したものが設定よりも優先されます。

ヘルプ、GitHub、および更新チェックが実際のページに到達するようになりました

内容: 設定ダイアログのHelpおよびGitHubボタン、および新しいバージョンの自動チェックが、指定されたページを開くようになりました。

理由: これら3つはすべて、プラグイン名の短縮形から構築されており、その短縮形ではページが存在しなかったため、それぞれがサイレントに失敗していました。ボタンは何もしないように見え、新しいバージョンがどれだけ長くリリースされていても、更新チェックは何も報告しませんでした。

2.0.1 - 2026-07-26

再生とブラウジングに関する修正のラウンドで、ポッドキャストと、再生やシャッフルを制御するコントローラーアプリ(BubbleUPnPなど)に焦点を当てています。

ポッドキャストが一時停止なしで再生を開始する

内容: ダウンロードされたポッドキャストのエピソードが、ディスク上のファイルから直接メディアメタデータに持ち込まれ、その実際の長さとファイルサイズを事前に表示するようになりました。

理由: 長さが明示されていないと、BubbleUPnPのようなコントローラーは、エピソードの長さを把握するためだけに、再生ボタンを押すたびにオーディオストリーム全体を再スキャンしていました。そのため、再生が始まるまでに目立った一時停止がありました。長さが通知されるようになったため、スムーズに開始されます。

アーティストでブラウズするとポッドキャストが表示される

内容: ポッドキャストの番組名がアーティストとして登録されるようになりました。これは、すでにアルバムフィールドに入力されているのとまったく同じように、アーティストおよびアルバムアーティストフィールド(およびそのソートバリアント)に反映されます。

理由: アーティストフィールドでグループ化するブラウジングパスは、以前はすべてのポッドキャストのアーティストが空白で、空のレベルで行き止まりになっていました。番組自体をアーティストとして扱うこと(アルバムとして扱うことと一貫しています)により、これらのパスは何も表示されない代わりにエピソードに到達するようになりました。

再起動後すぐに「最近再生した項目」とキャストが機能する

内容: ポッドキャスト、オーディオブック、受信トレイ、またはラジオトラックを直接再生すること(BubbleUPnPの「最近再生した項目」リストやキャストターゲットが行うように、最初にブラウズせずに)が失敗しなくなりました。プラグインは、IDで要求されたときにオンデマンドでトラックをロードするようになりました。

理由: これらのリストは、MusicBeeが再起動した直後、何もブラウズされていない状態でトラックを要求するため、プラグインはIDを見たことがなく、「不正なID」と応答していました。最初の直接要求で関連するソースを強制的にロードし、トラックを見つけるようになりました。

「ランダムトラック」と「ランダムアルバム」が結果を返す

内容: ライブラリ全体からランダムなスライスを要求するBubbleUPnPの「ランダムトラック」と「ランダムアルバム」のシャッフルフォルダが、結果で満たされるようになりました。

理由: これらは一致するタイトルがない検索であり、プラグインは以前は空の内部リストから応答していたため、常に何も表示されませんでした。これらは、ブラウジングの残りの部分が使用するのと同じオンデマンドクエリパスから、正しくページングされて提供されるようになりました。

空のタグを持つアルバムがトラックをリスト表示する

内容: 空の値でグループ化されたアルバム(たとえば、年を持たない受信トレイのトラックなど)を開くと、そのトラックが表示されるようになりました。

理由: アルバムのトラックを収集する一致は、「このタグは空である」を「一致なし」として扱っていたため、空白のフィールドから形成されたアルバムは何もブラウズされませんでした。空のグループ値が、それを共有するトラックと正しく一致するようになりました。


2.0.0 - 2026-07-22

これは、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 - 遅延(オンデマンド)ブラウズツリー

内容: 元のプラグインは、MusicBeeの起動時にHTTPポートを開く前に、すべてのブラウズツリー全体を構築していました。つまり、すべてのトラックを列挙し、ファイルごとに完全な 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. ユーザー通知。 フォールバックが発生した場合(保存されたポートが使用中のポートではない場合)、ローカライズされた MessageBoxWarnPortInUse)が、実際にサービスを提供しているポートと、デバイスがそれでもそれを見つけることをユーザーに通知します。これは、プラグインがヘッドレスで実行され、ダイアログ内のメッセージは既に問題を疑っている人にしか見られないためです。
  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 (Sort Album Artist) を読み取り、ブラウズビューでアーティストをグループ化/ソートするために使用するようになりました。

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


N08 - 複数値のAlbumArtist処理

内容: アルバムの 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 に切り詰める)を含むタグを持つトラックは、不良なトラックがページ分割されたバッチに入ると、Action Failed でブラウズ応答全体が失敗していました。

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


N13 - ページ分割されたブラウズ全体でラジオリストが決定論的になる

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

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


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

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

理由: ここで修正されました。アルバムクラスクエリは、個別のアルバム(AlbumArtist+Albumでグループ化)を列挙し、それぞれをカバーアート付きの適切な 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の実際のアートワークキャッシュ(デスクトップUIが読み込む %LocalAppData%\MusicBee\InternalCache\Subscriptions\<name>.jpg)をチェックせず、HTTPレイヤーのアンエスケープ+小文字化がフィードURLルートキーを最後のパスセグメントまで破壊していました。このフォークは、MBのInternalCacheからアートワークを解決し、HTTPレイヤーで無傷のまま残るURLセーフなスラッグ(PodcastSlug / podcastSubIdBySlug)を介してルックアップをルーティングします。

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


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

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

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


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

内容: ルートの単一のブラウズパスは、完全なショートパスではなく、そのグループ化フィールド(例: 「ジャンル」)でラベル付けされ、マージされた最初のフィールドグループの名前付け方法と一致します。

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


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

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

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


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

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

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


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

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

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


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

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

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


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

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

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


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

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

理由: これら2つのフィールドはMusicBeeで異なる意味を持ち、それらをまとめることでその区別が失われていました。両方を表示することで、ユーザーは意図に応じて、ある年のすべてのリリースをまとめる(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など、真にホットリロードできないものに限定されます。

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

将来の再利用性:

  • プロファイル不一致が検出されました(デバイスのユーザーエージェントがどのプロファイルとも一致せず、Genericにフォールバックしました)。
  • 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 をアドバタイズするデバイスの制御

内容: プラグインは、MusicBeeがレンダラーを駆動できるかどうかを判断するために、レンダラーのUPnPサービス記述をプローブします。元のプラグインは urn:schemas-upnp-org:device:MediaRenderer:1 のみに一致していました。最新のデバイスは :2 または :3 をアドバタイズします。F02は一致範囲を広げます。

理由: これがないと、最近のSonos / WiiM / Eversoloユニットは、同じプロトコルを話すにもかかわらず、MusicBeeの「再生先」デバイスリストにターゲットとして表示されません。単一の文字列プレフィックス一致の修正により、現代のデバイス世代全体が利用可能になります。


F03 - プロファイルごとの「ネイティブストリームを強制」オプション(デフォルトON)

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

理由: フォーラムの証言によると、これは再生品質において最大の改善点です。ハイファイユーザーは高価なレンダラーを購入する際、ビットパーフェクトな出力を明確に求めており、DSPによる変更は目的を損ないます。ほとんどの最新デバイスはユーザーが提供するあらゆるコーデックを処理できるため、デフォルトはONです。ReplayGain/EQはオプトインであるべきです。これはプロファイルごとであるため、古い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は、プラグインがNextURIをクリアするのを防ぎます。

理由: これがないと、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ドロップダウンに「FLAC」が6番目のオプションとして追加されます。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でのトラック終了時の自動進み動作のみに影響します。Next Track は常に前進します)、リピートワンの意味と矛盾します。

再生回数の注意点: リピートワンでは、再生回数の増加は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 がオンの場合、QueueNextFalse を早期に返します。連続ストリームは独自のギャップレスメカニズム(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タイプフラグ(LPCMWAVEMP3のようなプロファイル識別子)は、デバイスが受信するものと一致する必要があります。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の2つの出力箇所(ソースファイルパス + 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 は、ソースURLの Kind プロパティを Library_GetFileProperty 経由でプローブし、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〜500ms(1回のポーリング間隔)再生していた可能性があります。MusicBeeのプログレスバーは0から始まり、現実が追いついたときに前方にジャンプしていました。

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

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


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

内容: 新しいトラックに対して PlayToDevice が呼び出されたとき、プラグインはSOAP-Playと新しい再生状態を検出する最初のステータスタイマーポーリングの間の約100msのウィンドウで、currentPlayPositionMscurrentPlayStartTicks を前のトラックの値のままにしていました。MusicBeeのプログレスバーは、前のトラックの終わりを一時的に表示し、その後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 - 最大接続数の増加 + 警告ログ

内容: プラグインの同時ストリーム制限(Sockets_Stream_File / Sockets_Encoder_Start 周りの SemaphoreSlim)は、ハードコードされた4でした。F40では、これを一般設定ページでユーザーが設定可能にし(デフォルト16、範囲1-256)、リクエストがスロットを待つ必要がある場合に MaxConnections ログ行を追加し、MusicBeeの起動以降に制限に少なくとも1回達した場合、設定ダイアログの左下に赤い⚠最大接続数バッジを表示します。

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

実装:

  • MusicBeeUpnp.vbWaitOnSendBarrier(logTag) で待機を集中化しました。両方の呼び出しサイト(MediaServerDevice.GetFileEncoder.StartEncode)がこれを使用します。
  • Settings.MaxConnections は設定スキーマのv8で永続化されます。
  • Plugin.MaxConnectionsHitWaitOnSendBarrier 内で設定されるスティッキーセッションフラグです。MusicBeeの再起動時にのみリセットされます。
  • SettingsDialog.maxConnectionsBadge(16, 410) にある赤色の太字ラベルで、Plugin.MaxConnectionsHit がTrueの場合にのみ表示されます。原因と解決策を説明するツールチップがあります。
  • セマフォはタイプロード時に一度初期化されるため、設定を変更するには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 streaming URL> とともに source=<MusicBee library path> が含まれるようになりました。成功パスと失敗パス(QueueNext:Failed)にも同じ変更が適用されます。

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


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

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

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

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


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

内容: ContentDirectoryService.vbBrowse 例外ログは、以前のyaiol作業(Alia Voxバグセッション)で既に ObjectID とスタックトレースで強化されていました。F45はさらに BrowseFlag(メタデータ vs 子)、Filter(クライアントが要求した属性)、sortCriteriapartialResultLength(失敗するまでに生成された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-to-home)で、レンダラーが実際にトンネルを介して存在する場合、通常はデフォルトゲートウェイのないアダプターを提示するため、自動モードでもそれが削除されます。これらのユーザーは代わりにVPNアドレスをピン留めし、それがゲートウェイフィルターよりも優先されます。

目次