MusicBee UPnP Plugin Help

Wat is nieuw

2.0.2 - 2026-07-20

  • Een sjabloon kan niet langer worden verwijderd zolang er nog knooppunten aan gekoppeld zijn — de verwijderknop blijft eenvoudigweg uitgeschakeld, zodat een knooppunt nooit wees blijft. Elke sjabloonrij toont nu een live (n) telling van de gekoppelde knooppunten, wat een uitgeschakelde verwijderknop in één oogopslag verklaart; het verwijderen van een ongebruikt sjabloon blijft nu ook, in plaats van dat de standaardinstellingen stilletjes opnieuw verschijnen bij de volgende keer laden.
  • Een nieuwe trechter-knop naast de Weergaveboom toont precies welke knooppunten een sjabloon volgen: het filtert de boom tot alleen de volgers van het geselecteerde sjabloon, filtert opnieuw wanneer u andere sjablonen selecteert, en herstelt de volledige boom wanneer het wordt uitgeschakeld.
  • De knooppunten Radio en Podcasts zijn nu permanent gekoppeld aan het sjabloon van hun categorie — pas het sjabloon aan op het tabblad Paden en het knooppunt volgt vanzelf; er is niets toe te passen, dus de Toepassen-knop is voor hen uitgeschakeld. De sjabloonlijst weerspiegelt dit met twee banden, Standaard (waar alle nieuwe sjablonen worden gemaakt) en Gereserveerd (Radio + Podcasts).

2.0.1 - 2026-07-20

  • Pad-sjablonen zijn nu live gekoppeld aan de knooppunten die ze gebruiken. Door een sjabloon toe te passen, volgt het knooppunt het: bewerk het sjabloon later en elk knooppunt dat het volgt, wordt onmiddellijk opnieuw gevormd – u hoeft het niet op te sporen en knooppunt voor knooppunt opnieuw toe te passen. De weergaveboom toont welk sjabloon elk knooppunt volgt direct na de naam, een hernoeming verschijnt daar direct, en bij het verwijderen van een sjabloon wordt eerst aangegeven hoeveel knooppunten het volgen (ze behouden hun huidige lay-out en stoppen eenvoudigweg met het volgen van iets).
  • Het toepassen van een sjabloon op een verborgen knooppunt maakt het ook weer zichtbaar – toepassen is het gebaar "toon me dit, zo gevormd", terwijl verbergen blijft bij het selectievakje Zichtbaar. Het gereserveerde "Verborgen" sjabloon dat dit vervangt, is verdwenen.
  • De weergaveboom verliest uw plaats niet langer: vinkjes, uitgevouwen mappen en scrollpositie overleven allemaal het toepassen van sjablonen en andere vernieuwingen.

2.0.0 - 2026-06-16

Dit is de eerste openbare release van de open-source yaiol fork van de MusicBee UPnP-plugin. Het wordt in twee delen gepresenteerd: alles wat nieuw is in deze fork, en vervolgens de fixes en verbeteringen die aan de originele plugin zijn aangebracht. Elk item behoudt de Wat / Waarom-vorm van de interne featurecatalogus van het project, zodat de redenering achter elke wijziging op de pagina staat, en niet alleen de wijziging.

Nieuw in deze fork

MediaRenderer - afspelen naar MusicBee

N01 - MusicBee als afspeel-renderer

Wat: normaal gesproken werkt deze plugin één kant op: een telefoon of ander apparaat bladert door de bibliotheek van MusicBee en speelt de muziek op zichzelf af. Deze functie voegt de tegenovergestelde richting toe - het laat MusicBee de speler zijn. Vanuit een controller-app op je telefoon (zoals BubbleUPnP) kun je je desktop MusicBee kiezen als het apparaat dat afspeelt, en het vervolgens vanuit je hand bedienen: afspelen, pauzeren, stoppen, vooruit of achteruit springen, naar een punt in het nummer springen, en het volume wijzigen of dempen.

Waarom: het verandert je telefoon in een afstandsbediening voor de muziek die al op je pc staat. Ga op de bank zitten, blader door je bibliotheek op de telefoon, tik op een nummer, en het komt uit de luidsprekers die aan je desktop zijn gekoppeld - met volledige controle vanaf waar je zit. De originele plugin heeft dit nooit als een werkende functie geleverd.

Inschakelen: het staat standaard uit, omdat het inschakelen ervan alles op je thuisnetwerk afspelen op je pc toestaat. Je schakelt het in met een vinkje op het tabblad Algemeen van het instellingendialoogvenster. De drie rollen van de plugin hebben elk hun eigen vinkje daar - mijn bibliotheek delen (Server), anderen naar mij laten afspelen (Renderer), en afspelen naar andere apparaten (Control Point) - en het dialoogvenster toont alleen de instellingentabbladen die de rollen die je hebt ingeschakeld daadwerkelijk nodig hebben, zodat je nooit geconfronteerd wordt met opties die niet op jou van toepassing zijn.

Je machines uit elkaar houden: je kunt de renderer elke naam geven die je wilt (het begint als "MusicBee (yaiol)"). Die naam is wat verschijnt in de lijst met afspeeldoelen van je telefoon, dus wanneer meer dan één pc MusicBee draait, kun je zien welke welke is. Een naamswijziging wordt onmiddellijk van kracht, zonder herstart.

Best mogelijke geluid bij zelf afspelen: wanneer je door MusicBee's eigen bibliotheek bladert vanaf je telefoon en een nummer terugstuurt naar die zelfde MusicBee, herkent de plugin dat er wordt gevraagd om een van zijn eigen bestanden af te spelen en speelt het eenvoudigweg rechtstreeks vanaf je schijf af. Het resultaat is exact en direct - bit-perfect, met MusicBee's eigen equalizer en volumeregeling toegepast - in plaats van zinloos de audio het netwerk op te sturen en direct terug naar zichzelf.

Privé draaien: de drie rollen werken onafhankelijk, dus je kunt de renderer inschakelen terwijl het delen van de bibliotheek uitgeschakeld blijft. In die "alleen renderer"-opstelling blijft je bibliotheek volledig verborgen voor het netwerk - alleen het afspeeldoel wordt aangekondigd - en MusicBee zal nooit aanbieden om naar zichzelf af te spelen.


Afspeelgedrag

N02 - 5.1 FLAC niet automatisch gedownmixt

Wat: de kanaal-aantal-beperking in MediaServerDevice.GetEncodedFile was If StereoOnly OrElse Not isPcmData Then channelCount = 2. De Not isPcmData-clausule downmixte stilzwijgend elke niet-PCM-transcode (FLAC, MP3, AAC, Ogg) naar stereo, ongeacht het aantal bronkanalen, waardoor 5.1-compatibele renderers werden verslagen wanneer de bron 5.1 FLAC was. Nu sluit de tweede clausule FLAC uit: Not isPcmData AndAlso encoder.Codec <> FileCodec.Flac. FLAC 5.1 wordt doorgelaten; MP3/AAC/Ogg forceren nog steeds stereo omdat MusicBee's command-line encoders voor die formaten 2-kanaals invoer verwachten.

Waarom: het hele punt van het transcoderen van een 5.1 FLAC-bron naar FLAC-uitvoer is om de meerkanaalsmix te behouden. Stille downmix maakte de FLAC-transcode-optie nutteloos voor surround-luisteren. Met N02 doet het het juiste.


Architectuur

De structurele verandering die de fork levensvatbaar maakt op een grote bibliotheek - afwezig in de originele plugin.

N03 - Luie (on-demand) bladerboom

Wat: de originele plugin bouwde de hele bladerboom bij het opstarten van MusicBee - elk nummer opsommen, volledige Library_GetFileTags per bestand, de hele containerhiërarchie samenstellen - voordat de HTTP-poort werd geopend. Op een echte bibliotheek (50k+ nummers, 5400 podcastafleveringen, honderden zenders) zijn dat minuten koude start, en de boom blijft voor altijd in RAM, inclusief takken die geen enkele client ooit opent. Deze fork bouwt niets vooraf: de root exposeert één L:-voorvoegsel placeholder per eindpunt (L:music, L:podcast, L:filter:…); elk niveau wordt alleen berekend wanneer een client erin bladert (LazyBrowseEnsureLazyEndpointInMemory → caches per niveau), en bibliotheekwijzigingsmeldingen wissen de caches (SetLibraryDirty).

Waarom: koude start is in wezen direct - de HTTP-poort is open tegen de tijd dat MusicBee de plugin-init voltooit - en het geheugen blijft proportioneel aan wat is doorzocht, niet aan de bibliotheekgrootte. Afweging: de eerste keer bladeren in een eindpunt betaalt de laadkosten; opnieuw binnenkomen wordt gecached tot de volgende bibliotheekwijziging. Dit is de basis waar al het andere van afhangt. Volledige notities: FIXES.md.


Netwerken & robuustheid

Versteviging van het bindpad van de HTTP-server. De originele plugin sterft stilzwijgend wanneer zijn poort niet beschikbaar is.

N04 - Zelfherstellende HTTP-poortbinding

Wat: de HTTP-server van de plugin sterft niet langer wanneer de geconfigureerde poort niet beschikbaar is. Drie gekoppelde wijzigingen:

  1. Automatische terugval bij bindfout. HttpServer.Start probeert de geconfigureerde poort, en scant bij SocketException omhoog tot 20 poorten voor de eerste vrije. De daadwerkelijk gebonden poort wordt vastgelegd in een nieuwe Plugin.boundServerPort, en alles wat de server adverteert - SSDP LOCATION URL's (NOTIFY + M-SEARCH-antwoord), de apparaat-URL (PrimaryHostUrl), de router-poort-forward, en de SSDP/control-point zelf-filters - leest nu boundServerPort in plaats van Settings.ServerPort. UPnP-clients ontdekken de echte poort via SSDP, dus een verplaatste poort is transparant voor renderers.
  2. Gebruikersmelding. Wanneer een terugval plaatsvindt (de opgeslagen poort is niet de poort die in gebruik is), vertelt een gelokaliseerde MessageBox (WarnPortInUse) de gebruiker welke poort daadwerkelijk wordt gebruikt en dat apparaten deze nog steeds zullen vinden - omdat de plugin headless draait en een in-dialoogvenster-bericht alleen zou worden gezien door iemand die al een probleem vermoedde.
  3. Herstel na herstart. RestartServer (het herstartpad voor het opslaan van instellingen) derefereerde Plugin.controller / Plugin.server blindelings. Als de initiële Initialise een fout veroorzaakte voordat ze werden gemaakt (precies wat een mislukte binding veroorzaakte), veroorzaakte de volgende instellingen-opslaan een NullReferenceException - waardoor een halfdode plugin achterbleef. Het maakt ze nu opnieuw aan en start ze wanneer Nothing, dus het opslaan van een werkende poort herstelt de plugin zonder een volledige MusicBee-herstart.

Waarom: de trigger was een echt gebruikersincident. De oude standaardpoort 49382 bevindt zich in het Windows dynamische bereik (49152-65535), waar Hyper-V/WSL2/Docker/WinNAT grote blokken reserveren die bij elke opstart verschuiven - dus de binding mislukte met WSAEACCES ("toegang verboden") op een machine waar het maandenlang had gewerkt. Het wijzigen van de standaard naar een vrije poort botste vervolgens met Serviio (een afzonderlijke DLNA-server die al op de nieuwe poort draaide), wat mislukte met WSAEADDRINUSE. Elke fout werd opgeslokt in Initialise, waardoor de plugin stilzwijgend dood was en vervolgens NRE'de bij de volgende instellingen-opslaan. Na N04 herstelt een poortbotsing zichzelf - de server blijft draaien op de volgende vrije poort, de gebruiker wordt geïnformeerd, en clients ontdekken het opnieuw - in plaats van de hele plugin uit te schakelen.

Implementatie:

  • Standaardpoort verplaatst 493829779 (onder het dynamische bereik, dus Windows reserveert het nooit automatisch; geen bekende media-server standaard) in alle drie de ServerPort-declaraties + de instellingen-parse-fout-terugval.
  • Plugin.boundServerPort (nieuw gedeeld veld) bevat de live luisterpoort; activeServerPort blijft de geconfigureerde snapshot zodat de "Herstart Vereist"-badge-logica niet vals afgaat bij een terugval.
  • HttpServer.PortScanRange = 20; de scan stopt bij de eerste succesvolle TcpListener.Start() en werpt de laatste uitzondering alleen als alle pogingen mislukken.
  • Nieuwe EN-resource-sleutel WarnPortInUse (vertalingen volgen de locale-pass op publicatietijd).

N05 - SSDP-aankondigingen via de multicastgroep (VPN / point-to-point)

Wat: SSDP-aankondigingen worden verzonden naar de UPnP-multicastgroep (239.255.255.250) in plaats van een IP-broadcastadres. De onschadelijke "kan geen toegang krijgen tot een verwijderd object"-fout die wordt gelogd wanneer een SSDP-zoekreactie een serverherstart racet, wordt ook onderdrukt.

Waarom: op point-to-point / VPN-netwerkadapters is IP-broadcast niet van toepassing - de oude broadcast-verzending mislukte met "ongeldig argument" en aankondigingen werden gemist, dus de plugin was onzichtbaar voor clients op die verbindingen. Aankondigen aan de juiste multicastgroep lost de detectie op precies die adapters op.

Bibliotheeknavigatie

Deze zijn geleverd in deze fork en bevinden zich niet in de originele plugin. Ze kwamen voort uit het daadwerkelijk bladeren door de eigen uitvoer van de plugin vanuit echte UPnP-clients.

N06 - Filtergebaseerde bibliotheekblootstelling

Wat: de filtertabbladen van MusicBee (.xautopf-bestanden in de MusicBee-map van de gebruiker) worden UPnP-rootcontainers in de bibliotheek van de plugin. De nummers van elk filter zijn vervolgens doorzoekbaar in een hiërarchie van AlbumArtistSort → Album → Tracks.

Waarom: gebruikers met samengestelde MusicBee-filters (bijv. "5-sterren nummers", "Recent toegevoegd", "Klassiek → Barok") verwachten deze te vinden bij het bladeren door de plugin vanuit een UPnP-client. De originele plugin exposeerde alleen de ruwe bibliotheekboom.


N07 - SortAlbumArtist-veldbedrading

Wat: de plugin leest nu MusicBee's MetaDataType 165 (Sort Album Artist) en gebruikt dit om artiesten te groeperen/sorteren in bladerweergaven.

Waarom: hi-fi browsers en audiofielen gebruiken sorteer-artiestnamen ("Beethoven, Ludwig van" in plaats van "Ludwig van Beethoven") om bibliotheken te organiseren. Standaard verwachting voor serieuze luisteraars. Ontbreekt in beide upstreams.


N08 - Afhandeling van meerwaardige AlbumArtist

Wat: wanneer het AlbumArtist-veld van een album meerdere artiesten bevat, gescheiden door "; " (bijv. "yaiol; Ars Ricercata"), verschijnt het nummer nu onder elke artiest in bladerweergaven, niet onder een enkele Frankenstein-artiest die de namen combineert.

Waarom: samenwerkingsalbums en compilaties moeten onder elke medewerker verschijnen. Zonder dit zijn de helft van de zoekpaden om het album te vinden verbroken.


N09 - Album-container artwork (upnp:albumArtURI)

Wat: albumcontainernodes in DIDL Browse-antwoorden bevatten nu een upnp:albumArtURI-element dat verwijst naar de albumhoes.

Waarom: zonder dit toont elk album in de bladerweergave van een UPnP-client een generiek pictogram in plaats van de albumhoes. Visuele aanwijzing voor navigatie; verwacht door elke moderne hi-fi browser.


N10 - Nummerordening binnen filteralbums

Wat: nummers binnen een filter-geëxposeerd album worden nu gesorteerd op Disc Nummer, vervolgens op Track Nummer.

Waarom: standaard albumvolgorde. Zonder expliciete sortering kwamen nummers terug in welke volgorde het filter ze toevallig retourneerde - meestal willekeurig ogend.


N11 - Playlist-mapboomfix

Wat: de functie LoadLibraryPlaylists (oorspronkelijk van Steven Mayall, ~2014) slaagde er niet in om af te dalen in nieuw aangemaakte playlist-mappen. De eerste playlist in elke map, plus eventuele submappen, eindigde als wees op het rootniveau.

Waarom: aanwezig in de originele plugin gedurende elf jaar. Zichtbaar binnen 30 seconden na het openen van BubbleUPnP en klikken op Playlists. Opgelost in yaiol door correct te recursief af te dalen in nieuw aangemaakte mappen tijdens de boomconstructie.


N12 - XML-illegale controlekarakter-sanering

Wat: elk nummer met een tag die een C0-controlekarakter bevat (bijv. 0x19 van een slechte coderingspas - UTF-8 → Latin-1 → terug afkappen van 0x99 naar 0x19) zorgde ervoor dat de hele Browse-respons mislukte met Action Failed zodra het slechte nummer een gepagineerde batch binnenging.

Waarom: XML 1.0 verbiedt de meeste C0-controlekarakters, en XmlWriter werpt een uitzondering wanneer gevraagd wordt om er een te schrijven. Aanwezig in de originele plugin. Opgelost door ongeldige karakters te strippen bij elk Library_GetFileTags-uitgangspunt via XmlConvert.IsXmlChar.


N13 - Radio-lijst deterministisch over gepagineerde Browse

Wat: Browse voor de Radio-container viel in de generieke bestandslijst-tak, die files.Sort(AlbumFileComparer) aanriep bij elke aanroep. Radio-items hebben lege Album/Disc/Track-tags, dus elke vergelijking retourneerde 0 - List(Of T).Sort is instabiel, wat een andere volgorde oplevert bij elke aanroep. UPnP-control points pagineren (BubbleUPnP haalt 0..15 op, dan 16..einde); tussen de twee aanroepen werd de lijst herschikt, zodat sommige zenders op beide pagina's verschenen (duplicaten) en sommige op geen van beide (ontbrekend) - wat er bij elke vernieuwing willekeurig uitzag.

Waarom: aanwezig in de originele plugin (de auteur bladert nooit radio via UPnP). Hier opgelost met een speciale ContainerCategory.Radio-tak in Browse, geen sortering per aanroep; radioFiles wordt eenmaal bij het laden gesorteerd op Titel (stabiel). Gepagineerd bladeren ziet nu een deterministische volgorde; pagina 1 en pagina 2 zijn disjunct.


N14 - UPnP Search album-klasse retourneert albumcontainers

Wat: UPnP Search voor album-klasse queries (upnp:class = "object.container.album.musicAlbum", bijv. BubbleUPnP's "Random Albums") retourneerde de volledige tracklijst in plaats van albumcontainers, dus de client toonde nul albums. De originele handler parseerde alleen tussen haakjes geplaatste criteria, en dumpte vervolgens alle tracks ongeacht de gevraagde klasse.

Waarom: hier opgelost - album-klasse queries sommen nu afzonderlijke albums op (gegroepeerd op AlbumArtist+Album) en zenden elk uit als een correcte musicAlbum-container met hoesafbeelding, adresseerbaar via de virtuele-ID-ruimte Salb<idx> zodat de client in een resultaat kan doorklikken en het kan afspelen.


N15 - Werkende, scope-bewuste UPnP-zoekfunctie met doorklikken

Wat: de originele adverteerde geen zoekmogelijkheden (GetSearchCapabilities retourneerde leeg), dus clients weigerden zelfs een zoekopdracht te verzenden; en de oude backend las uit musicFiles, permanent leeg in het lazy-tree-tijdperk. Deze fork adverteert de echt doorzoekbare eigenschappen, implementeert tracks-op-titel en albums-op-titel tegen de lazy-bibliotheek (HandleLazySearch), beperkt de query tot de huidige tak van de client wanneer een echte container-ID wordt verzonden (anders vervangt L:music zodat top-bar-zoekopdrachten geen podcast/radio/audioboek-ruis meeslepen), en maakt albumresultaten klikbaar via synthetische Ssrch_alb_*-ID's die een Browse-vroege-tak terugkoppelt naar de tracks van het album. (Het deel met album-klasse-resultaten-als-containers is N14.)

Waarom: zoeken in BubbleUPnP ging van "Bibliotheek ondersteunt geen zoekfunctie" naar het retourneren van nuttige, gescoped, afspeelbare resultaten. Volledig ontwerp + afgewezen benaderingen: SEARCH.md.


N16 - UPnP-cache-invalidatie (SystemUpdateID)

Wat: de originele retourneerde een constante SystemUpdateID=0 - het UPnP ContentDirectory cache-invalidatiecontract - dus spec-compatibele clients (BubbleUPnP) behandelden de bibliotheek als nooit-veranderend: verouderde bladerresultaten, 404-thumbnails na een URL-schemawijziging, en de "herstart MusicBee twee keer om wijzigingen te zien"-dans. Deze fork zaait SystemUpdateID van epoch-seconden bij het laden (dus elke herstart is strikt vooruit op de laatste) en verhoogt het bij elke bibliotheekmutatie en instellingenwijziging (SetLibraryDirty / ResetCacheBumpSystemUpdateId).

Waarom: clients pikken bewerkingen, nieuwe bestanden en instellingenwijzigingen betrouwbaar op bij hun volgende bladeractie. Bekende beperking: geabonneerde clients krijgen de nieuwe waarde niet actief opnieuw gepusht via GENA (geparkeerd als toekomstig werk); ze zien het nog steeds bij hun volgende bladeractie.


N17 - Podcast-abonnement artwork

Wat: podcasttegels toonden geen afbeeldingen - elke /PodcastThumbnail/-aanvraag 404'de. Twee gestapelde bugs: de resolutieketen controleerde nooit MusicBee's daadwerkelijke artworkcache (%LocalAppData%\MusicBee\InternalCache\Subscriptions\<naam>.jpg, waar de desktop-UI van laadt), en de unescape+lowercase van de HTTP-laag verminkte de feed-URL-route-sleutel tot het laatste padsegment. Deze fork lost artwork op vanuit MB's InternalCache en routeert opzoekingen via een URL-veilige slug die de HTTP-laag intact overleeft (PodcastSlug / podcastSubIdBySlug).

Waarom: abonnementsartwork wordt nu weergegeven in bladerweergaven (alle 22 eerder 404'ende aanvragen worden opgelost).


N18 - Hiërarchisch (afgebakend) tag-bladeren

Wat: elk veld kan als hiërarchisch worden gemarkeerd op het tabblad Bibliotheekopties en een één-karakter scheidingsteken krijgen (een veldkiezer + scheidingstekenbox met toevoegen/verwijderen, opgeslagen in de plugin-instellingen). Stel Groepering in op / en een waarde zoals Jazz/Cool Jazz bladert dan als Jazz › Cool Jazz in plaats van één platte invoer. Nummers die precies op een tak zijn getagd (alleen Jazz) krijgen hun eigen [Jazz]-node zodat niets verborgen blijft, een tak met een enkel kind klapt vanzelf in, en ; wordt geweigerd als scheidingsteken omdat het MusicBee's eigen multi-waarde scheidingsteken is.

Waarom: diepe tag-taxonomieën die een gebruiker al heeft gecodeerd in een enkel veld (genre-bomen, stemming-hiërarchieën, "Klassiek/Barok/Concerto") bladeren eindelijk als de boom die de tag beschrijft, in plaats van een platte muur van schuin-gescheiden strings die de gebruiker van begin tot eind moet lezen.


N19 - Enkel rootpad gelabeld met zijn groeperingsveld

Wat: een enkel bladerpad op de root wordt gelabeld met zijn groeperingsveld (bijv. "Genre") in plaats van zijn volledige korte pad, overeenkomend met hoe samengevoegde eerste-veldgroepen worden benoemd.

Waarom: de bladerboom leest consistent - één naamgevingsregel, of een root-item nu alleen staat of is samengevoegd met broers en zussen (N20) - in plaats van een alleenstaand root-item dat een uitgebreid intern pad toont terwijl zijn samengevoegde buren een schone veldnaam tonen.


N20 - Bladerpaden samenvoegen die een eerste veld delen

Wat: twee bladerpaden die hetzelfde eerste veld delen - "Genre / Sorteer Albumartiest" en "Genre / Podcastmensen" - worden samengevoegd tot één enkele Genre-rootmap die eerst de genrewaarden weergeeft en vervolgens opsplitst in de twee weergaven, in plaats van twee bijna-dubbele "Genre / …"-items naast elkaar op de root.

Waarom: een gebruiker met verschillende gerelateerde weergaven genest onder een gemeenschappelijk veld zag de root rommelig met bijna identieke top-level items. Door ze samen te voegen blijft de root leesbaar en groepeert de gerelateerde weergaven waar ze thuishoren - onder hun gedeelde veld.


N21 - Categorie-getypeerde bladerpaden (Standaard / Radio / Podcast)

Wat: elk bladerpad is getypeerd per categorie - Standaard, Radio of Podcast. De sjabloonlijst is gegroepeerd in die drie secties, de veldkiezer van elk sjabloon biedt alleen de velden die de gegevens van die categorie daadwerkelijk kunnen leveren, en een sjabloon kan alleen worden toegepast op overeenkomende knooppunten in de weergaveboom (incompatibele knooppunten worden grijs en kunnen niet worden aangevinkt). De gereserveerde Radio- en Podcasts-sjablonen kunnen niet worden verwijderd, dus hun categoriesectie verdwijnt nooit.

Waarom: zonder de typering zou een gebruiker een lay-out kunnen bouwen die stilzwijgend leeg blijft - een radiostation heeft geen "album", een podcastaflevering heeft geen "albumartiest" - en dit pas ontdekken door vanuit een UPnP-client naar een lege map te bladeren. Het beperken van het veldmenu en de toepassingsdoelen tot de werkelijke gegevens van de categorie maakt lege lay-outs onbouwbaar.


N22 - Podcasts groeperen op publicatiejaar

Wat: de publicatiedatum van elke podcastaflevering wordt ingelezen, zodat een podcast-bladerpad met een Jaar-niveau afleveringen per jaar groepeert in plaats van ze onder een enkele "Onbekend" samen te voegen.

Waarom: grote podcastabonnementen worden navigeerbaar per jaar, net als de rest van de bibliotheek, in plaats van dat elke aflevering in één ongedateerde hoop terechtkomt omdat de plugin nooit naar de publicatiedatum per aflevering keek.


N23 - Groeperingsniveaus met één resultaat samenvouwen

Wat: een groeperingsniveau dat resulteert in een enkele waarde - een Record Type-niveau dat alleen "LP" toont voor een artiest die alleen LP's heeft gemaakt, of een letter-niveau met een enkele letter - wordt automatisch overgeslagen, waardoor de gebruiker direct in de inhoud terechtkomt.

Waarom: bladeren door een map die precies één map bevat, is pure frictie. Het samenvouwen van het niveau met één keuze verwijdert de dode klik zonder te veranderen wat de gebruiker kan bereiken.


N24 - Jaargroepering/zoekopdracht tegen MusicBee's datumveld

Wat: de jaarconditie bevraagt MusicBee's volledige-datum "Jaar"-veld niet langer met een kale viercijferige waarde, en de hardgecodeerde jaar-veld-aliasing is verdwenen, zodat elk groeperingsveld nu generiek wordt opgelost vanuit de paddefinitie.

Waarom: voor bibliotheken waarvan de Jaar-tag een volledige datum bevat, retourneerde groeperen of zoeken op jaar voorheen niets - de viercijferige query kwam nooit overeen met het volledige-datumveld. Het bevragen van het juiste veld zorgt ervoor dat jaargroepering en zoeken de nummers weer vinden.


N25 - Aparte "Jaar" en "Jaar (jjjj)" groeperingsvelden

Wat: albumgroepering en bladerpaden tonen nu beide eigen jaarvelden van MusicBee - Jaar (de volledige datumtag) en Jaar (jjjj) (alleen het viercijferige jaar) - zodat de gebruiker een van beide kan kiezen bij het definiëren van een albumgroepering of een bladerpad.

Waarom: de twee velden betekenen verschillende dingen in MusicBee, en het samenvouwen ervan deed dat onderscheid teniet. Het tonen van beide stelt een gebruiker in staat om elke release van een jaar samen te voegen (jjjj) of de exacte-datumvolgorde te behouden (volledige Jaar-tag), zoals ze bedoelen.


N32 - Vastgezette filters en afspeellijsten per soort gegroepeerd in de root

Wat: een vastgezet filter verschijnt nu direct onder de map Filters in de bladerroot, en een vastgezette afspeellijst direct onder de map Afspeellijsten, in plaats van dat alle vastgezette items zich verzamelen in één klomp aan het einde van de root. Elke vastgezette snelkoppeling staat bij zijn eigen soort.

Waarom: naarmate je meer snelkoppelingen vastzet, wordt één klomp van gemengde filters en afspeellijsten aan het einde moeilijker te scannen, en scheidt die elke snelkoppeling van de map waar hij bij hoort. Vastgezette items groeperen onder hun eigen categorie houdt de root leesbaar en elke snelkoppeling naast de dingen waar hij bij hoort.

Instellingendialoogvenster & verpakking

N26 - Gedeeld instellingendialoogvenster

Wat: de Voorkeuren-pagina kreeg een linker-navigatie-indeling met secties: Algemeen / Afspelen / Bibliotheek / Apparaatprofielen / Diagnostiek.

Waarom: het origineel was een enkele lange platte lijst van elke instelling - prima voor de ontwikkelaar die het bouwde, verwarrend voor iedereen anders. Secties groeperen gerelateerde opties en laten het dialoogvenster meer aanvoelen als moderne app-instellingen.


Assembly + plugin hernoemen (geen F-id - verpakkingsnotitie)

Wat: de gecompileerde DLL heet mb_UPnP_yaiol.dll en de plugin rapporteert zichzelf als "MusicBee UPnP (yaiol)". Onderscheiden van de originele mb_Upnp.dll.

Waarom: gebruikers kunnen yaiol naast de originele plugin installeren en gedrag naast elkaar vergelijken.


Badge-systeem - runtime-status weergeven (mechanisme achter F40)

Wat: een generiek UI-patroon voor het weergeven van belangrijke runtime-condities als zichtbare gekleurde badges in het instellingendialoogvenster. Huidige instanties:

  • ⚠ Max Conn (N04) - wordt geactiveerd wanneer de maximale-verbindingen-limiet ten minste één keer is bereikt sinds MusicBee is gestart. Sticky sessie-vlag Plugin.MaxConnectionsHit. Ingesteld binnen WaitOnSendBarrier wanneer er geen slot vrij is.
  • ⚠ Herstart Vereist - wordt geactiveerd wanneer een opgeslagen instelling een MusicBee-herstart nodig heeft om van kracht te worden. Sticky sessie-vlag Plugin.RestartRequired. Ingesteld in de Save-handler van het dialoogvenster wanneer de nieuwe opgeslagen waarde verschilt van de runtime-snapshot (Plugin.activeMaxConnections, Plugin.activeServerPort, Plugin.activeIpAddress). Instellingen die een herstart vereisen, zijn beperkt tot die welke echt niet hot-reload kunnen worden - HTTP-server bind-parameters en de SemaphoreSlim die eenmaal bij Initialise is gebouwd.

Waarom: het logbestand van de plugin is prima voor technische gebruikers die debuggen, maar een niet-technische gebruiker die naar "apparaat klinkt verkeerd" of "afspelen is traag" staart, zal nooit Diagnostiek → Logboek bekijken openen. Badges vangen de gevallen op waarin de gebruiker moet weten dat er iets is gebeurd en tonen het de volgende keer dat ze de plugin openen - vindbaar zonder iets te lezen.

Herbruikbaar voor de toekomst:

  • Profielmismatch gedetecteerd (apparaat-user-agent kwam nooit overeen met een profiel, viel terug op Generiek).
  • NextURI backoff geactiveerd (F13 - gapless uitgeschakeld voor de sessie op een onbetrouwbaar apparaat).
  • Bibliotheekscan mislukt / gedeeltelijk.
  • Renderer-verbinding verloren tijdens de sessie.
  • Elke andere conditie waarbij "eenmaal gebeurd, gebruiker moet weten" beter is dan "stilzwijgend gelogd tussen 1000 andere regels".

Implementatieconventies:

  • Badge-labels bevinden zich op dialoogvensterniveau (niet binnen een paneel) zodat ze zichtbaar zijn, ongeacht welke sectie de gebruiker zich bevindt.
  • Gepositioneerd langs de onderste rij nabij Opslaan/Annuleren (huidig: y=410 horizontaal gestapeld).
  • Elke badge heeft een corresponderende sticky sessie-vlag in Plugin die True wordt wanneer de conditie optreedt en alleen wordt gereset bij MusicBee-herstart.
  • Resources: <Conditie>Badge (labeltekst, voorvoegsel met ⚠) + <Conditie>BadgeTip (tooltip die oorzaak + oplossing uitlegt).
  • Voor "opgeslagen instelling vereist herstart"-badges, neem een runtime-snapshot bij Plugin.Initialise() en vergelijk met Settings.* na Settings.SaveSettings() in de dialoogvenster Save-handler.

N27 - Annuleren negeert pad-/sjabloonbewerkingen

Wat: bewerkingen die zijn aangebracht in paden en sjablonen in het instellingendialoogvenster worden nu genegeerd wanneer de gebruiker op Annuleren klikt, in plaats van stilzwijgend toegepast te blijven, en elk gereserveerd sjabloon dat tijdens de sessie is verwijderd, wordt opnieuw aangemaakt. (Sjablonen worden anders live opgeslagen terwijl ze worden bewerkt - er is geen aparte Opslaan-knop op het tabblad Paden.)

Waarom: Annuleren moet annuleren betekenen. Voorheen ontdekten gebruikers die experimenteerden met pad-/sjabloonwijzigingen en zich terugtrokken dat de wijzigingen al waren doorgevoerd, zonder mogelijkheid om ze ongedaan te maken, behalve door elk handmatig opnieuw te doen.


N28 - Letterlijke ampersands in het veldkiezer-menu

Wat: een veld waarvan de naam "&" bevat - bijv. "Stemming & Context" - geeft de ampersand letterlijk weer in het veldkiezer-menu in plaats van het te slikken als een Alt-mnemonic-voorvoegsel.

Waarom: veldnamen met een ampersand werden verkeerd weergegeven (het teken verdween en de volgende letter werd een sneltoets), waardoor de menu-invoer moeilijk te herkennen was.


N29 - Stabiele, onvertaalde instellingenvenstertitel

Wat: de titel van het instellingenvenster is vastgezet op de merknaam "MusicBee UPnP Plugin" en varieert niet langer met de interfacetaal; de per-taal DialogTitle-string is uit elke locale-bundel verwijderd.

Waarom: een venstertitel die per taal van formulering veranderde, was een vertaalbaar oppervlak zonder voordeel - de titel is een merknaam. Het vastzetten ervan houdt het stabiel en consistent overal.

Lokalisatie

De originele plugin is alleen Engelstalig. Deze fork is volledig lokaliseerbaar - elke gebruikersgerichte string stroomt door een resourcebundel, en de plugin detecteert automatisch de eigen UI-taal van MusicBee.

N30 - Meertalige UI (vertalingen in afwachting)

Wat: de lokalisatiemachine is compleet en wordt geleverd. Localisation.vb leest de geselecteerde taal van MusicBee uit MusicBee3Settings.ini (<SystemLanguage> endoniem) en past de overeenkomende .NET-cultuur toe op de thread, zodat My.Resources.Resources.* de gelokaliseerde string retourneert. Elk gebruikersgericht label/knop/bericht is gekoppeld aan een resource-sleutel (designer-besturingselementen via ApplyDesignerExtras + sync-en-locale.js; runtime-strings zoals WarnPortInUse handmatig toegevoegd). Wat nog niet is gedaan, is de daadwerkelijke vertaling: alleen de Engelse bronbundel (Resources.resx) bestaat - de satellietbundels voor de andere talen worden in één batch-pass geproduceerd wanneer de plugin feature-compleet is (stuksgewijs vertalen terwijl strings nog veranderen, verspilt moeite).

Doeltalen (de set die MusicBee zelf aanbiedt, 1:1 overeenkomend met endonymToCulture zodat de plugin automatisch de taal van MusicBee volgt):

Arabisch (ar) Tsjechisch (cs) Duits (de) Grieks (el)
Spaans (es) Frans (fr) Hongaars (hu) Italiaans (it)
Koreaans (ko) Nederlands (nl) Noors (nb) Pools (pl)
Portugees BR (pt-BR) Portugees PT (pt-PT) Zweeds (sv) Turks (tr)
Oekraïens (uk) Russisch (ru) Japans (ja) Vereenvoudigd Chinees (zh-CN)
Traditioneel Chinees (zh-TW) Engels (en, bron)

Variantbeleid (volgens de werkruimte-locale-regel): PT en ZH zijn opgesplitst in afzonderlijke bundels omdat woordenschat/schrift echt divergeert (pt-BR/pt-PT, zh-CN/zh-TW). EN is een enkele bundel - MusicBee's "English(US)" (en-US) valt terug op en via .NET's cultuurketen, dus er wordt geen aparte US-bundel geproduceerd. ES en FR zijn eveneens single-locale.

Waarom: de instellingen van een UPnP-plugin ("gebruik geen ruwe PCM", "forceer little-endian PCM", poort-terugvalwaarschuwingen) zijn cryptisch genoeg in de moedertaal. Het volgen van MusicBee's eigen UI-taal - in plaats van Engels te forceren - is het verschil tussen een tool die een niet-Engelstalige gebruiker kan configureren en een die ze niet kunnen. Geen van beide upstreams heeft dit geprobeerd.


N31 - Help-link opent in de volledige interfacetaal

Wat: het openen van de Help-link vanuit de plugin respecteert de volledige interfacetaal van de gebruiker (bijv. pt-BR, zh-CN) in plaats van terug te vallen op de basistaal, en stuurt een duidelijkere update-check-identificatie.

Waarom: een gebruiker die MusicBee in een regionale variant (Braziliaans Portugees, Vereenvoudigd Chinees) draaide, werd naar de help-pagina in de basistaal gestuurd. Het meenemen van de volledige cultuur brengt hen naar de help-pagina in de exacte taal die zij gebruiken.

Fixes en verbeteringen aan de originele plugin

Kernprotocol & afspelen

F01 - Bijgewerkte standaard DLNA-apparaatprofielen

Wat: levert nieuwe standaardprofielen voor PlayStation 4, Xbox 360/One en moderne BubbleUPnP, met capaciteitsvlaggen (samplefrequenties, bitdieptes, codecs) die weerspiegelen wat die apparaten vandaag de dag daadwerkelijk ondersteunen.

Waarom: de standaardinstellingen van de originele plugin waren bevroren rond 2014. PS4/Xbox/BubbleUPnP hebben sindsdien hi-res audio-ondersteuning gekregen. Out-of-the-box speelt een nieuwe installatie best-in-class op deze apparaten zonder dat de gebruiker de apparaatprofielinstellingen hoeft aan te raken.


F02 - Besturingsapparaten die MediaRenderer:3 adverteren

Wat: de plugin onderzoekt de UPnP-servicebeschrijving van een renderer om te bepalen of MusicBee deze kan aansturen. Het origineel kwam alleen overeen met urn:schemas-upnp-org:device:MediaRenderer:1. Moderne apparaten adverteren :2 of :3. F02 verbreedt de overeenkomst.

Waarom: zonder dit verschijnen recente Sonos / WiiM / Eversolo-eenheden eenvoudigweg niet als doelen in MusicBee's "Afspelen naar"-apparatenlijst - hoewel ze hetzelfde protocol spreken. Een enkele string-prefix-match-fix ontgrendelt de hele moderne apparaatgeneratie.


F03 - "Forceer native stream" optie per profiel (standaard AAN)

Wat: wanneer aangevinkt, stuurt de plugin de originele bestandsbytes naar het apparaat zonder transcodering, geen DSP, geen ReplayGain-verwerking toegepast. Gewoon het ruwe bestand dat de gebruiker heeft gekozen, byte-voor-byte (modulo HTTP-framing).

Waarom: volgens forumgetuigenissen is dit de grootste winst in afspeelkwaliteit. Hi-fi gebruikers die dure renderers kopen, willen expliciet bit-perfecte uitvoer; elke DSP-aanraking doet het punt teniet. Standaard AAN omdat de meeste moderne apparaten elke codec aankunnen die de gebruiker erop gooit, en ReplayGain/EQ opt-in moeten zijn. Het is per profiel, zodat je transcodering kunt behouden voor een oude Xbox terwijl je native naar een hi-fi DAC stuurt.


F04 - "Forceer transcodering" per profiel

Wat: per-profiel overschrijving die elke stream naar dit apparaat door de transcoder forceert, ongeacht native-codec-ondersteuning. Het omgekeerde van F03 (ForceNativeStream). Wederzijds exclusief met F03 - de UI vinkt de andere automatisch uit wanneer een van beide wordt ingeschakeld.

Waarom: een enkele globale schakelaar zou tegenstrijdig zijn met de per-profiel ForceNativeStream (F03). Real-world geval: apparaat A is een hi-fi DAC die bit-perfecte native streams wil; apparaat B is een oude AV-receiver die stikt in FLAC. Met een globale schakelaar moet de gebruiker kiezen - ten koste van het andere apparaat. Met per-profiel krijgt elk apparaat het juiste antwoord.

Implementatie:

  • StreamingProfile.ForceTranscoding As Boolean = False.
  • Persistentieschema verhoogd naar v9. Pre-v9-bestanden laden de legacy globale waarde eenmaal en kopiëren deze naar alle profielen, waardoor het oude gedrag behouden blijft tijdens de upgrade.
  • UI: verwijderd uit het Diagnostiek-paneel, toegevoegd aan de sectie Apparaatprofielen naast ForceNativeStream. Tweerichtings wederzijdse-uitsluitingshandlers (CheckedChanged op elk schrijft de andere uit voordat deze wordt omgeschakeld, om een oneindige lus te voorkomen).
  • Beslissingslocatie: Settings.ForceTranscodingstreamingProfile.ForceTranscoding in WriteAudioFileDIDL.

F05 - "Forceer little-endian PCM" per profiel

Wat: PCM-streams (L16/L24 mime-types) zijn big-endian volgens de specificatie. Sommige apparaten verwachten ten onrechte little-endian en spelen witte ruis af wanneer ze correcte big-endian-gegevens krijgen. F05 schakelt de bytevolgorde per profiel om.

Waarom: zonder dit produceren bepaalde apparaten een muur van statische ruis. Het symptoom is dramatisch en de oorzaak onzichtbaar zonder kennis van PCM-codering - de schakelaar geeft gebruikers een gok-en-controle-oplossing.


F06 - "Gebruik geen Raw PCM" per profiel

Wat: wanneer het apparaat beweert dat het ruwe PCM ondersteunt, gebruikt de plugin dat. Sommige apparaten liegen - ze accepteren de SOAP-handshake maar verminken daadwerkelijke ruwe PCM-gegevens, terwijl ze PCM correct verwerken dat in een WAVE-container is verpakt. F06 forceert PCM-over-Wave, ongeacht wat het apparaat adverteert.

Waarom: bepaalde Marantz-modellen specifiek - ze adverteren ruwe PCM, maar alleen WAVE werkt. Zonder dit komen ruwe PCM-streams vervormd uit, zonder foutmelding om naar te wijzen.


F07 - "Contentlengte" per profiel

Wat: welke waarde moet worden verzonden in de HTTP Content-Length-header. Vier opties:

  • Standaard - daadwerkelijk aantal bytes wanneer bekend, weglaten wanneer onbekend.
  • Geen - stuur de header nooit (alleen chunked encoding).
  • Alleen PCM - alleen verzenden voor ruwe PCM; weglaten voor al het andere.
  • Vast - stuur UInt32.MaxValue - 8192 (een sentinel voor "enorme onbekende lengte").

Waarom: UPnP/DLNA-apparaten variëren enorm in hoe ze reageren op Content-Length. Sommige hebben een exact nummer nodig, sommige haten het op streams, sommige hebben een sentinel "echt grote" waarde nodig om te blijven bufferen. Dit werd later uitgebreid van alleen PCM naar alle uitvoerformaten omdat dezelfde problemen optraden in getranscodeerde MP3/AAC-streams.


F08 - "NextURI niet wissen" per profiel

Wat: normaal gesproken wist de plugin de in de wachtrij geplaatste NextURI van het apparaat wanneer de wachtrij leeg is (door SetNextAVTransportURI met een lege URL te verzenden). Sommige apparaten (met name Denon) interpreteren een lege NextURI als "alles stoppen" en stoppen onmiddellijk met afspelen. F08 voorkomt dat de plugin deze ooit wist.

Waarom: zonder dit ervaren Denon-eigenaren dat het apparaat midden in een nummer afbreekt wanneer de wachtrij leeg is. Met F08 aangevinkt, behoudt het apparaat de verouderde NextURI in het geheugen (onschadelijk - het wordt gewoon overschreven de volgende keer dat iets in de wachtrij wordt geplaatst).


F09 - FLAC als transcode-uitvoerformaat

Wat: de transcode-formaat dropdown in Apparaatprofielen biedt nu FLAC aan naast PCM 16/24, MP3, AAC, Ogg. Het kiezen hiervan routeert de BASS-encoder via MusicBee's standaard FLAC-conversie commandoregel (hetzelfde mechanisme dat MP3/AAC/Ogg al gebruiken).

Waarom: voor apparaten die FLAC goed aankunnen maar de broncodec niet kunnen decoderen (bijv. een Eversolo die MusicBee's WMA-bibliotheek ontvangt, geconverteerd naar FLAC), behoudt dit lossless kwaliteit waar MP3/AAC audiogegevens zouden weggooien. Deblokkeert N02 (5.1 downmix-controle), wat niet kon worden aangepakt zonder een lossless transcode-optie.

Implementatie: éénregelige toevoeging aan Encoder.StartEncode's Select Case Codec-blok - FLAC voegt zich bij MP3/AAC/Ogg in de commandoregel-gestuurde tak. UI-dropdown krijgt "FLAC" als 6e optie. Laad/opslaan-mapping in SettingsDialog breidt uit om FileCodec.FlacSelectedIndex = 5 te herkennen. Mime, DLNA-type en encode-functie waren al bedraad in ItemManager.GetMimes / GetDlnaType / GetEncodeFeature van eerder werk (F21, F26).


Gapless (SetNextAVTransportURI)

F10 - SetNextAVTransportURI / NextURI kern

Wat: echt gapless afspelen. Wanneer het apparaat ondersteuning voor SetNextAVTransportURI adverteert in zijn UPnP-servicebeschrijving, plaatst de plugin het volgende nummer vooraf in de wachtrij op het apparaat voordat het huidige nummer eindigt. Het apparaat schakelt intern over zonder hoorbare pauze tussen nummers - wat je hoort op een cd-speler. Dit is niet de "continue stream"-hack (die alles samenvoegt tot één lange stream en metadata per nummer verliest).

Waarom: de vlaggenschip Tier-2-functie. Albums opgenomen als een continue live-uitvoering (live-opnames, klassieke bewegingen, DJ-sets) klinken verkeerd wanneer er een halve seconde stilte is tussen nummers. Dit correct oplossen is een vlaggenschipfunctie, nu in deze fork.

Opmerkingen: de in de wachtrij geplaatste audio wordt via de HTTP-server van de plugin geserveerd met streamHandle=0 (bibliotheek-fetch-modus), wat betekent dat MusicBee's audio-engine niet in de lus zit voor het in de wachtrij geplaatste nummer. Afweging: ReplayGain/DSP/EQ-effecten zijn niet van toepassing op het volgende nummer. Acceptabel wanneer "forceer native stream" is ingeschakeld (de standaard).


F11 - "NextURI-ondersteuning uitschakelen" per profiel

Wat: zelfs als een apparaat SetNextAVTransportURI adverteert, dwingt dit selectievakje de plugin om die advertentie te negeren en terug te vallen op afspelen van één nummer tegelijk.

Waarom: sommige apparaten adverteren NextURI maar hebben een buggy-implementatie (crashes, halve overgangen, vastlopers). In plaats van elk kapot apparaat te reverse-engineeren, krijgt de gebruiker een schakelaar om het "hier gewoon uit te schakelen".


F12 - NextURI-levenscyclus op de nu-spelende-lijst

Wat: wanneer MusicBee NowPlayingListChanged activeert, evalueert de plugin opnieuw wat in de wachtrij moet worden geplaatst voor gapless overgang. Het vraagt MusicBee om het nieuwe "volgende" nummer via NowPlayingList_GetNextIndex(1) + NowPlayingList_GetListFileUrl, vergelijkt dit met wat momenteel in de wachtrij staat op het apparaat (bijgehouden via het nieuwe nextPlaySourceUrl-veld), en plaatst opnieuw in de wachtrij als het is gewijzigd (of wist de wachtrij als MusicBee zegt dat er geen volgend nummer is).

Waarom: zonder F12 bleef het apparaat een verouderde NextURI afspelen wanneer de gebruiker het in de wachtrij geplaatste nummer verwijderde/herschikte. Dit kostte historisch gezien verschillende iteraties omdat elke lijstmutatie een andere afhandeling vereist - wij vereenvoudigden door te vertrouwen op NowPlayingList_GetNextIndex (die al rekening houdt met shuffle en repeat-all wrap-around), zodat alle varianten via dezelfde vergelijking worden geleid.

Implementatie:

  • Nieuw nextPlaySourceUrl-veld slaat de MusicBee-bibliotheek-URL op van wat in de wachtrij staat (de streaming-URL met handle-achtervoegsel is niet vergelijkbaar met een bibliotheekpad).
  • Nieuwe Public Sub RefreshQueuedNextUri() op MediaRendererDevice. Drie uitkomsten: geen NextURI in de wachtrij → no-op; in de wachtrij komt overeen met nieuwe "volgende" → no-op; in de wachtrij verschilt → roep QueueNext aan met de nieuwe URL (of QueueNext("") om te wissen - wat F08 DoNotClearNextUri respecteert).
  • Bedraad in Plugin.ReceiveNotification onder NotificationType.NowPlayingListChanged.

F13 - NextURI-foutterugval

Wat: na 4 opeenvolgende SetNextAVTransportURI-fouten op hetzelfde apparaat, schakelt de plugin gapless afspelen voor dat apparaat uit totdat MusicBee opnieuw wordt gestart.

Waarom: als een apparaat echt kapot is voor NextURI (intermitterende SOAP-fouten, netwerkstoringen), zou de plugin anders bij elk nummer blijven proberen. F13 stopt de ruis en valt stilzwijgend terug op afspelen van één nummer tegelijk.


F14 - Herhaalmodus + NextURI-integratie

Wat: F14 splitst zich in twee gevallen die worden afgehandeld bij de F15-overgangsdetector in OnAvTransportStatusCheck:

  • Alles herhalen: MusicBee geeft de juiste "wrap"-URL (nummer 1 aan het einde van de lijst) zelf door aan Plugin.QueueNext. Geen speciale plugin-logica nodig - het apparaat schakelt ernaar over en de F15-detector roept Player_PlayNextTrack aan zoals gewoonlijk, wat MusicBee's NPL-index terug naar 0 wikkelt.
  • Eén herhalen: MusicBee geeft DEZELFDE nummer-URL door aan Plugin.QueueNext. Het apparaat schakelt ernaar over (nieuwe stream-handle, dezelfde bron). De F15-detector bevraagt nu Player_GetRepeat() - als het RepeatMode.One is, slaagt het de Player_PlayNextTrack-aanroep over, zodat MusicBee de NPL-index niet van het herhalende nummer afschuift.

Waarom: zonder de Herhaal-Eén-overslag zou het aanroepen van Player_PlayNextTrack bij gapless overgang MusicBee naar het volgende nummer in de lijst doen doorschuiven (Herhaal-Eén beïnvloedt alleen het automatisch doorschuiven aan het einde van het nummer in de UI van de speler - Volgend nummer gaat altijd vooruit), wat in tegenspraak is met wat Herhaal-Eén betekent.

Afspeeltelling-waarschuwing: in Herhaal-Eén hangt de toename van de afspeeltelling af van MusicBee 3.7.9563+ die de herhaalde afspeling opmerkt. Oudere MusicBee-versies spelen de gapless herhaling correct af, maar missen de afspeeltelling-verhoging. Gedocumenteerd; blokkeert niet.


F15 - Track-overgangsdetectie-statusmachine

Wat: wanneer het apparaat intern overgaat van het huidige nummer naar NextURI, moet de plugin dit opmerken en MusicBee vertellen om zijn nu-spelende index te verhogen. Anders denkt MusicBee dat het nog steeds op het vorige nummer zit en lopen afspeeltellingen / UI / scrobbling uit de pas.

Implementatie: peilt GetPositionInfo.TrackURI bij elke status-timer-tick. Wanneer de gerapporteerde URI overeenkomt met degene die we via NextURI in de wachtrij hebben geplaatst, roepen we Player_PlayNextTrack aan op MusicBee en stellen we suppressNextSoapCall in, zodat de resulterende PlayToDevice SetAVTransportURI niet opnieuw verzendt (wat het gapless afspelen zou onderbreken).

Waarom: zonder F15 speelt het apparaat het volgende nummer af, maar MusicBee's UI zegt dat het nog steeds op het vorige nummer zit. Verwarrend, breekt scrobbling, breekt afspeeltelling-tracking. Track-overgangsdetectie vereist lange, per-renderer-iteratie omdat elk renderer-merk zijn eigen eigenaardigheden heeft in wanneer het de URI-wijziging rapporteert (sommige rapporteren eerst TRANSITIONING, sommige slaan direct over naar PLAYING met nieuwe URI, sommige hebben een korte STOPPED ertussenin).

Opmerkingen: onze eerste versie werkt op BubbleUPnP-renderer. Per-apparaat-randgevallen blijven in B6.


F16 - Pop-on-gapless-transition fix

Wat: de pop gebeurt wanneer het bronformaat (samplefrequentie / kanalen / codec) van het in de wachtrij geplaatste nummer verschilt van het momenteel afgespeelde nummer, waardoor de DAC van het apparaat opnieuw moet vergrendelen bij de overgang. F16 voegt een NextUri:FormatChange-diagnose toe die wordt geactiveerd op het moment van wachtrijplaatsing wanneer de formaten verschillen, waarbij beide zijden worden benoemd - zodat gebruikers die pops horen, dit kunnen correleren.

De diagnose wijst ook op de mitigatie: vink ForceTranscoding aan op het apparaatprofiel. Dat homogeniseert elk nummer naar een enkele transcode-codec/samplefrequentie/bitdiepte, waardoor het verschil in bronformaat volledig wordt geëlimineerd.

Waarom uitgesteld voor de daadwerkelijke transcode-to-match fix: de structurele fix (transcodeer het in de wachtrij geplaatste nummer om overeen te komen met het formaat van het afgespeelde nummer) vereist wijzigingen in het HTTP-server-URL-schema van de plugin - momenteel serveert /encode/{id}0.{ext} het in de wachtrij geplaatste bestand native. Een toekomstige v2 van F16 zou per-formaat /encode/{id}0_{rate}_{depth}.{ext}-routes toevoegen en deze via de encoder bedraden. Dat is een grotere architectonische verandering die de moeite waard is als een echt apparaat de pop vertoont nadat ForceTranscoding niet volstaat.

Implementatie vandaag:

  • lastSourceUrl-veld volgt de momenteel afgespeelde bron-URL.
  • QueueNext leest FilePropertyType.SampleRate/Channels/Kind voor zowel de huidige als de in de wachtrij geplaatste nummers en logt NextUri:FormatChange bij mismatch.

F17 - Voortgangsbalk resync na zoeken

Wat: de Seek()-functie riep al GetPlayPositionInformation() aan na een succesvolle Seek SOAP, wat het geval van "helemaal geen resync" oplost. F17 sluit de resterende drift van maximaal 1 seconde die wordt veroorzaakt door UPnP's 1-seconde RelTime-kwantisering: wanneer de gerapporteerde positie van het apparaat afrondt tot binnen 1 seconde van het door de gebruiker gevraagde doel, vertrouwt de plugin nu de sub-seconde-nauwkeurige waarde van de gebruiker in plaats van de afkapping van het apparaat. Alleen wanneer het apparaat iets dramatisch anders rapporteert (>1s afwijking) gebruiken we de waarde (het zoeken landde ergens anders dan gevraagd, bijv. snap-to-keyframe op sommige codecs).

Waarom: zonder dit zou het zoeken naar 2:30.500, verankerd aan de "2:30"-rapportage van het apparaat, de voortgangsbalk ongeveer 500ms achter de werkelijkheid laten zien. Na F17 komt de balk overeen met de intentie van de gebruiker voor het veelvoorkomende in-track scrub-geval, en respecteert nog steeds de rapportage van het apparaat voor de snap-to-keyframe-uitzondering.


F18 - Continue-stream / NextURI-vergrendeling

Wat: twee vergrendelingen zijn nu van kracht:

  1. Runtime: QueueNext retourneert vroeg False wanneer Settings.ContinuousOutput is ingeschakeld. Continue-stream is zijn eigen gapless mechanisme (één lange aaneengeschakelde stream); het verzenden van SetNextAVTransportURI erbovenop verward het apparaat over de vraag of elk nummer een discrete URI is of deel uitmaakt van de continue stroom.
  2. UI: wanneer de gebruiker het globale continue-stream selectievakje aanvinkt, wordt de forceNativeStream van het momenteel weergegeven profiel automatisch uitgevinkt. Continue-stream transcodeert altijd, dus force-native is zinloos in combinatie.

Waarom: voorkomt dat de gebruiker twee conflicterende gapless mechanismen tegelijk inschakelt. Zonder F18 zou het apparaat zowel een continue stream-URI ALS een NextURI ontvangen voor elk volgend nummer, met ongedefinieerd gedrag afhankelijk van de renderer.


F19 - Lege NextURI-fouten genegeerd

Wat: wanneer SetNextAVTransportURI wordt aangeroepen met een lege URL (bijv. laatste nummer in lijst), retourneren sommige apparaten een SOAP-fout. F19 slikt deze stilzwijgend in - gelogd maar niet gepropageerd als fouten.

Waarom: de "geen volgend nummer"-conditie is normaal, geen fout. Het als fataal behandelen vervuilt het logboek en (in sommige stromen) activeert herhaalde pogingen.


Mime-types & DLNA-metadata

F20 - MP3 mime → audio/mpeg

Wat: het standaarden-conforme MP3 mime-type is audio/mpeg, niet audio/mp3. Het laatste is een veelvoorkomende verkeerde benaming die de meeste apparaten tolereren, maar strengere renderers wijzen het af.

Waarom: lost stilzwijgend afspelen op strengere apparaten die de standaard volgen. De yaiol-codebase had dit al correct; geen wijziging nodig.


F21 - Mime-type volgorde: niet-x- variant eerst

Wat: wanneer een apparaat zowel audio/flac als audio/x-flac adverteert, retourneert de plugin eerst de niet-x- variant. Hetzelfde geldt voor elke codec met zowel standaard als experimentele mimes.

Waarom: het x- voorvoegsel markeert experimentele/onofficiele mimes. Sommige renderers gedragen zich beter met de standaardvorm. Kleine herschikking, reële impact.


F22 - Opus mime-type ondersteuning

Wat: herkent Opus als een streamable audiocodec; stuurt audio/opus mime bij het serveren van Opus-tracks.

Waarom: Opus is nu gebruikelijk (moderne spraak/muziek compromiscodec). Zonder F22 zou de plugin weigeren Opus-bestanden te streamen, zelfs naar apparaten die ze aankunnen.


F23 - Monkey Audio (APE) bronbestandondersteuning

Wat: herkent .ape-bestanden als een geldige broncodec voor streaming/transcodering.

Waarom: APE is een lossless formaat met een niche maar loyale gebruikersbasis. Het toevoegen ervan kost weinig en ontgrendelt de bibliotheek voor die gebruikers.


F24 - AAC / ALAC mime-terugval

Wat: als een apparaat AAC of ALAC ondersteunt, maar deze niet expliciet adverteert in zijn UPnP-servicebeschrijving, biedt de plugin ze toch aan als terugval.

Waarom: verschillende apparaten die AAC prima aankunnen, vergaten het in hun capaciteiten-XML te vermelden. Zonder F24 zal de plugin het niet eens proberen, waardoor transcodering wordt afgedwongen. Met F24 probeert de plugin het en laat het apparaat het native afhandelen als het kan.


F25 - DLNA-typevlag voor native + gecodeerde WAV-streams

Wat: de DLNA-typevlag (een profiel-identificatie zoals LPCM, WAVE, MP3) moet overeenkomen met wat het apparaat ontvangt. F25 zorgt ervoor dat native streams en gecodeerde WAV-streams correct worden gemarkeerd.

Waarom: een niet-overeenkomend DLNA-type zorgt ervoor dat sommige apparaten het afspelen volledig weigeren of de verkeerde decoder toepassen.


F26 - DLNA-header voor FLAC-bestanden

Wat: FLAC-streams krijgen de juiste DLNA-profiel-identificatie in hun headers.

Waarom: zonder dit herkennen sommige apparaten die FLAC ondersteunen de stream niet als zodanig.


F27 - Bitrateberekening fix in metadata

Wat: de continue-stream res@bitrate werd berekend als (sampleRate * channels * bitsPerSample) / 1000 - kbps, afwijkend met een factor van ~125 van de UPnP DIDL-specificatie die het attribuut definieert als bytes per seconde. Deelt nu door 8 in plaats van 1000.

Waarom: verkeerde bitrateweergave op het apparaat - cosmetisch op de meeste renderers, maar sommige wijzen stream-buffers toe op basis van de waarde en stotteren op streams die er ~125× kleiner uitzien dan ze zijn. Het niet-continue bronbestandspad had dit al correct ((bitrate_kbps * 1000) \ 8 = bytes/sec); alleen het continue-stream-pad was verkeerd.


F28 - Metadatatijdformaatfix (Marantz)

Wat: res@duration in DIDL was geformatteerd als H:MM:SS (bijv. 0:03:42). De UPnP DIDL-specificatie definieert het formaat als H+:MM:SS[.F+] - strikt met fractionele seconden optioneel maar aanbevolen; sommige Marantz-apparaten behandelen de kale vorm als ongeldig en laten hun duurweergave leeg. Nu geformatteerd als H:MM:SS.fff (bijv. 0:03:42.000).

Waarom: weergaveprobleem specifiek voor een merk; conform ISO-stijlformaat met fractionele seconden lost het op zonder andere apparaten te beïnvloeden. Toegepast op beide DIDL-emissieplaatsen (bronbestandspad + gecodeerd-stream-pad in WriteAudioFileDIDL).

Bonusfix in dezelfde pass: pv:addedTime en pv:lastPlayedTime gebruikten hh (12-uursklok) in hun DateTime-formaatstrings in plaats van HH (24-uurs). Elk nummer dat tussen 13:00 en 23:59 werd toegevoegd of afgespeeld, zou met een verkeerd uur worden weergegeven (bijv. 17:42 → "05:42") op apparaten die het veld tonen. Gebruikt nu HH.


F29 - Gecodeerde MP3-zoekondersteuning (CBR)

Wat: getranscodeerde MP3-streams adverteren nu DLNA.ORG_OP=11 (zowel byte- als tijdzoekfunctie) in plaats van DLNA.ORG_OP=10 (alleen byte). Apparaten die voorheen tijdzoekfunctie op getranscodeerde MP3 weigerden, kunnen nu hun voortgangsbalk/zoek-UI normaal bedienen.

Waarom: MusicBee's transcoder produceert constante-bitrate MP3 met de HighQuality-preset, dus byte ↔ tijd-mapping is lineair - het apparaat kan een tijdzoekverzoek zelf omzetten naar een HTTP Range byte-zoekverzoek zonder enige encoder-side ondersteuning. Het adverteren van OP=11 ontgrendelt die UI op het apparaat. Zonder F29 werden gebruikers die zochten in een getranscodeerde MP3 ofwel stilzwijgend genegeerd, ofwel teruggezet naar het begin van het nummer.

Implementatie: GetEncodeFeature in ItemManager.vb geherstructureerd om de inline If op te splitsen in een leesbare If/ElseIf/Else-keten. MP3 krijgt expliciet OP=11; andere niet-PCM-codecs behouden OP=10. Geen wijziging voor AAC/FLAC/etc. - die zouden codec-specifieke verificatie van CBR-ness nodig hebben die MusicBee niet garandeert.


F30 - .mpeg-bestandsextensie afgehandeld

Wat: bestanden met de .mpeg (en de nog zeldzamere .mpe) extensie worden nu herkend als FileCodec.Mp3 in GetCodec. Vóór F30 retourneerden ze FileCodec.Unknown en werden ze stilzwijgend geweigerd uit de bibliotheek / konden ze geen transcode-bronnen zijn.

Waarom: oude MPEG-1 Layer 3-archieven gebruikten soms .mpeg in plaats van .mp3 (de specificatie staat beide toe). Een handvol bestanden in een bibliotheek van 300k is genoeg om het gevoel te geven "MusicBee toont ze, maar de plugin niet" - verwarrend voor de gebruiker.


Afspeelgedrag

F31 - Radiostreams gebruiken automatisch continue modus

Wat: WriteAudioFileDIDL onderzoekt nu de Kind-eigenschap van de bron-URL via Library_GetFileProperty en behandelt elk bestand waarvan de Kind eindigt op "Stream" (MusicBee rapporteert "MP3 Stream", "Internet Stream", enz. voor radio) als continu, ongeacht de globale Settings.ContinuousOutput-schakelaar. De continue-stream DIDL-tak (Titel: "Continue Stream", id="continuousstream", vaste PCM/Wave-uitvoer) wordt gebruikt; het apparaat ziet een enkele oneindige-stijl stream.

Waarom: radiostreams hebben geen trackgrenzen, geen vaste lengte, geen zoekfunctie. Het behandelen ervan als discrete bestanden in de DIDL zorgde ervoor dat de plugin bytebereiken en duur adverteerde die niet bestaan. Automatisch overschakelen wanneer MusicBee ons al vertelde "dit is een Stream" verwijdert een valkuil waar de gebruiker niet over hoeft na te denken.

Bereik: is alleen van toepassing wanneer MusicBee het afspelen aanstuurt (musicBeePlayToMode). Het bibliotheek-fetch-pad (UPnP-client die bladert) is ongewijzigd - radio-URL's zijn daar zeldzaam en het gebruikersgerichte gedrag mag niet veranderen zonder expliciete tests.


F32 - Codec-advertentie terugval

Wat: als een apparaat bepaalde codecs niet adverteert (of de plugin de capaciteiten-XML van het apparaat niet kan parsen), wijst de plugin de stream niet onmiddellijk af. In plaats daarvan probeert het de stream te serveren en laat het apparaat beslissen.

Waarom: veel apparaten hebben onvolledige of onleesbare capaciteiten-XML, maar kunnen de codec eigenlijk prima aan. F32 ruilt een kleine "beste gok en probeer" in voor een regelrechte weigering.


F33 - Verbetering van de voortgangsbalksynchronisatie

Wat: de positie tussen de peilingen wordt al geëxtrapoleerd op basis van een enkele anker (currentPlayStartTicks), zodat de voortgangsbalk soepel wordt bijgewerkt met een sub-seconde snelheid. De resterende jitterbron was het initiële anker voor een vers gestart nummer: de vorige code ging uit van position=0 op het moment dat de status-timer voor het eerst opmerkte dat de status naar Afspelen ging, maar tegen die tijd had het apparaat mogelijk al 100-500ms afgespeeld (één peilinterval). MusicBee's voortgangsbalk zou op 0 beginnen en dan vooruit springen wanneer de werkelijkheid inhaalde.

F33 fix: bij de eerste overgang naar Afspelen op een nieuw nummer (currentPlayStartTimeEstimated=True), roep GetPlayPositionInformation() aan om de daadwerkelijke huidige positie van het apparaat te krijgen, en anker daar vervolgens tegenaan. UPnP rapporteert alleen een resolutie van 1 seconde, dus het anker is nog steeds gekwantiseerd, maar het is veel dichter bij de waarheid dan uitgaan van 0.

Waarom: vloeiendere + nauwkeurigere voortgangsweergave, vooral direct na een nummerwisseling. Er is geen manier om de 1-seconde UPnP-rapportageresolutie zelf te omzeilen - dat is specificatie.


F34 - Voortgangsbalk jitter na nummerwisseling

Wat: wanneer PlayToDevice wordt aangeroepen voor een nieuw nummer, liet de plugin currentPlayPositionMs en currentPlayStartTicks op hun vorige-nummerwaarden staan gedurende het ~100ms venster tussen SOAP-Play en de eerste status-timer-peiling die de nieuwe Afspeelstatus detecteerde. MusicBee's voortgangsbalk zou kort het einde van het vorige nummer tonen, dan terugspringen naar 0, dan klimmen. F34 zet beide op nul bij PlayToDevice-ingang - het moment dat we weten dat er een nummerwisseling plaatsvindt, vóór al het SOAP-werk.

Waarom: visuele glitch bij snelle-skip-gebruiksscenario's (handmatig volgende of gapless overgang). Nu retourneert MusicBee's eerste PlayPositionMs-query na Play netjes 0, waarna F33's GetPlayPositionInformation dit verfijnt naar de daadwerkelijke positie van het apparaat bij de eerste statuswijziging-tick.

Implementatie: vier regels bovenaan PlayToDevice, gekoppeld aan F33's overgangstijd-nauwkeurige verankering.


F35 - "Forceer transcodering" bug

Wat: geforceerde transcodering kon in bepaalde combinaties nog steeds transcodering overslaan. Na de F04 per-profiel herwerking werden twee specifieke hiaten gedicht:

  1. Prioriteit met ForceNativeStream. Wanneer beide True waren (wat kan gebeuren bij een schema-migratie of een gedeeltelijk instellingenbestand), wint ForceTranscoding nu ronduit (If streamingProfile.ForceTranscoding Then forceEncode = True ElseIf streamingProfile.ForceNativeStream Then forceEncode = False). UI-wederzijdse uitsluiting voorkomt dat de gebruiker beide aanvinkt, maar de runtime-beveiliging handelt elke inconsistent geladen status van schijf af.
  2. bypassTranscodeDecision logica. Voorheen: streamingProfile.ForceNativeStream AndAlso Not Settings.ForceTranscoding. Nu: streamingProfile.ForceNativeStream AndAlso Not streamingProfile.ForceTranscoding - dezelfde prioriteitsregel, maar op hetzelfde per-profielbereik.

Waarom: "forceer" moet forceer betekenen. Als de gebruiker expliciet ForceTranscoding voor een apparaat heeft ingeschakeld, mag de plugin nooit stilzwijgend terugvallen op native streaming, ongeacht hoe andere vlaggen toevallig combineren.


F36 - Renderer-gesloten uitzondering

Wat: Plugin.ReceiveNotification is omwikkeld met een top-level Try/Catch die elke niet-afgevangen uitzondering logt in plaats van deze terug te laten propageren naar MusicBee's notificatiepomp.

Waarom: notificaties van MusicBee (PlayStateChanged, VolumeMuteChanged, enz.) worden verzonden naar ControlPointManager die via SOAP met de renderer communiceert. Individuele aanroeplocaties hadden al Try/Catch rond hun SOAP-aanroepen, maar een voldoende vreemd timinggeval (bijv. renderer die sterft tussen twee SOAP-aanroepen in dezelfde notificatiehandler) kon nog steeds ontsnappen. De top-level wrapper is het laatste vangnet zodat de gebruiker nooit een generieke "TargetInvocationException"-popup van MusicBee ziet.

Implementatie: de bestaande body is hernoemd naar ReceiveNotificationInternal en er is een dunne wrapper ReceiveNotification toegevoegd die Try { ReceiveNotificationInternal(...) } Catch { LogError(...) } doet. De reeds bestaande per-methode Try/Catch-infrastructuur binnen ControlPointManager (rond elke PostSoapRequest-aanroep) blijft - F36 is riem + bretels.


F37 - Lange-track zoeken activeert valse overgang

Wat: zoeken binnen een lange track kan een korte Stopped→Playing-cyclus veroorzaken op sommige renderers. Zonder onderscheid behandelt ProcessNewPlayState.Stopped dat als natuurlijk einde van de track en roept Player_PlayNextTrack aan, waardoor MusicBee doorschuift terwijl de gebruiker alleen wilde scrubben. F37 stempelt lastUserInitiatedSeek in Seek() en voegt een 5-seconde beveiliging toe in de Stopped-handler (spiegelend de bestaande lastUserInitiatedStop-venster).

Waarom: stilzwijgend overslaan naar het volgende nummer tijdens een zoekactie is een van die bugs waarvan niemand de oorzaak kan raden - de gebruiker denkt "vreemd, ik probeerde vooruit te scrubben en nu speelt het het volgende nummer af". De oplossing is mechanisch: hetzelfde patroon als de reeds aanwezige gebruikersstop-discriminatie.


F38 - Verbeterde zoekafhandeling voor crashgevoelige codecs

Wat: BubbleUPnP dat crashte bij MP3-zoeken was het canonieke symptoom. Na audit doet de huidige yaiol-zoekcode al de juiste dingen - native pad verwerkt HTTP Range correct (206, Content-Range, AcceptRanges), gecodeerd pad adverteert X-AvailableSeekRange en parseert inkomende timeSeekRange.dlna.org / npt-headers, DLNA.ORG_OP-vlaggen weerspiegelen de werkelijke streamcapaciteiten (met DisablePcmTimeSeek opt-out voor probleem Platinum-apparaten). Gebruikerstests op huidige BubbleUPnP 4.6.4: geen crashes waargenomen.

Waarom: de BubbleUPnP MP3-zoekcrash werd rond 2024 gemeld en de app heeft sindsdien ~16 maanden aan fixes gehad. F29 (gecodeerde MP3 OP=11) was de nieuwe variabele die het opnieuw had kunnen blootleggen; doet dat niet, op geteste versies.

Als een crash terugkeert: de fix-vorm zou een per-profiel "beperkt zoeken"-schakelaar zijn die DLNA.ORG_OP=10 (alleen byte) afdwingt op gemarkeerde codecs - spiegelend hoe DisablePcmTimeSeek al werkt voor PCM. Dan toevoegen, niet preventief.


UI & logboekregistratie

F39 - "Toevoegen"-knop selecteert het nieuwe profiel

Wat: klikken op "Toevoegen" in de lijst met apparaatprofielen creëert een nieuw profiel EN selecteert het automatisch, zodat de gebruiker onmiddellijk velden kan bewerken. Onze herstructurering van het gedeelde dialoogvenster doet dit al - zowel het directe-Toevoegen-pad als het van-sjabloon-pad eindigen met Me.activeStreamingProfiles.SelectedIndex = Me.activeStreamingProfiles.Items.Count - 1. Een controle bevestigde dat onze fork dit al afhandelt - niets te wijzigen.

Waarom: kleine UX-ergernis die hier al geen ergernis bleek te zijn.


F40 - Grotere max-verbindingen + waarschuwingslogboek

Wat: de gelijktijdige-stream-limiet van de plugin (SemaphoreSlim rond Sockets_Stream_File / Sockets_Encoder_Start) was een hardgecodeerde 4. F40 maakt deze door de gebruiker configureerbaar op de pagina Algemene instellingen (standaard 16, bereik 1-256), voegt een MaxConnections-logregel toe wanneer een verzoek moet wachten op een slot, EN toont een rode ⚠ Max Conn-badge linksonder in het instellingendialoogvenster als de limiet ten minste één keer is bereikt sinds MusicBee is gestart.

Waarom: wanneer een apparaat parallelle verzoeken afvuurt (sommige Marantz/Linn tijdens artwork-scans, BubbleUPnP's metadata-probes naast actief afspelen), werden extra verzoeken stilzwijgend geblokkeerd achter de semaphore - de gebruiker zag "apparaat traag" zonder zichtbare oorzaak. De logregel is goed voor technische debugging, maar niet-technische gebruikers lezen nooit logs. De zichtbare badge in het instellingendialoogvenster maakt de limiet-bereikte-conditie vindbaar voor iedereen die de voorkeuren van de plugin opent.

Implementatie:

  • Centraliseerde het wachten in WaitOnSendBarrier(logTag) in MusicBeeUpnp.vb; beide aanroeplocaties (MediaServerDevice.GetFile, Encoder.StartEncode) gebruiken het.
  • Settings.MaxConnections opgeslagen in v8 van het instellingenschema.
  • Plugin.MaxConnectionsHit is een sticky sessie-vlag die wordt ingesteld binnen WaitOnSendBarrier; wordt alleen gereset bij MusicBee-herstart.
  • SettingsDialog.maxConnectionsBadge is een rood vetgedrukt label op (16, 410) dat alleen wordt weergegeven wanneer Plugin.MaxConnectionsHit True is. Heeft een tooltip die de oorzaak en oplossing uitlegt.
  • Semaphore wordt eenmaal geïnitialiseerd bij type-load, dus het wijzigen van de instelling vereist een MusicBee-herstart (vermeld in het veldlabel).

F41 - Log "codering vanwege ReplayGain/DSP"

Wat: in plaats van aparte "codering voor RG" / "codering voor DSP" logregels, bevat de enkele StreamDecision-regel van F42 MB-DSP/EQ, MB-ReplayGain, Profile-DSP/EQ, Profile-ReplayGain als verzamelde redenen. Dezelfde diagnostische waarde, minder ruis.

Waarom: gebruikers zien alle redenen waarom transcodering plaatsvindt voor een bepaald nummer op één logregel, niet verspreid. Zie F42 voor volledige details.


F42 - Log "renderer ondersteunt broncodec niet"

Wat: een StreamDecision-logregel per afspeel-naar-apparaat-nummer die zegt "native CODEC" of "transcode CODEC→CODEC reason=…". Het redenveld verzamelt elke conditie die transcodering activeerde: MB-DSP/EQ, MB-ReplayGain, Profile-DSP/EQ, Profile-ReplayGain, WebFile, VirtualFile, ForceTranscoding(global), SampleRate<min/SampleRate>max, DownmixToStereo, DeviceLacksCodec(X), BandwidthConstrained.

Waarom: gebruikers waren verward door onverwachte CPU-pieken op bestanden die ze verwachtten native te streamen. Eén logregel per nummer vertelt hen precies welke conditie transcodering veroorzaakte - en als het veld DeviceLacksCodec(Flac) toont, weten ze onmiddellijk dat de protocol-info van het apparaat onvolledig was en willen ze misschien dat F32's terugval in werking treedt.

Implementatie: enkele accumulatorstring incrementeel opgebouwd door de beslissingsketen; eenmaal gelogd aan het einde. Afgeschermd door Settings.LogDebugInfo om logruis in productie te voorkomen.


F43 - SetNextAVTransport-log toont bron-URL

Wat: QueueNext-logboekvermeldingen bevatten nu source=<MusicBee bibliotheekpad> naast stream=<HTTP streaming-URL>. Dezelfde wijziging is toegepast op het succespad en het faalpad (QueueNext:Failed).

Waarom: bij het debuggen van een probleem met een in de wachtrij geplaatst nummer is de streaming-URL (/encode/aabbccdd0.flac) op zichzelf ondoorzichtig - hetzelfde voor elk nummer. De bron-URL is het menselijk-grep-bare bibliotheekpad dat u precies vertelt welk bestand MusicBee probeerde in de wachtrij te plaatsen.


F44 - Betere mime-type foutenlogboekregistratie

Wat: twee nieuwe logboekvermeldingen tijdens Activate:

  • Activate:MimeUnverified - wordt geactiveerd per misvormde vermelding in de GetProtocolInfo-respons van het apparaat, waarbij wordt aangegeven welke vermelding niet kon worden geparseerd (zodat de gebruiker bijvoorbeeld kan zien "de Marantz retourneerde http-get:*::* voor een bepaalde codec - capaciteit is onverifieerbaar, F32's terugval zal gokken").
  • Activate:NoSinkInfo - wordt eenmaal geactiveerd als het apparaat helemaal geen <Sink>-element retourneerde. Betekent dat SupportedMimeTypes Niets blijft en IsCodecSupported degradeert naar "neem aan dat alles werkt" - nuttige context wanneer later "apparaat weigerde stream"-fouten verschijnen.

Waarom: vóór F44 lieten deze stilzwijgende capaciteits-terugvallen gebruikers raden waarom hun nummers ofwel tegen de verwachting in werden getranscodeerd, ofwel door het apparaat werden geweigerd. Nu toont een enkele grep voor Activate: of de capaciteitsinformatie van het apparaat bruikbaar was.


F45 - Betere metadata foutenlogboekregistratie

Wat: de Browse-uitzonderingslog in ContentDirectoryService.vb was al verrijkt in eerder yaiol-werk (de Alia Vox bug-sessie) met ObjectID en stack trace. F45 breidt dit verder uit met BrowseFlag (metadata vs kinderen), Filter (welke attributen de client vroeg), sortCriteria, en partialResultLength (hoeveel bytes DIDL werden geproduceerd vóór de fout - wijst op hoe ver door de batch het slechte nummer zit).

Waarom: wanneer er iets misgaat midden in DIDL, vertelt de partial-length-waarde u of de fout optrad bij het eerste nummer van de batch (partial=0) of halverwege (partial=N) - in combinatie met de startingIndex van de batch, kunt u de betreffende trackindex identificeren. Filter en BrowseFlag verklaren welk soort browse de client wilde; soms mislukt een metadata-only browse waar een children-browse voor dezelfde ID slaagt.


Netwerken

F46 - Automatische modus kondigt zich alleen aan op echte netwerkadapters

Wat: in de interfacemodus Automatisch kondigde de plug-in zichzelf voorheen aan (SSDP) op elke werkende IPv4-adapter. Op een machine die ook een VPN-tunnel (NordLynx) of een virtuele switch (Hyper-V / WSL / Docker) draait, werd dezelfde bibliotheek ook op elk van die adapters aangekondigd, zodat het control point van waaruit je cast de server twee of drie keer ontdekte en de bibliotheek als dubbele kopieën toonde. De automatische modus behoudt nu alleen adapters met een echte IPv4-standaardgateway (HasIPv4Gateway) - die de tunnel- en virtuele-switchadapters niet hebben - zodat die uit de aankondigingslijst vallen. Een door de gebruiker vastgezet adres wint nog steeds altijd (alleen op die interface aankondigen), en als geen enkele adapter een gateway meldt, valt de selectie terug op alle adapters, zodat de lijst met aangekondigde adressen nooit leeg is en de plug-in nooit onzichtbaar kan worden.

Waarom: het duplicaat wordt niet veroorzaakt door "op een VPN zitten" - het wordt veroorzaakt door tegelijk aankondigen op de LAN-adapter en de tunnel-/virtuele adapter, zodat één control point dezelfde server op twee adressen ziet. Een consumenten-VPN (NordVPN/NordLynx) tunnelt alleen verkeer richting internet; de DLNA-renderer leeft op het LAN en lokaal subnetverkeer gaat om de tunnel heen, dus de tunneladapter bereikt sowieso nooit een renderer - hem verwijderen haalt een fantoomkopie weg, nooit een werkend pad. De gateway-test is het goedkope, betrouwbare signaal dat een echte LAN/Wi-Fi-adapter onderscheidt van een tunnel of virtuele switch. Vult N05 aan (dat corrigeerde hoe aankondigingen op zulke verbindingen worden verzonden - multicast in plaats van broadcast); F46 bepaalt op welke adapters überhaupt wordt aangekondigd.

Bekende beperking: een mesh- / externe-toegangs-VPN (Tailscale, ZeroTier, WireGuard naar huis) waarvan de renderers echt aan de andere kant van de tunnel leven, toont meestal een adapter zonder standaardgateway, dus de automatische modus laat die ook vallen. Die gebruikers zetten in plaats daarvan het VPN-adres vast, dat voorrang heeft op het gateway-filter.