MusicBee UPnP Plugin Aiuto

Novità

2.0.2 - 2026-07-20

  • Un modello non può più essere eliminato mentre un nodo lo segue: il pulsante di eliminazione rimane semplicemente disabilitato, in modo che un nodo non possa mai rimanere orfano. Ogni riga del modello ora mostra un conteggio live (n) dei suoi follower, il che spiega a colpo d'occhio un'eliminazione disabilitata; l'eliminazione di un modello inutilizzato ora rimane, invece che le impostazioni predefinite spedite riappaiano silenziosamente al caricamento successivo.
  • Un nuovo pulsante imbuto accanto all'albero della Vista mostra esattamente quali nodi seguono un modello: filtra l'albero solo ai follower del modello selezionato, rifiltra mentre selezioni altri modelli e ripristina l'albero completo quando disattivato.
  • I nodi Radio e Podcast sono ora permanentemente abbinati al modello della loro categoria: rimodella il modello nella scheda Percorsi e il nodo segue da solo; non c'è nulla da applicare, quindi il pulsante Applica è disabilitato per loro. L'elenco dei modelli riflette questo con due fasce, Standard (dove vengono creati tutti i nuovi modelli) e Riservato (Radio + Podcast).

2.0.1 - 2026-07-20

  • I modelli di percorso sono ora collegati dinamicamente ai nodi che li utilizzano. L'applicazione di un modello fa in modo che il nodo lo segua: modifica il modello in seguito e ogni nodo che lo segue si rimodella immediatamente, senza doverlo cercare e riapplicare nodo per nodo. L'albero della vista mostra quale modello segue ogni nodo subito dopo il suo nome, una ridenominazione appare lì istantaneamente e l'eliminazione di un modello ti dice prima quanti nodi lo seguono (mantengono il loro layout attuale e smettono semplicemente di seguire qualsiasi cosa).
  • L'applicazione di un modello a un nodo nascosto lo rende anche nuovamente visibile: l'applicazione è il gesto "mostrami questo, modellato così", mentre il nascondere rimane con la casella di controllo Visibile. Il modello riservato "Nascosto" che questo sostituisce è sparito.
  • L'albero della vista non perde più la tua posizione: spunte, cartelle espanse e posizione di scorrimento sopravvivono all'applicazione di modelli e altri aggiornamenti.

2.0.0 - 2026-06-16

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 funzionalità in modo 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 tuoi computer: puoi dare al renderer il nome che preferisci (inizialmente è "MusicBee (yaiol)"). Questo nome è quello che appare nell'elenco dei dispositivi di riproduzione del tuo telefono, quindi quando più di un PC esegue MusicBee puoi distinguere quale sia quale. Una modifica del nome ha effetto immediato, senza riavvio.

Miglior suono possibile quando riproduce su se stesso: quando sfogli la propria libreria di MusicBee dal tuo telefono e invii 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 tuo 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 puoi attivare il renderer lasciando disattivata la condivisione della libreria. In questa configurazione "solo renderer", la tua libreria rimane completamente nascosta dalla rete - viene annunciato solo il target di riproduzione - e MusicBee non 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 di transcodificare 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

Il cambiamento strutturale che rende il fork praticabile 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 dei 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 (LazyBrowseEnsureLazyEndpointInMemory → 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 si blocca più quando la sua porta configurata non è disponibile. Tre modifiche collegate:

  1. Fallback automatico in caso di errore di binding. HttpServer.Start tenta la porta configurata e, in caso di SocketException, cerca fino a 20 porte per trovare la prima libera. La porta effettivamente associata viene registrata in un nuovo Plugin.boundServerPort, e tutto ciò che pubblicizza il server - URL SSDP LOCATION (risposta NOTIFY + M-SEARCH), l'URL del dispositivo (PrimaryHostUrl), il port-forward del router e i filtri automatici SSDP/control-point - ora legge boundServerPort invece di Settings.ServerPort. I client UPnP scoprono la porta reale tramite SSDP, quindi una porta spostata è trasparente per i renderer.
  2. Notifica all'utente. Quando si verifica un fallback (la porta salvata non è quella in uso), una MessageBox localizzata (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 verrebbe visto solo da chi già sospettava un problema.
  3. Ripristino del riavvio. RestartServer (il percorso di riavvio del salvataggio delle impostazioni) dereferenziava Plugin.controller / Plugin.server ciecamente. Se l'Initialise iniziale generava un'eccezione prima di crearli (esattamente ciò che causava un binding fallito), il successivo salvataggio delle impostazioni generava un NullReferenceException - lasciando un plugin semi-morto. Ora li ricrea e li avvia quando Nothing, quindi il salvataggio di una porta funzionante rianima il plugin senza un riavvio completo di MusicBee.

Perché: il trigger è stato un incidente reale dell'utente. 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 la porta predefinita con una porta libera entrava quindi in conflitto con Serviio (un server DLNA separato già sulla nuova porta), fallendo con WSAEADDRINUSE. Ogni fallimento veniva inghiottito in Initialise, lasciando il plugin silenziosamente morto e quindi NRE-ing al successivo salvataggio delle impostazioni. Dopo N04 una collisione di porte si auto-ripara - il server continua a funzionare sulla porta libera successiva, l'utente viene informato e i client lo riscoprono - invece di bloccare l'intero plugin.

Implementazione:

  • La porta predefinita è stata spostata da 49382 a 9779 (sotto l'intervallo dinamico, quindi Windows non la riserva mai automaticamente; non è una porta predefinita nota per i server multimediali) in tutte e tre le dichiarazioni di ServerPort + il fallback in caso di errore di analisi delle impostazioni.
  • Plugin.boundServerPort (nuovo campo condiviso) contiene la porta di ascolto attiva; activeServerPort rimane lo snapshot configurato 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 primo TcpListener.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 / punto-punto)

Cosa: gli annunci SSDP vengono inviati al gruppo multicast UPnP (239.255.255.250) invece di un indirizzo di broadcast IP. 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 punto-punto / 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. Sono nati dalla navigazione effettiva dell'output del plugin da parte di client UPnP reali.

N06 - Esposizione della libreria basata su filtro

Cosa: le schede filtro di MusicBee (file .xautopf nella cartella MusicBee dell'utente) 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 ora appare 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 - Copertina del contenitore dell'album (upnp:albumArtURI)

Cosa: i nodi del contenitore dell'album nelle risposte di 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 in modo 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 Browse 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 su Browse paginato

Cosa: la navigazione per il contenitore Radio rientrava nel ramo generico dell'elenco di file, che chiamava files.Sort(AlbumFileComparer) ad ogni chiamata. Le voci Radio hanno tag Album/Disc/Track 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 ricerca UPnP di classe album 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 degli 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 dello scope 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, con ambito 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 di UPnP ContentDirectory - quindi i client conformi alle specifiche (BubbleUPnP) trattavano la libreria come mai mutevole: risultati di navigazione obsoleti, miniature 404 dopo un cambio di 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 / ResetCacheBumpSystemUpdateId).

Perché: i client rilevano in modo affidabile le modifiche, i nuovi file e le 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 - Copertina 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 delle copertine di MusicBee (%LocalAppData%\MusicBee\InternalCache\Subscriptions\<name>.jpg, da cui carica l'interfaccia utente desktop), e il livello HTTP di unescape+lowercase rovinava la chiave di routing dell'URL del feed fino all'ultimo segmento del percorso. Questo fork risolve le copertine dalla InternalCache di MB e instrada le ricerche tramite uno slug URL-safe che sopravvive intatto al livello HTTP (PodcastSlug / podcastSubIdBySlug).

Perché: le copertine degli abbonamenti ora vengono renderizzate nelle visualizzazioni di navigazione (tutte le 22 richieste che in precedenza 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 generi, gerarchie di stati d'animo, "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 un capo all'altro.


N19 - Singolo percorso radice 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 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 / Artista Album Ordinato" e "Genere / Persone Podcast" - si uniscono in un'unica cartella radice Genere che elenca prima i valori del 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. L'unione le mantiene la radice leggibile e raggruppa 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 a 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 di 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 a risultato singolo

Cosa: un livello di raggruppamento che si risolve in un singolo valore - un livello Tipo di Record 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 data completa con un semplice valore a quattro cifre, e l'alias del campo anno hardcoded è sparito, quindi ogni campo di raggruppamento ora si risolve 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 restituivano 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 completo) 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 ha perso quella distinzione. Esporre entrambi consente all'utente di raggruppare ogni uscita 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 nella radice

Cosa: un filtro bloccato ora appare direttamente sotto la cartella Filtri nella radice di navigazione, e una playlist bloccata direttamente sotto la cartella Playlist, invece che tutti gli elementi bloccati si raccolgano in un unico gruppo alla fine della radice. Ogni scorciatoia bloccata sta con i suoi simili.

Perché: man mano che blocchi più scorciatoie, un unico gruppo finale di filtri e playlist mescolati 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 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 sezionatura raggruppa le opzioni correlate e fa sì che la finestra di dialogo assomigli di più alle impostazioni delle app moderne.


Ridenominazione dell'assembly + plugin (nessun F-id - nota di 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 di WaitOnSendBarrier quando 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 dallo snapshot 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 deve sapere che qualcosa è successo e lo mostrano la prossima volta che apre il plugin - scopribile 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 profilo generico).
  • Attivato il backoff NextURI (F13 - riproduzione senza interruzioni disabilitata 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 (attualmente: y=410 impilate orizzontalmente).
  • Ogni badge ha un flag di sessione persistente corrispondente in Plugin che 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 "impostazione salvata richiede riavvio", acquisire uno snapshot di runtime in Plugin.Initialise() e confrontarlo con Settings.* dopo Settings.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 applicate silenziosamente, 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 per 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" - renderizza l'ampersand letteralmente nel menu del selettore di campo invece di inghiottirlo come prefisso mnemonico Alt.

Perché: i nomi dei campi con una e commerciale 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 è fisso sulla 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'unica passata 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 offerto da MusicBee stesso, 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 dell'area di lavoro): PT e ZH sono suddivisi in bundle distinti perché il vocabolario/script diverge realmente (pt-BR/pt-PT, zh-CN/zh-TW). EN è un singolo bundle - "English(US)" (en-US) di MusicBee 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 grezzo", "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 di aiuto si apre nella lingua completa dell'interfaccia

Cosa: l'apertura del link di aiuto 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 eseguiva 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ò gestirlo. 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 grezzo scelto dall'utente, byte per byte (a parte l'incapsulamento 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 vanifica lo scopo. Predefinita 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 scapito dell'altro dispositivo. Con il per profilo ogni dispositivo ottiene la risposta giusta.

Implementazione:

  • StreamingProfile.ForceTranscoding As Boolean = False.
  • Lo schema di persistenza è stato 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 esclusione reciproca bidirezionali (CheckedChanged su ciascuno annulla l'iscrizione dell'altro prima di capovolgere, per evitare un ciclo infinito).
  • Sito di decisione: Settings.ForceTranscodingstreamingProfile.ForceTranscoding in WriteAudioFileDIDL.

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 ricevono 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 di prova ed errore.


F06 - "Non usare PCM grezzo" per profilo

Cosa: quando il dispositivo dichiara di supportare il PCM grezzo, il plugin lo utilizza. Alcuni dispositivi mentono: accettano l'handshake SOAP ma distorcono i dati PCM grezzi effettivi, mentre gestiscono correttamente il PCM incapsulato in un contenitore WAVE. F06 forza il PCM-over-Wave indipendentemente da ciò che il dispositivo pubblicizza.

Perché: alcuni modelli Marantz in particolare: pubblicizzano PCM grezzo ma solo WAVE funziona. Senza questo, i flussi PCM grezzi risultano distorti, senza alcun messaggio di errore che lo indichi.


F07 - "Lunghezza del 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 grezzo; 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 richiedono un numero esatto, alcuni lo odiano sui flussi, alcuni richiedono un valore sentinella "molto grande" per mantenere il buffering. Questo è stato successivamente ampliato da solo PCM a tutti i formati di output perché gli stessi problemi si sono presentati 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 possessori 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 riescono a 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.FlacSelectedIndex = 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 senza interruzioni. 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 del "flusso continuo" (che concatena tutto in un unico lungo flusso 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 silenzio di mezzo secondo 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 per la traccia in coda. Compromesso: gli effetti ReplayGain/DSP/EQ non si applicano alla traccia successiva. Accettabile quando "forza stream nativo" è attivo (l'impostazione predefinita).


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 a metà, 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 senza interruzioni. 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 cancella 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 tutti), quindi tutte le varianti confluiscono nello stesso confronto.

Implementazione:

  • Nuovo campo nextPlaySourceUrl memorizza 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() su MediaRendererDevice. Tre risultati: nessun NextURI in coda → no-op; in coda corrisponde al nuovo "successivo" → no-op; in coda differisce → chiama QueueNext con il nuovo URL (o QueueNext("") per cancellare - che rispetta F08 DoNotClearNextUri).
  • Cablato in Plugin.ReceiveNotification sotto NotificationType.NowPlayingListChanged.

F13 - Backoff in caso di errore di NextURI

Cosa: dopo 4 fallimenti consecutivi di SetNextAVTransportURI sullo stesso dispositivo, il plugin disabilita la riproduzione senza interruzioni 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 a riprovare ad ogni traccia. F13 interrompe il rumore e torna silenziosamente alla riproduzione di una traccia alla volta.


F14 - Modalità di ripetizione + integrazione 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.QueueNext stesso. Non è necessaria alcuna logica speciale del plugin - il dispositivo vi transita e il rilevatore F15 chiama Player_PlayNextTrack come 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 vi transita (nuovo handle di stream, stessa sorgente). Il rilevatore F15 ora interroga Player_GetRepeat() - se è RepeatMode.One, salta la chiamata a Player_PlayNextTrack in 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 senza interruzioni 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 senza interruzioni 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 passa internamente 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 delle riproduzioni / l'interfaccia utente / lo 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 senza interruzioni).

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 passano 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 sulla transizione senza interruzioni

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 riagganciarsi 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. Questo 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 cablerebbe tramite l'encoder. Questo è un cambiamento architettonico più grande che vale la pena fare se un dispositivo reale mostra il pop dopo che ForceTranscoding non è sufficiente.

Implementazione oggi:

  • Il campo lastSourceUrl tiene traccia dell'URL sorgente attualmente in riproduzione.
  • QueueNext legge FilePropertyType.SampleRate/Channels/Kind sia per le tracce corrente che per quelle in coda e registra NextUri:FormatChange in caso di mancata corrispondenza.

F17 - Risincronizzazione della barra di avanzamento dopo la ricerca

Cosa: la funzione Seek() chiamava già GetPlayPositionInformation() dopo un SOAP di Seek riuscito, il che risolve il caso di "nessuna risincronizzazione". F17 chiude il restante scostamento fino a 1s causato dalla quantizzazione RelTime di 1 secondo di UPnP: quando la posizione riportata dal dispositivo si arrotonda 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 in un punto diverso da quello 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 circa 500ms di ritardo 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:

  1. Runtime: QueueNext restituisce False in anticipo quando Settings.ContinuousOutput è attivo. Lo stream continuo è il suo meccanismo senza interruzioni (un lungo stream concatenato); l'invio di SetNextAVTransportURI in aggiunta confonde il dispositivo sul fatto che ogni traccia sia un URI discreto o parte del flusso continuo.
  2. UI: quando l'utente seleziona la casella di controllo globale dello stream continuo, forceNativeStream del 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 contemporaneamente due meccanismi senza interruzioni in conflitto. 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 - MP3 mime → 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- 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 nel mondo 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 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 di 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 del 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 del formato dell'ora dei 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 del file sorgente + percorso dello 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 sbagliata (ad esempio 17:42 → "05:42") sui dispositivi che mostrano il campo. Ora usa HH.


F29 - Supporto alla ricerca MP3 codificata (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 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 vedevano la ricerca ignorata silenziosamente o venivano riportati all'inizio della traccia.

Implementazione: ristrutturato GetEncodeFeature in ItemManager.vb 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. Nessun cambiamento per AAC/FLAC/ecc. - questi richiederebbero una verifica specifica del codec della CBR-ness che MusicBee non garantisce.


F30 - Estensione file .mpeg gestita

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é: i vecchi archivi MPEG-1 Layer 3 a volte usavano .mpeg invece di .mp3 (la specifica consente entrambi). Una manciata di file in una libreria da 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 limiti di traccia, lunghezza fissa, né possibilità di ricerca. Trattarli come file discreti nel DIDL causava al plugin di pubblicizzare 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 guidando la riproduzione (musicBeePlayToMode). Il percorso di recupero della libreria (navigazione del client UPnP) rimane invariato - gli URL radio sono rari lì 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 parsare 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 di capacità incompleti o illeggibili ma in realtà gestiscono bene il codec. F32 scambia un piccolo "tentativo 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 in base all'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 sarebbe partita da 0, quindi sarebbe saltata in avanti quando la realtà si fosse allineata.

Correzione F33: quando si passa per la prima volta a Riproduzione 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 del progresso più fluida e precisa, 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 mostrava brevemente la fine della traccia precedente, quindi tornava a 0, quindi saliva. 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 (avanti manuale o transizione senza interruzioni). 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 alcune combinazioni. Dopo la rielaborazione per profilo di F04, due lacune specifiche sono state sigillate:

  1. 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 che si è caricato in modo incoerente dal disco.
  2. Logica bypassTranscodeDecision. In precedenza: streamingProfile.ForceNativeStream AndAlso Not Settings.ForceTranscoding. Ora: streamingProfile.ForceNativeStream AndAlso Not streamingProfile.ForceTranscoding - stessa regola di precedenza ma allo 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 alla pompa 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 intorno alle loro chiamate SOAP, ma un caso di temporizzazione sufficientemente strano (ad esempio, il renderer che si blocca tra due chiamate SOAP nello stesso gestore di notifiche) poteva comunque sfuggire. Il wrapper di alto livello è la rete di sicurezza finale 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 (intorno 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 di arresto utente già in atto.


F38 - Migliorata la gestione della ricerca per codec soggetti a crash

Cosa: il crash di BubbleUPnP sulla 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 problematici dispositivi Platinum). Testato dall'utente su BubbleUPnP 4.6.4 attuale: nessun crash osservato.

Perché: il crash di BubbleUPnP sulla ricerca MP3 è stato segnalato intorno al 2024 e l'app ha avuto 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. La nostra refactorizzazione della finestra di dialogo sezionata fa già questo - 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 già non essere un'imperfezione qui.


F40 - Connessioni massime più grandi + log di avviso

Cosa: il limite di stream concorrenti del plugin (SemaphoreSlim intorno a Sockets_Stream_File / Sockets_Encoder_Start) era un valore fisso 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 delle copertine, 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) in MusicBeeUpnp.vb; entrambi i siti di chiamata (MediaServerDevice.GetFile, Encoder.StartEncode) lo usano.
  • Settings.MaxConnections persistito nella v8 dello schema delle impostazioni.
  • Plugin.MaxConnectionsHit è un flag di sessione persistente impostato all'interno di WaitOnSendBarrier; si resetta solo al riavvio di MusicBee.
  • SettingsDialog.maxConnectionsBadge è un'etichetta rossa in grassetto a (16, 410) che viene visualizzata solo quando Plugin.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 (indicato 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 su dispositivo che indica "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(globale), 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 in modo nativo. Una riga di log per traccia indica 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 QueueNext ora includono source=<percorso libreria MusicBee> insieme a stream=<URL di streaming HTTP>. Stessa modifica applicata al percorso di successo e al percorso di fallimento (QueueNext:Failed).

Perché: durante il debug di un problema di traccia in coda, l'URL di streaming (/encode/aabbccdd0.flac) è opaco di per sé - lo stesso per ogni traccia. L'URL sorgente è il percorso della libreria leggibile dall'uomo che indica 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 risposta GetProtocolInfo del dispositivo, indicando quale voce non è stata parsata (in modo che l'utente possa vedere ad esempio "il Marantz ha restituito http-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 che SupportedMimeTypes rimane Nothing e IsCodecSupported degrada a "assumi che tutto funzioni" - contesto utile quando in seguito compaiono errori "dispositivo ha rifiutato lo stream".

Perché: prima di F44 questi silenti fallimenti 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 quanto avanti nel batch si trova la traccia errata).

Perché: quando qualcosa va storto a metà DIDL, il valore partial-length indica se il fallimento è avvenuto sulla prima traccia del batch (partial=0) o a metà (partial=N) - combinato con startingIndex del batch, è possibile identificare l'indice della traccia offensiva. Filter e BrowseFlag spiegano che tipo di navigazione il client desiderava; a volte una navigazione solo metadati fallisce dove una navigazione figli per lo stesso ID ha successo.


Rete

F46 - La modalità automatica si annuncia solo sugli adattatori di rete reale

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 quegli adattatori, quindi il punto di controllo da cui trasmetti scopriva il server due o tre volte ed elencava la libreria come copie duplicate. La modalità automatica ora mantiene solo gli adattatori che hanno un vero gateway IPv4 predefinito (HasIPv4Gateway) - che gli adattatori di tunnel e switch virtuali non hanno - quindi quelli vengono esclusi dall'elenco degli annunci. Un indirizzo bloccato dall'utente vince comunque sempre (annuncio solo su quell'interfaccia), e se nessun adattatore segnala un gateway il selettore ricade su tutti gli adattatori, così l'elenco degli indirizzi annunciati non è mai vuoto e il plugin non può diventare invisibile.

Perché: il duplicato non è causato dall'"essere su una VPN" - è causato dall'annunciarsi contemporaneamente sull'adattatore LAN e sull'adattatore tunnel/virtuale, così un punto di controllo vede lo stesso server a due indirizzi. Una VPN consumer (NordVPN/NordLynx) fa passare nel tunnel solo il traffico diretto a internet; il renderer DLNA vive sulla LAN e il traffico della sottorete locale bypassa il tunnel, quindi l'adattatore del tunnel non raggiunge comunque mai 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 da uno switch virtuale. Complementa N05 (che ha corretto come vengono inviati gli annunci su tali collegamenti - multicast invece di broadcast); F46 governa su quali adattatori si annuncia in primo luogo.

Limite noto: una VPN mesh / di accesso remoto (Tailscale, ZeroTier, WireGuard verso casa) i cui renderer vivono davvero oltre il tunnel di solito presenta un adattatore senza gateway predefinito, quindi la modalità automatica scarta anche quello. Quegli utenti bloccano invece l'indirizzo VPN, che ha la precedenza sul filtro del gateway.