Nyheter
2.0.9 - 2026-08-23
Uppdateringsmeddelandet öppnar sina sidor på ditt språk
Vad: Länkarna Nyheter och Ladda ner i uppdateringsmeddelandet öppnar nu plugin-programmets sidor på MusicBees eget språk, snarare än på engelska.
Varför: Dessa två länkar begränsade språket till ett av fyra – engelska, franska, spanska eller tyska – innan de skickades till webbplatsen, så alla andra fick den engelska sidan även om en översättning fanns. De skickar nu MusicBees språk oförändrat och låter webbplatsen bestämma vad som ska visas, vilket är vad Hjälp-knappen bredvid dem alltid har gjort.
2.0.8 - 2026-08-22
Insticksprogrammet presenterar sig nu korrekt för de appar och enheter som upptäcker det i ditt nätverk, och inställningarna för Enhetsprofiler är återigen i linje.
Dina enheter visar rätt tillverkare, modell och version
Vad: när en kontrollapp, en telefon eller en TV hittar MusicBee i ditt nätverk, presenterar den nu detta insticksprogram som tillverkat av yaiol, pekar på insticksprogrammets egen webbplats, beskriver det som täckande alla tre roller – server, spelare och renderer – och rapporterar den version du faktiskt har installerad.
Varför: varje UPnP-enhet meddelar vem som tillverkat den och vad den är, och kontrollappar visar detta som enhetens identitet. Detta insticksprogram meddelade fortfarande detaljerna från det ursprungliga insticksprogrammet det förgrenades från: en annan författares namn, MusicBees webbplats istället för sin egen, och ett modellnummer som var låst till "1.0" sedan den allra första utgåvan. Från din telefon fanns det inget sätt att veta vilket insticksprogram du pratade med, än mindre vilken version av det. Dessa detaljer kommer nu från insticksprogrammet självt, så versionen som visas bredvid enheten förblir korrekt vid varje uppdatering.
Inställningarna för Enhetsprofiler är återigen i linje
Vad: på fliken Enhetsprofiler delar etiketterna och deras rutor en vänsterkant och sitter med jämnt avstånd, och samplingsfrekvensområdet läses som en enda rad.
Varför: fälten hade glidit isär när alternativ lades till på fliken över tid, och "till"-etiketten för samplingsfrekvensområdet hade hamnat ovanpå rutan bredvid den – läsbar när du visste vad den sa, förbryllande första gången du tittade.
2.0.7 - 2026-08-08
Större spår behåller sin titel och sin positionsreglage
Vad: ett större spår som skickas från en telefon – en lång FLAC, en högupplöst eller DSD-fil – visar nu sin korrekta titel och kan flyttas igenom, precis som ett litet. Tidigare spelades några av dem istället över nätverket, med en webbadress istället för titeln och ett reglage som inte gjorde något.
Varför: insticksprogrammet väntar på sin lokala kopia innan det startar, men det brukade bestämma i förväg om en fil var värd att vänta på, baserat på dess storlek. Det var egentligen en gissning om hur snabbt ditt nätverk är, vilket det inte har något sätt att veta: ett 65 MB-spår bedömdes vara för stort, men var sedan färdigt att ladda ner en sekund senare – bekvämt inom den väntetid det redan hade gett upp på. Det övervakar nu helt enkelt nedladdningen. Medan det fortfarande anländer fortsätter insticksprogrammet att vänta, hur lång tid det än tar; det ger upp endast när överföringen verkligen stannar, vilket det nu märker snabbare än den gamla fasta fördröjningen gjorde.
2.0.6 - 2026-08-03
Album som skickas från en telefon spelas nu upp utan mellanrum mellan spåren, och internetradio känns igen som radio istället för att behandlas som en ovanligt lång låt.
Album spelas utan mellanrum
Vad: när du skickar ett helt album från din telefon, spelar MusicBee nu upp från ett spår till nästa utan paus – så liveinspelningar, DJ-set och kontinuerliga klassiska verk håller ihop.
Varför: standarden låter en kontrollerande app säga "här är vad som kommer härnäst", vilket är det som gör en sömlös övergång möjlig. Den instruktionen accepterades inte alls, så appen hade ingenstans att placera det kommande spåret och meddelade det som om det vore det aktuella – orsaken till albumproblemet som åtgärdades i den tidigare versionen. Det accepteras nu korrekt: nästa spår hämtas medan det aktuella fortfarande spelas, och MusicBees egen spelare korsar gränsen.
Internetradio känns igen som radio
Vad: en live-station som skickas till MusicBee spelas som en ström och laddas aldrig ner.
Varför: att ladda ner en sändning är meningslöst – den har inget slut, och det finns inget att hoppa igenom – men plugin-programmet hade tidigare inget sätt att skilja den från en musikfil, så det började hämta och stoppade när nedladdningen växte över en fast storlek. Den kontrollerande appen anger vilken av de två den skickar, och det läses nu direkt. Ingen inställning, ingen gissning.
Långa högupplösta spår behåller sin kopia
Vad: DSD-filer och långa 24-bitars inspelningar kan nu flyttas igenom som vilket annat spår som helst.
Varför: de fångades av storleksgränsen ovan – en 20-minuters högupplöst sats eller ett 10-minuters DSD-spår överskred båda den – så deras kopia övergavs och positionsreglaget slutade fungera för just det material som troligen var värt att skrubba. Med radio korrekt identifierad behövs ingen storleksgräns.
2.0.5 - 2026-08-03
Två korrigeringar för att styra MusicBee från en telefon: volymen betyder nu samma sak i båda ändar, och att casta ett helt album fortsätter att fungera efter det första spåret.
Volymen på din telefon matchar volymen i MusicBee
Vad: att vrida volymen till max på din telefon når nu max i MusicBee, och MusicBees egen inställning läses tillbaka korrekt på telefonen.
Varför: insticksprogrammet berättade aldrig för den styrande appen vad dess högsta volym var, så varje app fick gissa. En av dem bestämde sig för 69, vilket innebar att dess 100 % bara nådde 69 % i MusicBee, medan MusicBees 100 % kom tillbaka som 144 % på telefonen – och telefonens volymknappar kunde aldrig riktigt nå toppen. Renderaren anger nu intervallet tydligt, så båda ändarna talar om samma skala.
Att casta ett album behåller dess titlar och dess positionsreglage
Vad: varje spår av ett album som skickas från en telefon visar nu sin korrekta titel och kan flyttas igenom, inte bara det första.
Varför: en styrande app meddelar nästa spår en bråkdel av en sekund efter det aktuella, och det meddelandet avbröt kopian som hämtades för spåret som skulle spelas – så de flesta spår föll tyst tillbaka till att spela över nätverket, vilket förlorar både titeln och möjligheten att hoppa runt. Kopior för flera spår hålls nu bredvid varandra, så ett meddelande kan inte längre avbryta den som används.
2.0.4 - 2026-08-02
Musik som skickas till MusicBee från en telefon eller en annan server beter sig nu som ett riktigt spår: du kan flytta dig genom det, och det visar sin korrekta titel direkt. Dessutom en fix för hi-fi-streamers som presenterar sig som en kombinerad enhet.
Flytta genom ett spår som skickats från annat håll
Vad: att dra positionsreglaget fungerar nu för ett spår som skickats från din telefon, en NAS eller en annan medieserver. För att göra det möjligt laddar MusicBee ner en kopia av spåret till en temporär mapp medan det börjar spela, och spelar den kopian. Det tar ungefär en sekund på ett hemnätverk, kopian raderas så snart du skickar ett annat spår, och eventuella rester rensas när MusicBee startar nästa gång.
Varför: MusicBee kan starta och stoppa något det lyssnar på över nätverket, men det kan inte flytta sig genom det – så reglaget verkade hoppa och gled sedan rakt tillbaka till där det var, utan förklaring. Att spela en vanlig fil på din egen disk tar bort begränsningen helt istället för att kringgå den.
Korrekt titel och längd från första tonen
Vad: ett spår som skickats av en app som inte ger sina filer en vanlig filändelse visar nu sin verkliga titel och längd så snart det startar, istället för att visas som en lång webbadress.
Varför: MusicBee identifierar ett spår – och hittar dess taggar – från filändelsen, och vissa spelare delar ut adresser utan någon ändelse alls. Den lokala kopian har alltid den rätta, så spåret känns igen oavsett vad den sändande appen kallar det.
Ett hopp som inte kan göras säger nu ifrån
Vad: om renderaren verkligen inte kan flytta till den begärda punkten, informeras den kontrollerande appen och rapporterar det.
Varför: den svarade tidigare "klar" oavsett, så reglaget gled tillbaka en sekund senare utan något som förklarade varför. Ett ärligt avslag är lättare att agera på än ett tyst.
Kombinerade hi-fi-streamers läses korrekt
Vad: när MusicBee spelar upp till en enhet som presenterar sig som en kombinerad enhet – en Marantz- eller Denon-streamer, där spelaren sitter inuti en tillverkar-wrapper tillsammans med en medieserver – läser plugin-programmet nu spelarens egna detaljer istället för medieserverns.
Varför: den hade frågat fel halva av enheten vilka ljudformat den kunde hantera, fick inget användbart svar och fortsatte utan att någonsin kontrollera – exakt den hårdvara där formatbehandling mest behöver vara rätt. En enhets modellbeskrivning tas nu också med i beräkningen när den matchas mot en enhetsprofil; den lästes från fel ställe och kasserades.
2.0.3 - 2026-08-02
Spela-till-rollen växer upp: MusicBee kan nu skickas musik som inte redan finns i dess bibliotek – en fil på din telefon, på en NAS, på en annan server – istället för bara spår den redan äger.
Spela musik skickad från din telefon, inte bara ditt eget bibliotek
Vad: när du använder en kontrollapp som Symfonium eller BubbleUPnP för att skicka musik till MusicBee, behöver spåret inte längre komma från MusicBees eget bibliotek. En fil lagrad på själva telefonen, på en NAS, eller på en annan medieserver spelas nu upp. Titeln och längden kommer från appen som skickade den, så spåret visas korrekt även om MusicBee aldrig har sett filen.
Varför: spela-till-rollen byggdes för fallet där du bläddrar i den här datorns bibliotek från din telefon och trycker på en låt – spåret fanns redan på datorn, så MusicBee spelade helt enkelt sin egen fil. Allt som kom från någon annanstans kasserades tyst, vilket gjorde funktionen oanvändbar för det lika naturliga fallet att trycka musik från telefonen till de bra högtalarna.
Ett spår som inte kan spelas säger ifrån
Vad: om renderaren verkligen inte kan spela upp det den skickades, rapporterar den nu det tillbaka till appen som skickade det.
Varför: den svarade tidigare "fattar" på allt, så kontrollappen fortsatte och tryckte på spela. Utan att något faktiskt laddades, startade MusicBee om vilket spår som helst som hade lämnats kvar från tidigare – och om den filen var borta, klagade den på att dess källa inte kunde hittas. Felet nämnde ett orelaterat spår och pekade ingenstans nära det verkliga problemet.
Att skicka ett nytt spår medan det är pausat spelar nu upp det spåret
Vad: om MusicBee är pausat och din kontrollapp skickar den något nytt, startar det nya spåret.
Varför: återupptagning tog prioritet över laddning, så det pausade spåret fortsatte där det slutade och spåret du just hade valt tappades utan ett ord.
2.0.2 - 2026-08-01
Sökning är temat: det fungerar nu efter artist, returnerar rätt resultat och är snabbt i ett stort bibliotek. Plus ett nytt sätt att rikta din kontrollapps blandning, och en fix för tre knappar som ledde ingenstans.
Sökning efter artist fungerar faktiskt
Vad: att söka efter en artist från din kontrollapp returnerar nu den artistens musik. Sökningen matchar både spårets Artist och albumets Album Artist, så en samling hittas oavsett om du skriver artistens namn eller namnet som albumet är arkiverat under.
Varför: en artistsökning misstogs tidigare för "ge mig allt av denna typ" – artisten du skrev kastades bort och hela biblioteket kom tillbaka, så en sökning som borde ha matchat några hundra spår returnerade tiotusentals. Att bara matcha ett av de två artistfälten skulle tyst ha förlorat hälften av resultaten, så båda kontrolleras.
Sökresultatsidan korrekt
Vad: att skrolla igenom en lång lista med sökresultat flyttar nu genom den. Varje sida du skrollar till är den sida du får.
Varför: servern brukade svara på varje begäran med de första handfull resultaten samtidigt som den rapporterade det fullständiga matchningsantalet, så en app som skrollade efter mer fortsatte att ta emot samma objekt och nådde aldrig slutet.
Sökning är mycket snabbare i ett stort bibliotek
Vad: en sökning kör nu en enda fråga för hela resultatuppsättningen och läser endast taggarna för den sida du tittar på.
Varför: varje sida körde tidigare om frågan mot biblioteket och laddade sedan taggarna för varje matchning – tusentals av dem – för att visa ett dussin. I en stor samling gjorde det varje skroll paus. Resultaten släpps också när biblioteket uppdateras, så en redigering serveras aldrig inaktuell.
Rikta din blandning mot ett filter
Vad: en ny inställning Slumpmässiga spelningar från på fliken Biblioteksinställningar. Lämna den på All musik och din kontrollapps mappar Slumpmässiga spår / Slumpmässiga album beter sig som tidigare; välj ett av dina MusicBee-filter och varje slumpmässig begäran drar istället från det filtret. Dolda filter erbjuds också.
Varför: en blandningsmapp ber om en del av "allt", och det är den enda begäran som inte bär någon ledtråd om vad du menade – så den drog alltid från hela biblioteket, talat ord och allt. Det är här du säger vad "allt" betyder. Att öppna en blandningsmapp inifrån ett filter på enheten blandar fortfarande det filtret: ett val du gör medan du bläddrar vinner över inställningen.
Hjälp, GitHub och uppdateringskontroll når riktiga sidor
Vad: knapparna Hjälp och GitHub i inställningsdialogen, och den automatiska kontrollen efter en ny version, öppnar nu de sidor de namnger.
Varför: alla tre byggdes från en förkortad form av pluginets namn som ingen sida någonsin har funnits på, så var och en misslyckades tyst – knapparna verkade inte göra något och uppdateringskontrollen rapporterade aldrig något, oavsett hur länge en ny version hade varit ute.
2.0.1 - 2026-07-26
En omgång med fixar för uppspelning och bläddring, med fokus på podcaster och på de kontrollappar (som BubbleUPnP) som styr uppspelning och blandning.
Podcaster börjar spelas utan paus
Vad: En nedladdad podcastepisod anger nu sin verkliga längd och filstorlek direkt, hämtad direkt från filen på disken till mediametadata.
Varför: Utan en angiven varaktighet skannar en kontrollapp som BubbleUPnP om hela ljudströmmen varje gång du trycker på play, bara för att ta reda på hur lång episoden är – så uppspelningen började först efter en märkbar paus. Med längden nu annonserad startar den rent.
Podcaster visas när du bläddrar efter artist
Vad: En podcasts programnamn är nu arkiverat som dess artist – speglat i fälten Artist och Album Artist (och deras sorteringsvarianter), precis som det redan fyller fältet Album.
Varför: En bläddringsväg som grupperar efter ett artistfält brukade hitta varje podcasts artist tom och hamna i en återvändsgränd på en tom nivå. Att behandla själva programmet som artisten – konsekvent med att behandla det som albumet – innebär att dessa vägar nu når episoderna istället för ingenting.
"Nyligen spelade" och casting fungerar direkt efter en omstart
Vad: Att spela en podcast, ljudbok, inkorg eller radiospår direkt – utan att först bläddra till det, som BubbleUPnPs "Nyligen spelade"-lista och cast-mål gör – misslyckas inte längre. Insticksprogrammet laddar nu spåret vid behov när det efterfrågas via id.
Varför: Dessa listor begär ett spår direkt efter att MusicBee startar om, innan något har bläddrats, så insticksprogrammet hade aldrig sett id:t och svarade "Dåligt id". Det tvingar nu att ladda de relevanta källorna vid den första direkta begäran och hittar spåret.
"Slumpmässiga spår" och "Slumpmässiga album" returnerar resultat
Vad: BubbleUPnPs "Slumpmässiga spår" och "Slumpmässiga album" blandningsmappar, som begär en slumpmässig del av hela biblioteket, kommer nu tillbaka fyllda.
Varför: Dessa är sökningar utan titel att matcha, och insticksprogrammet svarade tidigare på dem från en tom intern lista, så de visade alltid ingenting. De serveras nu – korrekt sidindelade – från samma on-demand-frågeväg som resten av bläddringen använder.
Album med en tom tagg listar sina spår
Vad: Att öppna ett album som grupperades på ett tomt värde – till exempel inkorgsspår som inte har något år – visar nu dess spår.
Varför: Matchningen som samlar ett albums spår behandlade "denna tagg är tom" som "ingen matchning", så alla album som bildades från ett tomt fält bläddrade till ingenting. Ett tomt gruppvärde matchar nu korrekt de spår som delar det.
2.0.0 - 2026-07-22
Detta är den första offentliga versionen av den öppen källkods-yaiol-forken av MusicBee UPnP-pluginet. Den presenteras i två delar: allt som är nytt i denna fork, sedan fixar och förbättringar som gjorts i det ursprungliga pluginet. Varje objekt behåller Vad / Varför-formen från projektets interna funktionskatalog så att resonemanget bakom varje ändring finns på sidan, inte bara ändringen.
Nytt i denna fork
MediaRenderer - spela upp till MusicBee
N01 - MusicBee som en uppspelnings-renderare
Vad: normalt fungerar detta plugin åt ett håll: en telefon eller annan enhet bläddrar i MusicBees bibliotek och spelar upp musiken på sig själv. Denna funktion lägger till den motsatta riktningen - den låter MusicBee vara spelaren. Från en kontrollapp på din telefon (som BubbleUPnP) kan du välja din stationära MusicBee som den enhet som spelar, och sedan styra den från din hand: spela, pausa, stoppa, hoppa framåt eller bakåt, hoppa till en punkt i spåret och ändra volymen eller stänga av ljudet.
Varför: det förvandlar din telefon till en fjärrkontroll för musiken som redan finns på din PC. Sitt i soffan, bläddra i ditt bibliotek på telefonen, tryck på ett spår, och det kommer ut ur högtalarna som är anslutna till din stationära dator - med full kontroll från där du sitter. Det ursprungliga pluginet levererade aldrig detta som en fungerande funktion.
Aktivera det: det är avstängt som standard, eftersom att slå på det låter vad som helst på ditt hemnätverk starta uppspelning på din PC. Du aktiverar det med en kryssruta på inställningsdialogens flik "Allmänt". Pluginets tre roller har var sin kryssruta där - dela mitt bibliotek (Server), låt andra spela upp till mig (Renderare) och spela upp till andra enheter (Kontrollpunkt) - och dialogrutan visar bara de inställningsflikar som de roller du aktiverade faktiskt behöver, så du möts aldrig av alternativ som inte gäller dig.
Skilja dina maskiner åt: du kan ge renderaren vilket namn du vill (det börjar som "MusicBee (yaiol)"). Det namnet är vad som visas i din telefons lista över uppspelningsmål, så när mer än en PC kör MusicBee kan du se vilken som är vilken. En namnändring träder i kraft omedelbart, utan omstart.
Bästa möjliga ljud när den spelar upp till sig själv: när du bläddrar i MusicBees eget bibliotek från din telefon och skickar ett spår tillbaka till samma MusicBee, känner pluginet igen att den ombeds spela upp en av sina egna filer och spelar den helt enkelt direkt från din disk. Resultatet är exakt och omedelbart - bit-perfekt, med MusicBees egen equalizer och volymutjämning tillämpad - istället för att meningslöst skicka ut ljudet på nätverket och rakt tillbaka till sig själv.
Köra det privat: de tre rollerna fungerar oberoende, så du kan slå på renderaren samtidigt som du lämnar biblioteksdelning avstängd. I den "endast renderare"-inställningen förblir ditt bibliotek helt dolt från nätverket - endast uppspelningsmålet annonseras - och MusicBee kommer aldrig att erbjuda att spela upp till sig själv.
Uppspelningsbeteende
N02 - 5.1 FLAC mixas inte automatiskt ned
Vad: kanalantalets begränsning i MediaServerDevice.GetEncodedFile var If StereoOnly OrElse Not isPcmData Then channelCount = 2. Klausulen Not isPcmData mixade tyst ned varje icke-PCM-transkodning (FLAC, MP3, AAC, Ogg) till stereo oavsett källans kanalantal, vilket gjorde 5.1-kompatibla renderare oanvändbara när källan var 5.1 FLAC. Nu exkluderar den andra klausulen FLAC: Not isPcmData AndAlso encoder.Codec <> FileCodec.Flac. FLAC 5.1 passerar igenom; MP3/AAC/Ogg tvingas fortfarande till stereo eftersom MusicBees kommandorads-encoders för dessa format förväntar sig 2-kanalsingång.
Varför: hela poängen med att transkoda en 5.1 FLAC-källa till FLAC-utgång är att bevara flerkanalsmixen. Tyst nedmixning gjorde FLAC-transkodningsalternativet oanvändbart för surroundlyssning. Med N02 gör det rätt.
Arkitektur
Den strukturella förändringen som gör forken livskraftig på ett stort bibliotek - frånvarande i det ursprungliga pluginet.
N03 - Lat (on-demand) bläddringsträd
Vad: det ursprungliga pluginet byggde hela bläddringsträdet vid MusicBee-start - räkna upp varje spår, full Library_GetFileTags per fil, montera hela containerhierarkin - innan HTTP-porten öppnades. På ett verkligt bibliotek (50k+ spår, 5400 poddavsnitt, hundratals stationer) är det minuter av kallstart, och trädet stannar i RAM för alltid inklusive grenar som ingen klient någonsin öppnar. Denna fork bygger inget i förväg: roten exponerar en L:-prefixad platshållare per slutpunkt (L:music, L:podcast, L:filter:…); varje nivå beräknas endast när en klient bläddrar in i den (LazyBrowse → EnsureLazyEndpointInMemory → cacheminnen per nivå), och biblioteksändringsmeddelanden rensar cacheminnena (SetLibraryDirty).
Varför: kallstart är i princip omedelbar - HTTP-porten är öppen när MusicBee avslutar plugin-init - och minnet förblir proportionellt mot vad som har bläddrats, inte mot biblioteksstorleken. Kompromiss: den första bläddringen in i en slutpunkt betalar dess laddningskostnad; återinträde cachas tills nästa biblioteksändring. Detta är grunden som allt annat beror på. Fullständiga anteckningar: FIXES.md.
Nätverk och robusthet
Förstärkning av HTTP-serverns bindningsväg. Det ursprungliga pluginet dör tyst när dess port är otillgänglig.
N04 - Självläkande HTTP-portbindning
Vad: pluginets HTTP-server dör inte längre när dess konfigurerade port är otillgänglig. Tre länkade ändringar:
- Automatisk återgång vid bindningsfel.
HttpServer.Startförsöker den konfigurerade porten, och vidSocketExceptionskannar uppåt upp till 20 portar efter den första lediga. Den faktiska bundna porten registreras i en nyPlugin.boundServerPort, och allt som annonserar servern - SSDPLOCATION-URL:er (NOTIFY + M-SEARCH-svar), enhets-URL:en (PrimaryHostUrl), routerns portvidarebefordran och SSDP/kontrollpunkts-självfiltren - läser nuboundServerPortistället förSettings.ServerPort. UPnP-klienter upptäcker den verkliga porten via SSDP, så en flyttad port är transparent för renderare. - Användarmeddelande. När en återgång sker (den sparade porten är inte den som används), berättar en lokaliserad
MessageBox(WarnPortInUse) för användaren vilken port som faktiskt används och att enheter fortfarande kommer att hitta den - eftersom pluginet körs huvudlöst och ett meddelande i dialogrutan endast skulle ses av någon som redan misstänkte ett problem. - Omstartsåterställning.
RestartServer(inställnings-spara-omstartsvägen) brukade derefereraPlugin.controller/Plugin.serverblint. Om den initialaInitialisekastade ett undantag innan de skapades (exakt vad ett misslyckat bindningsförsök orsakade), träffade nästa inställnings-spara enNullReferenceException- vilket lämnade ett halvdött plugin. Den återskapar och startar dem nu närNothing, så att spara en fungerande port återupplivar pluginet utan en fullständig MusicBee-omstart.
Varför: triggern var en verklig användarincident. Den gamla standardporten 49382 ligger i Windows dynamiska intervall (49152-65535), där Hyper-V/WSL2/Docker/WinNAT reserverar stora block som flyttas vid varje uppstart - så bindningen misslyckades med WSAEACCES ("åtkomst nekad") på en maskin där den hade fungerat i månader. Att ändra standard till en ledig port kolliderade sedan med Serviio (en separat DLNA-server som redan fanns på den nya porten), vilket misslyckades med WSAEADDRINUSE. Varje fel svaldes i Initialise, vilket lämnade pluginet tyst dött och sedan NRE-ade vid nästa inställningsspara. Efter N04 självläker en portkollision - servern fortsätter att köra på nästa lediga port, användaren informeras, och klienter återupptäcker den - istället för att ta ner hela pluginet.
Implementering:
- Standardport flyttad
49382→9779(under det dynamiska intervallet, så Windows reserverar den aldrig automatiskt; inte en känd mediaserverstandard) i alla treServerPort-deklarationer + inställnings-parse-fel-återgången. Plugin.boundServerPort(nytt delat fält) håller den aktiva lyssningsporten;activeServerPortförblir den konfigurerade ögonblicksbilden så att "Omstart krävs"-märkeslogiken inte falskt utlöses vid en återgång.HttpServer.PortScanRange = 20; skanningen stoppas vid den första framgångsrikaTcpListener.Start()och kastar det sista undantaget endast om alla försök misslyckas.- Ny EN-resursnyckel
WarnPortInUse(översättningar följer lokaliseringspasset vid publicering).
N05 - SSDP-annonseringar över multicast-gruppen (VPN / punkt-till-punkt)
Vad: SSDP-annonseringar skickas till UPnP multicast-gruppen (239.255.255.250) istället för en IP-broadcastadress. Det ofarliga felet "kan inte komma åt ett borttaget objekt" som loggas när ett SSDP-sökresultat tävlar med en serveromstart undertrycks också.
Varför: på punkt-till-punkt / VPN-nätverksadaptrar gäller inte IP-broadcast - den gamla broadcast-sändningen misslyckades med "ogiltigt argument" och annonseringar missades, så pluginet var osynligt för klienter på dessa länkar. Att annonsera till den korrekta multicast-gruppen fixar upptäckten på just dessa adaptrar.
Biblioteksnavigering
Dessa levererades i denna fork och finns inte i det ursprungliga pluginet. De kom från att faktiskt bläddra i pluginets egen utdata från verkliga UPnP-klienter.
N06 - Filterbaserad biblioteksexponering
Vad: MusicBees filterflikar (.xautopf-filer i användarens MusicBee-mapp) blir UPnP-rotcontainrar i pluginets bibliotek. Varje filters spår kan sedan bläddras i en hierarki av AlbumArtistSort → Album → Tracks.
Varför: användare med kurerade MusicBee-filter (t.ex. "5-stjärniga spår", "Nyligen tillagda", "Klassisk → Barock") förväntar sig att hitta dem när de bläddrar i pluginet från en UPnP-klient. Ursprungligt plugin exponerade endast det råa biblioteksträdet.
N07 - SortAlbumArtist-fältkoppling
Vad: pluginet läser nu MusicBees MetaDataType 165 (Sort Album Artist) och använder det för att gruppera/sortera artister i bläddringsvyer.
Varför: hi-fi-webbläsare och audiofiler använder sorteringsartistnamn ("Beethoven, Ludwig van" istället för "Ludwig van Beethoven") för att organisera bibliotek. Standardförväntan för seriösa lyssnare. Saknas i båda uppströmmarna.
N08 - Hantering av flera värden för AlbumArtist
Vad: när ett albums AlbumArtist-fält innehåller flera artister separerade med "; " (t.ex. "yaiol; Ars Ricercata"), visas spåret nu under varje artist i bläddringsvyer, inte under en enda Frankenstein-artist som kombinerar namnen.
Varför: samarbetande album och samlingar behöver visas under varje samarbetspartner. Utan detta är hälften av sökvägarna för att hitta albumet trasiga.
N09 - Album-container-konstverk (upnp:albumArtURI)
Vad: album-containernoder i DIDL Browse-svar inkluderar nu ett upnp:albumArtURI-element som pekar på albumomslaget.
Varför: utan detta visar varje album i en UPnP-klients bläddringsvy en generisk ikon istället för albumomslaget. Visuell ledtråd för navigering; förväntas av varje modern hi-fi-webbläsare.
N10 - Spårordning inom filteralbum
Vad: spår inom ett filterexponerat album sorteras nu efter skivnummer, sedan spårnummer.
Varför: standardalbumordning. Utan explicit sortering kom spåren tillbaka i den ordning som filtret råkade returnera dem - oftast slumpmässigt.
N11 - Fix för spellistmappträd
Vad: funktionen LoadLibraryPlaylists (ursprungligen av Steven Mayall, ~2014) misslyckades med att gå ner i nyskapade spellistmappar. Den första spellistan i varje mapp, plus eventuella undermappar, hamnade föräldralösa på rotnivån.
Varför: fanns i det ursprungliga pluginet i elva år. Synlig inom 30 sekunder efter att ha öppnat BubbleUPnP och klickat på Spellistor. Fixat i yaiol genom att korrekt rekursivt gå in i nyskapade mappar under trädkonstruktionen.
N12 - Sanering av XML-olagliga kontrolltecken
Vad: varje spår med en tagg som innehåller ett C0-kontrolltecken (t.ex. 0x19 från en dålig kodningspass - UTF-8 → Latin-1 → tillbaka trunkering av 0x99 till 0x19) orsakade att hela Browse-svaret misslyckades med Action Failed när det dåliga spåret kom in i en paginerad batch.
Varför: XML 1.0 förbjuder de flesta C0-kontrolltecken, och XmlWriter kastar ett undantag när den ombeds skriva något. Fanns i det ursprungliga pluginet. Fixat genom att strippa ogiltiga tecken vid varje Library_GetFileTags-utgångspunkt via XmlConvert.IsXmlChar.
N13 - Radiolista deterministisk över paginerad bläddring
Vad: bläddring för radiocontainern hamnade i den generiska fillistgrenen, som anropade files.Sort(AlbumFileComparer) vid varje anrop. Radioinlägg har tomma Album/Disc/Track-taggar, så varje jämförelse returnerade 0 - List(Of T).Sort är instabil, vilket producerar en annan ordning vid varje anrop. UPnP-kontrollpunkter paginerar (BubbleUPnP hämtar 0..15 sedan 16..slut); mellan de två anropen omorganiserades listan, så vissa stationer dök upp på båda sidorna (dubletter) och vissa på ingen (saknade) - vilket såg slumpmässigt ut vid varje uppdatering.
Varför: fanns i det ursprungliga pluginet (dess författare bläddrar aldrig radio via UPnP). Fixat här med en dedikerad ContainerCategory.Radio-gren i Browse, ingen sortering per anrop; radioFiles sorteras en gång vid laddningstid efter titel (stabil). Paginerad bläddring ser nu en deterministisk ordning; sida 1 och sida 2 är åtskilda.
N14 - UPnP Sök albumklass returnerar albumcontainrar
Vad: UPnP-sökning efter albumklassfrågor (upnp:class = "object.container.album.musicAlbum", t.ex. BubbleUPnPs "Slumpmässiga album") returnerade hela spårlistan istället för albumcontainrar, så klienten visade noll album. Den ursprungliga hanteraren analyserade endast parentesiserade kriterier, och dumpade sedan alla spår oavsett den begärda klassen.
Varför: fixat här - albumklassfrågor räknar nu upp distinkta album (grupperade efter AlbumArtist+Album) och avger varje som en korrekt musicAlbum-container med omslagsbild, adresserbar via det virtuella ID-utrymmet Salb<idx> så att klienten kan borra sig in i ett resultat och spela upp det.
N15 - Fungerande, omfångsmedveten UPnP-sökning med klick-genomgång
Vad: originalet annonserade inga sökfunktioner (GetSearchCapabilities returnerade tomt), så klienter vägrade ens att skicka en sökning; och den gamla backend läste från musicFiles, permanent tom i den lata träd-eran. Denna fork annonserar de verkliga sökbara egenskaperna, implementerar spår-efter-titel och album-efter-titel mot det lata biblioteket (HandleLazySearch), begränsar frågan till klientens aktuella gren när ett verkligt container-ID skickas (annars ersätter L:music så att sökningar i toppfältet inte drar in podcast/radio/ljudboksbrus), och gör albumresultat klickbara via syntetiska Ssrch_alb_*-ID:n som en Browse-tidig-gren mappar tillbaka till albumets spår. (Albumklass-resultat-som-containrar-delen är N14.)
Varför: sökning i BubbleUPnP gick från "Biblioteket stöder inte sökning" till att returnera användbara, omfångsmedvetna, spelbara resultat. Fullständig design + avvisade metoder: SEARCH.md.
N16 - UPnP-cacheinvalidering (SystemUpdateID)
Vad: originalet returnerade ett konstant SystemUpdateID=0 - UPnP ContentDirectory cache-invalideringskontraktet - så spec-kompatibla klienter (BubbleUPnP) behandlade biblioteket som aldrig föränderligt: inaktuella bläddringsresultat, 404-miniatyrer efter en URL-schemaändring, och "starta om MusicBee två gånger för att se ändringar"-dansen. Denna fork sår SystemUpdateID från epok-sekunder vid laddning (så varje omstart är strikt före den senaste) och höjer den vid varje biblioteksändring och inställningsändring (SetLibraryDirty / ResetCache → BumpSystemUpdateId).
Varför: klienter plockar pålitligt upp redigeringar, nya filer och inställningsändringar vid sin nästa bläddring. Känd begränsning: prenumererade klienter får inte aktivt den nya värdet via GENA (parkerat som framtida arbete); de ser det fortfarande vid sin nästa bläddring.
N17 - Podcast-prenumerationskonstverk
Vad: podcast-brickor visade inga bilder - varje /PodcastThumbnail/-förfrågan 404:ade. Två staplade buggar: upplösningskedjan kontrollerade aldrig MusicBees faktiska konstverkscache (%LocalAppData%\MusicBee\InternalCache\Subscriptions\<name>.jpg, där skrivbordsgränssnittet laddar från), och HTTP-lagrets unescape+lowercase förstörde feed-URL-rutnyckeln ner till dess sista sökvägssegment. Denna fork löser konstverk från MB:s InternalCache och dirigerar uppslag via en URL-säker slug som överlever HTTP-lagret intakt (PodcastSlug / podcastSubIdBySlug).
Varför: prenumerationskonstverk renderas nu i bläddringsvyer (alla 22 tidigare 404:ande förfrågningar löses).
N18 - Hierarkisk (avgränsad) taggbläddring
Vad: vilket fält som helst kan markeras som hierarkiskt på fliken Biblioteksinställningar och ges en en-teckens avgränsare (en fältväljare + avgränsarruta med lägg till/ta bort, sparas i plugininställningarna). Ställ in Gruppering till / och ett värde som Jazz/Cool Jazz bläddras sedan som Jazz › Cool Jazz istället för en platt post. Spår taggade exakt vid en gren (bara Jazz) får sin egen [Jazz]-nod så att inget döljs, en gren med ett enda barn kollapsar av sig själv, och ; nekas som avgränsare eftersom det är MusicBees egen flervärdesavgränsare.
Varför: djupa tagg-taxonomier som en användare redan har kodat i ett enda fält (genreträd, stämninghierarkier, "Klassisk/Barock/Konsert") bläddras äntligen som det träd taggen beskriver, istället för en platt vägg av snedstrecksseparerade strängar som användaren måste läsa från början till slut.
N19 - Enkel rotväg märkt med dess grupperingsfält
Vad: en enda bläddringsväg vid roten märks med dess grupperingsfält (t.ex. "Genre") snarare än dess fullständiga korta väg, vilket matchar hur sammanslagna förstafältsgrupper namnges.
Varför: bläddringsträdet läses konsekvent - en namngivningsregel oavsett om en rotpost står ensam eller slogs samman med syskon (N20) - istället för att en ensam rotpost visar en utförlig intern väg medan dess sammanslagna grannar visar ett rent fältnamn.
N20 - Slå samman bläddringsvägar som delar ett första fält
Vad: två bläddringsvägar som delar samma första fält - "Genre / Sortera Albumartist" och "Genre / Podcastpersoner" - kollapsar till en enda Genre-rotmapp som först listar genrevärdena och sedan delar sig i de två vyerna, istället för två nästan identiska "Genre / …"-poster sida vid sida vid roten.
Varför: en användare med flera relaterade vyer kapslade under ett gemensamt fält såg roten överfylld med nästan identiska toppnivåposter. Att slå samman dem håller roten läsbar och grupperar de relaterade vyerna där de hör hemma - under deras delade fält.
N21 - Kategoritypade bläddringsvägar (Standard / Radio / Podcast)
Vad: varje bläddringsväg är typad efter kategori - Standard, Radio eller Podcast. Mallistan är grupperad i dessa tre sektioner, varje malls fältväljare erbjuder endast de fält som den kategorins data faktiskt kan leverera, och en mall kan endast tillämpas på matchande noder i vyträdet (inkompatibla noder gråas ut och kan inte markeras). De reserverade mallarna för Radio och Podcasts kan inte raderas, så deras kategorisektion försvinner aldrig.
Varför: utan typningen kunde en användare bygga en layout som tyst kom upp tom - en radiostation har inget "album", ett poddavsnitt har ingen "albumartist" - och bara upptäcka det genom att bläddra till en död mapp från en UPnP-klient. Att begränsa fältmenyn och tillämpningsmålen till kategorins verkliga data gör tomma layouter omöjliga att bygga.
N22 - Gruppera podcaster efter publiceringsår
Vad: varje poddavsnitts publiceringsdatum läses in, så en poddsökväg med en årsnivå grupperar avsnitt efter år istället för att kollapsa dem under en enda "Okänd".
Varför: stora podcastprenumerationer blir navigerbara efter år som resten av biblioteket, istället för att varje avsnitt hamnar i en odaterad hög eftersom pluginet aldrig tittade på publiceringsdatumet per avsnitt.
N23 - Kollapsa grupperingsnivåer med ett enda resultat
Vad: en grupperingsnivå som löser sig till ett enda värde - en skivtypsnivå som bara visar "LP" för en artist som bara gjorde LP-skivor, eller en bokstavsnivå med en enda bokstav - hoppas över automatiskt, vilket släpper användaren direkt in i dess innehåll.
Varför: att bläddra igenom en mapp som innehåller exakt en mapp är ren friktion. Att kollapsa den enskilda valnivån tar bort det döda klicket utan att ändra vad användaren kan nå.
N24 - Årsgruppering/sökning mot MusicBees datumfält
Vad: årsvillkoret frågar inte längre MusicBees fullständiga datumfält "År" med ett rent fyrsiffrigt värde, och den hårdkodade år-fält-aliasingen är borta så att varje grupp-efter-fält nu löses generiskt från sökvägsdefinitionen.
Varför: för bibliotek vars årstagg innehåller ett komplett datum, returnerade gruppering eller sökning efter år tidigare ingenting - den fyrsiffriga frågan matchade aldrig det fullständiga datumfältet. Att fråga rätt fält gör att årsgruppering och sökning hittar spåren igen.
N25 - Separata "År" och "År (åååå)" grupp-efter-fält
Vad: albumgruppering och bläddringsvägar exponerar nu båda MusicBees egna årsfält - År (hela datumtaggen) och År (åååå) (bara det fyrsiffriga året) - så att användaren kan välja antingen när de definierar en albumgruppering eller en bläddringsväg.
Varför: de två fälten betyder olika saker i MusicBee, och att slå samman dem förlorade den distinktionen. Att visa båda låter en användare samla varje utgåva från ett år tillsammans (åååå) eller behålla exakt datumordning (fullständig årstagg), som de avser.
N32 - Fästa filter och spellistor grupperade efter typ vid roten
Vad: ett fäst filter visas nu direkt under mappen Filter vid bläddringsroten, och en fäst spellista direkt under mappen Spellistor, istället för att alla fästa objekt samlas i en klump i slutet av roten. Varje fäst genväg sitter med sin egen typ.
Varför: när en användare fäster fler genvägar blir en enda efterföljande klump av blandade filter och spellistor svårare att skanna och separerar varje genväg från den mapp den tillhör. Att gruppera fästa objekt under sin egen kategori håller roten läsbar och håller varje genväg bredvid de saker den tillhör.
Inställningsdialog och paketering
N26 - Sektionerad inställningsdialog
Vad: sidan Inställningar fick en vänsternavigeringslayout med sektioner: Allmänt / Uppspelning / Bibliotek / Enhetsprofiler / Diagnostik.
Varför: originalet var en enda lång platt lista över varje inställning - bra för utvecklaren som byggde den, förvirrande för alla andra. Sektionering grupperar relaterade alternativ och får dialogrutan att kännas mer som moderna appinställningar.
Sammansättning + plugin-namnbyte (inget F-id - paketeringsanteckning)
Vad: den kompilerade DLL:en heter mb_UPnP_yaiol.dll och pluginet rapporterar sig som "MusicBee UPnP (yaiol)". Skiljer sig från originalet mb_Upnp.dll.
Varför: användare kan installera yaiol bredvid det ursprungliga pluginet och jämföra beteende sida vid sida.
Märkessystem - synliggör körningstillstånd (mekanism bakom F40)
Vad: ett generiskt UI-mönster för att synliggöra viktiga körningsförhållanden som synliga färgade märken i inställningsdialogen. Aktuella instanser:
- ⚠ Max Conn (N04) - utlöses när max-anslutningsgränsen nåddes minst en gång sedan MusicBee startade. Klibbig sessionsflagga
Plugin.MaxConnectionsHit. Ställs in iWaitOnSendBarriernär ingen plats är ledig. - ⚠ Omstart krävs - utlöses när en sparad inställning kräver en MusicBee-omstart för att träda i kraft. Klibbig sessionsflagga
Plugin.RestartRequired. Ställs in i dialogens Spara-hanterare när det nya sparade värdet skiljer sig från körningsögonblicksbilden (Plugin.activeMaxConnections,Plugin.activeServerPort,Plugin.activeIpAddress). Inställningar som kräver omstart är begränsade till de som verkligen inte kan hot-reloadas - HTTP-serverns bindningsparametrar och SemaphoreSlim som byggs en gång vid Initialise.
Varför: pluginets loggfil är bra för tekniska användare som felsöker, men en icke-teknisk användare som stirrar på "enheten låter fel" eller "uppspelningen är långsam" kommer aldrig att öppna Diagnostik → Visa logg. Märken fångar upp de fall där användaren behöver veta att något hände och synliggör det nästa gång de öppnar pluginet - upptäckbart utan att läsa något.
Återanvändbart för framtiden:
- Profilmatchning upptäckt (enhetens user-agent matchade aldrig någon profil, återgick till Generisk).
- NextURI backoff utlöstes (F13 - gapless inaktiverat för sessionen på en opålitlig enhet).
- Biblioteksskanning misslyckades / delvis.
- Renderaranslutning förlorades mitt i sessionen.
- Alla andra förhållanden där "hände en gång, användaren borde veta" slår "loggades tyst bland 1000 andra rader".
Implementeringskonventioner:
- Märkesetiketter finns på dialognivå (inte inuti någon panel) så de är synliga oavsett vilken sektion användaren befinner sig i.
- Placerade längs den nedre raden nära Spara/Avbryt (nuvarande: y=410 horisontellt staplade).
- Varje märke har en motsvarande klibbig sessionsflagga i
Pluginsom blir True när tillståndet inträffar och återställs endast vid MusicBee-omstart. - Resurser:
<Condition>Badge(etiketttext, prefix med ⚠) +<Condition>BadgeTip(verktygstips som förklarar orsak + åtgärd). - För "sparad inställning kräver omstart"-märken, ta en körningsögonblicksbild vid
Plugin.Initialise()och jämför medSettings.*efterSettings.SaveSettings()i dialogens Spara-hanterare.
N27 - Avbryt kastar bort sökvägs-/mallredigeringar
Vad: redigeringar som gjorts i sökvägar och mallar i inställningsdialogen kastas nu bort när användaren klickar på Avbryt, istället för att tyst förbli tillämpade, och alla reserverade mallar som tagits bort under sessionen återskapas. (Mallar sparas annars live när de redigeras - det finns ingen separat Spara-knapp på fliken Sökvägar.)
Varför: Avbryt ska betyda avbryt. Tidigare fann en användare som experimenterade med sökvägs-/malländringar och backade ut att ändringarna redan var genomförda, utan möjlighet att ångra dem annat än att göra om varje för hand.
N28 - Bokstavliga et-tecken i fältväljarmenyn
Vad: ett fält vars namn innehåller "&" - t.ex. "Stämning & Kontext" - renderar et-tecknet bokstavligt i fältväljarmenyn istället för att svälja det som ett Alt-mnemonic-prefix.
Varför: fältnamn med ett et-tecken visades fel (tecknet försvann och nästa bokstav blev en accelerator), vilket gjorde menyalternativet svårt att känna igen.
N29 - Stabil, oöversatt inställningsfönstertitel
Vad: inställningsfönstrets titel är fastställd till varumärkessträngen "MusicBee UPnP Plugin" och varierar inte längre med gränssnittsspråket; den språkspecifika DialogTitle-strängen togs bort från varje språkpaket.
Varför: en fönstertitel som ändrade formulering per språk var en översättningsbar yta utan fördel - titeln är ett varumärke. Att fästa den håller den stabil och konsekvent överallt.
Lokalisering
Det ursprungliga pluginet är endast på engelska. Denna fork är fullt lokaliserbar - varje användarvänd sträng flödar genom ett resursbundle, och pluginet upptäcker automatiskt MusicBees eget UI-språk.
N30 - Flerspråkigt UI (översättningar väntar)
Vad: lokaliseringsmekanismen är komplett och levereras. Localisation.vb läser MusicBees valda språk från MusicBee3Settings.ini (<SystemLanguage> endonym) och tillämpar den matchande .NET-kulturen på tråden, så My.Resources.Resources.* returnerar den lokaliserade strängen. Varje användarvänd etikett/knapp/meddelande är kopplad till en resursnyckel (designer-kontroller via ApplyDesignerExtras + sync-en-locale.js; körningssträngar som WarnPortInUse handläggs). Vad som inte är klart ännu är den faktiska översättningen: endast det engelska källpaketet (Resources.resx) finns - satellitpaketen för de andra språken produceras i en batchpass när pluginet är funktionskomplett (att översätta bitvis medan strängar fortfarande ändras slösar bort ansträngning).
Målspråk (den uppsättning MusicBee själv erbjuder, matchad 1:1 av endonymToCulture så att pluginet automatiskt följer MusicBees språk):
Arabiska (ar) |
Tjeckiska (cs) |
Tyska (de) |
Grekiska (el) |
Spanska (es) |
Franska (fr) |
Ungerska (hu) |
Italienska (it) |
Koreanska (ko) |
Nederländska (nl) |
Norska (nb) |
Polska (pl) |
Portugisiska BR (pt-BR) |
Portugisiska PT (pt-PT) |
Svenska (sv) |
Turkiska (tr) |
Ukrainska (uk) |
Ryska (ru) |
Japanska (ja) |
Förenklad kinesiska (zh-CN) |
Traditionell kinesiska (zh-TW) |
Engelska (en, källa) |
Variantpolicy (enligt arbetsytans lokalregel): PT och ZH är uppdelade i distinkta paket eftersom ordförråd/skrift verkligen skiljer sig (pt-BR/pt-PT, zh-CN/zh-TW). EN är ett enda paket - MusicBees "English(US)" (en-US) återgår till en via .NETs kulturkedja, så inget separat US-paket produceras. ES och FR är likaså enskilda lokaler.
Varför: ett UPnP-plugins inställningar ("använd inte rå PCM", "tvinga little-endian PCM", port-fallback-varningar) är kryptiska nog på ens modersmål. Att följa MusicBees eget UI-språk - istället för att tvinga engelska - är skillnaden mellan ett verktyg en icke-engelsk användare kan konfigurera och ett de inte kan. Ingen av uppströmmarna försökte detta.
N31 - Hjälplänk öppnas på det fullständiga gränssnittsspråket
Vad: att öppna hjälplänken från pluginet respekterar användarens fullständiga gränssnittsspråk (t.ex. pt-BR, zh-CN) istället för att kollapsa till basspråket, och skickar en tydligare uppdateringskontrollidentifierare.
Varför: en användare som kör MusicBee i en regional variant (brasiliansk portugisiska, förenklad kinesiska) skickades till hjälpsidan på basspråket. Att bära hela kulturen landar dem på hjälpsidan på exakt det språk de använder.
Fixar och förbättringar av det ursprungliga pluginet
Kärnprotokoll och uppspelning
F01 - Uppdaterade standard DLNA-enhetsprofiler
Vad: levererar nya standardprofiler för PlayStation 4, Xbox 360/One och moderna BubbleUPnP, med kapacitetsflaggor (samplingsfrekvenser, bitdjup, codecs) som återspeglar vad dessa enheter faktiskt stöder idag.
Varför: det ursprungliga pluginets standardinställningar var frysta runt 2014. PS4/Xbox/BubbleUPnP har sedan dess fått stöd för högupplöst ljud. Direkt ur lådan spelar en ny installation bäst i klassen på dessa enheter utan att användaren behöver ändra enhetsprofilinställningarna.
F02 - Kontrollera enheter som annonserar MediaRenderer:3
Vad: pluginet undersöker en renderarens UPnP-tjänstbeskrivning för att avgöra om MusicBee kan styra den. Originalet matchade endast urn:schemas-upnp-org:device:MediaRenderer:1. Moderna enheter annonserar :2 eller :3. F02 breddar matchningen.
Varför: utan detta visas inte nya Sonos / WiiM / Eversolo-enheter som mål i MusicBees "Spela till"-enhetslista - även om de talar samma protokoll. En enkel strängprefixmatchningsfix låser upp hela den moderna enhetsgenerationen.
F03 - "Tvinga nativ ström" per-profilalternativ (standard PÅ)
Vad: när den är markerad skickar pluginet de ursprungliga filbytes till enheten med ingen transkodning, ingen DSP, ingen ReplayGain-bearbetning tillämpad. Bara den råa filen användaren valde, byte för byte (med undantag för HTTP-inramning).
Varför: enligt forumvittnesmål är detta den enskilt största vinsten för uppspelningskvalitet. Hi-fi-användare som köper dyra renderare vill uttryckligen ha bit-perfekt utdata; varje DSP-beröring motverkar syftet. Standard PÅ eftersom de flesta moderna enheter hanterar vilken codec användaren än kastade på dem, och ReplayGain/EQ bör vara opt-in. Det är per-profil så att du kan behålla transkodning för en gammal Xbox samtidigt som du skickar nativt till en hi-fi DAC.
F04 - "Tvinga transkodning" per-profil
Vad: per-profil-åsidosättning som tvingar varje ström till denna enhet genom transkodern, oavsett stöd för nativ codec. Det omvända av F03 (ForceNativeStream). Ömsesidigt uteslutande med F03 - UI:et avmarkerar automatiskt den andra när någon av dem växlas på.
Varför: en enda global växling skulle vara motsägelsefull med den per-profil ForceNativeStream (F03). Verkligt fall: enhet A är en hi-fi DAC som vill ha bit-perfekta nativa strömmar; enhet B är en gammal AV-mottagare som kvävs av FLAC. Med en global växling måste användaren välja - på bekostnad av den andra enheten. Med per-profil får varje enhet rätt svar.
Implementering:
StreamingProfile.ForceTranscoding As Boolean = False.- Beständighetsschema uppdaterat till v9. Filer före v9 laddar det äldre globala värdet en gång och kopierar det till alla profiler, vilket bevarar det gamla beteendet genom uppgraderingen.
- UI: borttagen från diagnostikpanelen, tillagd i avsnittet Enhetsprofiler bredvid ForceNativeStream. Tvåvägs ömsesidigt uteslutande hanterare (
CheckedChangedpå varje avregistrerar den andra innan den växlar, för att undvika en oändlig loop). - Beslutsplats:
Settings.ForceTranscoding→streamingProfile.ForceTranscodingiWriteAudioFileDIDL.
F05 - "Tvinga little-endian PCM" per-profil
Vad: PCM-strömmar (L16/L24 mime-typer) är big-endian enligt specifikationen. Vissa enheter förväntar sig felaktigt little-endian och spelar upp vitt brus när de får korrekt big-endian-data. F05 växlar byteordningen per-profil.
Varför: utan detta producerar vissa enheter en vägg av statiskt brus. Symptomet är dramatiskt och orsaken osynlig utan kunskap om PCM-kodning - växlingen ger användarna en gissnings- och kontrollfix.
F06 - "Använd inte rå PCM" per-profil
Vad: när enheten hävdar att den stöder rå PCM, använder pluginet det. Vissa enheter ljuger - de accepterar SOAP-handskakningen men förvränger faktiska råa PCM-data, samtidigt som de korrekt hanterar PCM insvept i en WAVE-container. F06 tvingar PCM-över-Wave oavsett vad enheten annonserar.
Varför: vissa Marantz-modeller specifikt - de annonserar rå PCM men endast WAVE fungerar. Utan detta kommer råa PCM-strömmar ut förvrängda, utan något felmeddelande som pekar på orsaken.
F07 - "Innehållslängd" per-profil
Vad: vilket värde som ska skickas i HTTP Content-Length-huvudet. Fyra alternativ:
- Standard - faktiskt byteantal när känt, utelämna när okänt.
- Ingen - skicka aldrig huvudet (endast chunked encoding).
- Endast PCM - skicka endast för rå PCM; utelämna för allt annat.
- Fast - skicka
UInt32.MaxValue - 8192(en sentinel för "enorm okänd längd").
Varför: UPnP/DLNA-enheter varierar vilt i hur de reagerar på Content-Length. Vissa behöver ett exakt nummer, vissa hatar det på strömmar, vissa behöver ett sentinel "riktigt stort" värde för att fortsätta buffra. detta breddades senare från endast PCM till alla utdataformat eftersom samma problem dök upp i transkodade MP3/AAC-strömmar.
F08 - "Rensa inte NextURI" per-profil
Vad: normalt rensar pluginet enhetens köade NextURI när kön töms (skickar SetNextAVTransportURI med en tom URL). Vissa enheter (särskilt Denon) tolkar en tom NextURI som "stoppa allt" och stoppar uppspelningen omedelbart. F08 förhindrar pluginet från att någonsin rensa den.
Varför: utan detta upplever Denon-ägare att enheten avbryter mitt i spåret när kön töms. Med F08 markerad behåller enheten den inaktuella NextURI i minnet (ofarligt - den skrivs bara över nästa gång något köas).
F09 - FLAC som transkodningsutdataformat
Vad: rullgardinsmenyn för transkodningsformat i Enhetsprofiler erbjuder nu FLAC tillsammans med PCM 16/24, MP3, AAC, Ogg. Att välja det dirigerar BASS-kodaren genom MusicBees standard FLAC-konverteringskommandorad (samma mekanism som MP3/AAC/Ogg redan använder).
Varför: för enheter som hanterar FLAC bra men inte kan avkoda källcodecen (t.ex. en Eversolo som tar emot MusicBees WMA-bibliotek konverterat till FLAC), bevarar detta förlustfri kvalitet där MP3/AAC skulle kasta bort ljuddata. Låser upp N02 (5.1 nedmixningskontroll), som inte kunde åtgärdas utan ett förlustfritt transkodningsalternativ.
Implementering: enradig tillägg till Encoder.StartEncodes Select Case Codec-block - FLAC ansluter sig till MP3/AAC/Ogg i den kommandoradsdrivna grenen. UI-rullgardinsmenyn får "FLAC" som ett 6:e alternativ. Laddnings-/sparningsmappning i SettingsDialog utökas för att känna igen FileCodec.Flac ↔ SelectedIndex = 5. Mime, DLNA-typ och kodningsfunktion var redan kopplade i ItemManager.GetMimes / GetDlnaType / GetEncodeFeature från tidigare arbete (F21, F26).
Gapless (SetNextAVTransportURI)
F10 - SetNextAVTransportURI / NextURI kärna
Vad: verklig gapless uppspelning. När enheten annonserar stöd för SetNextAVTransportURI i sin UPnP-tjänstbeskrivning, förköar pluginet nästa spår på enheten innan det nuvarande avslutas. Enheten övergår internt utan hörbart glapp mellan spåren - vad du hör på en CD-spelare. Detta är inte "kontinuerlig ström"-hacket (som sammanfogar allt till en lång ström och förlorar metadata per spår).
Varför: flaggskeppsfunktionen i nivå 2. Album inspelade som en kontinuerlig liveframförande (liveinspelningar, klassiska satser, DJ-set) låter fel när det är en halv sekunds tystnad mellan spåren. Att lösa detta korrekt är en flaggskeppsfunktion, nu i denna fork.
Anmärkningar: det köade ljudet serveras via pluginets HTTP-server med streamHandle=0 (biblioteks-hämtningsläge), vilket innebär att MusicBees ljudmotor inte är inblandad för det köade spåret. Kompromiss: ReplayGain/DSP/EQ-effekter tillämpas inte på nästa spår. Acceptabelt när "tvinga nativ ström" är på (standard).
F11 - "Inaktivera NextURI-stöd" per-profil
Vad: även om en enhet annonserar SetNextAVTransportURI, tvingar denna kryssruta pluginet att ignorera den annonseringen och återgå till uppspelning ett spår i taget.
Varför: vissa enheter annonserar NextURI men har en buggig implementering (kraschar, halva övergångar, hänger sig). Istället för att omvänd-konstruera varje trasig enhet får användaren en "stäng bara av det här"-växling.
F12 - NextURI livscykel på nu-spelar-listan
Vad: när MusicBee utlöser NowPlayingListChanged, omvärderar pluginet vad som ska köas för gapless övergång. Den ber MusicBee om det nya "nästa" spåret via NowPlayingList_GetNextIndex(1) + NowPlayingList_GetListFileUrl, jämför med vad som för närvarande är köat på enheten (spåras via det nya fältet nextPlaySourceUrl), och köar om det om det ändrats (eller rensar kön om MusicBee säger att det inte finns något nästa spår).
Varför: utan F12 fortsatte enheten att spela en inaktuell NextURI när användaren tog bort/omordnade det köade spåret. Detta tog historiskt flera iterationer eftersom varje listmutation behöver olika hantering - vi förenklade genom att lita på NowPlayingList_GetNextIndex (som redan respekterar shuffle och repeat-all wrap-around), så alla varianter kanaliseras genom samma jämförelse.
Implementering:
- Nytt
nextPlaySourceUrl-fält lagrar MusicBee-biblioteks-URL:en för det som är köat (streaming-URL:en med handtagssuffix är inte jämförbar med en bibliotekssökväg). - Ny
Public Sub RefreshQueuedNextUri()påMediaRendererDevice. Tre utfall: ingen NextURI köad → ingen åtgärd; köad matchar ny "nästa" → ingen åtgärd; köad skiljer sig → anropaQueueNextmed den nya URL:en (ellerQueueNext("")för att rensa - vilket respekterar F08 DoNotClearNextUri). - Kopplad i
Plugin.ReceiveNotificationunderNotificationType.NowPlayingListChanged.
F13 - NextURI-felåtergång
Vad: efter 4 på varandra följande SetNextAVTransportURI-fel på samma enhet, inaktiverar pluginet gapless för den enheten tills MusicBee startar om.
Varför: om en enhet är genuint trasig för NextURI (intermittenta SOAP-fel, nätverksfel), skulle pluginet annars fortsätta att försöka igen vid varje spår. F13 stoppar bruset och återgår tyst till uppspelning ett spår i taget.
F14 - Upprepa-läge + NextURI-integration
Vad: F14 delas upp i två fall som hanteras vid F15-övergångsdetektorn i OnAvTransportStatusCheck:
- Upprepa-Alla: MusicBee skickar den korrekta "wrap"-URL:en (spår 1 vid slutet av listan) till
Plugin.QueueNextsjälv. Ingen speciell plugin-logik behövs - enheten övergår till den och F15-detektorn anroparPlayer_PlayNextTracksom vanligt, vilket omsluter MusicBees NPL-index tillbaka till 0. - Upprepa-En: MusicBee skickar SAMMA spår-URL till
Plugin.QueueNext. Enheten övergår till den (nytt strömhandtag, samma källa). F15-detektorn frågar nuPlayer_GetRepeat()- om det ärRepeatMode.One, hoppar den överPlayer_PlayNextTrack-anropet så att MusicBee inte flyttar NPL-indexet bort från det loopande spåret.
Varför: utan Repeat-One-hoppet skulle anropet Player_PlayNextTrack vid gapless övergång flytta MusicBee till nästa spår i listan (Repeat-One påverkar endast automatisk framåt-beteende vid spårslut i spelarens UI - Nästa spår flyttar alltid framåt), vilket motsäger vad Repeat-One betyder.
Spelräkningsvarning: i Repeat-One beror ökningen av spelräkningen på att MusicBee 3.7.9563+ märker den loopade uppspelningen. Äldre MusicBee-versioner spelar den gapless upprepningen korrekt men missar ökningen av spelräkningen. Dokumenterat; inte blockerande.
F15 - Tillståndsmaskin för spårövergångsdetektering
Vad: när enheten internt övergår från aktuellt spår till NextURI, måste pluginet märka det och säga till MusicBee att flytta fram sitt nuvarande uppspelningsindex. Annars tror MusicBee att det fortfarande är på föregående spår och spelräkningar / UI / scrobbling hamnar i osynk.
Implementering: frågar GetPositionInfo.TrackURI vid varje status-timer-tick. När den rapporterade URI:n matchar den vi köade via NextURI, anropar vi Player_PlayNextTrack på MusicBee och ställer in suppressNextSoapCall så att den resulterande PlayToDevice inte skickar SetAVTransportURI igen (vilket skulle avbryta den gapless uppspelningen).
Varför: utan F15 spelar enheten nästa spår men MusicBees UI säger att det fortfarande är på det föregående. Förvirrande, bryter scrobbling, bryter spårning av spelräkning. Spårövergångsdetektering kräver lång, per-renderare-iteration eftersom varje renderarmärke har sina egna egenheter i när det rapporterar URI-ändringen (vissa rapporterar TRANSITIONING först, vissa hoppar direkt till PLAYING med ny URI, vissa har en kort STOPPED däremellan).
Anmärkningar: vår första version fungerar på BubbleUPnP-renderare. Per-enhet-kantfall finns kvar i B6.
F16 - Fix för pop vid gapless övergång
Vad: poppen uppstår när källformatet (samplingsfrekvens / kanaler / codec) för det köade spåret skiljer sig från det nuvarande spåret, vilket tvingar enhetens DAC att låsa om vid övergången. F16 lägger till en NextUri:FormatChange-diagnostik som utlöses vid kötid när formaten skiljer sig, och namnger båda sidor - så att användare som hör poppar kan korrelera.
Diagnostiken pekar också på åtgärden: markera ForceTranscoding på enhetsprofilen. Det homogeniserar varje spår till en enda transkodningscodec/samplingsfrekvens/bitdjup, vilket eliminerar skillnaden i källformat helt.
Varför uppskjuten för den faktiska transkodnings-till-matchningsfixen: den strukturella fixen (transkoda det köade spåret för att matcha det spelande spårets format) kräver ändringar i pluginets HTTP-server-URL-schema - för närvarande serverar /encode/{id}0.{ext} den köade filen nativt. En framtida v2 av F16 skulle lägga till per-format /encode/{id}0_{rate}_{depth}.{ext}-rutter och koppla dem genom kodaren. Det är en större arkitektonisk förändring som är värd att göra om en verklig enhet uppvisar poppen efter att ForceTranscoding inte räcker.
Implementering idag:
lastSourceUrl-fältet spårar den nuvarande spelande käll-URL:en.QueueNextläserFilePropertyType.SampleRate/Channels/Kindför både det aktuella och köade spåret och loggarNextUri:FormatChangevid mismatch.
F17 - Förloppsindikatorn synkroniseras om efter sökning
Vad: funktionen Seek() anropade redan GetPlayPositionInformation() efter en lyckad Seek SOAP, vilket fixar fallet med "ingen omsynkronisering alls". F17 stänger den återstående upp till 1 sekunds avvikelse orsakad av UPnP:s 1-sekunds RelTime-kvantisering: när enhetens rapporterade position avrundas till inom 1 sekund från användarens begärda mål, litar pluginet nu på användarens sub-sekund-noggranna värde istället för enhetens trunkering. Endast när enheten rapporterar något dramatiskt annorlunda (>1s avvikelse) använder vi dess värde (sökningen landade någon annanstans än begärt, t.ex. snap-to-keyframe på vissa codecs).
Varför: utan detta skulle sökning till 2:30.500 förankrat mot enhetens "2:30"-rapport få förloppsindikatorn att visa ~500ms efter verkligheten. Efter F17 matchar indikatorn användarens avsikt för det vanliga fallet med skrubbning i spåret, och respekterar fortfarande enhetens rapport för snap-to-keyframe-undantaget.
F18 - Kontinuerlig ström / NextURI-förregling
Vad: två förreglingar är nu på plats:
- Körning:
QueueNextreturnerar tidigtFalsenärSettings.ContinuousOutputär på. Kontinuerlig ström är sin egen gapless-mekanism (en lång sammanfogad ström); att skickaSetNextAVTransportURIovanpå den förvirrar enheten om huruvida varje spår är en diskret URI eller en del av det kontinuerliga flödet. - UI: när användaren markerar den globala kryssrutan för kontinuerlig ström, avmarkerar den för närvarande visade profilens
forceNativeStreamautomatiskt. Kontinuerlig ström transkoderar alltid, så force-native är meningslöst i kombination.
Varför: förhindrar användaren från att aktivera två motstridiga gapless-mekanismer samtidigt. Utan F18 skulle enheten ta emot både en kontinuerlig ström-URI OCH en NextURI för varje efterföljande spår, med odefinierat beteende beroende på renderaren.
F19 - Tomma NextURI-fel ignoreras
Vad: när SetNextAVTransportURI anropas med en tom URL (t.ex. sista spåret i listan), returnerar vissa enheter ett SOAP-fel. F19 sväljer dessa tyst - loggas men sprids inte som fel.
Varför: tillståndet "inget nästa spår" är normalt, inte ett fel. Att behandla det som fatalt förorenar loggen och (i vissa flöden) utlöser omförsöksstormar.
Mime-typer och DLNA-metadata
F20 - MP3 mime → audio/mpeg
Vad: den standardkonforma MP3 mime-typen är audio/mpeg, inte audio/mp3. Det senare är en vanlig felaktig benämning som de flesta enheter tolererar, men strängare renderare avvisar den.
Varför: fixar tyst uppspelning på strängare enheter som följer standarden. Yaiol-kodbasen hade detta redan korrekt; ingen ändring behövdes.
F21 - Mime-typordning: icke-x- variant först
Vad: när en enhet annonserar både audio/flac och audio/x-flac, returnerar pluginet den icke-x- varianten först. Samma för alla codecs med både standard- och experimentella mimes.
Varför: x--prefixet markerar experimentella/inofficiella mimes. Vissa renderare fungerar bättre med standardformen. Liten omordning, verklig påverkan.
F22 - Stöd för Opus mime-typ
Vad: känner igen Opus som en strömbar ljudcodec; skickar audio/opus mime när Opus-spår serveras.
Varför: Opus är nu vanligt (modern kompromisscodec för tal/musik). Utan F22 skulle pluginet vägra att strömma Opus-filer även till enheter som hanterar dem.
F23 - Stöd för Monkey Audio (APE) källfil
Vad: känner igen .ape-filer som en giltig källcodec för streaming/transkodning.
Varför: APE är ett förlustfritt format med en nischad men lojal användarbas. Att lägga till det kostar lite och låser upp biblioteket för dessa användare.
F24 - AAC / ALAC mime-återgång
Vad: om en enhet stöder AAC eller ALAC men inte uttryckligen annonserar dem i sin UPnP-tjänstbeskrivning, erbjuder pluginet dem ändå som en återgång.
Varför: flera enheter som hanterar AAC bra glömde att lista det i sin kapacitets-XML. Utan F24 kommer pluginet inte ens att försöka, vilket tvingar transkodning. Med F24 försöker pluginet och låter enheten hantera det nativt om den kan.
F25 - DLNA-typflagga för nativa + kodade WAV-strömmar
Vad: DLNA-typflaggan (en profilidentifierare som LPCM, WAVE, MP3) måste matcha vad enheten tar emot. F25 säkerställer att nativa strömmar och kodade WAV-strömmar flaggas korrekt.
Varför: felaktig DLNA-typ gör att vissa enheter vägrar uppspelning helt eller tillämpar fel avkodare.
F26 - DLNA-huvud för FLAC-filer
Vad: FLAC-strömmar får rätt DLNA-profilidentifierare i sina huvuden.
Varför: utan det känner vissa enheter som stöder FLAC inte igen strömmen som sådan.
F27 - Bitrate-beräkningsfix i metadata
Vad: den kontinuerliga strömmens res@bitrate beräknades som (sampleRate * channels * bitsPerSample) / 1000 - kbps, fel med en faktor på ~125 från UPnP DIDL-specifikationen som definierar attributet som byte per sekund. Delar nu med 8 istället för 1000.
Varför: felaktig bitrate-visning på enheten - kosmetiskt på de flesta renderare, men vissa allokerar strömbuffertar från värdet och stammar på strömmar som ser ~125× mindre ut än de är. Den icke-kontinuerliga källfilsvägen hade detta redan rätt ((bitrate_kbps * 1000) \ 8 = byte/sek); endast den kontinuerliga strömmens väg var fel.
F28 - Metadata tidsformatfix (Marantz)
Vad: res@duration i DIDL formaterades som H:MM:SS (t.ex. 0:03:42). UPnP DIDL-specifikationen definierar formatet som H+:MM:SS[.F+] - strikt med bråkdelar av sekunder valfritt men rekommenderat; vissa Marantz-enheter behandlar den bara formen som ogiltig och lämnar sin varaktighetsvisning tom. Nu formateras som H:MM:SS.fff (t.ex. 0:03:42.000).
Varför: visningsproblem specifikt för ett märke; konformt ISO-stilformat med bråkdelar av sekunder fixar det utan att påverka någon annan enhet. Tillämpas vid båda DIDL-emissionsplatserna (källfilsväg + kodad strömväg i WriteAudioFileDIDL).
Bonusfix i samma pass: pv:addedTime och pv:lastPlayedTime använde hh (12-timmarsklocka) i sina DateTime-formatsträngar istället för HH (24-timmars). Varje spår som lades till eller spelades mellan 13:00 och 23:59 skulle renderas med en felaktig timme (t.ex. 17:42 → "05:42") på enheter som visar fältet. Använder nu HH.
F29 - Kodad MP3-sökning (CBR)
Vad: transkodade MP3-strömmar annonserar nu DLNA.ORG_OP=11 (både byte- och tidsökning) istället för DLNA.ORG_OP=10 (endast byte). Enheter som tidigare vägrade tidsökning på transkodade MP3 kan nu styra sin förloppsindikator/sök-UI normalt.
Varför: MusicBees transkoder producerar konstant bitrate MP3 vid HighQuality-förinställningen, så byte ↔ tidsmappning är linjär - enheten kan konvertera en tidsökningsbegäran till en HTTP Range byte-sökning själv utan något kodarstöd. Att annonsera OP=11 låser upp det UI:et på enheten. Utan F29 ignorerades användare som sökte i en transkodad MP3 tyst eller släpptes till spårstart.
Implementering: omstrukturerade GetEncodeFeature i ItemManager.vb för att bryta den inbyggda If till en läsbar If/ElseIf/Else-kedja. MP3 får OP=11 explicit; andra icke-PCM-codecs behåller OP=10. Ingen ändring för AAC/FLAC/etc. - dessa skulle behöva codecspecifik verifiering av CBR-ness som MusicBee inte garanterar.
F30 - .mpeg-filändelse hanteras
Vad: filer med .mpeg (och den ännu mer sällsynta .mpe) filändelse känns nu igen som FileCodec.Mp3 i GetCodec. Före F30 returnerade de FileCodec.Unknown och avvisades tyst från biblioteket / kunde inte vara transkodningskällor.
Varför: gamla MPEG-1 Layer 3-arkiv använde ibland .mpeg istället för .mp3 (specifikationen tillåter båda). Ett fåtal filer i ett 300k-bibliotek är tillräckligt för att känna "MusicBee visar dem men pluginet gör det inte" - förvirrande för användaren.
Uppspelningsbeteende
F31 - Radioströmmar använder automatiskt kontinuerligt läge
Vad: WriteAudioFileDIDL undersöker nu käll-URL:ens Kind-egenskap via Library_GetFileProperty och behandlar alla filer vars Kind slutar på "Stream" (MusicBee rapporterar "MP3 Stream", "Internet Stream", etc. för radio) som kontinuerliga oavsett den globala Settings.ContinuousOutput-växlingen. Den kontinuerliga strömmens DIDL-gren (Titel: "Continuous Stream", id="continuousstream", fast PCM/Wave-utdata) används; enheten ser en enda oändlig ström.
Varför: radioströmmar har inga spårgränser, ingen fast längd, ingen sökning. Att behandla dem som diskreta filer i DIDL gjorde att pluginet annonserade byteintervall och varaktigheter som inte existerar. Att automatiskt växla när MusicBee redan berättade för oss "detta är en ström" tar bort en fotpistol som användaren inte borde behöva tänka på.
Omfattning: gäller endast när MusicBee driver uppspelning (musicBeePlayToMode). Biblioteks-hämtningsvägen (UPnP-klient som bläddrar) är oförändrad - radio-URL:er där är sällsynta och användarbeteendet bör inte ändras utan explicit testning.
F32 - Codec-annonseringsåtergång
Vad: om en enhet inte annonserar vissa codecs (eller om pluginet inte kan tolka enhetens kapacitets-XML), avvisar pluginet inte omedelbart strömmen. Istället försöker den servera den och låter enheten bestämma.
Varför: många enheter har ofullständig eller oläsbar kapacitets-XML men hanterar faktiskt codecen bra. F32 byter en liten "bästa gissning och försök" mot ett direkt avslag.
F33 - Förbättring av förloppsindikatorns synkronisering
Vad: positionen mellan omröstningarna extrapoleras redan med väggklocka från en enda ankare (currentPlayStartTicks), så förloppsindikatorn uppdateras smidigt med sub-sekunds hastighet. Den återstående källan till jitter var den initiala ankaren för ett nystartat spår: den tidigare koden antog position=0 i det ögonblick status-timern först märkte att tillståndet blev Spelar, men då kan enheten ha spelat i 100-500ms (ett omröstningsintervall). MusicBees förloppsindikator skulle starta på 0, sedan hoppa framåt när verkligheten kom ikapp.
F33-fix: vid övergång till Spelar för första gången på ett nytt spår (currentPlayStartTimeEstimated=True), anropa GetPlayPositionInformation() för att få enhetens faktiska aktuella position, och förankra sedan mot den. UPnP rapporterar endast 1-sekunds upplösning så ankaren är fortfarande kvantiserad, men den är mycket närmare sanningen än att anta 0.
Varför: smidigare + mer exakt förloppsvisning, särskilt direkt efter spårbyte. Ingen väg runt den 1-sekunds UPnP-rapporteringsupplösningen i sig - det är spec.
F34 - Förloppsindikatorns jitter efter spårbyte
Vad: när PlayToDevice anropas för ett nytt spår, brukade pluginet lämna currentPlayPositionMs och currentPlayStartTicks vid deras tidigare spårvärden under det ~100ms fönstret mellan SOAP-Play och den första status-timer-omröstningen som upptäckte det nya Playing-tillståndet. MusicBees förloppsindikator skulle kort visa slutet av det föregående spåret, sedan hoppa tillbaka till 0, sedan klättra. F34 nollställer båda vid PlayToDevice-inträdet - i det ögonblick vi vet att ett spårbyte sker, innan något av SOAP-arbetet.
Varför: visuell glitch vid snabba hopp-användningsfall (manuell nästa eller gapless övergång). Nu returnerar MusicBees första PlayPositionMs-fråga efter Play rent 0, sedan förfinar F33:s GetPlayPositionInformation det till enhetens faktiska position vid den första tillståndsändrings-ticken.
Implementering: fyra rader högst upp i PlayToDevice, parat med F33:s övergångstids-noggranna förankring.
F35 - Bugg med "Tvinga transkodning"
Vad: tvingad transkodning kunde fortfarande hoppa över transkodning i vissa kombinationer. Efter F04:s per-profil-omarbete förseglades två specifika luckor:
- Prioritet med ForceNativeStream. När båda var True (vilket kan hända vid en schemamigrering eller en partiell inställningsfil), vinner ForceTranscoding nu direkt (
If streamingProfile.ForceTranscoding Then forceEncode = True ElseIf streamingProfile.ForceNativeStream Then forceEncode = False). UI:s ömsesidiga uteslutning förhindrar användaren från att markera båda, men körningsskyddet hanterar alla tillstånd som laddades inkonsekvent från disk. bypassTranscodeDecision-logik. Tidigare:streamingProfile.ForceNativeStream AndAlso Not Settings.ForceTranscoding. Nu:streamingProfile.ForceNativeStream AndAlso Not streamingProfile.ForceTranscoding- samma prioriteringsregel men på samma per-profil-omfång.
Varför: "tvinga" ska betyda tvinga. Om användaren uttryckligen aktiverade ForceTranscoding för en enhet, får pluginet aldrig tyst falla igenom till nativ streaming, oavsett hur andra flaggor råkar kombineras.
F36 - Renderar-stängt undantag
Vad: omslöt Plugin.ReceiveNotification i en toppnivå Try/Catch som loggar alla okontrollerade undantag istället för att låta det sprida sig tillbaka till MusicBees meddelandepump.
Varför: meddelanden från MusicBee (PlayStateChanged, VolumeMuteChanged, etc.) skickas till ControlPointManager som kommunicerar med renderaren via SOAP. Individuella anropsplatser hade redan Try/Catch runt sina SOAP-anrop, men ett tillräckligt konstigt tidsscenario (t.ex. renderaren dör mellan två SOAP-anrop i samma meddelandehanterare) kunde fortfarande undkomma. Toppnivåomslaget är det sista skyddsnätet så att användaren aldrig ser en generisk "TargetInvocationException"-popup från MusicBee.
Implementering: döpte om den befintliga kroppen till ReceiveNotificationInternal och lade till en tunn omslag ReceiveNotification som gör Try { ReceiveNotificationInternal(...) } Catch { LogError(...) }. Den befintliga per-metod Try/Catch-infrastrukturen inuti ControlPointManager (runt varje PostSoapRequest-anrop) finns kvar - F36 är både hängslen och livrem.
F37 - Långspårsökning utlöser falsk övergång
Vad: att söka i ett långt spår kan producera en kort Stopped→Playing-cykel på vissa renderare. Utan diskriminering behandlar ProcessNewPlayState.Stopped det som naturligt spårslut och anropar Player_PlayNextTrack, vilket flyttar MusicBee framåt när användaren bara ville skrubba. F37 stämplar lastUserInitiatedSeek i Seek() och lägger till ett 5-sekunders skydd i Stopped-hanteraren (speglar det befintliga lastUserInitiatedStop-fönstret).
Varför: tyst hopp-till-nästa-spår under en sökning är en av de buggar ingen kan gissa orsaken till - användaren tänker "konstigt, jag försökte skrubba framåt och nu spelar den nästa låt". Fixen är mekanisk: samma mönster som den användarstoppsdiskriminering som redan finns.
F38 - Förbättrad sökhantering för kraschbenägna codecs
Vad: BubbleUPnP som kraschade vid MP3-sökning var det kanoniska symptomet. Efter granskning gör den nuvarande yaiol-sökkoden redan rätt saker - nativ sökväg hanterar HTTP Range korrekt (206, Content-Range, AcceptRanges), kodad sökväg annonserar X-AvailableSeekRange och tolkar inkommande timeSeekRange.dlna.org / npt-huvuden, DLNA.ORG_OP-flaggor återspeglar de faktiska strömkvaliteterna (med DisablePcmTimeSeek opt-out för problematiska Platinum-enheter). Användartestad på nuvarande BubbleUPnP 4.6.4: inga krascher observerade.
Varför: BubbleUPnP MP3-sök-kraschen rapporterades runt 2024 och appen har haft ~16 månaders fixar sedan dess. F29 (kodad MP3 OP=11) var den nya variabeln som kunde ha återexponerat den; gör det inte, på testade versioner.
Om en krasch återkommer: fix-formen skulle vara en per-profil "begränsad sökning"-växling som tvingar DLNA.ORG_OP=10 (endast byte) på flaggade codecs - speglar hur DisablePcmTimeSeek redan fungerar för PCM. Lägg till då, inte i förväg.
UI och loggning
F39 - "Lägg till"-knappen väljer den nya profilen
Vad: att klicka på "Lägg till" i listan över enhetsprofiler skapar en ny profil OCH väljer den automatiskt så att användaren omedelbart kan redigera fält. Vår omstrukturering av den sektionerade dialogrutan gör redan detta - både den direkta Lägg till-vägen och vägen från mall slutar med Me.activeStreamingProfiles.SelectedIndex = Me.activeStreamingProfiles.Items.Count - 1. En kontroll bekräftade att vår fork redan hanterar detta - inget att ändra.
Varför: liten UX-irritation som visade sig redan vara fixad här.
F40 - Större max-anslutningar + varningslogg
Vad: pluginets samtidiga strömgräns (SemaphoreSlim runt Sockets_Stream_File / Sockets_Encoder_Start) var hårdkodad till 4. F40 gör den användarkonfigurerbar på sidan Allmänna inställningar (standard 16, intervall 1-256), lägger till en MaxConnections-loggrad när en begäran måste vänta på en plats, OCH visar en röd ⚠ Max Conn-ikon längst ner till vänster i inställningsdialogen om gränsen nåddes minst en gång sedan MusicBee startade.
Varför: när en enhet skickar parallella förfrågningar (vissa Marantz/Linn under konstverksskanningar, BubbleUPnP:s metadata-sonderingar tillsammans med aktiv uppspelning), blockerades ytterligare förfrågningar tyst bakom semaforen - användaren såg "enheten långsam" utan synlig orsak. Loggraden är bra för teknisk felsökning men icke-tekniska användare läser aldrig loggar. Den synliga ikonen i inställningsdialogen gör gränsen-nådd-tillståndet upptäckbart för alla som öppnar pluginets inställningar.
Implementering:
- Centraliserade väntan i
WaitOnSendBarrier(logTag)iMusicBeeUpnp.vb; båda anropsplatserna (MediaServerDevice.GetFile,Encoder.StartEncode) använder den. Settings.MaxConnectionssparades i v8 av inställningsschemat.Plugin.MaxConnectionsHitär en klibbig sessionsflagga som ställs in iWaitOnSendBarrier; återställs endast vid MusicBee-omstart.SettingsDialog.maxConnectionsBadgeär en röd fet etikett vid(16, 410)som endast visas närPlugin.MaxConnectionsHitär True. Har ett verktygstips som förklarar orsaken och åtgärden.- Semaforen initieras en gång vid typ-laddning, så att ändra inställningen kräver en MusicBee-omstart (noteras i fältetiketten).
F41 - Logga "kodning på grund av ReplayGain/DSP"
Vad: istället för separata "kodning för RG" / "kodning för DSP"-loggrader, inkluderar den enda StreamDecision-raden från F42 MB-DSP/EQ, MB-ReplayGain, Profile-DSP/EQ, Profile-ReplayGain som ackumulerade orsaker. Samma diagnostiska värde, mindre brus.
Varför: användare ser alla anledningar till att transkodning sker för ett givet spår på en loggrad, inte utspridda. Se F42 för fullständiga detaljer.
F42 - Logga "renderaren stöder inte källcodecen"
Vad: lade till en StreamDecision-loggrad per spela-till-enhet-spår som säger antingen "nativ CODEC" eller "transkoda CODEC→CODEC orsak=…". Orsakfältet ackumulerar varje villkor som utlöste transkodning: MB-DSP/EQ, MB-ReplayGain, Profile-DSP/EQ, Profile-ReplayGain, WebFile, VirtualFile, ForceTranscoding(global), SampleRate<min/SampleRate>max, DownmixToStereo, DeviceLacksCodec(X), BandwidthConstrained.
Varför: användare var förvirrade av oväntade CPU-toppar på filer de förväntade sig att strömma nativt. En loggrad per spår berättar exakt vilket villkor som orsakade transkodning - och om fältet visar DeviceLacksCodec(Flac) vet de omedelbart att enhetens protokollinformation var ofullständig och kanske vill att F32:s återgång ska träda i kraft.
Implementering: en enda ackumulatorsträng byggd inkrementellt genom beslutsflödet; loggas en gång i slutet. Begränsad av Settings.LogDebugInfo för att undvika loggbrus i produktion.
F43 - SetNextAVTransport-loggen visar käll-URL
Vad: QueueNext-loggposter inkluderar nu source=<MusicBee library path> tillsammans med stream=<HTTP streaming URL>. Samma ändring tillämpas på framgångsvägen och felvägen (QueueNext:Failed).
Varför: vid felsökning av ett köat spårproblem är streaming-URL:en (/encode/aabbccdd0.flac) ogenomskinlig i sig - samma för varje spår. Käll-URL:en är den mänskligt sökbara bibliotekssökvägen som berättar exakt vilken fil MusicBee försökte köa.
F44 - Bättre felmeddelanden för mime-typer
Vad: två nya loggposter under Activate:
Activate:MimeUnverified- utlöses per felaktig post i enhetensGetProtocolInfo-svar, namnger vilken post som inte kunde parsas (så att användaren kan se t.ex. "Marantz returneradehttp-get:*::*för någon codec - kapaciteten är overifierad, F32:s återgång kommer att gissa").Activate:NoSinkInfo- utlöses en gång om enheten inte returnerade något<Sink>-element alls. Betyder attSupportedMimeTypesförblir Nothing ochIsCodecSupporteddegraderas till "anta att allt fungerar" - användbar kontext när senare "enheten vägrade ström" -fel uppstår.
Varför: före F44 lämnade dessa tysta kapacitets-fallbacks användare att gissa varför deras spår antingen transkodades mot förväntningarna eller vägrades av enheten. Nu visar en enda sökning efter Activate: om enhetens kapacitetsinformation var användbar.
F45 - Bättre felmeddelanden för metadata
Vad: Browse-undantagsloggen i ContentDirectoryService.vb berikades redan i tidigare yaiol-arbete (Alia Vox-buggsessionen) med ObjectID och stackspårning. F45 utökar den ytterligare med BrowseFlag (metadata vs barn), Filter (vilka attribut klienten begärde), sortCriteria och partialResultLength (hur många byte DIDL som producerades före felet - pekar på hur långt in i batchen det dåliga spåret sitter).
Varför: när något går fel mitt i DIDL, berättar partial-length-värdet om felet var på det första spåret i batchen (partial=0) eller en bit in (partial=N) - kombinerat med batchens startingIndex kan du identifiera det felaktiga spårindexet. Filter och BrowseFlag förklarar vilken typ av bläddring klienten ville ha; ibland misslyckas en metadata-endast-bläddring där en barn-bläddring för samma ID lyckas.
Nätverk
F46 - Automatiskt läge annonserar endast på verkliga nätverksadaptrar
Vad: i Automatiskt gränssnittsläge brukade pluginet annonsera sig själv (SSDP) på varje operativ IPv4-adapter. På en maskin som också kör en VPN-tunnel (NordLynx) eller en virtuell switch (Hyper-V / WSL / Docker), annonserades samma bibliotek på var och en av dessa adaptrar också, så kontrollpunkten du castade från upptäckte servern två eller tre gånger och listade biblioteket som dubbla kopior. Automatiskt läge behåller nu endast adaptrar som har en verklig IPv4-standardgateway (HasIPv4Gateway) - vilket tunnel- och virtuella switchadaptrar inte har - så dessa tas bort från annonseringslistan. En användarfäst adress vinner fortfarande direkt (annonsera endast på det gränssnittet), och om ingen adapter rapporterar en gateway återgår väljaren till varje adapter, så den annonserade adresslistan är aldrig tom och pluginet kan inte bli osynligt.
Varför: dubbletten orsakas inte av "att vara på en VPN" - den orsakas av att annonsera på LAN-adaptern och tunnel-/virtuella adaptern samtidigt, så en kontrollpunkt ser samma server på två adresser. En konsument-VPN (NordVPN/NordLynx) tunnlar endast internetbunden trafik; DLNA-renderaren finns på LAN och lokal-subnätstrafik kringgår tunneln, så tunneladaptern når aldrig en renderare ändå - att ta bort den tar bort en fantomkopia, aldrig en fungerande sökväg. Gateway-testet är den billiga, pålitliga signalen som skiljer en verklig LAN/Wi-Fi-adapter från en tunnel eller virtuell switch. Kompletterar N05 (som fixade hur annonseringar skickas på sådana länkar - multicast istället för broadcast); F46 styr vilka adaptrar som annonseras på överhuvudtaget.
Känd begränsning: en mesh / fjärråtkomst-VPN (Tailscale, ZeroTier, WireGuard-to-home) vars renderare verkligen finns över tunneln presenterar vanligtvis en adapter utan standardgateway, så automatiskt läge tar bort den också. Dessa användare fäster istället VPN-adressen, vilket har företräde framför gateway-filtret.