Novità
2.0.9 - 2026-08-23
L'avviso di aggiornamento apre le sue pagine nella tua lingua
Cosa: i collegamenti Novità e Scarica nell'avviso di aggiornamento ora aprono le pagine del plugin nella lingua di MusicBee, anziché in inglese.
Perché: quei due collegamenti restringevano la lingua a una delle quattro — inglese, francese, spagnolo o tedesco — prima di inviarla al sito web, quindi a tutti gli altri veniva consegnata la pagina inglese anche dove ne esisteva una traduzione. Ora passano la lingua di MusicBee inalterata e lasciano che il sito web decida cosa servire, cosa che il pulsante Aiuto accanto a loro ha sempre fatto.
2.0.8 - 2026-08-22
Il plugin ora si presenta correttamente alle app e ai dispositivi che lo scoprono sulla tua rete, e le impostazioni dei Profili dispositivo sono di nuovo allineate.
I tuoi dispositivi mostrano il produttore, il modello e la versione corretti
Cosa: quando un'app di controllo, un telefono o una TV trova MusicBee sulla tua rete, ora presenta questo plugin come prodotto da yaiol, punta al sito del plugin stesso, lo descrive come coprente tutti e tre i ruoli — server, player e renderer — e riporta la versione che hai effettivamente installato.
Perché: ogni dispositivo UPnP annuncia chi l'ha prodotto e cosa sia, e le app di controllo lo mostrano come identità del dispositivo. Questo plugin annunciava ancora i dettagli del plugin originale da cui era stato derivato: il nome di un altro autore, il sito web di MusicBee invece del proprio, e un numero di modello bloccato a "1.0" fin dalla primissima release. Dal tuo telefono non c'era modo di capire con quale plugin stessi parlando, figuriamoci quale versione. Questi dettagli ora provengono dal plugin stesso, quindi la versione mostrata accanto al dispositivo rimane corretta ad ogni aggiornamento.
Le impostazioni dei Profili dispositivo sono di nuovo allineate
Cosa: nella scheda Profili dispositivo le etichette e le relative caselle condividono un bordo sinistro e sono posizionate a distanza uniforme, e l'intervallo di frequenza di campionamento si legge come una singola riga.
Perché: i campi si erano allontanati man mano che le opzioni venivano aggiunte alla scheda nel tempo, e l'etichetta "a" dell'intervallo di frequenza di campionamento era finita sopra la casella accanto ad essa — leggibile una volta che si sapeva cosa diceva, ma sconcertante la prima volta che la si guardava.
2.0.7 - 2026-08-08
Le tracce più grandi mantengono il loro titolo e il loro cursore di posizione
Cosa: una traccia più grande inviata da un telefono — un file FLAC lungo, ad alta risoluzione o DSD — ora mostra il suo titolo corretto e può essere spostata, come una piccola. In precedenza alcune di esse venivano riprodotte invece tramite la rete, con un indirizzo web al posto del titolo e un cursore che non faceva nulla.
Perché: il plugin attende la sua copia locale prima di iniziare, ma in precedenza decideva in anticipo se valeva la pena attendere un file, in base alle sue dimensioni. Questa era in realtà una stima della velocità della tua rete, che non ha modo di conoscere: una traccia di 65 MB veniva giudicata troppo grande, poi terminava il download un secondo dopo — comodamente all'interno dell'attesa a cui aveva già rinunciato. Ora si limita a monitorare il download. Mentre sta ancora arrivando, il plugin continua ad attendere, per tutto il tempo necessario; rinuncia solo quando il trasferimento si blocca realmente, cosa che ora nota più velocemente di quanto facesse il vecchio ritardo fisso.
2.0.6 - 2026-08-03
Gli album inviati da un telefono ora vengono riprodotti senza interruzioni tra i brani, e la radio via internet viene riconosciuta come radio invece di essere trattata come una canzone insolitamente lunga.
Gli album vengono riprodotti senza interruzioni
Cosa: quando invii un intero album dal tuo telefono, MusicBee ora passa da un brano all'altro senza pause — così le registrazioni dal vivo, i DJ set e le opere classiche continue rimangono unite.
Perché: lo standard consente a un'app di controllo di dire "ecco cosa viene dopo", il che rende possibile un'unione senza soluzione di continuità. Quell'istruzione non era affatto accettata, quindi l'app non aveva dove mettere il brano successivo e lo annunciava come se fosse quello corrente — la causa del problema dell'album risolto nella versione precedente. Ora è accettata correttamente: il brano successivo viene recuperato mentre quello corrente è ancora in riproduzione, e il lettore di MusicBee attraversa il confine.
La radio via internet è riconosciuta come radio
Cosa: una stazione live inviata a MusicBee viene riprodotta come stream e mai scaricata.
Perché: scaricare una trasmissione non ha senso — non ha fine, e non c'è nulla da saltare — ma il plugin in precedenza non aveva modo di distinguerla da un file musicale, quindi iniziava a scaricare e si fermava una volta che il download superava una dimensione fissa. L'app di controllo indica quale dei due sta inviando, e questo ora viene letto direttamente. Nessuna impostazione, nessuna supposizione.
I brani lunghi ad alta risoluzione mantengono la loro copia
Cosa: i file DSD e le registrazioni lunghe a 24 bit possono ora essere spostati come qualsiasi altro brano.
Perché: erano bloccati dal limite di dimensione di cui sopra — un movimento ad alta risoluzione di 20 minuti o un brano DSD di 10 minuti lo superavano entrambi — quindi la loro copia veniva abbandonata e il cursore di posizione smetteva di funzionare proprio per il materiale che più probabilmente valeva la pena di scorrere. Con la radio identificata correttamente, non è necessario alcun limite di dimensione.
2.0.5 - 2026-08-03
Due correzioni per il controllo di MusicBee da un telefono: il volume ora significa la stessa cosa a entrambe le estremità, e il casting di un intero album continua a funzionare oltre la prima traccia.
Il volume sul telefono corrisponde al volume in MusicBee
Cosa: portare il volume al massimo sul telefono ora raggiunge il massimo in MusicBee, e l'impostazione di MusicBee viene letta correttamente sul telefono.
Perché: il plugin non ha mai comunicato all'app di controllo qual era il suo volume massimo, quindi ogni app doveva indovinare. Una di esse si è stabilita su 69, il che significava che il suo 100% raggiungeva solo il 69% in MusicBee, mentre il 100% di MusicBee tornava come 144% sul telefono — e i pulsanti del volume del telefono non potevano mai raggiungere il massimo. Il renderer ora dichiara chiaramente l'intervallo, quindi entrambe le estremità parlano della stessa scala.
Il casting di un album mantiene i suoi titoli e il suo cursore di posizione
Cosa: ogni traccia di un album inviato da un telefono ora mostra il suo titolo corretto e può essere spostata, non solo la prima.
Perché: un'app di controllo annuncia la traccia successiva una frazione di secondo dopo quella corrente, e quell'annuncio annullava la copia in fase di recupero per la traccia in procinto di essere riprodotta — così la maggior parte delle tracce tornava silenziosamente a essere riprodotta in rete, il che perde sia il titolo che la capacità di saltare. Le copie per diverse tracce sono ora mantenute una accanto all'altra, quindi un annuncio non può più annullare quella in uso.
2.0.4 - 2026-08-02
La musica inviata a MusicBee da un telefono o da un altro server ora si comporta come una vera traccia: puoi spostarti al suo interno e mostra subito il suo titolo corretto. Inoltre, una correzione per gli streamer hi-fi che si presentano come un unico dispositivo combinato.
Spostarsi all'interno di una traccia inviata da altrove
Cosa: trascinare il cursore di posizione ora funziona per una traccia inviata dal telefono, da un NAS o da un altro server multimediale. Per rendere ciò possibile, MusicBee scarica una copia della traccia in una cartella temporanea mentre inizia la riproduzione e riproduce quella copia. Ci vuole circa un secondo su una rete domestica, la copia viene eliminata non appena si invia un'altra traccia e tutti i residui vengono cancellati al successivo avvio di MusicBee.
Perché: MusicBee può avviare e interrompere qualcosa che sta ascoltando sulla rete, ma non può spostarsi al suo interno, quindi il cursore sembrava saltare e poi scivolava subito indietro al punto in cui si trovava, senza alcuna spiegazione. La riproduzione di un file ordinario sul proprio disco rimuove completamente la limitazione anziché aggirarla.
Titolo e lunghezza corretti dalla prima nota
Cosa: una traccia inviata da un'app che non assegna ai suoi file un'estensione di file ordinaria ora mostra il suo titolo e la sua lunghezza reali non appena inizia, invece di apparire come un lungo indirizzo web.
Perché: MusicBee identifica una traccia — e trova i suoi tag — dall'estensione del file, e alcuni lettori distribuiscono indirizzi senza alcuna estensione. La copia locale porta sempre quella giusta, quindi la traccia viene riconosciuta qualunque sia il nome che le dà l'app mittente.
Un salto che non può essere fatto ora lo dice
Cosa: se il renderer non può effettivamente spostarsi al punto richiesto, l'app di controllo viene informata e lo segnala.
Perché: in precedenza rispondeva "fatto" a prescindere, quindi il cursore scivolava indietro un secondo dopo senza nulla che spiegasse il motivo. Un rifiuto onesto è più facile da gestire di uno silenzioso.
Gli streamer hi-fi combinati vengono letti correttamente
Cosa: quando MusicBee riproduce su un dispositivo che si presenta come un'unica unità combinata — uno streamer Marantz o Denon, dove il lettore si trova all'interno di un wrapper del produttore insieme a un server multimediale — il plugin ora legge i dettagli del lettore stesso invece di quelli del server multimediale.
Perché: in precedenza chiedeva alla metà sbagliata del dispositivo quali formati audio potesse gestire, non ottenendo alcuna risposta utilizzabile, e proseguiva senza mai controllare — esattamente l'hardware in cui la gestione dei formati deve essere più corretta. La descrizione del modello di un dispositivo viene ora presa in considerazione anche quando lo si abbina a un profilo di dispositivo; in precedenza veniva letta dal posto sbagliato e scartata.
2.0.3 - 2026-08-02
Il ruolo di riproduzione si evolve: ora è possibile inviare a MusicBee musica che non è già presente nella sua libreria — un file sul telefono, su un NAS, su un altro server — invece di riprodurre solo i brani che già possiede.
Riproduci musica inviata dal tuo telefono, non solo dalla tua libreria
Cosa: quando usi un'app di controllo come Symfonium o BubbleUPnP per inviare musica a MusicBee, il brano non deve più provenire dalla libreria di MusicBee. Un file memorizzato sul telefono stesso, su un NAS o su un altro server multimediale ora viene riprodotto. Il titolo e la lunghezza provengono dall'app che lo ha inviato, quindi il brano viene visualizzato correttamente anche se MusicBee non ha mai visto il file.
Perché: il ruolo di riproduzione è stato creato per il caso in cui si sfoglia la libreria di questo PC dal telefono e si tocca una canzone — il brano era già sul PC, quindi MusicBee ha semplicemente riprodotto il proprio file. Qualsiasi cosa arrivasse da altrove veniva silenziosamente scartata, il che rendeva la funzione inutile per il caso altrettanto naturale di spingere musica dal telefono verso i buoni altoparlanti.
Un brano che non può essere riprodotto lo indica
Cosa: se il renderer non riesce a riprodurre ciò che gli è stato inviato, ora lo segnala all'app che lo ha inviato.
Perché: in precedenza rispondeva "ricevuto" a qualsiasi cosa, quindi l'app di controllo procedeva e premeva play. Senza nulla di effettivamente caricato, MusicBee riavviava qualsiasi brano fosse rimasto da prima — e se quel file era sparito, si lamentava che la sua fonte non poteva essere trovata. L'errore nominava un brano non correlato e non indicava affatto il problema reale.
L'invio di un nuovo brano mentre è in pausa ora riproduce quel brano
Cosa: se MusicBee è in pausa e la tua app di controllo gli invia qualcosa di nuovo, il nuovo brano inizia.
Perché: la ripresa aveva la priorità sul caricamento, quindi il brano in pausa riprendeva da dove era stato interrotto e il brano che avevi appena scelto veniva scartato senza una parola.
2.0.2 - 2026-08-01
La ricerca è il tema: ora funziona per artista, restituisce i risultati giusti ed è veloce su una libreria di grandi dimensioni. Inoltre, un nuovo modo per indirizzare la riproduzione casuale della tua app di controllo e una correzione per tre pulsanti che non portavano da nessuna parte.
La ricerca per artista funziona davvero
Cosa: la ricerca di un artista dalla tua app di controllo ora restituisce la musica di quell'artista. La ricerca corrisponde sia all'artista del brano che all'artista dell'album, quindi una compilation viene trovata sia che tu digiti il nome dell'esecutore o il nome con cui l'album è archiviato.
Perché: una ricerca per artista era precedentemente scambiata per "dammi tutto di questo tipo" — l'artista che avevi digitato veniva ignorato e veniva restituita l'intera libreria, quindi una ricerca che avrebbe dovuto corrispondere a poche centinaia di brani ne restituiva decine di migliaia. Corrispondere solo a uno dei due campi artista avrebbe silenziosamente perso metà dei risultati, quindi entrambi vengono controllati.
La pagina dei risultati di ricerca è corretta
Cosa: lo scorrimento di un lungo elenco di risultati di ricerca ora si muove attraverso di esso. Ogni pagina su cui scorri è la pagina che ottieni.
Perché: il server rispondeva a ogni richiesta con la prima manciata di risultati, pur riportando il conteggio completo delle corrispondenze, quindi un'app che scorreva per ottenere di più continuava a ricevere gli stessi elementi e non raggiungeva mai la fine.
La ricerca è molto più veloce su una grande libreria
Cosa: una ricerca ora esegue una singola query per l'intero set di risultati e legge solo i tag della pagina che stai visualizzando.
Perché: ogni pagina in precedenza rieseguiva la query sulla libreria e poi caricava i tag di ogni corrispondenza — migliaia di essi — per mostrarne una dozzina. Su una grande collezione questo faceva sì che ogni scorrimento si mettesse in pausa. I risultati vengono anche eliminati ogni volta che la libreria viene aggiornata, quindi una modifica non viene mai servita in modo obsoleto.
Indirizza la tua riproduzione casuale a un filtro
Cosa: una nuova impostazione Riproduzioni casuali da nella scheda Opzioni libreria. Lasciala su Tutta la musica e le cartelle Brani casuali / Album casuali della tua app di controllo si comporteranno come prima; scegli uno dei tuoi filtri MusicBee e ogni richiesta casuale attingerà invece da quel filtro. Vengono offerti anche i filtri nascosti.
Perché: una cartella casuale chiede una fetta di "tutto", ed è l'unica richiesta che non porta alcun indizio su ciò che intendevi — quindi attingeva sempre dall'intera libreria, parlato e tutto il resto. Questo è il punto in cui dici cosa significa "tutto". L'apertura di una cartella casuale all'interno di un filtro sul dispositivo mescola comunque quel filtro: una scelta che fai durante la navigazione prevale sull'impostazione.
Aiuto, GitHub e controllo aggiornamenti raggiungono pagine reali
Cosa: i pulsanti Aiuto e GitHub nella finestra di dialogo delle impostazioni, e il controllo automatico di una nuova versione, ora aprono le pagine che nominano.
Perché: tutti e tre erano stati costruiti da una forma abbreviata del nome del plugin per la quale non è mai esistita alcuna pagina, quindi ognuno falliva silenziosamente — i pulsanti sembravano non fare nulla e il controllo degli aggiornamenti non riportava mai nulla, per quanto tempo fosse uscita una nuova versione.
2.0.1 - 2026-07-26
Una serie di correzioni per la riproduzione e la navigazione, incentrate sui podcast e sulle app controller (come BubbleUPnP) che gestiscono la riproduzione e la riproduzione casuale.
I podcast iniziano a essere riprodotti senza pausa
Cosa: un episodio di podcast scaricato ora dichiara in anticipo la sua lunghezza e dimensione del file reali, trasferite direttamente dal file su disco ai metadati multimediali.
Perché: senza una durata dichiarata, un controller come BubbleUPnP riesamina l'intero flusso audio ogni volta che si preme play, solo per capire quanto dura l'episodio — quindi la riproduzione iniziava solo dopo una pausa evidente. Con la lunghezza ora pubblicizzata, inizia in modo pulito.
I podcast appaiono quando si naviga per artista
Cosa: il nome di un podcast è ora archiviato come suo artista — rispecchiato nei campi Artista e Artista Album (e nelle loro varianti di ordinamento), esattamente come già riempie il campo Album.
Perché: un percorso di navigazione che raggruppa per un campo artista trovava l'artista di ogni podcast vuoto e si bloccava su un livello vuoto. Trattare lo show stesso come l'artista — coerentemente con il trattarlo come l'album — significa che quei percorsi ora raggiungono gli episodi invece di nulla.
"Riprodotti di recente" e il casting funzionano correttamente dopo un riavvio
Cosa: la riproduzione di un podcast, audiolibro, inbox o traccia radiofonica direttamente — senza prima navigare ad essa, come fanno l'elenco "Riprodotti di recente" di BubbleUPnP e i target di casting — non fallisce più. Il plugin ora carica la traccia su richiesta quando viene richiesta per ID.
Perché: quegli elenchi richiedono una traccia subito dopo il riavvio di MusicBee, prima che sia stato navigato qualcosa, quindi il plugin non aveva mai visto l'ID e rispondeva "ID non valido". Ora carica forzatamente le fonti pertinenti su quella prima richiesta diretta e trova la traccia.
"Tracce casuali" e "Album casuali" restituiscono risultati
Cosa: le cartelle di riproduzione casuale "Tracce casuali" e "Album casuali" di BubbleUPnP, che richiedono una fetta casuale dell'intera libreria, ora tornano piene.
Perché: queste sono ricerche senza titolo da abbinare, e il plugin in precedenza rispondeva loro da un elenco interno vuoto, quindi mostravano sempre nulla. Ora vengono servite — correttamente impaginate — dallo stesso percorso di query su richiesta che utilizza il resto della navigazione.
Gli album con un tag vuoto elencano le loro tracce
Cosa: l'apertura di un album raggruppato su un valore vuoto — ad esempio tracce inbox che non riportano l'anno — ora mostra le sue tracce.
Perché: la corrispondenza che raccoglie le tracce di un album trattava "questo tag è vuoto" come "nessuna corrispondenza", quindi qualsiasi album formato da un campo vuoto non mostrava nulla. Un valore di gruppo vuoto ora corrisponde correttamente alle tracce che lo condividono.
2.0.0 - 2026-07-22
Questa è la prima versione pubblica del fork open-source yaiol del plugin UPnP di MusicBee. È presentata in due parti: tutto ciò che è nuovo in questo fork, quindi le correzioni e i miglioramenti apportati al plugin originale. Ogni elemento mantiene la forma Cosa / Perché del catalogo di funzionalità interne del progetto, in modo che il ragionamento dietro ogni modifica sia sulla pagina, non solo la modifica.
Novità di questo fork
MediaRenderer - riproduci su MusicBee
N01 - MusicBee come renderer di riproduzione
Cosa: normalmente questo plugin funziona in un solo modo: un telefono o un altro dispositivo sfoglia la libreria di MusicBee e riproduce la musica su se stesso. Questa funzione aggiunge la direzione opposta: consente a MusicBee di essere il lettore. Da un'app controller sul telefono (come BubbleUPnP) è possibile scegliere MusicBee sul desktop come dispositivo di riproduzione, quindi controllarlo dal telefono: riproduci, metti in pausa, ferma, salta avanti o indietro, salta a un punto della traccia e cambia il volume o disattiva l'audio.
Perché: trasforma il telefono in un telecomando per la musica già presente sul PC. Siediti sul divano, sfoglia la libreria sul telefono, tocca una traccia e questa uscirà dagli altoparlanti collegati al desktop, con il controllo completo da dove sei seduto. Il plugin originale non ha mai fornito questa funzione come funzionante.
Attivazione: è disattivata per impostazione predefinita, perché l'attivazione consente a qualsiasi dispositivo sulla rete domestica di avviare la riproduzione sul PC. La si abilita con una casella di spunta nella scheda Generale della finestra di dialogo delle impostazioni. I tre ruoli del plugin hanno ciascuno la propria casella di spunta: condividi la mia libreria (Server), consenti ad altri di riprodurre su di me (Renderer) e riproduci su altri dispositivi (Punto di controllo), e la finestra di dialogo mostra solo le schede delle impostazioni che i ruoli attivati richiedono effettivamente, in modo da non trovarsi mai di fronte a opzioni che non si applicano.
Distinguere i propri dispositivi: è possibile assegnare al renderer qualsiasi nome si desideri (inizialmente è "MusicBee (yaiol)"). Questo nome è quello che appare nell'elenco dei dispositivi di riproduzione del telefono, quindi quando più di un PC esegue MusicBee è possibile distinguere quale sia quale. Una modifica del nome ha effetto immediatamente, senza riavvio.
Miglior suono possibile quando riproduce su se stesso: quando si sfoglia la propria libreria di MusicBee dal telefono e si invia una traccia a quello stesso MusicBee, il plugin riconosce che gli viene chiesto di riprodurre uno dei suoi file e lo riproduce semplicemente direttamente dal disco. Il risultato è esatto e istantaneo - bit-perfect, con l'equalizzatore e la normalizzazione del volume di MusicBee applicati - invece di spingere inutilmente l'audio sulla rete e subito di nuovo a se stesso.
Esecuzione privata: i tre ruoli funzionano indipendentemente, quindi è possibile attivare il renderer lasciando disattivata la condivisione della libreria. In questa configurazione "solo renderer", la libreria rimane completamente nascosta dalla rete - viene annunciato solo il dispositivo di riproduzione - e MusicBee non si offrirà mai di riprodurre su se stesso.
Comportamento di riproduzione
N02 - FLAC 5.1 non downmixato automaticamente
Cosa: il limite del numero di canali in MediaServerDevice.GetEncodedFile era If StereoOnly OrElse Not isPcmData Then channelCount = 2. La clausola Not isPcmData downmixava silenziosamente ogni transcodifica non PCM (FLAC, MP3, AAC, Ogg) a stereo indipendentemente dal numero di canali sorgente, rendendo inutili i renderer compatibili con il 5.1 quando la sorgente era FLAC 5.1. Ora la seconda clausola esclude FLAC: Not isPcmData AndAlso encoder.Codec <> FileCodec.Flac. FLAC 5.1 passa; MP3/AAC/Ogg forzano ancora lo stereo perché gli encoder da riga di comando di MusicBee per questi formati si aspettano un input a 2 canali.
Perché: l'intero scopo della transcodifica di una sorgente FLAC 5.1 in output FLAC è preservare il mix multicanale. Il downmix silenzioso rendeva l'opzione di transcodifica FLAC inutile per l'ascolto surround. Con N02 fa la cosa giusta.
Architettura
La modifica strutturale che rende il fork utilizzabile su una grande libreria - assente nel plugin originale.
N03 - Albero di navigazione pigro (su richiesta)
Cosa: il plugin originale costruiva l'intero albero di navigazione all'avvio di MusicBee - enumerava ogni traccia, Library_GetFileTags completo per file, assemblava l'intera gerarchia di contenitori - prima di aprire la porta HTTP. Su una libreria reale (50k+ tracce, 5400 episodi di podcast, centinaia di stazioni) questo significa minuti di avvio a freddo, e l'albero rimane in RAM per sempre, inclusi i rami che nessun client apre mai. Questo fork non costruisce nulla in anticipo: la radice espone un segnaposto con prefisso L: per ogni endpoint (L:music, L:podcast, L:filter:…); ogni livello viene calcolato solo quando un client lo sfoglia (LazyBrowse → EnsureLazyEndpointInMemory → cache per livello), e le notifiche di modifica della libreria cancellano le cache (SetLibraryDirty).
Perché: l'avvio a freddo è essenzialmente istantaneo - la porta HTTP è aperta quando MusicBee termina l'inizializzazione del plugin - e la memoria rimane proporzionale a ciò che è stato sfogliato, non alla dimensione della libreria. Compromesso: la prima navigazione in un endpoint paga il costo del suo caricamento; il rientro è memorizzato nella cache fino alla successiva modifica della libreria. Questa è la base su cui tutto il resto dipende. Note complete: FIXES.md.
Rete e robustezza
Rafforzamento del percorso di binding del server HTTP. Il plugin originale muore silenziosamente quando la sua porta non è disponibile.
N04 - Binding della porta HTTP auto-riparante
Cosa: il server HTTP del plugin non muore più quando la sua porta configurata non è disponibile. Tre modifiche collegate:
- Fallback automatico in caso di errore di binding.
HttpServer.Starttenta la porta configurata e, in caso diSocketException, scansiona fino a 20 porte verso l'alto per la prima disponibile. La porta effettivamente associata viene registrata in un nuovoPlugin.boundServerPort, e tutto ciò che pubblicizza il server - URL SSDPLOCATION(risposta NOTIFY + M-SEARCH), l'URL del dispositivo (PrimaryHostUrl), il port-forward del router e i filtri SSDP/control-point - ora leggeboundServerPortinvece diSettings.ServerPort. I client UPnP scoprono la porta reale tramite SSDP, quindi una porta spostata è trasparente per i renderer. - Notifica all'utente. Quando si verifica un fallback (la porta salvata non è quella in uso), un
MessageBoxlocalizzato (WarnPortInUse) informa l'utente quale porta è effettivamente in servizio e che i dispositivi la troveranno comunque - perché il plugin funziona in modalità headless e un messaggio nella finestra di dialogo sarebbe visto solo da chi già sospettava un problema. - Ripristino del riavvio.
RestartServer(il percorso di riavvio del salvataggio delle impostazioni) dereferenziavaPlugin.controller/Plugin.serverciecamente. Se l'Initialiseiniziale generava un'eccezione prima di crearli (esattamente ciò che causava un binding fallito), il successivo salvataggio delle impostazioni generava unaNullReferenceException- lasciando un plugin semi-morto. Ora li ricrea e li avvia quandoNothing, quindi il salvataggio di una porta funzionante rianima il plugin senza un riavvio completo di MusicBee.
Perché: il trigger è stato un incidente utente reale. La vecchia porta predefinita 49382 si trova nell'intervallo dinamico di Windows (49152-65535), dove Hyper-V/WSL2/Docker/WinNAT riservano grandi blocchi che si spostano ad ogni avvio - quindi il binding falliva con WSAEACCES ("accesso negato") su una macchina dove aveva funzionato per mesi. Cambiare il valore predefinito a una porta libera si scontrava poi con Serviio (un server DLNA separato già sulla nuova porta), fallendo con WSAEADDRINUSE. Ogni errore veniva inghiottito in Initialise, lasciando il plugin silenziosamente morto e poi NRE-ing al successivo salvataggio delle impostazioni. Dopo N04 una collisione di porte si auto-ripara - il server continua a funzionare sulla prima porta libera, l'utente viene informato e i client lo riscoprono - invece di bloccare l'intero plugin.
Implementazione:
- Porta predefinita spostata
49382→9779(sotto l'intervallo dinamico, quindi Windows non la riserva mai automaticamente; non è un valore predefinito noto per i server multimediali) in tutte e tre le dichiarazioniServerPort+ il fallback in caso di errore di analisi delle impostazioni. Plugin.boundServerPort(nuovo campo condiviso) contiene la porta di ascolto attiva;activeServerPortrimane l'istantanea configurata in modo che la logica del badge "Riavvio richiesto" non si attivi falsamente in caso di fallback.HttpServer.PortScanRange = 20; la scansione si ferma al primoTcpListener.Start()riuscito e genera l'ultima eccezione solo se tutti i tentativi falliscono.- Nuova chiave di risorsa EN
WarnPortInUse(le traduzioni seguono il passaggio di localizzazione al momento della pubblicazione).
N05 - Annunci SSDP tramite il gruppo multicast (VPN / point-to-point)
Cosa: gli annunci SSDP vengono inviati al gruppo multicast UPnP (239.255.255.250) invece di un indirizzo IP broadcast. Viene anche soppresso l'innocuo errore "impossibile accedere a un oggetto eliminato" registrato quando una risposta di ricerca SSDP compete con un riavvio del server.
Perché: su schede di rete point-to-point / VPN, il broadcast IP non si applica - il vecchio invio broadcast falliva con "argomento non valido" e gli annunci venivano persi, quindi il plugin era invisibile ai client su quei collegamenti. L'annuncio al gruppo multicast corretto risolve la scoperta esattamente su quelle schede.
Navigazione della libreria
Questi sono stati inclusi in questo fork e non sono presenti nel plugin originale. Provengono dalla navigazione effettiva dell'output del plugin da parte di client UPnP reali.
N06 - Esposizione della libreria basata su filtri
Cosa: le schede filtro di MusicBee (file .xautopf nella cartella utente di MusicBee) diventano contenitori radice UPnP nella libreria del plugin. Le tracce di ogni filtro sono quindi navigabili in una gerarchia di AlbumArtistSort → Album → Tracks.
Perché: gli utenti con filtri MusicBee curati (ad esempio "Tracce a 5 stelle", "Aggiunti di recente", "Classica → Barocco") si aspettano di trovarli quando navigano nel plugin da un client UPnP. Il plugin originale esponeva solo l'albero della libreria grezza.
N07 - Cablaggio del campo SortAlbumArtist
Cosa: il plugin ora legge MetaDataType 165 (Sort Album Artist) di MusicBee e lo usa per raggruppare/ordinare gli artisti nelle visualizzazioni di navigazione.
Perché: i browser hi-fi e gli audiofili usano i nomi degli artisti per l'ordinamento ("Beethoven, Ludwig van" invece di "Ludwig van Beethoven") per organizzare le librerie. Aspettativa standard per gli ascoltatori seri. Mancante in entrambi gli upstream.
N08 - Gestione di AlbumArtist con più valori
Cosa: quando il campo AlbumArtist di un album contiene più artisti separati da "; " (ad esempio "yaiol; Ars Ricercata"), la traccia appare ora sotto ogni artista nelle visualizzazioni di navigazione, non sotto un singolo artista Frankenstein che combina i nomi.
Perché: gli album collaborativi e le compilation devono apparire sotto ogni collaboratore. Senza questo, metà dei percorsi di ricerca per trovare l'album sono interrotti.
N09 - Artwork del contenitore album (upnp:albumArtURI)
Cosa: i nodi del contenitore album nelle risposte DIDL Browse ora includono un elemento upnp:albumArtURI che punta alla copertina dell'album.
Perché: senza questo, ogni album nella visualizzazione di navigazione di un client UPnP mostra un'icona generica invece della copertina dell'album. Indizio visivo per la navigazione; atteso da ogni moderno browser hi-fi.
N10 - Ordinamento delle tracce all'interno degli album filtro
Cosa: le tracce all'interno di un album esposto tramite filtro sono ora ordinate per numero di disco, quindi per numero di traccia.
Perché: ordine standard dell'album. Senza un ordinamento esplicito, le tracce venivano restituite nell'ordine in cui il filtro le restituiva - di solito casuale.
N11 - Correzione dell'albero delle cartelle delle playlist
Cosa: la funzione LoadLibraryPlaylists (originariamente di Steven Mayall, ~2014) non riusciva a scendere nelle cartelle delle playlist appena create. La prima playlist in ogni cartella, più eventuali sottocartelle, finiva orfana a livello radice.
Perché: presente nel plugin originale per undici anni. Visibile entro 30 secondi dall'apertura di BubbleUPnP e dal clic su Playlist. Corretto in yaiol ricorsivamente correttamente nelle cartelle appena create durante la costruzione dell'albero.
N12 - Sanificazione dei caratteri di controllo XML-illegali
Cosa: qualsiasi traccia con un tag contenente un carattere di controllo C0 (ad esempio 0x19 da un passaggio di codifica errato - UTF-8 → Latin-1 → troncamento di 0x99 in 0x19) causava il fallimento dell'intera risposta di navigazione con Action Failed una volta che la traccia errata entrava in un batch paginato.
Perché: XML 1.0 proibisce la maggior parte dei caratteri di controllo C0, e XmlWriter genera un'eccezione quando gli viene chiesto di scriverne uno. Presente nel plugin originale. Corretto rimuovendo i caratteri non validi in ogni punto di uscita di Library_GetFileTags tramite XmlConvert.IsXmlChar.
N13 - Elenco radio deterministico tramite Browse paginato
Cosa: la navigazione per il contenitore Radio cadeva nel ramo generico dell'elenco di file, che chiamava files.Sort(AlbumFileComparer) ad ogni chiamata. Le voci Radio hanno tag Album/Disco/Traccia vuoti, quindi ogni confronto restituiva 0 - List(Of T).Sort è instabile, producendo un ordine diverso ad ogni invocazione. I punti di controllo UPnP paginano (BubbleUPnP recupera 0..15 poi 16..fine); tra le due chiamate l'elenco si rimescolava, quindi alcune stazioni apparivano in entrambe le pagine (duplicati) e alcune in nessuna (mancanti) - apparendo casuali ad ogni aggiornamento.
Perché: presente nel plugin originale (il suo autore non naviga mai la radio tramite UPnP). Corretto qui con un ramo ContainerCategory.Radio dedicato in Browse, nessun ordinamento per chiamata; radioFiles viene ordinato una volta al momento del caricamento per Titolo (stabile). La navigazione paginata ora vede un ordine deterministico; pagina 1 e pagina 2 sono disgiunte.
N14 - La classe album di ricerca UPnP restituisce contenitori album
Cosa: la ricerca UPnP per query di classe album (upnp:class = "object.container.album.musicAlbum", ad esempio "Album casuali" di BubbleUPnP) restituiva l'elenco completo delle tracce invece dei contenitori album, quindi il client mostrava zero album. Il gestore originale analizzava solo i criteri tra parentesi, quindi scaricava tutte le tracce indipendentemente dalla classe richiesta.
Perché: corretto qui - le query di classe album ora enumerano album distinti (raggruppati per AlbumArtist+Album) ed emettono ciascuno come un contenitore musicAlbum appropriato con copertina, indirizzabile tramite lo spazio ID virtuale Salb<idx> in modo che il client possa approfondire un risultato e riprodurlo.
N15 - Ricerca UPnP funzionante e consapevole dell'ambito con click-through
Cosa: l'originale non pubblicizzava alcuna capacità di ricerca (GetSearchCapabilities restituiva vuoto), quindi i client si rifiutavano persino di inviare una ricerca; e il vecchio backend leggeva da musicFiles, permanentemente vuoto nell'era dell'albero pigro. Questo fork pubblicizza le proprietà ricercabili reali, implementa tracce per titolo e album per titolo rispetto alla libreria pigra (HandleLazySearch), limita la query al ramo corrente del client quando viene inviato un ID contenitore reale (altrimenti sostituisce L:music in modo che le ricerche nella barra superiore non trascinino rumore di podcast/radio/audiolibri), e rende i risultati degli album cliccabili tramite ID sintetici Ssrch_alb_* che un ramo iniziale di Browse mappa nuovamente alle tracce dell'album. (Il pezzo dei risultati di classe album come contenitori è N14.)
Perché: la ricerca in BubbleUPnP è passata da "La libreria non supporta la ricerca" a restituire risultati utili, mirati e riproducibili. Design completo + approcci rifiutati: SEARCH.md.
N16 - Invalidazione della cache UPnP (SystemUpdateID)
Cosa: l'originale restituiva un SystemUpdateID=0 costante - il contratto di invalidazione della cache della ContentDirectory UPnP - quindi i client conformi alle specifiche (BubbleUPnP) trattavano la libreria come mai mutevole: risultati di navigazione obsoleti, miniature 404 dopo una modifica dello schema URL e la danza "riavvia MusicBee due volte per vedere le modifiche". Questo fork inizializza SystemUpdateID dai secondi epoch al caricamento (quindi ogni riavvio è strettamente successivo all'ultimo) e lo incrementa ad ogni mutazione della libreria e modifica delle impostazioni (SetLibraryDirty / ResetCache → BumpSystemUpdateId).
Perché: i client rilevano in modo affidabile modifiche, nuovi file e modifiche alle impostazioni alla loro successiva navigazione. Limite noto: i client sottoscritti non ricevono attivamente il nuovo valore tramite GENA (parcheggiato come lavoro futuro); lo vedono comunque alla loro successiva navigazione.
N17 - Artwork degli abbonamenti ai podcast
Cosa: le tessere dei podcast non mostravano immagini - ogni richiesta /PodcastThumbnail/ restituiva 404. Due bug sovrapposti: la catena di risoluzione non controllava mai la cache effettiva degli artwork di MusicBee (%LocalAppData%\MusicBee\InternalCache\Subscriptions\<name>.jpg, da cui carica l'interfaccia utente desktop), e il livello HTTP di unescape+minuscolo rovinava la chiave di routing dell'URL del feed fino all'ultimo segmento del percorso. Questo fork risolve gli artwork dalla InternalCache di MB e instrada le ricerche tramite uno slug URL-safe che sopravvive intatto al livello HTTP (PodcastSlug / podcastSubIdBySlug).
Perché: l'artwork dell'abbonamento ora viene visualizzato nelle visualizzazioni di navigazione (tutte le 22 richieste che prima restituivano 404 vengono risolte).
N18 - Navigazione gerarchica (delimitata) dei tag
Cosa: qualsiasi campo può essere contrassegnato come gerarchico nella scheda Opzioni libreria e gli può essere assegnato un delimitatore di un carattere (un selettore di campo + casella delimitatore con aggiungi/rimuovi, persistito nelle impostazioni del plugin). Impostando Raggruppamento su / e un valore come Jazz/Cool Jazz si naviga come Jazz › Cool Jazz invece di una singola voce piatta. Le tracce taggate esattamente a un ramo (solo Jazz) ottengono il proprio nodo [Jazz] in modo che nulla sia nascosto, un ramo con un singolo figlio si comprime da solo, e ; viene rifiutato come delimitatore perché è il separatore multi-valore di MusicBee.
Perché: le tassonomie di tag profonde che un utente ha già codificato in un singolo campo (alberi di genere, gerarchie di umore, "Classica/Barocco/Concerto") finalmente vengono navigate come l'albero che il tag descrive, invece di una parete piatta di stringhe separate da slash che l'utente deve leggere da capo a fine.
N19 - Percorso radice singolo etichettato dal suo campo di raggruppamento
Cosa: un singolo percorso di navigazione alla radice è etichettato dal suo campo di raggruppamento (ad esempio "Genere") piuttosto che dal suo percorso breve completo, corrispondente a come vengono denominati i gruppi del primo campo uniti.
Perché: l'albero di navigazione si legge in modo coerente - una regola di denominazione sia che una voce radice sia da sola o sia stata unita a elementi simili (N20) - invece di una voce radice solitaria che mostra un percorso interno verboso mentre i suoi vicini uniti mostrano un nome di campo pulito.
N20 - Unisci percorsi di navigazione che condividono un primo campo
Cosa: due percorsi di navigazione che condividono lo stesso primo campo - "Genere / Ordina Artista Album" e "Genere / Persone Podcast" - si uniscono in una singola cartella radice Genere che elenca prima i valori di genere e poi si divide nelle due visualizzazioni, invece di due voci "Genere / ..." quasi duplicate affiancate alla radice.
Perché: un utente con diverse visualizzazioni correlate annidate sotto un campo comune vedeva la radice ingombra di voci di primo livello quasi identiche. Unendole si mantiene la radice leggibile e si raggruppano le visualizzazioni correlate dove appartengono - sotto il loro campo condiviso.
N21 - Percorsi di navigazione tipizzati per categoria (Standard / Radio / Podcast)
Cosa: ogni percorso di navigazione è tipizzato per categoria - Standard, Radio o Podcast. L'elenco dei modelli è raggruppato in queste tre sezioni, il selettore di campo di ogni modello offre solo i campi che i dati di quella categoria possono effettivamente fornire, e un modello può essere applicato solo ai nodi corrispondenti nell'albero della vista (i nodi incompatibili vengono disattivati e non possono essere selezionati). I modelli riservati Radio e Podcast non possono essere eliminati, quindi la loro sezione di categoria non scompare mai.
Perché: senza la tipizzazione un utente potrebbe costruire un layout che risulta silenziosamente vuoto - una stazione radio non ha un "album", un episodio di podcast non ha un "artista dell'album" - e scoprirlo solo navigando in una cartella morta da un client UPnP. Limitare il menu dei campi e i target di applicazione ai dati reali della categoria rende i layout vuoti non costruibili.
N22 - Raggruppa i podcast per anno di pubblicazione
Cosa: la data di pubblicazione di ogni episodio di podcast viene letta, in modo che un percorso di navigazione podcast con un livello Anno raggruppi gli episodi per anno invece di raggrupparli sotto un singolo "Sconosciuto".
Perché: i grandi abbonamenti ai podcast diventano navigabili per anno come il resto della libreria, invece di ogni episodio che finisce in un unico mucchio senza data perché il plugin non ha mai guardato la data di pubblicazione per episodio.
N23 - Comprimi i livelli di raggruppamento con un solo risultato
Cosa: un livello di raggruppamento che si risolve in un singolo valore - un livello Tipo di registrazione che mostra solo "LP" per un artista che ha realizzato solo LP, o un livello lettera con una singola lettera - viene saltato automaticamente, portando l'utente direttamente al suo contenuto.
Perché: navigare attraverso una cartella che contiene esattamente una cartella è pura frizione. Comprimere il livello a scelta singola rimuove il clic morto senza cambiare ciò che l'utente può raggiungere.
N24 - Raggruppamento/ricerca per anno rispetto al campo data di MusicBee
Cosa: la condizione dell'anno non interroga più il campo "Anno" di MusicBee con un valore di quattro cifre, e l'alias del campo anno hardcoded è sparito, quindi ogni campo di raggruppamento si risolve ora genericamente dalla definizione del percorso.
Perché: per le librerie il cui tag Anno contiene una data completa, il raggruppamento o la ricerca per anno in precedenza non restituiva nulla - la query a quattro cifre non corrispondeva mai al campo data completa. Interrogare il campo giusto fa sì che il raggruppamento e la ricerca per anno trovino di nuovo le tracce.
N25 - Campi di raggruppamento separati "Anno" e "Anno (aaaa)"
Cosa: i percorsi di raggruppamento e navigazione degli album ora espongono entrambi i campi anno di MusicBee - Anno (il tag data completa) e Anno (aaaa) (solo l'anno a quattro cifre) - in modo che l'utente possa scegliere l'uno o l'altro quando definisce un raggruppamento di album o un percorso di navigazione.
Perché: i due campi significano cose diverse in MusicBee, e la loro unione perdeva quella distinzione. Esporre entrambi consente all'utente di raggruppare ogni pubblicazione di un anno (aaaa) o di mantenere l'ordinamento per data esatta (tag Anno completo), come intende.
N32 - Filtri e playlist bloccati raggruppati per tipo alla radice
Cosa: un filtro bloccato ora appare direttamente sotto la cartella Filtri alla radice di navigazione, e una playlist bloccata direttamente sotto la cartella Playlist, invece di tutti i pin che si raccolgono in un unico gruppo alla fine della radice. Ogni scorciatoia bloccata si trova con il proprio tipo.
Perché: man mano che un utente blocca più scorciatoie, un singolo gruppo finale di filtri e playlist misti diventa più difficile da scansionare e separa ogni scorciatoia dalla cartella a cui appartiene. Raggruppare gli elementi bloccati sotto la propria categoria mantiene la radice leggibile e mantiene ogni scorciatoia accanto alle cose a cui appartiene.
Finestra di dialogo delle impostazioni e packaging
N26 - Finestra di dialogo delle impostazioni sezionata
Cosa: la pagina Preferenze ha ottenuto un layout di navigazione a sinistra con sezioni: Generale / Riproduzione / Libreria / Profili dispositivo / Diagnostica.
Perché: l'originale era un'unica lunga lista piatta di ogni impostazione - va bene per lo sviluppatore che l'ha creata, sconcertante per tutti gli altri. La suddivisione in sezioni raggruppa le opzioni correlate e rende la finestra di dialogo più simile alle impostazioni delle app moderne.
Ridenominazione dell'assembly + plugin (nessun F-id - nota sul packaging)
Cosa: la DLL compilata si chiama mb_UPnP_yaiol.dll e il plugin si presenta come "MusicBee UPnP (yaiol)". Distinto dall'originale mb_Upnp.dll.
Perché: gli utenti possono installare yaiol insieme al plugin originale e confrontare il comportamento fianco a fianco.
Sistema di badge - visualizzazione dello stato di runtime (meccanismo dietro F40)
Cosa: un modello UI generico per visualizzare importanti condizioni di runtime come badge colorati visibili nella finestra di dialogo delle impostazioni. I casi attuali:
- ⚠ Max Conn (N04) - si attiva quando il limite massimo di connessioni è stato raggiunto almeno una volta dall'avvio di MusicBee. Flag di sessione persistente
Plugin.MaxConnectionsHit. Impostato all'interno diWaitOnSendBarrierquando non c'è uno slot libero. - ⚠ Riavvio richiesto - si attiva quando un'impostazione salvata richiede un riavvio di MusicBee per avere effetto. Flag di sessione persistente
Plugin.RestartRequired. Impostato nel gestore di salvataggio della finestra di dialogo quando il nuovo valore persistente differisce dall'istantanea di runtime (Plugin.activeMaxConnections,Plugin.activeServerPort,Plugin.activeIpAddress). Le impostazioni che richiedono il riavvio sono limitate a quelle che non possono essere ricaricate a caldo - parametri di binding del server HTTP e il SemaphoreSlim costruito una volta all'inizializzazione.
Perché: il file di log del plugin va bene per gli utenti tecnici che eseguono il debug, ma un utente non tecnico che si trova di fronte a "il dispositivo suona male" o "la riproduzione è lenta" non aprirà mai Diagnostica → Visualizza log. I badge catturano i casi in cui l'utente ha bisogno di sapere che qualcosa è successo e lo visualizzano la prossima volta che apre il plugin - scopribili senza leggere nulla.
Riutilizzabile per il futuro:
- Rilevata mancata corrispondenza del profilo (l'user-agent del dispositivo non corrispondeva a nessun profilo, è stato utilizzato il Generico).
- Attivato il backoff di NextURI (F13 - gapless disabilitato per la sessione su un dispositivo instabile).
- Scansione della libreria fallita / parziale.
- Connessione del renderer persa a metà sessione.
- Qualsiasi altra condizione in cui "è successo una volta, l'utente dovrebbe saperlo" è meglio di "registrato silenziosamente tra altre 1000 righe".
Convenzioni di implementazione:
- Le etichette dei badge si trovano a livello della finestra di dialogo (non all'interno di alcun pannello) in modo che siano visibili indipendentemente dalla sezione in cui si trova l'utente.
- Posizionate lungo la riga inferiore vicino a Salva/Annulla (attuale: y=410 impilate orizzontalmente).
- Ogni badge ha un flag di sessione persistente corrispondente in
Pluginche diventa True quando si verifica la condizione e si resetta solo al riavvio di MusicBee. - Risorse:
<Condition>Badge(testo dell'etichetta, prefisso con ⚠) +<Condition>BadgeTip(tooltip che spiega la causa + il rimedio). - Per i badge "l'impostazione salvata richiede il riavvio", acquisire un'istantanea di runtime in
Plugin.Initialise()e confrontarla conSettings.*dopoSettings.SaveSettings()nel gestore di salvataggio della finestra di dialogo.
N27 - Annulla scarta le modifiche a percorsi/modelli
Cosa: le modifiche apportate a percorsi e modelli all'interno della finestra di dialogo delle impostazioni vengono ora scartate quando l'utente fa clic su Annulla, invece di rimanere silenziosamente applicate, e qualsiasi modello riservato rimosso durante la sessione viene ricreato. (I modelli altrimenti vengono salvati in tempo reale mentre vengono modificati - non c'è un pulsante Salva separato nella scheda Percorsi.)
Perché: Annulla dovrebbe significare annulla. In precedenza un utente che sperimentava modifiche a percorsi/modelli e tornava indietro trovava le modifiche già commesse, senza alcun modo di annullarle se non rifacendo ciascuna a mano.
N28 - Ampersand letterali nel menu del selettore di campo
Cosa: un campo il cui nome contiene "&" - ad esempio "Umore & Contesto" - visualizza l'ampersand letteralmente nel menu del selettore di campo invece di inghiottirlo come prefisso mnemonico Alt.
Perché: i nomi dei campi con un ampersand venivano visualizzati in modo errato (il carattere scompariva e la lettera successiva diventava un acceleratore), rendendo difficile riconoscere la voce del menu.
N29 - Titolo della finestra delle impostazioni stabile e non tradotto
Cosa: il titolo della finestra delle impostazioni è fissato alla stringa del marchio "MusicBee UPnP Plugin" e non varia più con la lingua dell'interfaccia; la stringa DialogTitle per lingua è stata rimossa da ogni bundle di localizzazione.
Perché: un titolo di finestra che cambiava formulazione per lingua era una superficie traducibile senza alcun beneficio - il titolo è un marchio. Fissarlo lo mantiene stabile e coerente ovunque.
Localizzazione
Il plugin originale è solo in inglese. Questo fork è completamente localizzabile - ogni stringa rivolta all'utente passa attraverso un bundle di risorse, e il plugin rileva automaticamente la lingua dell'interfaccia utente di MusicBee.
N30 - Interfaccia utente multilingue (traduzioni in sospeso)
Cosa: il meccanismo di localizzazione è completo e in fase di spedizione. Localisation.vb legge la lingua selezionata di MusicBee da MusicBee3Settings.ini (endonimo <SystemLanguage>) e applica la cultura .NET corrispondente al thread, in modo che My.Resources.Resources.* restituisca la stringa localizzata. Ogni etichetta/pulsante/messaggio rivolto all'utente è collegato a una chiave di risorsa (controlli del designer tramite ApplyDesignerExtras + sync-en-locale.js; stringhe di runtime come WarnPortInUse aggiunte a mano). Ciò che non è ancora fatto è la traduzione effettiva: esiste solo il bundle sorgente inglese (Resources.resx) - i bundle satellite per le altre lingue vengono prodotti in un unico passaggio batch quando il plugin è completo di funzionalità (tradurre a pezzi mentre le stringhe sono ancora in fase di modifica spreca sforzi).
Lingue target (il set che MusicBee stesso offre, abbinato 1:1 da endonymToCulture in modo che il plugin segua automaticamente la lingua di MusicBee):
Arabo (ar) |
Ceco (cs) |
Tedesco (de) |
Greco (el) |
Spagnolo (es) |
Francese (fr) |
Ungherese (hu) |
Italiano (it) |
Coreano (ko) |
Olandese (nl) |
Norvegese (nb) |
Polacco (pl) |
Portoghese BR (pt-BR) |
Portoghese PT (pt-PT) |
Svedese (sv) |
Turco (tr) |
Ucraino (uk) |
Russo (ru) |
Giapponese (ja) |
Cinese semplificato (zh-CN) |
Cinese tradizionale (zh-TW) |
Inglese (en, sorgente) |
Politica delle varianti (secondo la regola della locale dello spazio di lavoro): PT e ZH sono divisi in bundle distinti perché il vocabolario/script diverge realmente (pt-BR/pt-PT, zh-CN/zh-TW). EN è un singolo bundle - "English(US)" di MusicBee (en-US) ricade su en tramite la catena di culture di .NET, quindi non viene prodotto un bundle US separato. ES e FR sono allo stesso modo a locale singola.
Perché: le impostazioni di un plugin UPnP ("non usare PCM raw", "forza PCM little-endian", avvisi di fallback della porta) sono abbastanza criptiche nella propria lingua madre. Seguire la lingua dell'interfaccia utente di MusicBee - piuttosto che imporre l'inglese - è la differenza tra uno strumento che un utente non inglese può configurare e uno che non può. Nessuno degli upstream ha tentato questo.
N31 - Il link della guida si apre nella lingua completa dell'interfaccia
Cosa: l'apertura del link della Guida dal plugin rispetta la lingua completa dell'interfaccia dell'utente (ad esempio pt-BR, zh-CN) invece di collassare alla lingua base, e invia un identificatore di controllo aggiornamenti più chiaro.
Perché: un utente che esegue MusicBee in una variante regionale (portoghese brasiliano, cinese semplificato) veniva indirizzato alla pagina di aiuto nella lingua base. Trasportare la cultura completa li porta alla pagina di aiuto nella lingua esatta che usano.
Correzioni e miglioramenti al plugin originale
Protocollo e riproduzione principali
F01 - Profili dispositivo DLNA predefiniti aggiornati
Cosa: fornisce nuovi profili predefiniti per PlayStation 4, Xbox 360/One e BubbleUPnP moderno, con flag di capacità (frequenze di campionamento, profondità di bit, codec) che riflettono ciò che questi dispositivi supportano effettivamente oggi.
Perché: i valori predefiniti del plugin originale erano congelati intorno al 2014. PS4/Xbox/BubbleUPnP hanno da allora acquisito il supporto audio ad alta risoluzione. Fuori dalla scatola, una nuova installazione riproduce al meglio su questi dispositivi senza che l'utente debba toccare le impostazioni del profilo del dispositivo.
F02 - Controlla i dispositivi che pubblicizzano MediaRenderer:3
Cosa: il plugin sonda la descrizione del servizio UPnP di un renderer per decidere se MusicBee può controllarlo. L'originale corrispondeva solo a urn:schemas-upnp-org:device:MediaRenderer:1. I dispositivi moderni pubblicizzano :2 o :3. F02 amplia la corrispondenza.
Perché: senza questo, le recenti unità Sonos / WiiM / Eversolo semplicemente non compaiono come target nell'elenco dei dispositivi "Riproduci su" di MusicBee, anche se parlano lo stesso protocollo. Una singola correzione di corrispondenza del prefisso di stringa sblocca l'intera generazione di dispositivi moderni.
F03 - Opzione "Forza stream nativo" per profilo (predefinita ON)
Cosa: quando selezionata, il plugin invia i byte del file originale al dispositivo senza transcodifica, senza DSP, senza elaborazione ReplayGain applicata. Solo il file raw scelto dall'utente, byte per byte (a parte la frammentazione HTTP).
Perché: secondo le testimonianze del forum, questo è il più grande miglioramento della qualità di riproduzione. Gli utenti hi-fi che acquistano renderer costosi desiderano esplicitamente un output bit-perfect; qualsiasi intervento DSP ne vanifica lo scopo. Predefinito ON perché la maggior parte dei dispositivi moderni gestisce qualsiasi codec l'utente gli abbia dato, e ReplayGain/EQ dovrebbe essere opt-in. È per profilo in modo da poter mantenere la transcodifica per una vecchia Xbox mentre si invia nativo a un DAC hi-fi.
F04 - "Forza transcodifica" per profilo
Cosa: override per profilo che forza ogni stream a questo dispositivo attraverso il transcodificatore, indipendentemente dal supporto del codec nativo. L'inverso di F03 (ForceNativeStream). Mutuamente esclusivo con F03 - l'interfaccia utente deseleziona automaticamente l'altro quando uno dei due viene attivato.
Perché: un singolo interruttore globale sarebbe contraddittorio con il ForceNativeStream per profilo (F03). Caso reale: il dispositivo A è un DAC hi-fi che desidera stream nativi bit-perfect; il dispositivo B è un vecchio ricevitore AV che si blocca con FLAC. Con un interruttore globale l'utente deve scegliere - a costo dell'altro dispositivo. Con per profilo ogni dispositivo ottiene la risposta giusta.
Implementazione:
StreamingProfile.ForceTranscoding As Boolean = False.- Schema di persistenza aggiornato alla v9. I file pre-v9 caricano il valore globale legacy una volta e lo copiano in tutti i profili, preservando il vecchio comportamento attraverso l'aggiornamento.
- UI: rimosso dal pannello Diagnostica, aggiunto alla sezione Profili dispositivo accanto a ForceNativeStream. Gestori di mutua esclusione bidirezionali (
CheckedChangedsu ciascuno annulla l'iscrizione dell'altro prima di attivare, per evitare un loop infinito). - Sito di decisione:
Settings.ForceTranscoding→streamingProfile.ForceTranscodinginWriteAudioFileDIDL.
F05 - "Forza PCM little-endian" per profilo
Cosa: i flussi PCM (tipi mime L16/L24) sono big-endian per specifica. Alcuni dispositivi si aspettano erroneamente little-endian e riproducono rumore bianco quando vengono forniti dati big-endian corretti. F05 commuta l'ordine dei byte per profilo.
Perché: senza questo, alcuni dispositivi emettono un muro di statico. Il sintomo è drammatico e la causa invisibile senza conoscenza della codifica PCM - l'interruttore offre agli utenti una soluzione per tentativi ed errori.
F06 - "Non usare PCM Raw" per profilo
Cosa: quando il dispositivo dichiara di supportare il PCM raw, il plugin lo utilizza. Alcuni dispositivi mentono - accettano l'handshake SOAP ma distorcono i dati PCM raw effettivi, mentre gestiscono correttamente il PCM avvolto in un contenitore WAVE. F06 forza il PCM-over-Wave indipendentemente da ciò che il dispositivo pubblicizza.
Perché: specificamente alcuni modelli Marantz - pubblicizzano PCM raw ma solo WAVE funziona. Senza questo, i flussi PCM raw risultano distorti, senza alcun messaggio di errore che lo indichi.
F07 - "Lunghezza contenuto" per profilo
Cosa: quale valore inviare nell'intestazione HTTP Content-Length. Quattro opzioni:
- Predefinito - conteggio effettivo dei byte quando noto, ometti quando sconosciuto.
- Nessuno - non inviare mai l'intestazione (solo codifica a blocchi).
- Solo PCM - invia solo per PCM raw; ometti per tutto il resto.
- Fisso - invia
UInt32.MaxValue - 8192(un valore sentinella per "lunghezza sconosciuta enorme").
Perché: i dispositivi UPnP/DLNA variano enormemente nel modo in cui reagiscono a Content-Length. Alcuni hanno bisogno di un numero esatto, alcuni lo odiano sui flussi, alcuni hanno bisogno di un valore sentinella "davvero grande" per mantenere il buffering. Questo è stato successivamente ampliato da solo PCM a tutti i formati di output perché gli stessi problemi si sono manifestati nei flussi MP3/AAC transcodificati.
F08 - "Non cancellare NextURI" per profilo
Cosa: normalmente il plugin cancella il NextURI in coda del dispositivo quando la coda si svuota (inviando SetNextAVTransportURI con un URL vuoto). Alcuni dispositivi (in particolare Denon) interpretano un NextURI vuoto come "ferma tutto" e interrompono immediatamente la riproduzione. F08 impedisce al plugin di cancellarlo.
Perché: senza questo, i proprietari di Denon sperimentano l'interruzione del dispositivo a metà traccia quando la coda si svuota. Con F08 selezionato, il dispositivo mantiene il NextURI obsoleto in memoria (innocuo - viene semplicemente sovrascritto la prossima volta che qualcosa viene messo in coda).
F09 - FLAC come formato di output di transcodifica
Cosa: il menu a discesa del formato di transcodifica in Profili dispositivo ora offre FLAC insieme a PCM 16/24, MP3, AAC, Ogg. Selezionandolo, l'encoder BASS viene instradato tramite la riga di comando di conversione FLAC standard di MusicBee (stesso meccanismo già utilizzato da MP3/AAC/Ogg).
Perché: per i dispositivi che gestiscono bene FLAC ma non possono decodificare il codec sorgente (ad esempio, un Eversolo che riceve la libreria WMA di MusicBee convertita in FLAC), questo preserva la qualità lossless dove MP3/AAC scarterebbero i dati audio. Sblocca N02 (controllo del downmix 5.1), che non poteva essere affrontato senza un'opzione di transcodifica lossless.
Implementazione: aggiunta di una riga al blocco Select Case Codec di Encoder.StartEncode - FLAC si unisce a MP3/AAC/Ogg nel ramo guidato dalla riga di comando. Il menu a discesa dell'interfaccia utente ottiene "FLAC" come sesta opzione. La mappatura di caricamento/salvataggio in SettingsDialog si estende per riconoscere FileCodec.Flac ↔ SelectedIndex = 5. Mime, tipo DLNA e funzione di codifica erano già cablati in ItemManager.GetMimes / GetDlnaType / GetEncodeFeature da lavori precedenti (F21, F26).
Gapless (SetNextAVTransportURI)
F10 - SetNextAVTransportURI / NextURI core
Cosa: vera riproduzione gapless. Quando il dispositivo pubblicizza il supporto per SetNextAVTransportURI nella sua descrizione del servizio UPnP, il plugin pre-accoda la traccia successiva sul dispositivo prima che quella corrente finisca. Il dispositivo effettua la transizione internamente senza interruzioni udibili tra le tracce - ciò che si sente su un lettore CD. Questo non è l'hack "stream continuo" (che concatena tutto in un unico lungo stream e perde i metadati per traccia).
Perché: la funzionalità di punta di livello 2. Gli album registrati come una performance live continua (dischi live, movimenti classici, DJ set) suonano male quando c'è un mezzo secondo di silenzio tra le tracce. Risolvere questo problema correttamente è una funzionalità di punta, ora in questo fork.
Note: l'audio in coda viene servito tramite il server HTTP del plugin utilizzando streamHandle=0 (modalità di recupero della libreria), il che significa che il motore audio di MusicBee non è coinvolto nella traccia in coda. Compromesso: gli effetti ReplayGain/DSP/EQ non si applicano alla traccia successiva. Accettabile quando "forza stream nativo" è attivo (il valore predefinito).
F11 - "Disabilita supporto NextURI" per profilo
Cosa: anche se un dispositivo pubblicizza SetNextAVTransportURI, questa casella di controllo forza il plugin a ignorare tale pubblicità e a tornare alla riproduzione di una traccia alla volta.
Perché: alcuni dispositivi pubblicizzano NextURI ma hanno un'implementazione buggata (crash, transizioni incomplete, blocchi). Invece di fare reverse engineering su ogni dispositivo difettoso, l'utente ottiene un interruttore "basta disattivarlo qui".
F12 - Ciclo di vita di NextURI nell'elenco di riproduzione corrente
Cosa: quando MusicBee attiva NowPlayingListChanged, il plugin rivaluta cosa dovrebbe essere messo in coda per la transizione gapless. Chiede a MusicBee la nuova traccia "successiva" tramite NowPlayingList_GetNextIndex(1) + NowPlayingList_GetListFileUrl, confronta con ciò che è attualmente in coda sul dispositivo (tracciato tramite il nuovo campo nextPlaySourceUrl), e rimette in coda se è cambiato (o svuota la coda se MusicBee dice che non c'è una traccia successiva).
Perché: senza F12, il dispositivo continuava a riprodurre un NextURI obsoleto quando l'utente rimuoveva/riordinava la traccia in coda. Questo storicamente ha richiesto diverse iterazioni perché ogni mutazione dell'elenco richiede una gestione diversa - noi abbiamo semplificato fidandoci di NowPlayingList_GetNextIndex (che rispetta già la riproduzione casuale e il loop di ripetizione di tutto), quindi tutte le varianti passano attraverso lo stesso confronto.
Implementazione:
- Il nuovo campo
nextPlaySourceUrlmemorizza l'URL della libreria MusicBee di ciò che è in coda (l'URL di streaming con suffisso handle non è paragonabile a un percorso di libreria). - Nuova
Public Sub RefreshQueuedNextUri()suMediaRendererDevice. Tre risultati: nessun NextURI in coda → nessuna operazione; in coda corrisponde al nuovo "successivo" → nessuna operazione; in coda differisce → chiamaQueueNextcon il nuovo URL (oQueueNext("")per cancellare - che rispetta F08 DoNotClearNextUri). - Cablato in
Plugin.ReceiveNotificationsottoNotificationType.NowPlayingListChanged.
F13 - Backoff in caso di errore di NextURI
Cosa: dopo 4 fallimenti consecutivi di SetNextAVTransportURI sullo stesso dispositivo, il plugin disabilita il gapless per quel dispositivo fino al riavvio di MusicBee.
Perché: se un dispositivo è realmente difettoso per NextURI (errori SOAP intermittenti, problemi di rete), il plugin continuerebbe altrimenti a riprovare ad ogni traccia. F13 interrompe il rumore e torna silenziosamente alla riproduzione di una traccia alla volta.
F14 - Integrazione modalità ripetizione + NextURI
Cosa: F14 si divide in due casi gestiti dal rilevatore di transizione F15 in OnAvTransportStatusCheck:
- Ripeti tutto: MusicBee passa l'URL "wrap" corretto (traccia 1 alla fine dell'elenco) a
Plugin.QueueNextstesso. Non è necessaria alcuna logica speciale del plugin - il dispositivo effettua la transizione e il rilevatore F15 chiamaPlayer_PlayNextTrackcome al solito, che riporta l'indice NPL di MusicBee a 0. - Ripeti uno: MusicBee passa lo STESSO URL della traccia a
Plugin.QueueNext. Il dispositivo effettua la transizione (nuovo handle di stream, stessa sorgente). Il rilevatore F15 ora interrogaPlayer_GetRepeat()- se èRepeatMode.One, salta la chiamata aPlayer_PlayNextTrackin modo che MusicBee non avanzi l'indice NPL dalla traccia in loop.
Perché: senza il salto di Ripeti-Uno, la chiamata a Player_PlayNextTrack alla transizione gapless farebbe avanzare MusicBee alla traccia successiva nell'elenco (Ripeti-Uno influisce solo sul comportamento di avanzamento automatico a fine traccia nell'interfaccia utente del lettore - Traccia successiva avanza sempre), contraddicendo il significato di Ripeti-Uno.
Avvertenza sul conteggio delle riproduzioni: in Ripeti-Uno, l'incremento del conteggio delle riproduzioni dipende da MusicBee 3.7.9563+ che rileva la riproduzione in loop. Le versioni precedenti di MusicBee riproducono correttamente la ripetizione gapless ma perdono l'incremento del conteggio delle riproduzioni. Documentato; non bloccante.
F15 - Macchina a stati per il rilevamento della transizione di traccia
Cosa: quando il dispositivo effettua una transizione interna dalla traccia corrente a NextURI, il plugin deve accorgersene e dire a MusicBee di avanzare il suo indice di riproduzione corrente. Altrimenti MusicBee pensa di essere ancora sulla traccia precedente e i conteggi di riproduzione / UI / scrobbling si disallineano.
Implementazione: interroga GetPositionInfo.TrackURI ad ogni tick del timer di stato. Quando l'URI riportato corrisponde a quello che abbiamo messo in coda tramite NextURI, chiamiamo Player_PlayNextTrack su MusicBee e impostiamo suppressNextSoapCall in modo che il PlayToDevice risultante non invii nuovamente SetAVTransportURI (il che interromperebbe la riproduzione gapless).
Perché: senza F15 il dispositivo riproduce la traccia successiva ma l'interfaccia utente di MusicBee dice che è ancora sulla precedente. Confuso, interrompe lo scrobbling, interrompe il tracciamento del conteggio delle riproduzioni. Il rilevamento della transizione di traccia richiede una lunga iterazione per renderer perché ogni marca di renderer ha le sue peculiarità su quando riporta la modifica dell'URI (alcuni riportano prima TRANSITIONING, alcuni saltano direttamente a PLAYING con il nuovo URI, alcuni hanno un breve STOPPED nel mezzo).
Note: la nostra prima versione funziona sul renderer BubbleUPnP. I casi limite per dispositivo rimangono in B6.
F16 - Correzione del pop in transizione gapless
Cosa: il pop si verifica quando il formato sorgente (frequenza di campionamento / canali / codec) della traccia in coda differisce dalla traccia attualmente in riproduzione, costringendo il DAC del dispositivo a ri-bloccare alla transizione. F16 aggiunge una diagnostica NextUri:FormatChange che si attiva al momento della coda ogni volta che i formati differiscono, nominando entrambi i lati - in modo che gli utenti che sentono pop possano correlare.
La diagnostica indica anche la mitigazione: selezionare ForceTranscoding sul profilo del dispositivo. Ciò omogeneizza ogni traccia a un singolo codec/frequenza di campionamento/profondità di bit di transcodifica, eliminando completamente la differenza di formato sorgente.
Perché rimandato per la correzione effettiva della transcodifica per la corrispondenza: la correzione strutturale (transcodificare la traccia in coda per corrispondere al formato della traccia in riproduzione) richiede modifiche allo schema URL del server HTTP del plugin - attualmente /encode/{id}0.{ext} serve il file in coda in modo nativo. Una futura v2 di F16 aggiungerebbe per formato percorsi /encode/{id}0_{rate}_{depth}.{ext} e li collegherebbe tramite l'encoder. Questa è una modifica architettonica più grande che vale la pena fare se un dispositivo reale mostra il pop dopo che ForceTranscoding non è sufficiente.
Implementazione oggi:
- Il campo
lastSourceUrltiene traccia dell'URL sorgente attualmente in riproduzione. QueueNextleggeFilePropertyType.SampleRate/Channels/Kindper entrambe le tracce corrente e in coda e registraNextUri:FormatChangein caso di mancata corrispondenza.
F17 - Risincronizzazione della barra di avanzamento dopo la ricerca
Cosa: la funzione Seek() chiamava già GetPlayPositionInformation() dopo un Seek SOAP riuscito, il che risolve il caso di "nessuna risincronizzazione". F17 chiude il restante scostamento fino a 1 secondo causato dalla quantizzazione RelTime di 1 secondo di UPnP: quando la posizione riportata dal dispositivo si arrotonda a entro 1 secondo dal target richiesto dall'utente, il plugin ora si fida del valore sub-secondo-accurato dell'utente invece del troncamento del dispositivo. Solo quando il dispositivo riporta qualcosa di drasticamente diverso (>1s di scostamento) usiamo il suo valore (la ricerca è atterrata altrove rispetto a quanto richiesto, ad esempio snap-to-keyframe su alcuni codec).
Perché: senza questo, la ricerca a 2:30.500 ancorata al rapporto "2:30" del dispositivo farebbe sì che la barra di avanzamento mostri ~500ms indietro rispetto alla realtà. Dopo F17 la barra corrisponde all'intento dell'utente per il comune caso di scorrimento all'interno della traccia, e rispetta comunque il rapporto del dispositivo per il caso anomalo di snap-to-keyframe.
F18 - Interblocco stream continuo / NextURI
Cosa: due interblocchi ora in atto:
- Runtime:
QueueNextrestituisceFalsein anticipo quandoSettings.ContinuousOutputè attivo. Lo stream continuo è il suo meccanismo gapless (un lungo stream concatenato); l'invio diSetNextAVTransportURIin aggiunta confonde il dispositivo sul fatto che ogni traccia sia un URI discreto o parte del flusso continuo. - UI: quando l'utente seleziona la casella di controllo globale dello stream continuo,
forceNativeStreamdel profilo attualmente visualizzato si deseleziona automaticamente. Lo stream continuo transcodifica sempre, quindi force-native è privo di significato in combinazione.
Perché: impedisce all'utente di abilitare due meccanismi gapless in conflitto contemporaneamente. Senza F18, il dispositivo riceverebbe sia un URI di stream continuo CHE un NextURI per ogni traccia successiva, con un comportamento indefinito a seconda del renderer.
F19 - Errori NextURI vuoti ignorati
Cosa: quando SetNextAVTransportURI viene chiamato con un URL vuoto (ad esempio, l'ultima traccia nell'elenco), alcuni dispositivi restituiscono un errore SOAP. F19 li inghiotte silenziosamente - registrati ma non propagati come errori.
Perché: la condizione "nessuna traccia successiva" è normale, non un errore. Trattarla come fatale inquina il log e (in alcuni flussi) innesca tempeste di tentativi.
Tipi MIME e metadati DLNA
F20 - Tipo mime MP3 → audio/mpeg
Cosa: il tipo mime MP3 conforme agli standard è audio/mpeg, non audio/mp3. Quest'ultimo è un errore comune che la maggior parte dei dispositivi tollera, ma i renderer più severi lo rifiutano.
Perché: risolve silenziosamente la riproduzione su dispositivi più severi che seguono lo standard. Il codebase yaiol aveva già questo corretto; nessuna modifica necessaria.
F21 - Ordine dei tipi mime: variante non x- per prima
Cosa: quando un dispositivo pubblicizza sia audio/flac che audio/x-flac, il plugin restituisce prima la variante non x-. Lo stesso vale per qualsiasi codec con mime sia standard che sperimentali.
Perché: il prefisso x- contrassegna i mime sperimentali/non ufficiali. Alcuni renderer si comportano meglio con la forma standard. Piccolo riordino, impatto reale.
F22 - Supporto tipo mime Opus
Cosa: riconosce Opus come codec audio riproducibile in streaming; invia il tipo mime audio/opus quando serve tracce Opus.
Perché: Opus è ora comune (codec di compromesso moderno per voce/musica). Senza F22 il plugin si rifiuterebbe di riprodurre in streaming file Opus anche su dispositivi che li gestiscono.
F23 - Supporto file sorgente Monkey Audio (APE)
Cosa: riconosce i file .ape come un codec sorgente valido per lo streaming/transcodifica.
Perché: APE è un formato lossless con una base di utenti di nicchia ma fedele. Aggiungerlo costa poco e sblocca la libreria per questi utenti.
F24 - Fallback mime AAC / ALAC
Cosa: se un dispositivo supporta AAC o ALAC ma non li pubblicizza esplicitamente nella sua descrizione del servizio UPnP, il plugin li offre comunque come fallback.
Perché: diversi dispositivi che gestiscono bene AAC hanno dimenticato di elencarlo nel loro XML delle capacità. Senza F24 il plugin non ci proverebbe nemmeno, forzando la transcodifica. Con F24 il plugin ci prova e lascia che il dispositivo lo gestisca nativamente se può.
F25 - Flag tipo DLNA per stream WAV nativi + codificati
Cosa: il flag del tipo DLNA (un identificatore di profilo come LPCM, WAVE, MP3) deve corrispondere a ciò che il dispositivo riceve. F25 assicura che gli stream nativi e gli stream WAV codificati siano contrassegnati correttamente.
Perché: un tipo DLNA non corrispondente fa sì che alcuni dispositivi rifiutino completamente la riproduzione o applichino il decoder sbagliato.
F26 - Intestazione DLNA per file FLAC
Cosa: i flussi FLAC ottengono l'identificatore di profilo DLNA corretto nelle loro intestazioni.
Perché: senza di esso, alcuni dispositivi che supportano FLAC non riconoscono il flusso come tale.
F27 - Correzione del calcolo del bitrate nei metadati
Cosa: il res@bitrate dello stream continuo veniva calcolato come (sampleRate * channels * bitsPerSample) / 1000 - kbps, con un errore di un fattore di circa 125 rispetto alla specifica UPnP DIDL che definisce l'attributo come byte al secondo. Ora divide per 8 invece di 1000.
Perché: visualizzazione errata del bitrate sul dispositivo - cosmetica sulla maggior parte dei renderer, ma alcuni allocano buffer di stream dal valore e balbettano su stream che sembrano circa 125 volte più piccoli di quanto siano. Il percorso del file sorgente non continuo aveva già questo corretto ((bitrate_kbps * 1000) \ 8 = byte/sec); solo il percorso dello stream continuo era sbagliato.
F28 - Correzione formato ora metadati (Marantz)
Cosa: res@duration in DIDL era formattato come H:MM:SS (ad esempio 0:03:42). La specifica UPnP DIDL definisce il formato come H+:MM:SS[.F+] - strettamente con secondi frazionari opzionali ma raccomandati; alcuni dispositivi Marantz trattano la forma nuda come non valida e lasciano vuota la loro visualizzazione della durata. Ora formattato come H:MM:SS.fff (ad esempio 0:03:42.000).
Perché: problema di visualizzazione specifico di un marchio; il formato conforme allo stile ISO con secondi frazionari lo risolve senza influenzare nessun altro dispositivo. Applicato in entrambi i siti di emissione DIDL (percorso file sorgente + percorso stream codificato in WriteAudioFileDIDL).
Correzione bonus nello stesso passaggio: pv:addedTime e pv:lastPlayedTime utilizzavano hh (orologio a 12 ore) nelle loro stringhe di formato DateTime invece di HH (24 ore). Qualsiasi traccia aggiunta o riprodotta tra le 13:00 e le 23:59 verrebbe visualizzata con un'ora errata (ad esempio 17:42 → "05:42") sui dispositivi che visualizzano il campo. Ora usa HH.
F29 - Supporto seek MP3 codificato (CBR)
Cosa: i flussi MP3 transcodificati ora pubblicizzano DLNA.ORG_OP=11 (ricerca sia per byte che per tempo) invece di DLNA.ORG_OP=10 (solo per byte). I dispositivi che in precedenza rifiutavano la ricerca per tempo su MP3 transcodificati possono ora gestire normalmente la loro barra di avanzamento/interfaccia utente di ricerca.
Perché: il transcodificatore di MusicBee produce MP3 a bitrate costante con il preset HighQuality, quindi la mappatura byte ↔ tempo è lineare - il dispositivo può convertire una richiesta di ricerca per tempo in una ricerca per byte HTTP Range da solo senza alcun supporto lato encoder. La pubblicità di OP=11 sblocca quell'interfaccia utente sul dispositivo. Senza F29, gli utenti che cercavano all'interno di un MP3 transcodificato avevano la ricerca ignorata silenziosamente o venivano riportati all'inizio della traccia.
Implementazione: GetEncodeFeature in ItemManager.vb è stato ristrutturato per suddividere l'If in linea in una catena If/ElseIf/Else leggibile. MP3 ottiene OP=11 esplicitamente; altri codec non PCM mantengono OP=10. Nessuna modifica per AAC/FLAC/ecc. - questi richiederebbero una verifica specifica del codec della CBR-ness che MusicBee non garantisce.
F30 - Gestione estensione file .mpeg
Cosa: i file con estensione .mpeg (e il più raro .mpe) sono ora riconosciuti come FileCodec.Mp3 in GetCodec. Prima di F30 restituivano FileCodec.Unknown e venivano silenziosamente rifiutati dalla libreria / non potevano essere sorgenti di transcodifica.
Perché: vecchi archivi MPEG-1 Layer 3 a volte usavano .mpeg invece di .mp3 (la specifica consente entrambi). Una manciata di file in una libreria di 300k è sufficiente per far sentire "MusicBee li mostra ma il plugin no" - confuso per l'utente.
Comportamento di riproduzione
F31 - I flussi radio utilizzano automaticamente la modalità continua
Cosa: WriteAudioFileDIDL ora sonda la proprietà Kind dell'URL sorgente tramite Library_GetFileProperty e tratta qualsiasi file la cui Kind termina con "Stream" (MusicBee riporta "MP3 Stream", "Internet Stream", ecc. per la radio) come continuo indipendentemente dall'interruttore globale Settings.ContinuousOutput. Viene utilizzato il ramo DIDL dello stream continuo (Titolo: "Stream continuo", id="continuousstream", output PCM/Wave fisso); il dispositivo vede un singolo stream di tipo infinito.
Perché: i flussi radio non hanno confini di traccia, lunghezza fissa, né possibilità di ricerca. Trattarli come file discreti nel DIDL faceva sì che il plugin pubblicizzasse intervalli di byte e durate che non esistono. Il passaggio automatico quando MusicBee ci ha già detto "questo è uno Stream" rimuove un problema che l'utente non dovrebbe dover considerare.
Ambito: si applica solo quando MusicBee sta gestendo la riproduzione (musicBeePlayToMode). Il percorso di recupero della libreria (navigazione del client UPnP) rimane invariato - gli URL radio lì sono rari e il comportamento rivolto all'utente non dovrebbe cambiare senza test espliciti.
F32 - Fallback pubblicità codec
Cosa: se un dispositivo non pubblicizza determinati codec (o il plugin non riesce a analizzare l'XML delle capacità del dispositivo), il plugin non rifiuta immediatamente lo stream. Invece, prova a servirlo e lascia che il dispositivo decida.
Perché: molti dispositivi hanno XML delle capacità incompleti o illeggibili ma in realtà gestiscono bene il codec. F32 scambia un piccolo "migliore ipotesi e prova" con un rifiuto categorico.
F33 - Miglioramento della sincronizzazione della barra di avanzamento
Cosa: la posizione tra un sondaggio e l'altro è già estrapolata dall'orologio da un singolo ancoraggio (currentPlayStartTicks), quindi la barra di avanzamento si aggiorna fluidamente a una velocità inferiore al secondo. La restante fonte di jitter era l'ancoraggio iniziale per una traccia appena avviata: il codice precedente assumeva position=0 nel momento in cui il timer di stato notava per la prima volta che lo stato era passato a Riproduzione, ma a quel punto il dispositivo potrebbe aver riprodotto per 100-500ms (un intervallo di sondaggio). La barra di avanzamento di MusicBee inizierebbe a 0, quindi salterebbe in avanti quando la realtà si allineava.
Correzione F33: quando si passa a Riproduzione per la prima volta su una nuova traccia (currentPlayStartTimeEstimated=True), chiama GetPlayPositionInformation() per ottenere la posizione attuale del dispositivo, quindi ancorati a quella. UPnP riporta solo una risoluzione di 1 secondo, quindi l'ancoraggio è ancora quantizzato, ma è molto più vicino alla verità che assumere 0.
Perché: visualizzazione più fluida e precisa dell'avanzamento, soprattutto subito dopo il cambio di traccia. Non c'è modo di aggirare la risoluzione di segnalazione UPnP di 1 secondo stessa - è una specifica.
F34 - Jitter della barra di avanzamento dopo il cambio di traccia
Cosa: quando PlayToDevice viene chiamato per una nuova traccia, il plugin lasciava currentPlayPositionMs e currentPlayStartTicks ai loro valori della traccia precedente per la finestra di ~100ms tra SOAP-Play e il primo sondaggio del timer di stato che rilevava il nuovo stato di Riproduzione. La barra di avanzamento di MusicBee avrebbe brevemente mostrato la fine della traccia precedente, quindi sarebbe tornata a 0, quindi sarebbe salita. F34 azzera entrambi all'ingresso di PlayToDevice - nel momento in cui sappiamo che sta avvenendo un cambio di traccia, prima di qualsiasi lavoro SOAP.
Perché: glitch visivo nei casi d'uso di salto rapido (avanzamento manuale o transizione gapless). Ora la prima query PlayPositionMs di MusicBee dopo Play restituisce 0 in modo pulito, quindi GetPlayPositionInformation di F33 la affina alla posizione effettiva del dispositivo al primo tick di cambio di stato.
Implementazione: quattro righe all'inizio di PlayToDevice, abbinate all'ancoraggio accurato del tempo di transizione di F33.
F35 - Bug "Forza transcodifica"
Cosa: la transcodifica forzata poteva ancora saltare la transcodifica in determinate combinazioni. Dopo la rielaborazione per profilo di F04, sono state sigillate due lacune specifiche:
- Precedenza con ForceNativeStream. Quando entrambi erano True (il che può accadere durante una migrazione di schema o un file di impostazioni parziale), ForceTranscoding ora vince in modo assoluto (
If streamingProfile.ForceTranscoding Then forceEncode = True ElseIf streamingProfile.ForceNativeStream Then forceEncode = False). L'esclusione reciproca dell'interfaccia utente impedisce all'utente di selezionare entrambi, ma la protezione di runtime gestisce qualsiasi stato caricato in modo incoerente dal disco. - Logica bypassTranscodeDecision. In precedenza:
streamingProfile.ForceNativeStream AndAlso Not Settings.ForceTranscoding. Ora:streamingProfile.ForceNativeStream AndAlso Not streamingProfile.ForceTranscoding- stessa regola di precedenza ma con lo stesso ambito per profilo.
Perché: "forza" dovrebbe significare forza. Se l'utente ha esplicitamente abilitato ForceTranscoding per un dispositivo, il plugin non deve mai passare silenziosamente allo streaming nativo, indipendentemente da come si combinano gli altri flag.
F36 - Eccezione renderer-chiuso
Cosa: Plugin.ReceiveNotification è stato avvolto in un Try/Catch di alto livello che registra qualsiasi eccezione non gestita invece di lasciarla propagare al pump di notifica di MusicBee.
Perché: le notifiche da MusicBee (PlayStateChanged, VolumeMuteChanged, ecc.) vengono inviate a ControlPointManager che comunica con il renderer tramite SOAP. I singoli siti di chiamata avevano già Try/Catch attorno alle loro chiamate SOAP, ma un caso di temporizzazione sufficientemente strano (ad esempio, il renderer che muore tra due chiamate SOAP nello stesso gestore di notifica) poteva comunque sfuggire. Il wrapper di alto livello è l'ultima rete di sicurezza in modo che l'utente non veda mai un popup generico "TargetInvocationException" da MusicBee.
Implementazione: il corpo esistente è stato rinominato in ReceiveNotificationInternal ed è stato aggiunto un sottile wrapper ReceiveNotification che esegue Try { ReceiveNotificationInternal(...) } Catch { LogError(...) }. L'infrastruttura Try/Catch preesistente per metodo all'interno di ControlPointManager (attorno a ogni chiamata PostSoapRequest) rimane - F36 è cintura + bretelle.
F37 - Ricerca traccia lunga che attiva una falsa transizione
Cosa: la ricerca all'interno di una traccia lunga può produrre un breve ciclo Stopped→Playing su alcuni renderer. Senza discriminazione, ProcessNewPlayState.Stopped lo tratta come fine naturale della traccia e chiama Player_PlayNextTrack, facendo avanzare MusicBee quando l'utente voleva solo scorrere. F37 imposta lastUserInitiatedSeek in Seek() e aggiunge una protezione di 5 secondi nel gestore Stopped (rispecchiando la finestra lastUserInitiatedStop esistente).
Perché: il salto silenzioso alla traccia successiva durante una ricerca è uno di quei bug di cui nessuno può indovinare la causa - l'utente pensa "strano, ho provato a scorrere in avanti e ora sta riproducendo la canzone successiva". La correzione è meccanica: lo stesso schema della discriminazione dell'arresto utente già in atto.
F38 - Migliorata la gestione della ricerca per codec soggetti a crash
Cosa: il crash di BubbleUPnP durante la ricerca MP3 era il sintomo canonico. Dopo un'analisi, il codice di ricerca yaiol attuale fa già le cose giuste - il percorso nativo gestisce correttamente HTTP Range (206, Content-Range, AcceptRanges), il percorso codificato pubblicizza X-AvailableSeekRange e analizza le intestazioni timeSeekRange.dlna.org / npt in arrivo, i flag DLNA.ORG_OP riflettono le effettive capacità dello stream (con DisablePcmTimeSeek opt-out per i dispositivi Platinum problematici). Testato dall'utente su BubbleUPnP 4.6.4 attuale: nessun crash osservato.
Perché: il crash di BubbleUPnP MP3-seek è stato segnalato intorno al 2024 e l'app ha ricevuto circa 16 mesi di correzioni da allora. F29 (MP3 codificato OP=11) era la nuova variabile che avrebbe potuto ri-esporlo; non lo fa, sulle versioni testate.
Se un crash si ripresenta: la forma della correzione sarebbe un interruttore "ricerca limitata" per profilo che forza DLNA.ORG_OP=10 (solo byte) sui codec contrassegnati - rispecchiando il funzionamento di DisablePcmTimeSeek per PCM. Aggiungere allora, non preventivamente.
UI e logging
F39 - Il pulsante "Aggiungi" seleziona il nuovo profilo
Cosa: cliccando su "Aggiungi" nell'elenco dei profili dispositivo si crea un nuovo profilo E lo si seleziona automaticamente in modo che l'utente possa immediatamente modificare i campi. Il nostro refactoring della finestra di dialogo sezionata lo fa già - sia il percorso di aggiunta diretta che il percorso da modello terminano con Me.activeStreamingProfiles.SelectedIndex = Me.activeStreamingProfiles.Items.Count - 1. Un controllo ha confermato che il nostro fork gestisce già questo - nulla da cambiare.
Perché: piccola imperfezione UX che si è rivelata non essere già un'imperfezione qui.
F40 - Connessioni massime maggiori + log di avviso
Cosa: il limite di stream concorrenti del plugin (SemaphoreSlim attorno a Sockets_Stream_File / Sockets_Encoder_Start) era un valore hardcoded di 4. F40 lo rende configurabile dall'utente nella pagina delle impostazioni Generali (predefinito 16, intervallo 1-256), aggiunge una riga di log MaxConnections quando una richiesta deve attendere uno slot, E visualizza un badge rosso ⚠ Max Conn in basso a sinistra della finestra di dialogo delle impostazioni se il limite è stato raggiunto almeno una volta dall'avvio di MusicBee.
Perché: quando un dispositivo invia richieste parallele (alcuni Marantz/Linn durante le scansioni degli artwork, le sonde dei metadati di BubbleUPnP insieme alla riproduzione attiva), le richieste aggiuntive venivano bloccate silenziosamente dietro il semaforo - l'utente vedeva "dispositivo lento" senza una causa visibile. La riga di log è utile per il debug tecnico ma gli utenti non tecnici non leggono mai i log. Il badge visibile nella finestra di dialogo delle impostazioni rende la condizione di raggiungimento del limite scopribile a chiunque apra le preferenze del plugin.
Implementazione:
- Centralizzata l'attesa in
WaitOnSendBarrier(logTag)inMusicBeeUpnp.vb; entrambi i siti di chiamata (MediaServerDevice.GetFile,Encoder.StartEncode) lo usano. Settings.MaxConnectionspersistito nella v8 dello schema delle impostazioni.Plugin.MaxConnectionsHitè un flag di sessione persistente impostato all'interno diWaitOnSendBarrier; si resetta solo al riavvio di MusicBee.SettingsDialog.maxConnectionsBadgeè un'etichetta rossa in grassetto a(16, 410)che viene visualizzata solo quandoPlugin.MaxConnectionsHitè True. Ha un tooltip che spiega la causa e il rimedio.- Il semaforo viene inizializzato una volta al caricamento del tipo, quindi la modifica dell'impostazione richiede un riavvio di MusicBee (annotato nell'etichetta del campo).
F41 - Log "codifica a causa di ReplayGain/DSP"
Cosa: invece di righe di log separate "codifica per RG" / "codifica per DSP", la singola riga StreamDecision di F42 include MB-DSP/EQ, MB-ReplayGain, Profile-DSP/EQ, Profile-ReplayGain come ragioni accumulate. Stesso valore diagnostico, meno rumore.
Perché: gli utenti vedono tutte le ragioni per cui la transcodifica sta avvenendo per una data traccia su una riga di log, non sparse. Vedi F42 per i dettagli completi.
F42 - Log "il renderer non supporta il codec sorgente"
Cosa: aggiunta una riga di log StreamDecision per traccia riprodotta sul dispositivo che dice "codec nativo" o "transcodifica CODEC→CODEC motivo=…". Il campo motivo accumula ogni condizione che ha attivato la transcodifica: MB-DSP/EQ, MB-ReplayGain, Profile-DSP/EQ, Profile-ReplayGain, WebFile, VirtualFile, ForceTranscoding(global), SampleRate<min/SampleRate>max, DownmixToStereo, DeviceLacksCodec(X), BandwidthConstrained.
Perché: gli utenti erano confusi da picchi inaspettati della CPU su file che si aspettavano di riprodurre in streaming nativamente. Una riga di log per traccia dice loro esattamente quale condizione ha causato la transcodifica - e se il campo mostra DeviceLacksCodec(Flac) sanno immediatamente che le informazioni sul protocollo del dispositivo erano incomplete e potrebbero voler che il fallback di F32 si attivi.
Implementazione: singola stringa accumulatore costruita incrementalmente attraverso la catena di decisione; registrata una volta alla fine. Vincolata a Settings.LogDebugInfo per evitare rumore nel log in produzione.
F43 - Il log SetNextAVTransport mostra l'URL sorgente
Cosa: le voci di log di QueueNext ora includono source=<percorso libreria MusicBee> accanto a stream=<URL di streaming HTTP>. Stessa modifica applicata al percorso di successo e al percorso di errore (QueueNext:Failed).
Perché: durante il debug di un problema di traccia in coda, l'URL di streaming (/encode/aabbccdd0.flac) è opaco da solo - lo stesso per ogni traccia. L'URL sorgente è il percorso della libreria leggibile dall'uomo che ti dice esattamente quale file MusicBee ha cercato di mettere in coda.
F44 - Migliore registrazione degli errori del tipo mime
Cosa: due nuove voci di log durante Activate:
Activate:MimeUnverified- si attiva per ogni voce malformata nella rispostaGetProtocolInfodel dispositivo, indicando quale voce non è stata analizzata (in modo che l'utente possa vedere ad esempio "il Marantz ha restituitohttp-get:*::*per un codec - la capacità non è verificata, il fallback di F32 indovinerà").Activate:NoSinkInfo- si attiva una volta se il dispositivo non ha restituito alcun elemento<Sink>. Significa cheSupportedMimeTypesrimane Nothing eIsCodecSupporteddegrada a "presumi che tutto funzioni" - contesto utile quando in seguito compaiono errori "dispositivo ha rifiutato lo stream".
Perché: prima di F44 questi fallback silenziosi delle capacità lasciavano gli utenti a indovinare perché le loro tracce venivano transcodificate contro le aspettative o rifiutate dal dispositivo. Ora una singola ricerca per Activate: mostra se le informazioni sulle capacità del dispositivo erano utilizzabili.
F45 - Migliore registrazione degli errori dei metadati
Cosa: il log delle eccezioni Browse in ContentDirectoryService.vb era già stato arricchito in un precedente lavoro yaiol (la sessione di bug di Alia Vox) con ObjectID e stack trace. F45 lo espande ulteriormente con BrowseFlag (metadati vs figli), Filter (quali attributi il client ha richiesto), sortCriteria e partialResultLength (quanti byte di DIDL sono stati prodotti prima del fallimento - indica a che punto del batch si trova la traccia errata).
Perché: quando qualcosa va storto a metà DIDL, il valore partial-length ti dice se il fallimento è stato sulla prima traccia del batch (partial=0) o a metà (partial=N) - combinato con startingIndex del batch, puoi identificare l'indice della traccia offensiva. Filter e BrowseFlag spiegano che tipo di navigazione il client voleva; a volte una navigazione solo metadati fallisce dove una navigazione figli per lo stesso ID ha successo.
Rete
F46 - La modalità automatica pubblicizza solo su schede di rete reali
Cosa: in modalità interfaccia Automatica il plugin si annunciava (SSDP) su ogni adattatore IPv4 operativo. Su una macchina che esegue anche un tunnel VPN (NordLynx) o uno switch virtuale (Hyper-V / WSL / Docker), la stessa libreria veniva annunciata anche su ciascuno di questi adattatori, quindi il punto di controllo da cui si trasmetteva scopriva il server due o tre volte e elencava la libreria come copie duplicate. La modalità automatica ora mantiene solo gli adattatori che hanno un gateway predefinito IPv4 reale (HasIPv4Gateway) - che gli adattatori tunnel e switch virtuali non hanno - quindi questi vengono rimossi dall'elenco degli annunci. Un indirizzo bloccato dall'utente vince comunque (annuncia solo su quell'interfaccia), e se nessun adattatore riporta un gateway il selettore torna a ogni adattatore, quindi l'elenco degli indirizzi pubblicizzati non è mai vuoto e il plugin non può diventare invisibile.
Perché: il duplicato non è causato da "essere su una VPN" - è causato dall'annuncio sull'adattatore LAN e sull'adattatore tunnel/virtuale contemporaneamente, quindi un punto di controllo vede lo stesso server a due indirizzi. Una VPN consumer (NordVPN/NordLynx) instrada solo il traffico internet; il renderer DLNA vive sulla LAN e il traffico della sottorete locale bypassa il tunnel, quindi l'adattatore tunnel non raggiunge comunque un renderer - rimuoverlo elimina una copia fantasma, mai un percorso funzionante. Il test del gateway è il segnale economico e affidabile che separa un vero adattatore LAN/Wi-Fi da un tunnel o uno switch virtuale. Completa N05 (che ha corretto come vengono inviati gli annunci su tali collegamenti - multicast invece di broadcast); F46 governa quali adattatori vengono annunciati.
Limite noto: una VPN mesh / accesso remoto (Tailscale, ZeroTier, WireGuard-to-home) i cui renderer vivono realmente attraverso il tunnel di solito presenta un adattatore senza gateway predefinito, quindi la modalità automatica lo rimuove anch'esso. Questi utenti bloccano invece l'indirizzo VPN, che ha la precedenza sul filtro del gateway.