MusicBee UPnP Plugin Pomoc

Co nowego

2.0.2 - 2026-07-20

  • Szablonu nie można już usunąć, gdy jakikolwiek węzeł go śledzi — przycisk usuwania pozostaje po prostu wyłączony, więc węzeł nigdy nie może zostać osierocony. Każdy wiersz szablonu pokazuje teraz na żywo liczbę (n) jego obserwujących, co na pierwszy rzut oka wyjaśnia wyłączone usuwanie; usunięcie nieużywanego szablonu również pozostaje teraz, zamiast cicho pojawiać się ponownie przy następnym ładowaniu.
  • Nowy przycisk lejek obok drzewa Widok pokazuje dokładnie, które węzły śledzą szablon: filtruje drzewo tylko do obserwujących wybranego szablonu, ponownie filtruje, gdy wybierasz inne szablony, i przywraca pełne drzewo po wyłączeniu.
  • Węzły Radio i Podcasty są teraz trwale sparowane z szablonem swojej kategorii — zmień kształt szablonu na karcie Ścieżki, a węzeł sam podąży; nie ma nic do zastosowania, więc przycisk Zastosuj jest dla nich wyłączony. Lista szablonów odzwierciedla to dwoma pasmami, Standardowe (gdzie tworzone są wszystkie nowe szablony) i Zarezerwowane (Radio + Podcasty).

2.0.1 - 2026-07-20

  • Szablony ścieżek są teraz połączone na żywo z węzłami, które ich używają. Zastosowanie szablonu sprawia, że węzeł go śledzi: edytuj szablon później, a każdy węzeł, który go śledzi, natychmiast się przekształci — bez konieczności szukania go i ponownego stosowania węzeł po węźle. Drzewo widoku pokazuje, który szablon śledzi każdy węzeł tuż po jego nazwie, zmiana nazwy pojawia się tam natychmiast, a usunięcie szablonu najpierw informuje, ile węzłów go śledzi (zachowują one swój bieżący układ i po prostu przestają cokolwiek śledzić).
  • Zastosowanie szablonu do ukrytego węzła również sprawia, że staje się on ponownie widoczny — zastosowanie to gest „pokaż mi to, ukształtowane w ten sposób”, podczas gdy ukrywanie pozostaje przy polu wyboru Widoczne. Zastąpiony przez to zarezerwowany szablon „Ukryty” został usunięty.
  • Drzewo widoku nie traci już Twojego miejsca: zaznaczenia, rozwinięte foldery i pozycja przewijania przetrwają zastosowanie szablonów i inne odświeżenia.

2.0.0 - 2026-06-16

To jest pierwsze publiczne wydanie otwartego forka yaiol wtyczki MusicBee UPnP. Jest ono przedstawione w dwóch częściach: wszystko, co jest nowe w tym forku, a następnie poprawki i ulepszenia wprowadzone do oryginalnej wtyczki. Każdy element zachowuje formę Co / Dlaczego z wewnętrznego katalogu funkcji projektu, dzięki czemu uzasadnienie każdej zmiany jest widoczne na stronie, a nie tylko sama zmiana.

Nowości w tym forku

MediaRenderer - odtwarzanie do MusicBee

N01 - MusicBee jako renderer do odtwarzania

Co: normalnie ta wtyczka działa w jedną stronę: telefon lub inne urządzenie przegląda bibliotekę MusicBee i odtwarza muzykę na sobie. Ta funkcja dodaje przeciwny kierunek - pozwala MusicBee być odtwarzaczem. Z aplikacji kontrolera na telefonie (takiej jak BubbleUPnP) możesz wybrać swój komputerowy MusicBee jako urządzenie odtwarzające, a następnie sterować nim z ręki: odtwarzać, pauzować, zatrzymywać, przewijać do przodu lub do tyłu, przeskakiwać do punktu w utworze oraz zmieniać głośność lub wyciszać.

Dlaczego: zamienia to Twój telefon w pilota do muzyki już znajdującej się na Twoim komputerze. Usiądź na kanapie, przeglądaj swoją bibliotekę na telefonie, dotknij utworu, a on zostanie odtworzony przez głośniki podłączone do Twojego komputera - z pełną kontrolą z miejsca, w którym siedzisz. Oryginalna wtyczka nigdy nie dostarczyła tej funkcji w działającej formie.

Włączanie: jest domyślnie wyłączone, ponieważ włączenie jej pozwala każdemu urządzeniu w Twojej sieci domowej rozpocząć odtwarzanie na Twoim komputerze. Włączasz ją za pomocą pola wyboru na karcie Ogólne w oknie ustawień. Trzy role wtyczki mają tam swoje własne pola wyboru - udostępnij moją bibliotekę (Serwer), pozwól innym odtwarzać do mnie (Renderer) i odtwarzaj do innych urządzeń (Punkt kontrolny) - a okno dialogowe pokazuje tylko te karty ustawień, które są faktycznie potrzebne dla włączonych ról, więc nigdy nie masz do czynienia z opcjami, które Cię nie dotyczą.

Rozróżnianie maszyn: możesz nadać rendererowi dowolną nazwę (domyślnie "MusicBee (yaiol)"). Ta nazwa pojawia się na liście celów odtwarzania w Twoim telefonie, więc gdy więcej niż jeden komputer uruchamia MusicBee, możesz rozróżnić, który jest który. Zmiana nazwy wchodzi w życie natychmiast, bez restartu.

Najlepszy możliwy dźwięk podczas odtwarzania do siebie: gdy przeglądasz własną bibliotekę MusicBee z telefonu i wysyłasz utwór z powrotem do tego samego MusicBee, wtyczka rozpoznaje, że jest proszona o odtworzenie jednego z własnych plików i po prostu odtwarza go bezpośrednio z dysku. Rezultat jest dokładny i natychmiastowy - bit-perfect, z zastosowanym własnym korektorem i wyrównywaniem głośności MusicBee - zamiast bezcelowego wypychania dźwięku do sieci i z powrotem do siebie.

Uruchamianie prywatnie: trzy role działają niezależnie, więc możesz włączyć renderer, pozostawiając wyłączone udostępnianie biblioteki. W tej konfiguracji "tylko renderer" Twoja biblioteka pozostaje całkowicie ukryta przed siecią - ogłaszany jest tylko cel odtwarzania - a MusicBee nigdy nie zaoferuje odtwarzania do siebie.


Zachowanie odtwarzania

N02 - FLAC 5.1 nie jest automatycznie downmixowany

Co: ograniczenie liczby kanałów w MediaServerDevice.GetEncodedFile było If StereoOnly OrElse Not isPcmData Then channelCount = 2. Klauzula Not isPcmData cicho downmixowała każdą transkodowaną (FLAC, MP3, AAC, Ogg) do stereo, niezależnie od liczby kanałów źródłowych, co uniemożliwiało korzystanie z rendererów obsługujących 5.1, gdy źródłem był FLAC 5.1. Teraz druga klauzula wyklucza FLAC: Not isPcmData AndAlso encoder.Codec <> FileCodec.Flac. FLAC 5.1 przechodzi; MP3/AAC/Ogg nadal są wymuszane na stereo, ponieważ kodery MusicBee z wiersza poleceń dla tych formatów oczekują 2-kanałowego wejścia.

Dlaczego: cały sens transkodowania źródła FLAC 5.1 do wyjścia FLAC polega na zachowaniu miksu wielokanałowego. Cichy downmix sprawił, że opcja transkodowania FLAC była bezużyteczna do słuchania dźwięku przestrzennego. Z N02 działa to prawidłowo.


Architektura

Zmiana strukturalna, która sprawia, że fork jest wykonalny dla dużej biblioteki - brak w oryginalnej wtyczce.

N03 - Leniwe (na żądanie) drzewo przeglądania

Co: oryginalna wtyczka budowała całe drzewo przeglądania przy uruchomieniu MusicBee - wyliczała każdy utwór, pełne Library_GetFileTags dla każdego pliku, składała całą hierarchię kontenerów - zanim otworzyła port HTTP. W przypadku prawdziwej biblioteki (ponad 50 tys. utworów, 5400 odcinków podcastów, setki stacji) to minuty zimnego startu, a drzewo pozostaje w pamięci RAM na zawsze, w tym gałęzie, których żaden klient nigdy nie otwiera. Ten fork niczego nie buduje z góry: korzeń eksponuje jeden symbol zastępczy z prefiksem L: na punkt końcowy (L:music, L:podcast, L:filter:…); każdy poziom jest obliczany tylko wtedy, gdy klient go przegląda (LazyBrowseEnsureLazyEndpointInMemory → pamięci podręczne na poziomie), a powiadomienia o zmianie biblioteki czyszczą pamięci podręczne (SetLibraryDirty).

Dlaczego: zimny start jest praktycznie natychmiastowy - port HTTP jest otwarty, zanim MusicBee zakończy inicjalizację wtyczki - a pamięć pozostaje proporcjonalna do tego, co zostało przeglądane, a nie do rozmiaru biblioteki. Kompromis: pierwsze przeglądanie punktu końcowego ponosi koszty ładowania; ponowne wejście jest buforowane do następnej zmiany biblioteki. To jest podstawa, od której zależy wszystko inne. Pełne uwagi: FIXES.md.


Sieć i niezawodność

Wzmocnienie ścieżki wiązania serwera HTTP. Oryginalna wtyczka umiera cicho, gdy jej port jest niedostępny.

N04 - Samonaprawiające się wiązanie portu HTTP

Co: serwer HTTP wtyczki nie umiera już, gdy jego skonfigurowany port jest niedostępny. Trzy powiązane zmiany:

  1. Automatyczne awaryjne przełączanie w przypadku błędu wiązania. HttpServer.Start próbuje skonfigurowanego portu, a w przypadku SocketException skanuje w górę do 20 portów w poszukiwaniu pierwszego wolnego. Rzeczywisty związany port jest zapisywany w nowym Plugin.boundServerPort, a wszystko, co reklamuje serwer - adresy URL SSDP LOCATION (odpowiedź NOTIFY + M-SEARCH), adres URL urządzenia (PrimaryHostUrl), przekierowanie portów routera i autofiltry SSDP/punktu kontrolnego - teraz odczytuje boundServerPort zamiast Settings.ServerPort. Klienci UPnP odkrywają rzeczywisty port za pośrednictwem SSDP, więc przeniesiony port jest przezroczysty dla rendererów.
  2. Powiadomienie użytkownika. Gdy nastąpi awaryjne przełączenie (zapisany port nie jest używany), zlokalizowany MessageBox (WarnPortInUse) informuje użytkownika, który port faktycznie obsługuje i że urządzenia nadal go znajdą - ponieważ wtyczka działa bez interfejsu graficznego, a wiadomość w oknie dialogowym byłaby widoczna tylko dla kogoś, kto już podejrzewał problem.
  3. Odzyskiwanie po restarcie. RestartServer (ścieżka restartu po zapisaniu ustawień) używał do dereferencji Plugin.controller / Plugin.server na ślepo. Jeśli początkowe Initialise zgłosiło wyjątek przed ich utworzeniem (dokładnie to, co spowodowało nieudane wiązanie), następne zapisanie ustawień spowodowało NullReferenceException - pozostawiając wtyczkę w połowie martwą. Teraz tworzy je i uruchamia, gdy Nothing, więc zapisanie działającego portu ożywia wtyczkę bez pełnego restartu MusicBee.

Dlaczego: wyzwalaczem był prawdziwy incydent użytkownika. Stary domyślny port 49382 znajduje się w zakresie dynamicznym systemu Windows (49152-65535), gdzie Hyper-V/WSL2/Docker/WinNAT rezerwują duże bloki, które zmieniają się przy każdym uruchomieniu - więc wiązanie nie powiodło się z WSAEACCES ("dostęp zabroniony") na maszynie, na której działało przez miesiące. Zmiana domyślnego portu na wolny kolidowała następnie z Serviio (oddzielnym serwerem DLNA już na nowym porcie), kończąc się niepowodzeniem z WSAEADDRINUSE. Każda awaria była połykana w Initialise, pozostawiając wtyczkę cicho martwą, a następnie NRE-ing przy następnym zapisie ustawień. Po N04 kolizja portów samonaprawia się - serwer działa na następnym wolnym porcie, użytkownik jest informowany, a klienci ponownie go odkrywają - zamiast wyłączać całą wtyczkę.

Implementacja:

  • Domyślny port przeniesiony 493829779 (poniżej zakresu dynamicznego, więc Windows nigdy go nie rezerwuje automatycznie; nie jest to znany domyślny serwer multimediów) we wszystkich trzech deklaracjach ServerPort + awaryjne przełączanie w przypadku błędu analizy ustawień.
  • Plugin.boundServerPort (nowe wspólne pole) przechowuje aktywny port nasłuchu; activeServerPort pozostaje skonfigurowaną migawką, aby logika odznaki "Wymagany restart" nie uruchamiała się fałszywie w przypadku awaryjnego przełączania.
  • HttpServer.PortScanRange = 20; skanowanie zatrzymuje się przy pierwszym udanym TcpListener.Start() i zgłasza ostatni wyjątek tylko wtedy, gdy wszystkie próby zakończą się niepowodzeniem.
  • Nowy klucz zasobu EN WarnPortInUse (tłumaczenia następują po przejściu lokalizacji w czasie publikacji).

N05 - Ogłoszenia SSDP przez grupę multicast (VPN / punkt-punkt)

Co: ogłoszenia SSDP są wysyłane do grupy multicast UPnP (239.255.255.250) zamiast na adres rozgłoszeniowy IP. Niegroźny błąd "cannot access a disposed object" (nie można uzyskać dostępu do usuniętego obiektu) logowany, gdy odpowiedź na wyszukiwanie SSDP ściga się z restartem serwera, jest również pomijany.

Dlaczego: na kartach sieciowych punkt-punkt / VPN rozgłaszanie IP nie ma zastosowania - stare wysyłanie rozgłoszeniowe kończyło się błędem "invalid argument" (nieprawidłowy argument), a ogłoszenia były pomijane, więc wtyczka była niewidoczna dla klientów na tych łączach. Ogłaszanie do właściwej grupy multicast naprawia wykrywanie dokładnie na tych adapterach.

Nawigacja po bibliotece

Te funkcje zostały dostarczone w tym forku i nie ma ich w oryginalnej wtyczce. Powstały one z faktycznego przeglądania własnych danych wyjściowych wtyczki z prawdziwych klientów UPnP.

N06 - Ekspozycja biblioteki oparta na filtrach

Co: karty filtrów MusicBee (pliki .xautopf w folderze MusicBee użytkownika) stają się kontenerami głównymi UPnP w bibliotece wtyczki. Utwory każdego filtra są następnie przeglądane w hierarchii AlbumArtistSort → Album → Tracks.

Dlaczego: użytkownicy z wyselekcjonowanymi filtrami MusicBee (np. "Utwory 5-gwiazdkowe", "Ostatnio dodane", "Klasyka → Barok") oczekują, że znajdą je podczas przeglądania wtyczki z klienta UPnP. Oryginalna wtyczka eksponowała tylko surowe drzewo biblioteki.


N07 - Okablowanie pola SortAlbumArtist

Co: wtyczka odczytuje teraz MetaDataType 165 (Sort Album Artist) z MusicBee i używa go do grupowania/sortowania artystów w widokach przeglądania.

Dlaczego: przeglądarki hi-fi i audiofile używają nazw artystów do sortowania (np. "Beethoven, Ludwig van" zamiast "Ludwig van Beethoven") do organizowania bibliotek. Standardowe oczekiwanie dla poważnych słuchaczy. Brak w obu źródłach.


N08 - Obsługa wielu wartości AlbumArtist

Co: gdy pole AlbumArtist albumu zawiera wielu artystów oddzielonych "; " (np. "yaiol; Ars Ricercata"), utwór pojawia się teraz pod każdym artystą w widokach przeglądania, a nie pod jednym, połączonym artystą.

Dlaczego: albumy kolaboracyjne i kompilacje muszą być widoczne pod każdym współpracownikiem. Bez tego połowa ścieżek wyszukiwania albumu jest uszkodzona.


N09 - Grafika kontenera albumu (upnp:albumArtURI)

Co: węzły kontenerów albumów w odpowiedziach DIDL Browse zawierają teraz element upnp:albumArtURI wskazujący na okładkę albumu.

Dlaczego: bez tego każdy album w widoku przeglądania klienta UPnP pokazuje ogólną ikonę zamiast okładki albumu. Wskazówka wizualna do nawigacji; oczekiwana przez każdą nowoczesną przeglądarkę hi-fi.


N10 - Kolejność utworów w albumach filtrów

Co: utwory w albumie eksponowanym przez filtr są teraz sortowane według numeru płyty, a następnie numeru utworu.

Dlaczego: standardowa kolejność albumów. Bez jawnego sortowania utwory były zwracane w dowolnej kolejności, w jakiej filtr je zwrócił - zazwyczaj wyglądało to losowo.


N11 - Naprawa drzewa folderów playlist

Co: funkcja LoadLibraryPlaylists (pierwotnie autorstwa Stevena Mayalla, ok. 2014 r.) nie schodziła do nowo utworzonych folderów playlist. Pierwsza playlista w każdym folderze, plus wszelkie podfoldery, kończyły jako osierocone na poziomie głównym.

Dlaczego: obecne w oryginalnej wtyczce przez jedenaście lat. Widoczne w ciągu 30 sekund od otwarcia BubbleUPnP i kliknięcia Playlisty. Naprawione w yaiol poprzez prawidłowe rekurencyjne wchodzenie do nowo utworzonych folderów podczas budowania drzewa.


N12 - Sanityzacja nielegalnych znaków kontrolnych XML

Co: każdy utwór z tagiem zawierającym znak kontrolny C0 (np. 0x19 z błędnego kodowania - UTF-8 → Latin-1 → z powrotem obcinając 0x99 do 0x19) powodował, że cała odpowiedź Browse kończyła się błędem Action Failed po wejściu błędnego utworu do paginowanej partii.

Dlaczego: XML 1.0 zabrania większości znaków kontrolnych C0, a XmlWriter zgłasza wyjątek, gdy zostanie poproszony o zapisanie któregokolwiek z nich. Obecne w oryginalnej wtyczce. Naprawione poprzez usunięcie nieprawidłowych znaków w każdym punkcie wyjścia Library_GetFileTags za pomocą XmlConvert.IsXmlChar.


N13 - Lista radiowa deterministyczna w paginowanym przeglądaniu

Co: przeglądanie kontenera Radio wpadało do ogólnej gałęzi listy plików, która wywoływała files.Sort(AlbumFileComparer) przy każdym wywołaniu. Wpisy radiowe mają puste tagi Album/Disc/Track, więc każde porównanie zwracało 0 - List(Of T).Sort jest niestabilne, produkując inną kolejność przy każdym wywołaniu. Punkty kontrolne UPnP paginują (BubbleUPnP pobiera 0..15, a następnie 16..koniec); między dwoma wywołaniami lista się przetasowywała, więc niektóre stacje pojawiały się na obu stronach (duplikaty), a niektóre na żadnej (brakujące) - wyglądając losowo przy każdym odświeżeniu.

Dlaczego: obecne w oryginalnej wtyczce (jej autor nigdy nie przegląda radia przez UPnP). Naprawione tutaj z dedykowaną gałęzią ContainerCategory.Radio w Browse, bez sortowania na wywołanie; radioFiles jest sortowane raz przy ładowaniu według tytułu (stabilne). Paginowane przeglądanie widzi teraz deterministyczną kolejność; strona 1 i strona 2 są rozłączne.


N14 - Wyszukiwanie UPnP klasy albumu zwraca kontenery albumów

Co: wyszukiwanie UPnP dla zapytań klasy albumu (upnp:class = "object.container.album.musicAlbum", np. "Losowe albumy" w BubbleUPnP) zwracało pełną listę utworów zamiast kontenerów albumów, więc klient pokazywał zero albumów. Oryginalny handler analizował tylko kryteria w nawiasach, a następnie wyrzucał wszystkie utwory niezależnie od żądanej klasy.

Dlaczego: naprawione tutaj - zapytania klasy albumu teraz wyliczają odrębne albumy (zgrupowane według AlbumArtist+Album) i emitują każdy jako właściwy kontener musicAlbum z okładką, adresowalny za pomocą wirtualnej przestrzeni ID Salb<idx>, dzięki czemu klient może zagłębić się w wynik i go odtworzyć.


N15 - Działające, świadome zakresu wyszukiwanie UPnP z kliknięciem

Co: oryginalna wtyczka nie reklamowała żadnych możliwości wyszukiwania (GetSearchCapabilities zwracało puste), więc klienci odmawiali nawet wysyłania wyszukiwania; a stary backend odczytywał z musicFiles, trwale pustego w erze leniwego drzewa. Ten fork reklamuje rzeczywiste właściwości do wyszukiwania, implementuje wyszukiwanie utworów według tytułu i albumów według tytułu w stosunku do leniwej biblioteki (HandleLazySearch), ogranicza zapytanie do bieżącej gałęzi klienta, gdy wysyłany jest rzeczywisty identyfikator kontenera (w przeciwnym razie podstawia L:music, aby wyszukiwania z górnego paska nie wciągały szumu podcastów/radia/audiobooków) i sprawia, że wyniki albumów są klikalne za pomocą syntetycznych identyfikatorów Ssrch_alb_*, które wczesna gałąź Browse mapuje z powrotem do utworów albumu. (Część wyników klasy albumu jako kontenerów to N14.)

Dlaczego: wyszukiwanie w BubbleUPnP zmieniło się z "Biblioteka nie obsługuje wyszukiwania" na zwracanie użytecznych, ograniczonych, odtwarzalnych wyników. Pełny projekt + odrzucone podejścia: SEARCH.md.


N16 - Unieważnienie pamięci podręcznej UPnP (SystemUpdateID)

Co: oryginał zwracał stałe SystemUpdateID=0 - kontrakt unieważniania pamięci podręcznej UPnP ContentDirectory - więc klienci zgodni ze specyfikacją (BubbleUPnP) traktowali bibliotekę jako nigdy się nie zmieniającą: nieaktualne wyniki przeglądania, miniatury 404 po zmianie schematu URL i taniec "restartuj MusicBee dwa razy, aby zobaczyć zmiany". Ten fork inicjuje SystemUpdateID z sekund epoki przy ładowaniu (więc każdy restart jest ściśle przed ostatnim) i zwiększa go przy każdej mutacji biblioteki i zmianie ustawień (SetLibraryDirty / ResetCacheBumpSystemUpdateId).

Dlaczego: klienci niezawodnie odbierają edycje, nowe pliki i zmiany ustawień przy następnym przeglądaniu. Znane ograniczenie: subskrybowani klienci nie są aktywnie ponownie wysyłani nowej wartości za pośrednictwem GENA (zaparkowane jako przyszłe prace); nadal widzą ją przy następnym przeglądaniu.


N17 - Grafika subskrypcji podcastów

Co: kafelki podcastów nie wyświetlały obrazów - każde żądanie /PodcastThumbnail/ zwracało 404. Dwa nakładające się błędy: łańcuch rozwiązywania nigdy nie sprawdzał rzeczywistej pamięci podręcznej grafik MusicBee (%LocalAppData%\MusicBee\InternalCache\Subscriptions\<name>.jpg, skąd ładuje się interfejs użytkownika pulpitu), a warstwa HTTP unescape+lowercase zniekształcała klucz trasy URL kanału do ostatniego segmentu ścieżki. Ten fork rozwiązuje grafikę z InternalCache MB i kieruje wyszukiwania za pomocą bezpiecznego dla URL slug, który przetrwa warstwę HTTP w nienaruszonym stanie (PodcastSlug / podcastSubIdBySlug).

Dlaczego: grafika subskrypcji jest teraz renderowana w widokach przeglądania (wszystkie 22 wcześniej zwracające 404 żądania są rozwiązywane).


N18 - Hierarchiczne (rozdzielone) przeglądanie tagów

Co: każde pole może być oznaczone jako hierarchiczne na karcie Opcje biblioteki i otrzymać jednoliterowy separator (selektor pola + pole separatora z dodawaniem/usuwaniem, zachowywane w ustawieniach wtyczki). Ustaw Grupowanie na / i wartość taką jak Jazz/Cool Jazz będzie przeglądana jako Jazz › Cool Jazz zamiast jednego płaskiego wpisu. Utwory otagowane dokładnie na gałęzi (tylko Jazz) otrzymują swój własny węzeł [Jazz], dzięki czemu nic nie jest ukryte, gałąź z jednym dzieckiem zwija się sama, a ; jest odrzucane jako separator, ponieważ jest to własny separator wielu wartości MusicBee.

Dlaczego: głębokie taksonomie tagów, które użytkownik już zakodował w jednym polu (drzewa gatunków, hierarchie nastrojów, "Klasyka/Barok/Koncert"), w końcu przeglądają się jako drzewo, które opisuje tag, zamiast płaskiej ściany ciągów znaków oddzielonych ukośnikami, które użytkownik musi czytać od początku do końca.


N19 - Pojedyncza ścieżka główna oznaczona polem grupowania

Co: pojedyncza ścieżka przeglądania w katalogu głównym jest oznaczona polem grupowania (np. "Gatunek"), a nie pełną krótką ścieżką, co odpowiada sposobowi nazywania połączonych grup pierwszego pola.

Dlaczego: drzewo przeglądania jest spójne - jedna zasada nazewnictwa, niezależnie od tego, czy wpis główny stoi sam, czy został połączony z rodzeństwem (N20) - zamiast pojedynczego wpisu głównego pokazującego szczegółową ścieżkę wewnętrzną, podczas gdy jego połączone sąsiedzi pokazują czystą nazwę pola.


N20 - Łączenie ścieżek przeglądania, które mają wspólne pierwsze pole

Co: dwie ścieżki przeglądania, które mają to samo pierwsze pole - "Gatunek / Sortuj artystę albumu" i "Gatunek / Ludzie podcastu" - zwijają się w jeden folder główny Gatunek, który najpierw wyświetla wartości gatunków, a następnie dzieli się na dwa widoki, zamiast dwóch prawie identycznych wpisów "Gatunek / ..." obok siebie w katalogu głównym.

Dlaczego: użytkownik z kilkoma powiązanymi widokami zagnieżdżonymi pod wspólnym polem widział katalog główny zaśmiecony prawie identycznymi wpisami najwyższego poziomu. Ich połączenie sprawia, że katalog główny jest czytelny i grupuje powiązane widoki tam, gdzie powinny być - pod ich wspólnym polem.


N21 - Ścieżki przeglądania według kategorii (Standardowe / Radio / Podcast)

Co: każda ścieżka przeglądania jest typowana według kategorii - Standardowe, Radio lub Podcast. Lista szablonów jest pogrupowana w te trzy sekcje, selektor pól każdego szablonu oferuje tylko pola, które dane tej kategorii mogą faktycznie dostarczyć, a szablon może być zastosowany tylko do pasujących węzłów w drzewie widoków (niekompatybilne węzły są wyszarzone i nie można ich zaznaczyć). Zarezerwowane szablony Radio i Podcasty nie mogą być usunięte, więc ich sekcja kategorii nigdy nie znika.

Dlaczego: bez typowania użytkownik mógłby zbudować układ, który cicho okazałby się pusty - stacja radiowa nie ma "albumu", odcinek podcastu nie ma "artysty albumu" - i odkryłby to tylko przeglądając martwy folder z klienta UPnP. Ograniczenie menu pól i celów zastosowania do rzeczywistych danych kategorii sprawia, że puste układy są niemożliwe do zbudowania.


N22 - Grupuj podcasty według roku publikacji

Co: data publikacji każdego odcinka podcastu jest odczytywana, więc ścieżka przeglądania podcastu z poziomem Roku grupuje odcinki według roku zamiast zwijać je pod jednym "Nieznanym".

Dlaczego: duże subskrypcje podcastów stają się nawigowalne według roku, jak reszta biblioteki, zamiast każdego odcinka lądującego w jednej nieoznaczonej datą stercie, ponieważ wtyczka nigdy nie patrzyła na datę publikacji poszczególnych odcinków.


N23 - Zwijanie poziomów grupowania z pojedynczym wynikiem

Co: poziom grupowania, który rozwiązuje się do pojedynczej wartości - poziom typu rekordu pokazujący tylko "LP" dla artysty, który tworzył tylko LP, lub poziom literowy z pojedynczą literą - jest automatycznie pomijany, przenosząc użytkownika bezpośrednio do jego zawartości.

Dlaczego: przeglądanie folderu, który zawiera dokładnie jeden folder, to czyste tarcie. Zwijanie poziomu z pojedynczym wyborem usuwa martwe kliknięcie bez zmiany tego, co użytkownik może osiągnąć.


N24 - Grupowanie/wyszukiwanie według roku w polu daty MusicBee

Co: warunek roku nie odpytuje już pełnego pola daty "Rok" MusicBee z samą czterocyfrową wartością, a twardo zakodowane aliasowanie pola roku zniknęło, więc każde pole grupowania jest teraz rozwiązywane ogólnie z definicji ścieżki.

Dlaczego: w przypadku bibliotek, których tag Rok zawiera pełną datę, grupowanie lub wyszukiwanie według roku wcześniej nie zwracało nic - czterocyfrowe zapytanie nigdy nie pasowało do pola pełnej daty. Odpytywanie właściwego pola sprawia, że grupowanie i wyszukiwanie według roku ponownie znajduje utwory.


N25 - Oddzielne pola grupowania "Rok" i "Rok (rrrr)"

Co: grupowanie albumów i ścieżki przeglądania eksponują teraz oba pola roku MusicBee - Rok (pełny tag daty) i Rok (rrrr) (tylko czterocyfrowy rok) - dzięki czemu użytkownik może wybrać jedno z nich podczas definiowania grupowania albumów lub ścieżki przeglądania.

Dlaczego: te dwa pola oznaczają różne rzeczy w MusicBee, a ich połączenie utraciło to rozróżnienie. Wyświetlanie obu pozwala użytkownikowi zebrać wszystkie wydania z danego roku (rrrr) lub zachować dokładną kolejność dat (pełny tag Roku), zgodnie z jego zamierzeniami.


N32 - Przypięte filtry i playlisty pogrupowane według rodzaju w katalogu głównym

Co: przypięty filtr pojawia się teraz bezpośrednio pod folderem Filtry w katalogu głównym przeglądania, a przypięta playlista bezpośrednio pod folderem Playlisty, zamiast wszystkich przypiętych elementów zbierających się w jednej grupie na końcu katalogu głównego. Każdy przypięty skrót znajduje się obok swojego rodzaju.

Dlaczego: im więcej skrótów przypinasz, tym trudniej przeglądać jedną końcową grupę wymieszanych filtrów i playlist, a każdy skrót jest oddzielony od folderu, do którego należy. Grupowanie przypiętych elementów pod ich własną kategorią utrzymuje katalog główny czytelnym, a każdy skrót obok rzeczy, do których należy.

Okno ustawień i pakowanie

N26 - Sekcjonowane okno ustawień

Co: strona Preferencje otrzymała układ z lewym panelem nawigacyjnym z sekcjami: Ogólne / Odtwarzanie / Biblioteka / Profile urządzeń / Diagnostyka.

Dlaczego: oryginał był pojedynczą, długą, płaską listą wszystkich ustawień - w porządku dla dewelopera, który go zbudował, ale dezorientujący dla wszystkich innych. Sekcjonowanie grupuje powiązane opcje i sprawia, że okno dialogowe przypomina bardziej nowoczesne ustawienia aplikacji.


Zmiana nazwy zestawu + wtyczki (bez F-id - uwaga dotycząca pakowania)

Co: skompilowana biblioteka DLL nosi nazwę mb_UPnP_yaiol.dll, a wtyczka zgłasza się jako "MusicBee UPnP (yaiol)". Różni się od oryginalnego mb_Upnp.dll.

Dlaczego: użytkownicy mogą zainstalować yaiol obok oryginalnej wtyczki i porównywać zachowanie obok siebie.


System odznak - wyświetlanie stanu środowiska wykonawczego (mechanizm za F40)

Co: ogólny wzorzec interfejsu użytkownika do wyświetlania ważnych warunków środowiska wykonawczego jako widocznych, kolorowych odznak w oknie dialogowym Ustawienia. Obecne instancje:

  • ⚠ Max Conn (N04) - uruchamia się, gdy limit maksymalnej liczby połączeń został osiągnięty co najmniej raz od uruchomienia MusicBee. Flaga sesji Plugin.MaxConnectionsHit. Ustawiana w WaitOnSendBarrier, gdy nie ma wolnego miejsca.
  • ⚠ Wymagany restart - uruchamia się, gdy zapisane ustawienie wymaga restartu MusicBee, aby weszło w życie. Flaga sesji Plugin.RestartRequired. Ustawiana w obsłudze zapisu okna dialogowego, gdy nowa trwała wartość różni się od migawki środowiska wykonawczego (Plugin.activeMaxConnections, Plugin.activeServerPort, Plugin.activeIpAddress). Ustawienia wymagające restartu są ograniczone do tych, które naprawdę nie mogą być ponownie ładowane na gorąco - parametry wiązania serwera HTTP i SemaphoreSlim zbudowane raz przy inicjalizacji.

Dlaczego: plik dziennika wtyczki jest w porządku dla użytkowników technicznych debugujących, ale użytkownik nietechniczny, patrzący na "urządzenie brzmi źle" lub "odtwarzanie jest wolne", nigdy nie otworzy Diagnostyka → Wyświetl dziennik. Odznaki rejestrują przypadki, w których użytkownik musi wiedzieć, że coś się stało i wyświetlają to następnym razem, gdy otworzy wtyczkę - możliwe do odkrycia bez czytania czegokolwiek.

Możliwe do ponownego wykorzystania w przyszłości:

  • Wykryto niezgodność profilu (user-agent urządzenia nigdy nie pasował do żadnego profilu, powrócono do Generic).
  • Uruchomiono wycofanie NextURI (F13 - odtwarzanie bez przerw wyłączone dla sesji na niestabilnym urządzeniu).
  • Skanowanie biblioteki nie powiodło się / częściowe.
  • Utracono połączenie z rendererem w trakcie sesji.
  • Każdy inny warunek, w którym "zdarzyło się raz, użytkownik powinien wiedzieć" jest lepsze niż "zalogowano cicho wśród 1000 innych linii".

Konwencje implementacji:

  • Etykiety odznak znajdują się na poziomie okna dialogowego (nie w żadnym panelu), dzięki czemu są widoczne niezależnie od tego, w której sekcji znajduje się użytkownik.
  • Umieszczone wzdłuż dolnego rzędu w pobliżu Zapisz/Anuluj (obecnie: y=410 ułożone poziomo).
  • Każda odznaka ma odpowiadającą jej flagę sesji w Plugin, która zmienia się na True, gdy wystąpi warunek i resetuje się tylko po restarcie MusicBee.
  • Zasoby: <Condition>Badge (tekst etykiety, poprzedzony ⚠) + <Condition>BadgeTip (podpowiedź wyjaśniająca przyczynę + rozwiązanie).
  • Dla odznak "zapisane ustawienie wymaga restartu" wykonaj migawkę środowiska wykonawczego w Plugin.Initialise() i porównaj z Settings.* po Settings.SaveSettings() w obsłudze zapisu okna dialogowego.

N27 - Anuluj odrzuca edycje ścieżek/szablonów

Co: edycje ścieżek i szablonów w oknie ustawień są teraz odrzucane, gdy użytkownik kliknie Anuluj, zamiast cicho pozostawać zastosowane, a każdy zarezerwowany szablon usunięty podczas sesji jest ponownie tworzony. (Szablony w przeciwnym razie zapisują się na żywo podczas edycji - na karcie Ścieżki nie ma oddzielnego przycisku Zapisz.)

Dlaczego: Anuluj powinno oznaczać anulowanie. Wcześniej użytkownik, który eksperymentował ze zmianami ścieżek/szablonów i wycofał się, stwierdził, że zmiany zostały już zatwierdzone, bez możliwości ich cofnięcia, poza ręcznym ponownym wykonaniem każdej z nich.


N28 - Dosłowne ampersandy w menu wyboru pola

Co: pole, którego nazwa zawiera "&" - np. "Nastrój & Kontekst" - renderuje ampersand dosłownie w menu wyboru pola, zamiast połykać go jako prefiks Alt-mnemonic.

Dlaczego: nazwy pól z ampersandem wyświetlały się nieprawidłowo (znak znikał, a następna litera stawała się akceleratorem), co utrudniało rozpoznanie wpisu w menu.


N29 - Stabilny, nieprzetłumaczony tytuł okna ustawień

Co: tytuł okna ustawień jest ustalony na ciąg znaków marki "MusicBee UPnP Plugin" i nie zmienia się już wraz z językiem interfejsu; ciąg znaków DialogTitle dla poszczególnych języków został usunięty z każdego pakietu lokalizacji.

Dlaczego: tytuł okna, który zmieniał się w zależności od języka, był powierzchnią do tłumaczenia bez korzyści - tytuł jest znakiem marki. Utrwalenie go sprawia, że jest stabilny i spójny wszędzie.

Lokalizacja

Oryginalna wtyczka jest tylko w języku angielskim. Ten fork jest w pełni lokalizowalny - każdy ciąg znaków widoczny dla użytkownika przechodzi przez pakiet zasobów, a wtyczka automatycznie wykrywa język interfejsu MusicBee.

N30 - Interfejs użytkownika w wielu językach (tłumaczenia w toku)

Co: mechanizm lokalizacji jest kompletny i dostarczany. Localisation.vb odczytuje wybrany język MusicBee z MusicBee3Settings.ini (endonim <SystemLanguage>) i stosuje pasującą kulturę .NET do wątku, więc My.Resources.Resources.* zwraca zlokalizowany ciąg znaków. Każda etykieta/przycisk/wiadomość widoczna dla użytkownika jest podłączona do klucza zasobu (kontrolki projektanta za pośrednictwem ApplyDesignerExtras + sync-en-locale.js; ciągi znaków środowiska wykonawczego, takie jak WarnPortInUse, dodawane ręcznie). Co nie jest jeszcze zrobione, to rzeczywiste tłumaczenie: istnieje tylko angielski pakiet źródłowy (Resources.resx) - pakiety satelitarne dla innych języków są produkowane w jednej partii, gdy wtyczka jest kompletna pod względem funkcji (tłumaczenie fragmentaryczne, podczas gdy ciągi znaków nadal się zmieniają, marnuje wysiłek).

Języki docelowe (zestaw oferowany przez samo MusicBee, dopasowany 1:1 przez endonymToCulture, więc wtyczka automatycznie podąża za językiem MusicBee):

Arabski (ar) Czeski (cs) Niemiecki (de) Grecki (el)
Hiszpański (es) Francuski (fr) Węgierski (hu) Włoski (it)
Koreański (ko) Holenderski (nl) Norweski (nb) Polski (pl)
Portugalski BR (pt-BR) Portugalski PT (pt-PT) Szwedzki (sv) Turecki (tr)
Ukraiński (uk) Rosyjski (ru) Japoński (ja) Chiński uproszczony (zh-CN)
Chiński tradycyjny (zh-TW) Angielski (en, źródło)

Polityka wariantów (zgodnie z zasadą lokalizacji obszaru roboczego): PT i ZH są podzielone na odrębne pakiety, ponieważ słownictwo/skrypt faktycznie się różni (pt-BR/pt-PT, zh-CN/zh-TW). EN to pojedynczy pakiet - "English(US)" (en-US) MusicBee powraca do en za pośrednictwem łańcucha kultur .NET, więc nie jest produkowany oddzielny pakiet US. ES i FR są również pojedynczymi lokalizacjami.

Dlaczego: ustawienia wtyczki UPnP ("nie używaj surowego PCM", "wymuś PCM little-endian", ostrzeżenia o awaryjnym przełączaniu portów) są wystarczająco enigmatyczne w języku ojczystym. Podążanie za własnym językiem interfejsu MusicBee - zamiast wymuszania angielskiego - to różnica między narzędziem, które użytkownik nieanglojęzyczny może skonfigurować, a takim, którego nie może. Żadne z źródeł nie próbowało tego.


N31 - Link pomocy otwiera się w pełnym języku interfejsu

Co: otwarcie linku Pomocy z wtyczki respektuje pełny język interfejsu użytkownika (np. pt-BR, zh-CN) zamiast sprowadzać go do języka podstawowego i wysyła jaśniejszy identyfikator sprawdzania aktualizacji.

Dlaczego: użytkownik uruchamiający MusicBee w wariancie regionalnym (portugalski brazylijski, chiński uproszczony) był kierowany na stronę pomocy w języku podstawowym. Przeniesienie pełnej kultury kieruje go na stronę pomocy w dokładnie tym języku, którego używa.

Poprawki i ulepszenia oryginalnej wtyczki

Podstawowy protokół i odtwarzanie

F01 - Zaktualizowane domyślne profile urządzeń DLNA

Co: dostarcza świeże domyślne profile dla PlayStation 4, Xbox 360/One i nowoczesnego BubbleUPnP, z flagami możliwości (częstotliwości próbkowania, głębie bitowe, kodeki), które odzwierciedlają to, co te urządzenia faktycznie obsługują dzisiaj.

Dlaczego: domyślne ustawienia oryginalnej wtyczki były zamrożone około 2014 roku. PS4/Xbox/BubbleUPnP od tego czasu zyskały obsługę dźwięku wysokiej rozdzielczości. Po wyjęciu z pudełka, nowa instalacja działa najlepiej na tych urządzeniach bez konieczności dotykania ustawień profilu urządzenia przez użytkownika.


F02 - Sterowanie urządzeniami, które reklamują MediaRenderer:3

Co: wtyczka bada opis usługi UPnP renderera, aby zdecydować, czy MusicBee może nim sterować. Oryginał pasował tylko do urn:schemas-upnp-org:device:MediaRenderer:1. Nowoczesne urządzenia reklamują :2 lub :3. F02 rozszerza dopasowanie.

Dlaczego: bez tego, najnowsze jednostki Sonos / WiiM / Eversolo po prostu nie pojawiają się jako cele na liście urządzeń "Odtwórz do" w MusicBee - mimo że mówią tym samym protokołem. Pojedyncza poprawka dopasowania prefiksu ciągu znaków odblokowuje całą nowoczesną generację urządzeń.


F03 - Opcja "Wymuś natywny strumień" dla każdego profilu (domyślnie WŁ.)

Co: po zaznaczeniu, wtyczka wysyła oryginalne bajty pliku do urządzenia bez transkodowania, bez DSP, bez przetwarzania ReplayGain. Po prostu surowy plik wybrany przez użytkownika, bajt po bajcie (z wyjątkiem ramkowania HTTP).

Dlaczego: według świadectw na forum jest to największa pojedyncza poprawa jakości odtwarzania. Użytkownicy hi-fi kupujący drogie renderery wyraźnie chcą wyjścia bit-perfect; każde dotknięcie DSP niweczy sens. Domyślnie WŁĄCZONE, ponieważ większość nowoczesnych urządzeń obsługuje dowolny kodek, który użytkownik im podał, a ReplayGain/EQ powinno być opcjonalne. Jest to dla każdego profilu, więc możesz zachować transkodowanie dla starego Xboxa, jednocześnie wysyłając natywny strumień do DAC-a hi-fi.


F04 - "Wymuś transkodowanie" dla każdego profilu

Co: nadpisanie dla każdego profilu, które wymusza transkodowanie każdego strumienia do tego urządzenia, niezależnie od obsługi natywnego kodeka. Odwrotność F03 (ForceNativeStream). Wzajemnie wykluczające się z F03 - interfejs użytkownika automatycznie odznacza drugie, gdy jedno z nich zostanie włączone.

Dlaczego: pojedynczy globalny przełącznik byłby sprzeczny z opcją ForceNativeStream (F03) dla każdego profilu. Prawdziwy przypadek: urządzenie A to DAC hi-fi, który chce strumieni natywnych bit-perfect; urządzenie B to stary amplituner AV, który dławi się na FLAC. Przy globalnym przełączniku użytkownik musiałby wybrać - kosztem drugiego urządzenia. Z opcją dla każdego profilu każde urządzenie otrzymuje właściwą odpowiedź.

Implementacja:

  • StreamingProfile.ForceTranscoding As Boolean = False.
  • Schemat trwałości zwiększony do v9. Pliki sprzed v9 ładują starszą globalną wartość raz i kopiują ją do wszystkich profili, zachowując stare zachowanie podczas aktualizacji.
  • Interfejs użytkownika: usunięty z panelu Diagnostyka, dodany do sekcji Profile urządzeń obok ForceNativeStream. Dwukierunkowe wzajemnie wykluczające się handlery (CheckedChanged na każdym odsubskrybuje drugie przed przełączeniem, aby uniknąć nieskończonej pętli).
  • Miejsce decyzji: Settings.ForceTranscodingstreamingProfile.ForceTranscoding w WriteAudioFileDIDL.

F05 - "Wymuś PCM little-endian" dla każdego profilu

Co: strumienie PCM (typy mime L16/L24) są domyślnie big-endian. Niektóre urządzenia błędnie oczekują little-endian i odtwarzają biały szum, gdy otrzymają prawidłowe dane big-endian. F05 przełącza kolejność bajtów dla każdego profilu.

Dlaczego: bez tego niektóre urządzenia emitują ścianę szumu. Objaw jest dramatyczny, a przyczyna niewidoczna bez znajomości kodowania PCM - przełącznik daje użytkownikom możliwość zgadywania i sprawdzania.


F06 - "Nie używaj surowego PCM" dla każdego profilu

Co: gdy urządzenie twierdzi, że obsługuje surowe PCM, wtyczka go używa. Niektóre urządzenia kłamią - akceptują uzgadnianie SOAP, ale zniekształcają rzeczywiste dane surowego PCM, jednocześnie prawidłowo obsługując PCM opakowane w kontener WAVE. F06 wymusza PCM-over-Wave niezależnie od tego, co reklamuje urządzenie.

Dlaczego: konkretnie niektóre modele Marantz - reklamują surowe PCM, ale działa tylko WAVE. Bez tego surowe strumienie PCM są zniekształcone, bez komunikatu o błędzie wskazującego przyczynę.


F07 - "Długość zawartości" dla każdego profilu

Co: jaką wartość wysłać w nagłówku HTTP Content-Length. Cztery opcje:

  • Domyślne - rzeczywista liczba bajtów, gdy znana, pominięta, gdy nieznana.
  • Brak - nigdy nie wysyłaj nagłówka (tylko kodowanie chunked).
  • Tylko PCM - wysyłaj tylko dla surowego PCM; pomiń dla wszystkiego innego.
  • Stałe - wyślij UInt32.MaxValue - 8192 (znacznik dla "ogromnej nieznanej długości").

Dlaczego: urządzenia UPnP/DLNA bardzo różnie reagują na Content-Length. Niektóre potrzebują dokładnej liczby, niektóre nienawidzą jej w strumieniach, niektóre potrzebują znacznika "naprawdę dużej" wartości, aby utrzymać buforowanie. To zostało później rozszerzone z tylko PCM na wszystkie formaty wyjściowe, ponieważ te same problemy pojawiły się w transkodowanych strumieniach MP3/AAC.


F08 - "Nie czyść NextURI" dla każdego profilu

Co: normalnie wtyczka czyści zakolejkowane NextURI urządzenia, gdy kolejka się opróżnia (wysyłając SetNextAVTransportURI z pustym adresem URL). Niektóre urządzenia (zwłaszcza Denon) interpretują pusty NextURI jako "zatrzymaj wszystko" i natychmiast zatrzymują odtwarzanie. F08 zapobiega czyszczeniu go przez wtyczkę.

Dlaczego: bez tego właściciele Denona doświadczają, że urządzenie przerywa odtwarzanie w połowie utworu, gdy kolejka się opróżnia. Po zaznaczeniu F08 urządzenie zachowuje nieaktualne NextURI w pamięci (nieszkodliwe - po prostu zostanie nadpisane następnym razem, gdy coś zostanie zakolejkowane).


F09 - FLAC jako format wyjściowy transkodowania

Co: rozwijana lista formatów transkodowania w profilach urządzeń oferuje teraz FLAC obok PCM 16/24, MP3, AAC, Ogg. Wybranie go kieruje koder BASS przez standardową linię poleceń konwersji FLAC MusicBee (ten sam mechanizm, którego już używają MP3/AAC/Ogg).

Dlaczego: dla urządzeń, które dobrze obsługują FLAC, ale nie potrafią dekodować kodeka źródłowego (np. Eversolo odbierające bibliotekę WMA MusicBee przekonwertowaną na FLAC), zachowuje to bezstratną jakość, gdzie MP3/AAC odrzuciłoby dane audio. Odblokowuje N02 (kontrola downmixu 5.1), którego nie można było rozwiązać bez opcji transkodowania bezstratnego.

Implementacja: dodanie jednej linii do bloku Select Case Codec w Encoder.StartEncode - FLAC dołącza do MP3/AAC/Ogg w gałęzi sterowanej wierszem poleceń. Rozwijana lista interfejsu użytkownika otrzymuje "FLAC" jako 6. opcję. Mapowanie ładowania/zapisu w SettingsDialog rozszerza się, aby rozpoznawać FileCodec.FlacSelectedIndex = 5. Typy Mime, DLNA i funkcja kodowania były już okablowane w ItemManager.GetMimes / GetDlnaType / GetEncodeFeature z wcześniejszych prac (F21, F26).


Odtwarzanie bez przerw (SetNextAVTransportURI)

F10 - SetNextAVTransportURI / NextURI core

Co: prawdziwe odtwarzanie bez przerw. Gdy urządzenie reklamuje obsługę SetNextAVTransportURI w swoim opisie usługi UPnP, wtyczka wstępnie kolejkuje następny utwór na urządzeniu, zanim bieżący się zakończy. Urządzenie przechodzi wewnętrznie bez słyszalnej przerwy między utworami - to, co słyszysz na odtwarzaczu CD. To nie jest hack "ciągłego strumienia" (który łączy wszystko w jeden długi strumień i traci metadane dla każdego utworu).

Dlaczego: flagowa funkcja Tier-2. Albumy nagrane jako ciągły występ na żywo (nagrania na żywo, utwory klasyczne, sety DJ-skie) brzmią źle, gdy między utworami jest półsekundowa cisza. Rozwiązanie tego problemu w sposób właściwy jest flagową funkcją, teraz w tym forku.

Uwagi: zakolejkowany dźwięk jest obsługiwany przez serwer HTTP wtyczki za pomocą streamHandle=0 (tryb pobierania biblioteki), co oznacza, że silnik audio MusicBee nie jest zaangażowany w zakolejkowany utwór. Kompromis: efekty ReplayGain/DSP/EQ nie mają zastosowania do następnego utworu. Akceptowalne, gdy włączona jest opcja "wymuś natywny strumień" (domyślnie).


F11 - "Wyłącz obsługę NextURI" dla każdego profilu

Co: nawet jeśli urządzenie reklamuje SetNextAVTransportURI, to pole wyboru zmusza wtyczkę do zignorowania tej reklamy i powrotu do odtwarzania utwór po utworze.

Dlaczego: niektóre urządzenia reklamują NextURI, ale mają błędną implementację (awarie, częściowe przejścia, zawieszenia). Zamiast odtwarzać inżynierię wsteczną każdego uszkodzonego urządzenia, użytkownik otrzymuje przełącznik "po prostu wyłącz to tutaj".


F12 - Cykl życia NextURI na liście odtwarzanych utworów

Co: gdy MusicBee uruchamia NowPlayingListChanged, wtyczka ponownie ocenia, co powinno być zakolejkowane do płynnego przejścia. Pyta MusicBee o nowy "następny" utwór za pomocą NowPlayingList_GetNextIndex(1) + NowPlayingList_GetListFileUrl, porównuje z tym, co jest aktualnie zakolejkowane na urządzeniu (śledzone za pomocą nowego pola nextPlaySourceUrl), i ponownie kolejkuje, jeśli się zmieniło (lub czyści kolejkę, jeśli MusicBee mówi, że nie ma następnego utworu).

Dlaczego: bez F12 urządzenie odtwarzało nieaktualne NextURI, gdy użytkownik usunął/zmienił kolejność zakolejkowanego utworu. Historycznie zajęło to kilka iteracji, ponieważ każda mutacja listy wymaga innej obsługi - uprościliśmy to, ufając NowPlayingList_GetNextIndex (który już respektuje tasowanie i zawijanie powtarzania wszystkich), więc wszystkie warianty przechodzą przez to samo porównanie.

Implementacja:

  • Nowe pole nextPlaySourceUrl przechowuje adres URL biblioteki MusicBee tego, co jest zakolejkowane (adres URL strumieniowania z sufiksem uchwytu nie jest porównywalny ze ścieżką biblioteki).
  • Nowa Public Sub RefreshQueuedNextUri() w MediaRendererDevice. Trzy wyniki: brak zakolejkowanego NextURI → brak operacji; zakolejkowane pasuje do nowego "następnego" → brak operacji; zakolejkowane różni się → wywołaj QueueNext z nowym adresem URL (lub QueueNext("") aby wyczyścić - co respektuje F08 DoNotClearNextUri).
  • Okablowane w Plugin.ReceiveNotification pod NotificationType.NowPlayingListChanged.

F13 - Wycofanie po awarii NextURI

Co: po 4 kolejnych awariach SetNextAVTransportURI na tym samym urządzeniu, wtyczka wyłącza odtwarzanie bez przerw dla tego urządzenia do momentu ponownego uruchomienia MusicBee.

Dlaczego: jeśli urządzenie jest faktycznie uszkodzone dla NextURI (sporadyczne błędy SOAP, usterki sieciowe), wtyczka w przeciwnym razie próbowałaby ponownie przy każdym utworze. F13 zatrzymuje szum i cicho wraca do odtwarzania utwór po utworze.


F14 - Tryb powtarzania + integracja NextURI

Co: F14 dzieli się na dwa przypadki obsługiwane przez detektor przejścia F15 w OnAvTransportStatusCheck:

  • Powtórz wszystko: MusicBee sam przekazuje prawidłowy adres URL "zawijania" (utwór 1 na końcu listy) do Plugin.QueueNext. Nie jest potrzebna żadna specjalna logika wtyczki - urządzenie przechodzi do niego, a detektor F15 wywołuje Player_PlayNextTrack jak zwykle, co zawija indeks NPL MusicBee z powrotem do 0.
  • Powtórz jeden: MusicBee przekazuje TEN SAM adres URL utworu do Plugin.QueueNext. Urządzenie przechodzi do niego (nowy uchwyt strumienia, to samo źródło). Detektor F15 teraz odpytuje Player_GetRepeat() - jeśli jest to RepeatMode.One, pomija wywołanie Player_PlayNextTrack, aby MusicBee nie przesuwał indeksu NPL od zapętlonego utworu.

Dlaczego: bez pominięcia Repeat-One, wywołanie Player_PlayNextTrack przy przejściu bez przerw przesunęłoby MusicBee do następnego utworu na liście (Repeat-One wpływa tylko na automatyczne przewijanie na końcu utworu w interfejsie odtwarzacza - Następny utwór zawsze przesuwa się do przodu), co byłoby sprzeczne z tym, co oznacza Repeat-One.

Uwaga dotycząca liczby odtworzeń: w trybie Repeat-One, zwiększenie liczby odtworzeń zależy od MusicBee 3.7.9563+ zauważającego zapętlone odtwarzanie. Starsze wersje MusicBee prawidłowo odtwarzają powtórzenie bez przerw, ale pomijają zwiększenie liczby odtworzeń. Udokumentowane; nie blokujące.


F15 - Automat stanów wykrywania przejścia utworu

Co: gdy urządzenie wewnętrznie przechodzi z bieżącego utworu do NextURI, wtyczka musi to zauważyć i poinformować MusicBee, aby przesunął swój indeks odtwarzania. W przeciwnym razie MusicBee myśli, że nadal jest na poprzednim utworze, a liczniki odtworzeń / interfejs użytkownika / scrobbling rozjeżdżają się.

Implementacja: odpytuje GetPositionInfo.TrackURI przy każdym takcie timera statusu. Gdy zgłoszony URI pasuje do tego, który zakolejkowaliśmy za pośrednictwem NextURI, wywołujemy Player_PlayNextTrack na MusicBee i ustawiamy suppressNextSoapCall, aby wynikowe PlayToDevice nie wysyłało ponownie SetAVTransportURI (co przerwałoby odtwarzanie bez przerw).

Dlaczego: bez F15 urządzenie odtwarza następny utwór, ale interfejs użytkownika MusicBee mówi, że nadal jest na poprzednim. Mylące, psuje scrobbling, psuje śledzenie liczby odtworzeń. Wykrywanie przejścia utworu wymaga długiej, iteracyjnej pracy dla każdego renderera, ponieważ każda marka renderera ma swoje własne dziwactwa w kiedy zgłasza zmianę URI (niektóre najpierw zgłaszają TRANSITIONING, niektóre przechodzą bezpośrednio do PLAYING z nowym URI, niektóre mają krótkie STOPPED pomiędzy).

Uwagi: nasza pierwsza wersja działa na rendererze BubbleUPnP. Przypadki brzegowe dla poszczególnych urządzeń pozostają w B6.


F16 - Naprawa trzasków przy przejściu bez przerw

Co: trzask występuje, gdy format źródłowy (częstotliwość próbkowania / kanały / kodek) zakolejkowanego utworu różni się od aktualnie odtwarzanego utworu, zmuszając przetwornik cyfrowo-analogowy urządzenia do ponownego zablokowania przy przejściu. F16 dodaje diagnostykę NextUri:FormatChange, która uruchamia się w momencie kolejkowania, gdy formaty się różnią, nazywając obie strony - dzięki czemu użytkownicy słyszący trzaski mogą skorelować.

Diagnostyka wskazuje również na rozwiązanie: zaznacz ForceTranscoding w profilu urządzenia. To ujednolica każdy utwór do jednego kodeka transkodowania/częstotliwości próbkowania/głębi bitowej, całkowicie eliminując różnicę formatu źródłowego.

Dlaczego odłożono na później rzeczywistą poprawkę transkodowania do dopasowania: strukturalna poprawka (transkodowanie zakolejkowanego utworu w celu dopasowania do formatu odtwarzanego utworu) wymaga zmian w schemacie URL serwera HTTP wtyczki - obecnie /encode/{id}0.{ext} obsługuje zakolejkowany plik natywnie. Przyszła wersja 2 F16 dodałaby trasy /encode/{id}0_{rate}_{depth}.{ext} dla każdego formatu i podłączyła je przez koder. To większa zmiana architektoniczna, warta wykonania, jeśli rzeczywiste urządzenie wykazuje trzaski po tym, jak ForceTranscoding nie wystarcza.

Implementacja dzisiaj:

  • Pole lastSourceUrl śledzi aktualnie odtwarzany adres URL źródła.
  • QueueNext odczytuje FilePropertyType.SampleRate/Channels/Kind dla bieżącego i zakolejkowanego utworu i loguje NextUri:FormatChange w przypadku niezgodności.

F17 - Ponowna synchronizacja paska postępu po przewinięciu

Co: funkcja Seek() już wywoływała GetPlayPositionInformation() po udanym SOAP Seek, co naprawia przypadek "brak synchronizacji w ogóle". F17 eliminuje pozostałe dryfowanie do 1 sekundy spowodowane kwantyzacją RelTime UPnP do 1 sekundy: gdy zgłoszona pozycja urządzenia zaokrągla się do 1 sekundy od żądanego celu użytkownika, wtyczka ufa teraz wartości użytkownika z dokładnością do ułamka sekundy, zamiast obcinania urządzenia. Tylko wtedy, gdy urządzenie zgłasza coś dramatycznie innego (>1s różnicy), używamy jego wartości (przewinięcie wylądowało gdzie indziej niż żądano, np. przyciąganie do klatki kluczowej w niektórych kodekach).

Dlaczego: bez tego, przewinięcie do 2:30.500 zakotwiczone w raporcie urządzenia "2:30" sprawiłoby, że pasek postępu pokazywałby ~500ms za rzeczywistością. Po F17 pasek odpowiada intencji użytkownika dla typowego przypadku przewijania w utworze i nadal respektuje raport urządzenia dla przypadku odstającego przyciągania do klatki kluczowej.


F18 - Blokada strumienia ciągłego / NextURI

Co: dwie blokady są teraz na miejscu:

  1. Środowisko wykonawcze: QueueNext wcześnie zwraca False, gdy Settings.ContinuousOutput jest włączone. Strumień ciągły to własny mechanizm odtwarzania bez przerw (jeden długi, połączony strumień); wysyłanie SetNextAVTransportURI na wierzch myli urządzenie co do tego, czy każdy utwór jest dyskretnym URI, czy częścią ciągłego przepływu.
  2. Interfejs użytkownika: gdy użytkownik zaznaczy globalne pole wyboru strumienia ciągłego, forceNativeStream aktualnie wyświetlanego profilu automatycznie się odznacza. Strumień ciągły zawsze transkoduje, więc wymuszenie natywnego jest bezsensowne w połączeniu.

Dlaczego: zapobiega to włączaniu przez użytkownika dwóch sprzecznych mechanizmów odtwarzania bez przerw jednocześnie. Bez F18 urządzenie otrzymałoby zarówno ciągły strumień URI, jak i NextURI dla każdego kolejnego utworu, z niezdefiniowanym zachowaniem w zależności od renderera.


F19 - Ignorowanie błędów pustego NextURI

Co: gdy SetNextAVTransportURI jest wywoływane z pustym adresem URL (np. ostatni utwór na liście), niektóre urządzenia zwracają błąd SOAP. F19 cicho je połyka - logowane, ale nie propagowane jako błędy.

Dlaczego: warunek "brak następnego utworu" jest normalny, a nie błędem. Traktowanie go jako krytycznego zanieczyszcza dziennik i (w niektórych przepływach) wyzwala burze ponownych prób.


Typy mime i metadane DLNA

F20 - Typ mime MP3 → audio/mpeg

Co: zgodny ze standardami typ mime MP3 to audio/mpeg, a nie audio/mp3. Ten drugi jest powszechnym błędnym określeniem, które większość urządzeń toleruje, ale bardziej rygorystyczne renderery go odrzucają.

Dlaczego: cicho naprawia odtwarzanie na bardziej rygorystycznych urządzeniach, które przestrzegają standardu. Kod yaiol miał to już poprawne; nie wymagało to zmian.


F21 - Kolejność typów mime: najpierw wariant bez x-

Co: gdy urządzenie reklamuje zarówno audio/flac, jak i audio/x-flac, wtyczka zwraca najpierw wariant bez x-. To samo dotyczy każdego kodeka z zarówno standardowymi, jak i eksperymentalnymi typami mime.

Dlaczego: prefiks x- oznacza eksperymentalne/nieoficjalne typy mime. Niektóre renderery zachowują się lepiej ze standardową formą. Małe przestawienie, rzeczywisty wpływ.


F22 - Obsługa typu mime Opus

Co: rozpoznaje Opus jako strumieniowy kodek audio; wysyła typ mime audio/opus podczas serwowania utworów Opus.

Dlaczego: Opus jest obecnie powszechny (nowoczesny kodek kompromisowy dla mowy/muzyki). Bez F22 wtyczka odmówiłaby strumieniowania plików Opus nawet do urządzeń, które je obsługują.


F23 - Obsługa plików źródłowych Monkey Audio (APE)

Co: rozpoznaje pliki .ape jako prawidłowy kodek źródłowy do strumieniowania/transkodowania.

Dlaczego: APE to bezstratny format z niszową, ale lojalną bazą użytkowników. Dodanie go kosztuje niewiele i odblokowuje bibliotekę dla tych użytkowników.


F24 - Awaryjne przełączanie typu mime AAC / ALAC

Co: jeśli urządzenie obsługuje AAC lub ALAC, ale nie reklamuje ich jawnie w swoim opisie usługi UPnP, wtyczka i tak oferuje je jako awaryjne rozwiązanie.

Dlaczego: kilka urządzeń, które dobrze obsługują AAC, zapomniało umieścić go w swoim XML-u możliwości. Bez F24 wtyczka nawet nie spróbuje, wymuszając transkodowanie. Z F24 wtyczka próbuje i pozwala urządzeniu obsługiwać go natywnie, jeśli może.


F25 - Flaga typu DLNA dla natywnych i zakodowanych strumieni WAV

Co: flaga typu DLNA (identyfikator profilu, taki jak LPCM, WAVE, MP3) musi pasować do tego, co odbiera urządzenie. F25 zapewnia, że strumienie natywne i zakodowane strumienie WAV są prawidłowo oznaczane.

Dlaczego: niezgodny typ DLNA powoduje, że niektóre urządzenia całkowicie odmawiają odtwarzania lub stosują niewłaściwy dekoder.


F26 - Nagłówek DLNA dla plików FLAC

Co: strumienie FLAC otrzymują prawidłowy identyfikator profilu DLNA w swoich nagłówkach.

Dlaczego: bez niego niektóre urządzenia obsługujące FLAC nie rozpoznają strumienia jako takiego.


F27 - Naprawa obliczania bitrate w metadanych

Co: bitrate res@bitrate strumienia ciągłego był obliczany jako (sampleRate * channels * bitsPerSample) / 1000 - kbps, różniący się o czynnik ~125 od specyfikacji UPnP DIDL, która definiuje atrybut jako bajty na sekundę. Teraz dzieli przez 8 zamiast 1000.

Dlaczego: błędne wyświetlanie bitrate na urządzeniu - kosmetyczne na większości rendererów, ale niektóre alokują bufory strumienia z wartości i zacinają się na strumieniach, które wyglądają na ~125 razy mniejsze niż są. Ścieżka pliku źródłowego nieciągłego miała to już poprawne ((bitrate_kbps * 1000) \ 8 = bajty/sek); tylko ścieżka strumienia ciągłego była błędna.


F28 - Naprawa formatu czasu metadanych (Marantz)

Co: res@duration w DIDL było formatowane jako H:MM:SS (np. 0:03:42). Specyfikacja UPnP DIDL definiuje format jako H+:MM:SS[.F+] - ściśle z opcjonalnymi, ale zalecanymi ułamkami sekund; niektóre urządzenia Marantz traktują gołą formę jako nieprawidłową i pozostawiają wyświetlanie czasu trwania puste. Teraz formatowane jako H:MM:SS.fff (np. 0:03:42.000).

Dlaczego: problem z wyświetlaniem specyficzny dla marki; zgodny format w stylu ISO z ułamkami sekund naprawia go bez wpływu na żadne inne urządzenie. Zastosowano w obu miejscach emisji DIDL (ścieżka pliku źródłowego + ścieżka strumienia zakodowanego w WriteAudioFileDIDL).

Dodatkowa poprawka w tym samym przejściu: pv:addedTime i pv:lastPlayedTime używały hh (zegar 12-godzinny) w swoich ciągach formatu DateTime zamiast HH (24-godzinny). Każdy utwór dodany lub odtworzony między 13:00 a 23:59 byłby renderowany z błędną godziną (np. 17:42 → "05:42") na urządzeniach, które wyświetlają to pole. Teraz używa HH.


F29 - Obsługa przewijania zakodowanych MP3 (CBR)

Co: transkodowane strumienie MP3 reklamują teraz DLNA.ORG_OP=11 (przewijanie zarówno bajtowe, jak i czasowe) zamiast DLNA.ORG_OP=10 (tylko bajtowe). Urządzenia, które wcześniej odmawiały przewijania czasowego w transkodowanych MP3, mogą teraz normalnie sterować swoim paskiem postępu/interfejsem przewijania.

Dlaczego: transkoder MusicBee produkuje MP3 o stałej przepływności przy ustawieniu HighQuality, więc mapowanie bajt ↔ czas jest liniowe - urządzenie może samodzielnie przekonwertować żądanie przewijania czasowego na żądanie przewijania bajtowego HTTP Range bez żadnego wsparcia po stronie kodera. Reklamowanie OP=11 odblokowuje ten interfejs użytkownika na urządzeniu. Bez F29, użytkownicy przewijający w transkodowanym MP3 mieli albo cicho ignorowane przewijanie, albo byli przenoszeni na początek utworu.

Implementacja: zreorganizowano GetEncodeFeature w ItemManager.vb, aby rozbić wbudowany If na czytelny łańcuch If/ElseIf/Else. MP3 otrzymuje jawnie OP=11; inne kodeki nie-PCM zachowują OP=10. Brak zmian dla AAC/FLAC/itp. - te wymagałyby specyficznej dla kodeka weryfikacji CBR-ności, której MusicBee nie gwarantuje.


F30 - Obsługa rozszerzenia pliku .mpeg

Co: pliki z rozszerzeniem .mpeg (i jeszcze rzadszym .mpe) są teraz rozpoznawane jako FileCodec.Mp3 w GetCodec. Przed F30 zwracały FileCodec.Unknown i były cicho odrzucane z biblioteki / nie mogły być źródłami transkodowania.

Dlaczego: stare archiwa MPEG-1 Layer 3 czasami używały .mpeg zamiast .mp3 (specyfikacja dopuszcza oba). Kilka plików w bibliotece 300 tys. wystarczy, aby poczuć "MusicBee je pokazuje, ale wtyczka nie" - mylące dla użytkownika.


Zachowanie odtwarzania

F31 - Strumienie radiowe automatycznie używają trybu ciągłego

Co: WriteAudioFileDIDL teraz bada właściwość Kind adresu URL źródła za pomocą Library_GetFileProperty i traktuje każdy plik, którego Kind kończy się na "Stream" (MusicBee zgłasza "MP3 Stream", "Internet Stream" itp. dla radia) jako ciągły, niezależnie od globalnego przełącznika Settings.ContinuousOutput. Używana jest gałąź DIDL strumienia ciągłego (Tytuł: "Ciągły strumień", id="continuousstream", stałe wyjście PCM/Wave); urządzenie widzi pojedynczy strumień w stylu nieskończonym.

Dlaczego: strumienie radiowe nie mają granic utworów, stałej długości, możliwości przewijania. Traktowanie ich jak dyskretnych plików w DIDL powodowało, że wtyczka reklamowała zakresy bajtów i czasy trwania, które nie istnieją. Automatyczne przełączanie, gdy MusicBee już powiedział nam "to jest strumień", usuwa pułapkę, o której użytkownik nie powinien musieć myśleć.

Zakres: dotyczy tylko sytuacji, gdy MusicBee steruje odtwarzaniem (musicBeePlayToMode). Ścieżka pobierania biblioteki (przeglądanie klienta UPnP) pozostaje niezmieniona - adresy URL radia są tam rzadkie, a zachowanie widoczne dla użytkownika nie powinno się zmieniać bez wyraźnych testów.


F32 - Awaryjne przełączanie reklam kodeków

Co: jeśli urządzenie nie reklamuje niektórych kodeków (lub wtyczka nie może przeanalizować XML-a możliwości urządzenia), wtyczka nie odrzuca natychmiast strumienia. Zamiast tego próbuje go obsłużyć i pozwala urządzeniu zdecydować.

Dlaczego: wiele urządzeń ma niekompletny lub nieczytelny XML możliwości, ale faktycznie dobrze obsługuje kodek. F32 zamienia małe "zgadnij i spróbuj" na całkowitą odmowę.


F33 - Poprawa synchronizacji paska postępu

Co: pozycja między odpytywaniami jest już ekstrapolowana z zegara ściennego z pojedynczego punktu zakotwiczenia (currentPlayStartTicks), więc pasek postępu aktualizuje się płynnie z częstotliwością poniżej sekundy. Pozostałym źródłem drgań był początkowy punkt zakotwiczenia dla świeżo rozpoczętego utworu: poprzedni kod zakładał position=0 w momencie, gdy timer statusu po raz pierwszy zauważył, że stan zmienił się na Odtwarzanie, ale do tego czasu urządzenie mogło odtwarzać przez 100-500 ms (jeden interwał odpytywania). Pasek postępu MusicBee zaczynałby od 0, a następnie przeskakiwałby do przodu, gdy rzeczywistość dogoniła.

Poprawka F33: podczas przechodzenia do Odtwarzania po raz pierwszy na nowym utworze (currentPlayStartTimeEstimated=True), wywołaj GetPlayPositionInformation(), aby uzyskać rzeczywistą bieżącą pozycję urządzenia, a następnie zakotwicz się w niej. UPnP zgłasza tylko rozdzielczość 1 sekundy, więc punkt zakotwiczenia jest nadal kwantyzowany, ale jest znacznie bliżej prawdy niż zakładanie 0.

Dlaczego: płynniejsze + dokładniejsze wyświetlanie postępu, zwłaszcza zaraz po zmianie utworu. Nie ma sposobu na obejście samej rozdzielczości raportowania UPnP do 1 sekundy - to jest specyfikacja.


F34 - Drgania paska postępu po zmianie utworu

Co: gdy PlayToDevice jest wywoływane dla nowego utworu, wtyczka pozostawiała currentPlayPositionMs i currentPlayStartTicks na ich poprzednich wartościach utworu przez okno ~100ms między SOAP-Play a pierwszym odpytywaniem timera statusu wykrywającym nowy stan Odtwarzania. Pasek postępu MusicBee krótko pokazywałby koniec poprzedniego utworu, a następnie wracałby do 0, a następnie wspinał się. F34 zeruje oba przy wejściu do PlayToDevice - w momencie, gdy wiemy, że następuje zmiana utworu, zanim jakakolwiek praca SOAP.

Dlaczego: usterka wizualna w przypadkach szybkiego przewijania (ręczne następne lub płynne przejście). Teraz pierwsze zapytanie MusicBee PlayPositionMs po Play zwraca czyste 0, a następnie GetPlayPositionInformation F33 udoskonala je do rzeczywistej pozycji urządzenia przy pierwszym takcie zmiany stanu.

Implementacja: cztery linie na początku PlayToDevice, sparowane z dokładnym zakotwiczeniem F33 w czasie przejścia.


F35 - Błąd "Wymuś transkodowanie"

Co: wymuszone transkodowanie nadal mogło pomijać transkodowanie w niektórych kombinacjach. Po przeróbce F04 dla każdego profilu, dwie konkretne luki zostały uszczelnione:

  1. Pierwszeństwo z ForceNativeStream. Gdy oba były True (co może się zdarzyć podczas migracji schematu lub częściowego pliku ustawień), ForceTranscoding teraz wygrywa (If streamingProfile.ForceTranscoding Then forceEncode = True ElseIf streamingProfile.ForceNativeStream Then forceEncode = False). Wzajemne wykluczanie w interfejsie użytkownika zapobiega zaznaczeniu obu przez użytkownika, ale zabezpieczenie środowiska wykonawczego obsługuje każdy stan, który załadował się niespójnie z dysku.
  2. Logika bypassTranscodeDecision. Wcześniej: streamingProfile.ForceNativeStream AndAlso Not Settings.ForceTranscoding. Teraz: streamingProfile.ForceNativeStream AndAlso Not streamingProfile.ForceTranscoding - ta sama zasada pierwszeństwa, ale w tym samym zakresie dla każdego profilu.

Dlaczego: "wymuś" powinno oznaczać wymuś. Jeśli użytkownik jawnie włączył ForceTranscoding dla urządzenia, wtyczka nigdy nie może cicho przejść do strumieniowania natywnego, niezależnie od tego, jak inne flagi się połączą.


F36 - Wyjątek zamknięcia renderera

Co: opakowano Plugin.ReceiveNotification w najwyższego poziomu Try/Catch, który loguje każdy nieprzechwycony wyjątek, zamiast pozwalać mu propagować z powrotem do pompy powiadomień MusicBee.

Dlaczego: powiadomienia z MusicBee (PlayStateChanged, VolumeMuteChanged itp.) są wysyłane do ControlPointManager, który komunikuje się z rendererem za pośrednictwem SOAP. Poszczególne miejsca wywołań miały już Try/Catch wokół swoich wywołań SOAP, ale wystarczająco dziwny przypadek synchronizacji (np. renderer umierający między dwoma wywołaniami SOAP w tym samym handlerze powiadomień) mógł nadal uciec. Wrapper najwyższego poziomu jest ostateczną siatką bezpieczeństwa, dzięki czemu użytkownik nigdy nie widzi ogólnego wyskakującego okienka "TargetInvocationException" z MusicBee.

Implementacja: zmieniono nazwę istniejącego ciała na ReceiveNotificationInternal i dodano cienki wrapper ReceiveNotification, który wykonuje Try { ReceiveNotificationInternal(...) } Catch { LogError(...) }. Istniejąca infrastruktura Try/Catch dla każdej metody w ControlPointManager (wokół każdego wywołania PostSoapRequest) pozostaje - F36 to pas + szelki.


F37 - Długie przewijanie utworu wyzwalające fałszywe przejście

Co: przewijanie w długim utworze może spowodować krótkotrwały cykl Stopped→Playing na niektórych rendererach. Bez rozróżnienia, ProcessNewPlayState.Stopped traktuje to jako naturalny koniec utworu i wywołuje Player_PlayNextTrack, przesuwając MusicBee, gdy użytkownik chciał tylko przewinąć. F37 stempluje lastUserInitiatedSeek w Seek() i dodaje 5-sekundową blokadę w handlerze Stopped (odzwierciedlając istniejące okno lastUserInitiatedStop).

Dlaczego: ciche przewijanie do następnego utworu podczas przewijania to jeden z tych błędów, których nikt nie potrafi odgadnąć przyczyny - użytkownik myśli "dziwne, próbowałem przewinąć do przodu, a teraz odtwarza następny utwór". Naprawienie jest mechaniczne: ten sam wzorzec, co już istniejące rozróżnienie zatrzymania przez użytkownika.


F38 - Ulepszona obsługa przewijania dla kodeków podatnych na awarie

Co: awaria BubbleUPnP podczas przewijania MP3 była kanonicznym objawem. Po audycie, obecny kod przewijania yaiol już robi właściwe rzeczy - ścieżka natywna prawidłowo obsługuje HTTP Range (206, Content-Range, AcceptRanges), ścieżka zakodowana reklamuje X-AvailableSeekRange i analizuje przychodzące nagłówki timeSeekRange.dlna.org / npt, flagi DLNA.ORG_OP odzwierciedlają rzeczywiste możliwości strumienia (z opcją DisablePcmTimeSeek dla problematycznych urządzeń Platinum). Testowane przez użytkowników na aktualnym BubbleUPnP 4.6.4: nie zaobserwowano awarii.

Dlaczego: awaria BubbleUPnP podczas przewijania MP3 została zgłoszona około 2024 roku, a aplikacja od tego czasu otrzymała około 16 miesięcy poprawek. F29 (zakodowane MP3 OP=11) było nową zmienną, która mogła ją ponownie ujawnić; nie robi tego, na testowanych wersjach.

Jeśli awaria powróci: kształt poprawki byłby przełącznikiem "ograniczone przewijanie" dla każdego profilu, który wymusza DLNA.ORG_OP=10 (tylko bajtowe) na oznaczonych kodekach - odzwierciedlając sposób, w jaki DisablePcmTimeSeek już działa dla PCM. Dodaj wtedy, a nie zapobiegawczo.


Interfejs użytkownika i logowanie

F39 - Przycisk "Dodaj" wybiera nowy profil

Co: kliknięcie "Dodaj" na liście profili urządzeń tworzy nowy profil ORAZ automatycznie go wybiera, dzięki czemu użytkownik może natychmiast edytować pola. Nasza refaktoryzacja okna dialogowego z sekcjami już to robi - zarówno ścieżka bezpośredniego dodawania, jak i ścieżka z szablonu kończą się Me.activeStreamingProfiles.SelectedIndex = Me.activeStreamingProfiles.Items.Count - 1. Sprawdzenie potwierdziło, że nasz fork już to obsługuje - nic do zmiany.

Dlaczego: mała niedogodność UX, która okazała się już nie być niedogodnością tutaj.


F40 - Większa maksymalna liczba połączeń + log ostrzegawczy

Co: limit równoczesnych strumieni wtyczki (SemaphoreSlim wokół Sockets_Stream_File / Sockets_Encoder_Start) był twardo zakodowany na 4. F40 sprawia, że jest on konfigurowalny przez użytkownika na stronie ustawień ogólnych (domyślnie 16, zakres 1-256), dodaje linię logu MaxConnections, gdy żądanie musi czekać na wolne miejsce, ORAZ wyświetla czerwoną odznakę ⚠ Max Conn w lewym dolnym rogu okna dialogowego ustawień, jeśli limit został osiągnięty co najmniej raz od uruchomienia MusicBee.

Dlaczego: gdy urządzenie wysyła równoległe żądania (niektóre Marantz/Linn podczas skanowania grafik, sondy metadanych BubbleUPnP obok aktywnego odtwarzania), dodatkowe żądania blokowały się cicho za semaforem - użytkownik widział "urządzenie wolne" bez widocznej przyczyny. Linia logu jest dobra do debugowania technicznego, ale użytkownicy nietechniczni nigdy nie czytają logów. Widoczna odznaka w oknie dialogowym ustawień sprawia, że warunek osiągnięcia limitu jest wykrywalny dla każdego, kto otworzy preferencje wtyczki.

Implementacja:

  • Scentralizowano oczekiwanie w WaitOnSendBarrier(logTag) w MusicBeeUpnp.vb; oba miejsca wywołań (MediaServerDevice.GetFile, Encoder.StartEncode) go używają.
  • Settings.MaxConnections zachowane w v8 schematu ustawień.
  • Plugin.MaxConnectionsHit to trwała flaga sesji ustawiana w WaitOnSendBarrier; resetuje się tylko po restarcie MusicBee.
  • SettingsDialog.maxConnectionsBadge to czerwona, pogrubiona etykieta na (16, 410), która wyświetla się tylko wtedy, gdy Plugin.MaxConnectionsHit jest True. Ma podpowiedź wyjaśniającą przyczynę i rozwiązanie.
  • Semafor jest inicjalizowany raz przy ładowaniu typu, więc zmiana ustawienia wymaga restartu MusicBee (zaznaczone w etykiecie pola).

F41 - Logowanie "kodowanie z powodu ReplayGain/DSP"

Co: zamiast oddzielnych linii logu "kodowanie dla RG" / "kodowanie dla DSP", pojedyncza linia StreamDecision z F42 zawiera MB-DSP/EQ, MB-ReplayGain, Profile-DSP/EQ, Profile-ReplayGain jako skumulowane powody. Ta sama wartość diagnostyczna, mniej szumu.

Dlaczego: użytkownicy widzą wszystkie powody transkodowania dla danego utworu w jednej linii logu, a nie rozproszone. Zobacz F42, aby uzyskać pełne szczegóły.


F42 - Logowanie "renderer nie obsługuje kodeka źródłowego"

Co: dodano linię logu StreamDecision dla każdego utworu odtwarzanego na urządzeniu, która mówi "natywny KODEK" lub "transkoduj KODEK→KODEK powód=…". Pole powodu gromadzi każdy warunek, który wyzwolił transkodowanie: MB-DSP/EQ, MB-ReplayGain, Profile-DSP/EQ, Profile-ReplayGain, WebFile, VirtualFile, ForceTranscoding(global), SampleRate<min/SampleRate>max, DownmixToStereo, DeviceLacksCodec(X), BandwidthConstrained.

Dlaczego: użytkownicy byli zdezorientowani nieoczekiwanymi skokami procesora na plikach, które spodziewali się strumieniować natywnie. Jedna linia logu na utwór dokładnie informuje ich, który warunek spowodował transkodowanie - a jeśli pole pokazuje DeviceLacksCodec(Flac), natychmiast wiedzą, że informacje o protokole urządzenia były niekompletne i mogą chcieć, aby zadziałało awaryjne przełączanie F32.

Implementacja: pojedynczy ciąg akumulujący budowany przyrostowo przez łańcuch decyzyjny; logowany raz na końcu. Ograniczony przez Settings.LogDebugInfo, aby uniknąć szumu w logach w produkcji.


F43 - Log SetNextAVTransport pokazuje adres URL źródła

Co: wpisy logu QueueNext zawierają teraz source=<ścieżka biblioteki MusicBee> obok stream=<adres URL strumieniowania HTTP>. Ta sama zmiana zastosowana do ścieżki sukcesu i ścieżki błędu (QueueNext:Failed).

Dlaczego: podczas debugowania problemu z zakolejkowanym utworem, adres URL strumieniowania (/encode/aabbccdd0.flac) jest sam w sobie nieprzejrzysty - taki sam dla każdego utworu. Adres URL źródła to czytelna dla człowieka ścieżka biblioteki, która dokładnie informuje, który plik MusicBee próbował zakolejkować.


F44 - Lepsze logowanie błędów typu mime

Co: dwa nowe wpisy w dzienniku podczas Activate:

  • Activate:MimeUnverified - uruchamia się dla każdego źle sformułowanego wpisu w odpowiedzi GetProtocolInfo urządzenia, nazywając, który wpis nie mógł zostać przeanalizowany (aby użytkownik mógł zobaczyć np. "Marantz zwrócił http-get:*::* dla jakiegoś kodeka - możliwość jest niezweryfikowana, awaryjne przełączanie F32 zgadnie").
  • Activate:NoSinkInfo - uruchamia się raz, jeśli urządzenie w ogóle nie zwróciło elementu <Sink>. Oznacza to, że SupportedMimeTypes pozostaje Nothing, a IsCodecSupported pogarsza się do "założenia, że wszystko działa" - przydatny kontekst, gdy później pojawiają się błędy "urządzenie odmówiło strumienia".

Dlaczego: przed F44 te ciche awaryjne przełączania możliwości pozostawiały użytkowników zgadujących, dlaczego ich utwory były albo transkodowane wbrew oczekiwaniom, albo odrzucane przez urządzenie. Teraz pojedyncze wyszukiwanie Activate: pokazuje, czy informacje o możliwościach urządzenia były użyteczne.


F45 - Lepsze logowanie błędów metadanych

Co: dziennik wyjątków Browse w ContentDirectoryService.vb został już wzbogacony we wcześniejszych pracach yaiol (sesja błędów Alia Vox) o ObjectID i ślad stosu. F45 rozszerza go dalej o BrowseFlag (metadane vs dzieci), Filter (które atrybuty żądał klient), sortCriteria i partialResultLength (ile bajtów DIDL zostało wyprodukowanych przed awarią - wskazuje, jak daleko w partii znajduje się zły utwór).

Dlaczego: gdy coś pójdzie nie tak w trakcie DIDL, wartość partial-length informuje, czy awaria nastąpiła na pierwszym utworze partii (partial=0), czy w trakcie (partial=N) - w połączeniu z startingIndex partii, można zidentyfikować indeks problematycznego utworu. Filter i BrowseFlag wyjaśniają, jakiego rodzaju przeglądania chciał klient; czasami przeglądanie tylko metadanych kończy się niepowodzeniem, gdzie przeglądanie dzieci dla tego samego ID kończy się sukcesem.


Sieć

F46 - Tryb automatyczny ogłasza się tylko na adapterach prawdziwej sieci

Co: w trybie interfejsu Automatycznym wtyczka ogłaszała się wcześniej (SSDP) na każdym działającym adapterze IPv4. Na komputerze, na którym działa także tunel VPN (NordLynx) lub przełącznik wirtualny (Hyper-V / WSL / Docker), ta sama biblioteka była ogłaszana również na każdym z tych adapterów, więc punkt kontrolny, z którego przesyłasz, odkrywał server dwa lub trzy razy i wyświetlał bibliotekę jako zduplikowane kopie. Tryb automatyczny zachowuje teraz tylko adaptery, które mają prawdziwą domyślną bramę IPv4 (HasIPv4Gateway) - której adaptery tuneli i przełączników wirtualnych nie mają - więc są one usuwane z listy ogłaszania. Adres przypięty przez użytkownika nadal wygrywa bezwzględnie (ogłaszanie tylko na tym interfejsie), a jeśli żaden adapter nie zgłasza bramy, selektor wraca do wszystkich adapterów, więc lista ogłaszanych adresów nigdy nie jest pusta i wtyczka nie może stać się niewidoczna.

Dlaczego: duplikat nie jest spowodowany tym, że „jesteś na VPN" - jest spowodowany jednoczesnym ogłaszaniem na adapterze LAN i adapterze tunelu/wirtualnym, przez co jeden punkt kontrolny widzi ten sam server pod dwoma adresami. Konsumencki VPN (NordVPN/NordLynx) tuneluje tylko ruch kierowany do internetu; renderer DLNA żyje w sieci LAN, a ruch lokalnej podsieci omija tunel, więc adapter tunelu i tak nigdy nie dociera do renderera - usunięcie go usuwa kopię-widmo, nigdy działającą ścieżkę. Test bramy to tani, niezawodny sygnał odróżniający prawdziwy adapter LAN/Wi-Fi od tunelu lub przełącznika wirtualnego. Uzupełnia N05 (który naprawił, jak ogłoszenia są wysyłane na takich łączach - multicast zamiast broadcastu); F46 decyduje, na których adapterach w ogóle się ogłaszać.

Znane ograniczenie: VPN typu mesh / zdalnego dostępu (Tailscale, ZeroTier, WireGuard do domu), którego renderery naprawdę żyją po drugiej stronie tunelu, zwykle prezentuje adapter bez domyślnej bramy, więc tryb automatyczny również go odrzuca. Tacy użytkownicy przypinają zamiast tego adres VPN, który ma pierwszeństwo przed filtrem bramy.