Nouveautés
2.0.2 - 2026-07-20
- Un modèle ne peut plus être supprimé tant qu'un nœud le suit — le bouton de suppression reste simplement désactivé, de sorte qu'un nœud ne peut jamais être orphelin. Chaque ligne de modèle affiche désormais un compte (n) en direct de ses suiveurs, ce qui explique une suppression désactivée en un coup d'œil ; la suppression d'un modèle inutilisé persiste également maintenant, au lieu que les valeurs par défaut livrées ne réapparaissent discrètement au prochain chargement.
- Un nouveau bouton entonnoir à côté de l'arborescence de la vue montre exactement quels nœuds suivent un modèle : il filtre l'arborescence pour n'afficher que les suiveurs du modèle sélectionné, re-filtre lorsque vous sélectionnez d'autres modèles, et restaure l'arborescence complète lorsqu'il est désactivé.
- Les nœuds Radio et Podcasts sont maintenant jumelés en permanence avec le modèle de leur catégorie — modifiez le modèle sur l'onglet Chemins et le nœud suit de lui-même ; il n'y a rien à appliquer, donc le bouton Appliquer est désactivé pour eux. La liste des modèles reflète cela avec deux sections, Standard (où tous les nouveaux modèles sont créés) et Réservée (Radio + Podcasts).
2.0.1 - 2026-07-20
- Les modèles de chemin sont désormais liés en direct aux nœuds qui les utilisent. L'application d'un modèle fait en sorte que le nœud le suive : modifiez le modèle plus tard et chaque nœud qui le suit se remodèle immédiatement — plus besoin de le rechercher et de le réappliquer nœud par nœud. L'arborescence de la vue indique quel modèle chaque nœud suit juste après son nom, un renommage y apparaît instantanément, et la suppression d'un modèle vous indique d'abord combien de nœuds le suivent (ils conservent leur disposition actuelle et cessent simplement de suivre quoi que ce soit).
- L'application d'un modèle à un nœud caché le rend également visible à nouveau — l'application est le geste "montre-moi ceci, mis en forme comme cela", tandis que le masquage reste avec la case à cocher Visible. Le modèle réservé "Hidden" qu'il remplace a disparu.
- L'arborescence de la vue ne perd plus votre position : les coches, les dossiers développés et la position de défilement survivent tous à l'application de modèles et à d'autres actualisations.
2.0.0 - 2026-06-16
Voici la première version publique du fork open-source yaiol du plugin UPnP de MusicBee. Elle est présentée en deux parties : tout ce qui est nouveau dans ce fork, puis les corrections et améliorations apportées au plugin original. Chaque élément conserve la forme Quoi / Pourquoi du catalogue de fonctionnalités interne du projet, de sorte que la raison de chaque changement est sur la page, et pas seulement le changement.
Nouveautés de ce fork
MediaRenderer - lecture vers MusicBee
N01 - MusicBee en tant que rendu de lecture
Quoi : normalement, ce plugin fonctionne dans un seul sens : un téléphone ou un autre appareil parcourt la bibliothèque de MusicBee et lit la musique sur lui-même. Cette fonctionnalité ajoute la direction opposée - elle permet à MusicBee d'être le lecteur. À partir d'une application de contrôle sur votre téléphone (comme BubbleUPnP), vous pouvez choisir votre MusicBee de bureau comme appareil de lecture, puis le contrôler depuis votre main : lecture, pause, arrêt, avance ou retour rapide, saut à un point de la piste, et modification du volume ou mise en sourdine.
Pourquoi : cela transforme votre téléphone en une télécommande pour la musique déjà présente sur votre PC. Asseyez-vous sur le canapé, parcourez votre bibliothèque sur le téléphone, appuyez sur une piste, et elle sortira des haut-parleurs connectés à votre bureau - avec un contrôle total depuis l'endroit où vous êtes assis. Le plugin original n'a jamais livré cette fonctionnalité comme étant fonctionnelle.
Activation : elle est désactivée par défaut, car l'activer permet à n'importe quel appareil de votre réseau domestique de lancer la lecture sur votre PC. Vous l'activez en cochant une case dans l'onglet Général de la boîte de dialogue des paramètres. Les trois rôles du plugin ont chacun leur propre case à cocher - partager ma bibliothèque (Server), permettre aux autres de lire vers moi (Rendu), et lire vers d'autres appareils (Point de contrôle) - et la boîte de dialogue n'affiche que les onglets de paramètres dont les rôles que vous avez activés ont réellement besoin, de sorte que vous n'êtes jamais confronté à des options qui ne vous concernent pas.
Distinguer vos machines : vous pouvez donner au rendu le nom que vous voulez (il commence par "MusicBee (yaiol)"). Ce nom est ce qui apparaît dans la liste des cibles de lecture de votre téléphone, donc lorsque plusieurs PC exécutent MusicBee, vous pouvez distinguer lequel est lequel. Un changement de nom prend effet immédiatement, sans redémarrage.
Meilleur son possible lors de la lecture vers soi-même : lorsque vous parcourez la propre bibliothèque de MusicBee depuis votre téléphone et que vous renvoyez une piste vers ce même MusicBee, le plugin reconnaît qu'on lui demande de lire l'un de ses propres fichiers et le lit simplement directement depuis votre disque. Le résultat est exact et instantané - bit-perfect, avec l'égaliseur et le nivellement du volume de MusicBee appliqués - au lieu de pousser inutilement l'audio sur le réseau et de le renvoyer directement à lui-même.
Exécution en privé : les trois rôles fonctionnent indépendamment, vous pouvez donc activer le rendu tout en laissant le partage de bibliothèque désactivé. Dans cette configuration "rendu uniquement", votre bibliothèque reste complètement cachée du réseau - seule la cible de lecture est annoncée - et MusicBee ne proposera jamais de lire vers lui-même.
Comportement de lecture
N02 - Le FLAC 5.1 n'est pas automatiquement downmixé
Quoi : la limitation du nombre de canaux dans MediaServerDevice.GetEncodedFile était If StereoOnly OrElse Not isPcmData Then channelCount = 2. La clause Not isPcmData downmixait silencieusement chaque transcodage non-PCM (FLAC, MP3, AAC, Ogg) en stéréo, quel que soit le nombre de canaux source, rendant les rendus compatibles 5.1 inefficaces lorsque la source était du FLAC 5.1. Désormais, la deuxième clause exclut le FLAC : Not isPcmData AndAlso encoder.Codec <> FileCodec.Flac. Le FLAC 5.1 passe ; le MP3/AAC/Ogg force toujours la stéréo car les encodeurs en ligne de commande de MusicBee pour ces formats attendent une entrée à 2 canaux.
Pourquoi : l'intérêt de transcoder une source FLAC 5.1 en sortie FLAC est de préserver le mixage multicanal. Le downmix silencieux rendait l'option de transcodage FLAC inutile pour l'écoute surround. Avec N02, cela fonctionne correctement.
Architecture
Le changement structurel qui rend le fork viable sur une grande bibliothèque - absent du plugin original.
N03 - Arborescence de navigation paresseuse (à la demande)
Quoi : le plugin original construisait l'intégralité de l'arborescence de navigation au démarrage de MusicBee - énumérant chaque piste, Library_GetFileTags complet par fichier, assemblant toute la hiérarchie des conteneurs - avant d'ouvrir le port HTTP. Sur une vraie bibliothèque (plus de 50 000 pistes, 5 400 épisodes de podcasts, des centaines de stations), cela représente des minutes de démarrage à froid, et l'arborescence reste en RAM pour toujours, y compris les branches qu'aucun client n'ouvre jamais. Ce fork ne construit rien à l'avance : la racine expose un espace réservé préfixé par L: par point de terminaison (L:music, L:podcast, L:filter:…) ; chaque niveau n'est calculé que lorsqu'un client y navigue (LazyBrowse → EnsureLazyEndpointInMemory → caches par niveau), et les notifications de changement de bibliothèque effacent les caches (SetLibraryDirty).
Pourquoi : le démarrage à froid est essentiellement instantané - le port HTTP est ouvert au moment où MusicBee termine l'initialisation du plugin - et la mémoire reste proportionnelle à ce qui a été parcouru, et non à la taille de la bibliothèque. Compromis : la première navigation dans un point de terminaison paie son coût de chargement ; la réentrée est mise en cache jusqu'au prochain changement de bibliothèque. C'est la base dont tout le reste dépend. Notes complètes : FIXES.md.
Réseau et robustesse
Renforcement du chemin de liaison du server HTTP. Le plugin original meurt silencieusement lorsque son port est indisponible.
N04 - Liaison de port HTTP auto-réparatrice
Quoi : le server HTTP du plugin ne meurt plus lorsque son port configuré est indisponible. Trois changements liés :
- Restauration automatique en cas d'échec de liaison.
HttpServer.Startessaie le port configuré, et en cas deSocketException, scanne jusqu'à 20 ports vers le haut pour trouver le premier disponible. Le port réellement lié est enregistré dans un nouveauPlugin.boundServerPort, et tout ce qui annonce le server - les URLLOCATIONSSDP (NOTIFY + réponse M-SEARCH), l'URL de l'appareil (PrimaryHostUrl), le transfert de port du routeur, et les auto-filtres SSDP/point de contrôle - lit désormaisboundServerPortau lieu deSettings.ServerPort. Les clients UPnP découvrent le vrai port via SSDP, donc un port déplacé est transparent pour les rendus. - Notification de l'utilisateur. Lorsqu'une restauration se produit (le port enregistré n'est pas celui utilisé), une
MessageBoxlocalisée (WarnPortInUse) indique à l'utilisateur quel port est réellement utilisé et que les appareils le trouveront toujours - car le plugin s'exécute sans interface graphique et un message dans la boîte de dialogue ne serait vu que par quelqu'un qui soupçonnait déjà un problème. - Récupération après redémarrage.
RestartServer(le chemin de redémarrage de la sauvegarde des paramètres) déréférençaitPlugin.controller/Plugin.serveraveuglément. Si l'Initialiseinitial levait une exception avant de les créer (exactement ce qu'un échec de liaison provoquait), la prochaine sauvegarde des paramètres entraînait uneNullReferenceException- laissant un plugin à moitié mort. Il les recrée et les démarre désormais lorsqueNothing, de sorte que la sauvegarde d'un port fonctionnel ravive le plugin sans un redémarrage complet de MusicBee.
Pourquoi : le déclencheur était un incident utilisateur réel. L'ancien port par défaut 49382 se trouve dans la plage dynamique de Windows (49152-65535), où Hyper-V/WSL2/Docker/WinNAT réservent de grands blocs qui se déplacent à chaque démarrage - la liaison a donc échoué avec WSAEACCES ("accès interdit") sur une machine où elle avait fonctionné pendant des mois. Changer le port par défaut pour un port libre est ensuite entré en collision avec Serviio (un server DLNA distinct déjà sur le nouveau port), échouant avec WSAEADDRINUSE. Chaque échec était ignoré dans Initialise, laissant le plugin silencieusement mort puis provoquant une NRE lors de la prochaine sauvegarde des paramètres. Après N04, une collision de port s'auto-répare - le server continue de fonctionner sur le prochain port libre, l'utilisateur est informé, et les clients le redécouvrent - au lieu de faire tomber tout le plugin.
Implémentation :
- Le port par défaut est passé de
49382à9779(en dessous de la plage dynamique, donc Windows ne le réserve jamais automatiquement ; ce n'est pas un défaut de server multimédia connu) dans les trois déclarationsServerPort+ la restauration en cas d'échec d'analyse des paramètres. Plugin.boundServerPort(nouveau champ partagé) contient le port d'écoute actif ;activeServerPortreste l'instantané configuré afin que la logique du badge "Redémarrage requis" ne se déclenche pas faussement en cas de restauration.HttpServer.PortScanRange = 20; le scan s'arrête au premierTcpListener.Start()réussi et ne lève la dernière exception que si toutes les tentatives échouent.- Nouvelle clé de ressource EN
WarnPortInUse(les traductions suivent le passage de locale au moment de la publication).
N05 - Annonces SSDP via le groupe multicast (VPN / point à point)
Quoi : les annonces SSDP sont envoyées au groupe multicast UPnP (239.255.255.250) au lieu d'une adresse de diffusion IP. L'erreur inoffensive "cannot access a disposed object" (impossible d'accéder à un objet supprimé) enregistrée lorsqu'une réponse de recherche SSDP entre en concurrence avec un redémarrage du server est également supprimée.
Pourquoi : sur les adaptateurs réseau point à point / VPN, la diffusion IP ne s'applique pas - l'ancienne diffusion a échoué avec "argument invalide" et les annonces ont été manquées, de sorte que le plugin était invisible pour les clients sur ces liens. L'annonce au groupe multicast approprié corrige la découverte sur exactement ces adaptateurs.
Navigation dans la bibliothèque
Ces fonctionnalités ont été livrées dans ce fork et ne sont pas présentes dans le plugin original. Elles proviennent de la navigation réelle dans la sortie du plugin à partir de vrais clients UPnP.
N06 - Exposition de la bibliothèque basée sur les filtres
Quoi : les onglets de filtre de MusicBee (fichiers .xautopf dans le dossier utilisateur de MusicBee) deviennent des conteneurs racine UPnP dans la bibliothèque du plugin. Les pistes de chaque filtre sont ensuite navigables dans une hiérarchie AlbumArtistSort → Album → Tracks.
Pourquoi : les utilisateurs avec des filtres MusicBee personnalisés (par exemple, "Pistes 5 étoiles", "Récemment ajoutées", "Classique → Baroque") s'attendent à les retrouver lorsqu'ils parcourent le plugin depuis un client UPnP. Le plugin original n'exposait que l'arborescence brute de la bibliothèque.
N07 - Câblage du champ SortAlbumArtist
Quoi : le plugin lit désormais MetaDataType 165 (Sort Album Artist) de MusicBee et l'utilise pour regrouper/trier les artistes dans les vues de navigation.
Pourquoi : les navigateurs hi-fi et les audiophiles utilisent les noms d'artiste de tri ("Beethoven, Ludwig van" au lieu de "Ludwig van Beethoven") pour organiser les bibliothèques. Attente standard pour les auditeurs sérieux. Manquant dans les deux versions amont.
N08 - Gestion des artistes d'album à valeurs multiples
Quoi : lorsqu'un champ AlbumArtist d'un album contient plusieurs artistes séparés par "; " (par exemple, "yaiol; Ars Ricercata"), la piste apparaît désormais sous chaque artiste dans les vues de navigation, et non sous un seul artiste Frankenstein combinant les noms.
Pourquoi : les albums collaboratifs et les compilations doivent apparaître sous chaque collaborateur. Sans cela, la moitié des chemins de recherche pour trouver l'album sont brisés.
N09 - Pochette de conteneur d'album (upnp:albumArtURI)
Quoi : les nœuds de conteneur d'album dans les réponses DIDL Browse incluent désormais un élément upnp:albumArtURI pointant vers la pochette de l'album.
Pourquoi : sans cela, chaque album dans la vue de navigation d'un client UPnP affiche une icône générique au lieu de la pochette de l'album. Indice visuel pour la navigation ; attendu par tous les navigateurs hi-fi modernes.
N10 - Ordre des pistes dans les albums filtrés
Quoi : les pistes à l'intérieur d'un album exposé par filtre sont désormais triées par numéro de disque, puis par numéro de piste.
Pourquoi : ordre d'album standard. Sans tri explicite, les pistes revenaient dans l'ordre dans lequel le filtre les renvoyait - généralement aléatoire.
N11 - Correction de l'arborescence des dossiers de listes de lecture
Quoi : la fonction LoadLibraryPlaylists (initialement par Steven Mayall, ~2014) ne parvenait pas à descendre dans les dossiers de listes de lecture nouvellement créés. La première liste de lecture de chaque dossier, ainsi que les sous-dossiers, se retrouvaient orphelins au niveau racine.
Pourquoi : présent dans le plugin original pendant onze ans. Visible en 30 secondes après l'ouverture de BubbleUPnP et le clic sur Listes de lecture. Corrigé dans yaiol en récursant correctement dans les dossiers nouvellement créés lors de la construction de l'arborescence.
N12 - Nettoyage des caractères de contrôle illégaux pour XML
Quoi : toute piste avec une balise contenant un caractère de contrôle C0 (par exemple, 0x19 d'un mauvais passage d'encodage - UTF-8 → Latin-1 → troncation de 0x99 en 0x19) entraînait l'échec de toute la réponse Browse avec Action Failed une fois que la mauvaise piste entrait dans un lot paginé.
Pourquoi : XML 1.0 interdit la plupart des caractères de contrôle C0, et XmlWriter lève une exception lorsqu'on lui demande d'en écrire. Présent dans le plugin original. Corrigé en supprimant les caractères invalides à chaque point de sortie de Library_GetFileTags via XmlConvert.IsXmlChar.
N13 - Liste radio déterministe sur Browse paginé
Quoi : la navigation pour le conteneur Radio tombait dans la branche générique de la liste de fichiers, qui appelait files.Sort(AlbumFileComparer) à chaque appel. Les entrées radio ont des balises Album/Disque/Piste vides, donc chaque comparaison renvoyait 0 - List(Of T).Sort est instable, produisant un ordre différent à chaque invocation. Les points de contrôle UPnP paginent (BubbleUPnP récupère 0..15 puis 16..fin) ; entre les deux appels, la liste se réorganisait, de sorte que certaines stations apparaissaient sur les deux pages (doublons) et d'autres sur aucune (manquantes) - semblant aléatoires à chaque rafraîchissement.
Pourquoi : présent dans le plugin original (son auteur ne navigue jamais la radio via UPnP). Corrigé ici avec une branche ContainerCategory.Radio dédiée dans Browse, pas de tri par appel ; radioFiles est trié une fois au chargement par Titre (stable). La navigation paginée voit désormais un ordre déterministe ; la page 1 et la page 2 sont disjointes.
N14 - La recherche UPnP de classe album renvoie des conteneurs d'album
Quoi : la recherche UPnP pour les requêtes de classe album (upnp:class = "object.container.album.musicAlbum", par exemple "Albums aléatoires" de BubbleUPnP) renvoyait la liste complète des pistes au lieu des conteneurs d'album, de sorte que le client affichait zéro album. Le gestionnaire original ne traitait que les critères entre parenthèses, puis vidait toutes les pistes quelle que soit la classe demandée.
Pourquoi : corrigé ici - les requêtes de classe album énumèrent désormais les albums distincts (groupés par Artiste de l'album + Album) et émettent chacun comme un conteneur musicAlbum approprié avec la pochette, adressable via l'espace d'ID virtuel Salb<idx> afin que le client puisse explorer un résultat et le lire.
N15 - Recherche UPnP fonctionnelle et sensible au contexte avec clic-traversée
Quoi : l'original n'annonçait aucune capacité de recherche (GetSearchCapabilities renvoyait vide), de sorte que les clients refusaient même d'envoyer une recherche ; et l'ancien backend lisait à partir de musicFiles, qui était en permanence vide à l'ère de l'arborescence paresseuse. Ce fork annonce les propriétés réellement recherchables, implémente la recherche de pistes par titre et d'albums par titre sur la bibliothèque paresseuse (HandleLazySearch), scope la requête à la branche actuelle du client lorsqu'un ID de conteneur réel est envoyé (sinon substitue L:music afin que les recherches de la barre supérieure n'incluent pas le bruit des podcasts/radios/audiolivres), et rend les résultats d'album cliquables via des ID synthétiques Ssrch_alb_* qu'une branche précoce de Browse mappe aux pistes de l'album. (La partie des résultats de classe album en tant que conteneurs est N14.)
Pourquoi : la recherche dans BubbleUPnP est passée de "La bibliothèque ne prend pas en charge la recherche" à la restitution de résultats utiles, ciblés et jouables. Conception complète + approches rejetées : SEARCH.md.
N16 - Invalidation du cache UPnP (SystemUpdateID)
Quoi : l'original renvoyait un SystemUpdateID=0 constant - le contrat d'invalidation du cache ContentDirectory UPnP - de sorte que les clients conformes aux spécifications (BubbleUPnP) traitaient la bibliothèque comme ne changeant jamais : résultats de navigation obsolètes, vignettes 404 après un changement de schéma d'URL, et la danse "redémarrer MusicBee deux fois pour voir les changements". Ce fork initialise SystemUpdateID à partir des secondes d'époque au chargement (de sorte que chaque redémarrage est strictement en avance sur le précédent) et l'incrémente à chaque mutation de bibliothèque et changement de paramètres (SetLibraryDirty / ResetCache → BumpSystemUpdateId).
Pourquoi : les clients récupèrent de manière fiable les modifications, les nouveaux fichiers et les changements de paramètres lors de leur prochaine navigation. Limite connue : les clients abonnés ne reçoivent pas activement la nouvelle valeur via GENA (mis en attente pour un travail futur) ; ils la voient toujours lors de leur prochaine navigation.
N17 - Pochette d'abonnement aux podcasts
Quoi : les vignettes de podcasts n'affichaient aucune image - chaque requête /PodcastThumbnail/ renvoyait une erreur 404. Deux bugs superposés : la chaîne de résolution ne vérifiait jamais le cache d'illustrations réel de MusicBee (%LocalAppData%\MusicBee\InternalCache\Subscriptions\<name>.jpg, d'où l'interface utilisateur de bureau charge), et la couche HTTP d'échappement+minuscule corrompait la clé de routage de l'URL du flux jusqu'à son dernier segment de chemin. Ce fork résout les illustrations à partir du cache interne de MB et achemine les recherches via un slug sécurisé pour les URL qui survit intact à la couche HTTP (PodcastSlug / podcastSubIdBySlug).
Pourquoi : les illustrations d'abonnement s'affichent désormais dans les vues de navigation (les 22 requêtes précédemment en 404 sont résolues).
N18 - Navigation hiérarchique (délimitée) par balises
Quoi : n'importe quel champ peut être marqué comme hiérarchique dans l'onglet Options de la bibliothèque et se voir attribuer un délimiteur à un caractère (un sélecteur de champ + une boîte de délimiteur avec ajout/suppression, persisté dans les paramètres du plugin). Définir Regroupement sur / et une valeur comme Jazz/Cool Jazz navigue alors comme Jazz › Cool Jazz au lieu d'une seule entrée plate. Les pistes balisées exactement à une branche (juste Jazz) obtiennent leur propre nœud [Jazz] afin que rien ne soit caché, une branche avec un seul enfant se réduit d'elle-même, et ; est refusé comme délimiteur car c'est le propre séparateur multi-valeurs de MusicBee.
Pourquoi : les taxonomies de balises profondes qu'un utilisateur a déjà encodées dans un seul champ (arborescences de genres, hiérarchies d'ambiances, "Classique/Baroque/Concerto") naviguent enfin comme l'arborescence que la balise décrit, au lieu d'un mur plat de chaînes séparées par des barres obliques que l'utilisateur doit lire de bout en bout.
N19 - Chemin racine unique étiqueté par son champ de regroupement
Quoi : un chemin de navigation unique à la racine est étiqueté par son champ de regroupement (par exemple, "Genre") plutôt que par son chemin court complet, correspondant à la façon dont les groupes de premier champ fusionnés sont nommés.
Pourquoi : l'arborescence de navigation se lit de manière cohérente - une seule règle de nommage, qu'une entrée racine soit seule ou fusionnée avec des frères et sœurs (N20) - au lieu qu'une entrée racine isolée affiche un chemin interne verbeux tandis que ses voisins fusionnés affichent un nom de champ propre.
N20 - Fusionner les chemins de navigation qui partagent un premier champ
Quoi : deux chemins de navigation qui partagent le même premier champ - "Genre / Artiste de l'album de tri" et "Genre / Personnes de podcast" - se réduisent en un seul dossier racine Genre qui liste d'abord les valeurs de genre, puis se divise en deux vues, au lieu de deux entrées "Genre / ..." presque identiques côte à côte à la racine.
Pourquoi : un utilisateur ayant plusieurs vues connexes imbriquées sous un champ commun voyait la racine encombrée d'entrées de niveau supérieur presque identiques. Les fusionner maintient la racine lisible et regroupe les vues connexes là où elles doivent être - sous leur champ partagé.
N21 - Chemins de navigation typés par catégorie (Standard / Radio / Podcast)
Quoi : chaque chemin de navigation est typé par catégorie - Standard, Radio ou Podcast. La liste des modèles est regroupée en ces trois sections, le sélecteur de champ de chaque modèle n'offre que les champs que les données de cette catégorie peuvent réellement fournir, et un modèle ne peut être appliqué qu'aux nœuds correspondants dans l'arborescence de la vue (les nœuds incompatibles sont grisés et ne peuvent pas être cochés). Les modèles réservés Radio et Podcasts ne peuvent pas être supprimés, de sorte que leur section de catégorie ne disparaît jamais.
Pourquoi : sans la typisation, un utilisateur pourrait construire une mise en page qui apparaîtrait silencieusement vide - une station de radio n'a pas d'"album", un épisode de podcast n'a pas d'"artiste de l'album" - et ne le découvrir qu'en naviguant vers un dossier mort depuis un client UPnP. Restreindre le menu des champs et les cibles d'application aux données réelles de la catégorie rend les mises en page vides impossibles à construire.
N22 - Regrouper les podcasts par année de publication
Quoi : la date de publication de chaque épisode de podcast est lue, de sorte qu'un chemin de navigation de podcast avec un niveau Année regroupe les épisodes par année au lieu de les regrouper sous un seul "Inconnu".
Pourquoi : les grands abonnements aux podcasts deviennent navigables par année comme le reste de la bibliothèque, au lieu que chaque épisode atterrisse dans un seul tas non daté parce que le plugin n'a jamais regardé la date de publication par épisode.
N23 - Réduire les niveaux de regroupement à un seul résultat
Quoi : un niveau de regroupement qui se résout en une seule valeur - un niveau Type d'enregistrement n'affichant que "LP" pour un artiste qui n'a fait que des LP, ou un niveau de lettre avec une seule lettre - est automatiquement ignoré, plongeant l'utilisateur directement dans son contenu.
Pourquoi : naviguer à travers un dossier qui contient exactement un dossier est une pure friction. Réduire le niveau à choix unique supprime le clic inutile sans changer ce que l'utilisateur peut atteindre.
N24 - Regroupement/recherche par année par rapport au champ de date de MusicBee
Quoi : la condition d'année ne recherche plus le champ "Année" de date complète de MusicBee avec une simple valeur à quatre chiffres, et l'aliasing du champ d'année codé en dur a disparu, de sorte que chaque champ de regroupement est désormais résolu génériquement à partir de la définition du chemin.
Pourquoi : pour les bibliothèques dont la balise Année contient une date complète, le regroupement ou la recherche par année ne renvoyait auparavant rien - la requête à quatre chiffres ne correspondait jamais au champ de date complète. La recherche du bon champ permet au regroupement et à la recherche par année de retrouver les pistes.
N25 - Séparer les champs de regroupement "Année" et "Année (aaaa)"
Quoi : les chemins de regroupement et de navigation d'album exposent désormais les deux champs d'année de MusicBee - Année (la balise de date complète) et Année (aaaa) (juste l'année à quatre chiffres) - afin que l'utilisateur puisse choisir l'un ou l'autre lors de la définition d'un regroupement d'album ou d'un chemin de navigation.
Pourquoi : les deux champs signifient des choses différentes dans MusicBee, et les regrouper faisait perdre cette distinction. Les afficher tous les deux permet à un utilisateur de rassembler toutes les sorties d'une année (aaaa) ou de conserver l'ordre exact des dates (balise Année complète), selon son intention.
N32 - Filtres et listes de lecture épinglés regroupés par type à la racine
Quoi : un filtre épinglé apparaît désormais directement sous le dossier Filtres à la racine de navigation, et une liste de lecture épinglée directement sous le dossier Listes de lecture, au lieu que toutes les épingles ne s'accumulent en un seul bloc à la fin de la racine. Chaque raccourci épinglé se trouve avec son propre type.
Pourquoi : à mesure que vous épinglez davantage de raccourcis, un unique bloc final mélangeant filtres et listes de lecture devient plus difficile à parcourir et sépare chaque raccourci du dossier auquel il appartient. Regrouper les éléments épinglés sous leur propre catégorie garde la racine lisible et maintient chaque raccourci à côté des éléments auxquels il appartient.
Boîte de dialogue des paramètres et empaquetage
N26 - Boîte de dialogue des paramètres sectionnée
Quoi : la page Préférences a obtenu une disposition de navigation à gauche avec des sections : Général / Lecture / Bibliothèque / Profils d'appareil / Diagnostics.
Pourquoi : l'original était une longue liste plate de tous les paramètres - bien pour le développeur qui l'a construit, déroutant pour tout le monde. Le sectionnement regroupe les options connexes et donne à la boîte de dialogue l'impression de paramètres d'application modernes.
Renommage de l'assemblage + du plugin (pas d'ID F - note d'empaquetage)
Quoi : la DLL compilée est nommée mb_UPnP_yaiol.dll et le plugin se présente comme "MusicBee UPnP (yaiol)". Distinct de l'original mb_Upnp.dll.
Pourquoi : les utilisateurs peuvent installer yaiol à côté du plugin original et comparer les comportements côte à côte.
Système de badges - affichage de l'état d'exécution (mécanisme derrière F40)
Quoi : un modèle d'interface utilisateur générique pour afficher les conditions d'exécution importantes sous forme de badges colorés visibles dans la boîte de dialogue des paramètres. Instances actuelles :
- ⚠ Conn Max (N04) - se déclenche lorsque la limite de connexions maximales a été atteinte au moins une fois depuis le démarrage de MusicBee. Indicateur de session persistant
Plugin.MaxConnectionsHit. Défini dansWaitOnSendBarrierlorsqu'aucun emplacement n'est libre. - ⚠ Redémarrage Requis - se déclenche lorsqu'un paramètre enregistré nécessite un redémarrage de MusicBee pour prendre effet. Indicateur de session persistant
Plugin.RestartRequired. Défini dans le gestionnaire de sauvegarde de la boîte de dialogue lorsque la nouvelle valeur persistante diffère de l'instantané d'exécution (Plugin.activeMaxConnections,Plugin.activeServerPort,Plugin.activeIpAddress). Les paramètres nécessitant un redémarrage sont limités à ceux qui ne peuvent pas être rechargés à chaud - les paramètres de liaison du server HTTP et le SemaphoreSlim construit une fois à l'initialisation.
Pourquoi : le fichier journal du plugin est utile pour les utilisateurs techniques qui déboguent, mais un utilisateur non technique confronté à "le son de l'appareil est incorrect" ou "la lecture est lente" n'ouvrira jamais Diagnostics → Afficher le journal. Les badges capturent les cas où l'utilisateur doit savoir que quelque chose s'est produit et l'affichent la prochaine fois qu'il ouvre le plugin - découvrable sans rien lire.
Réutilisable pour l'avenir :
- Incompatibilité de profil détectée (l'agent utilisateur de l'appareil n'a jamais correspondu à aucun profil, retour au Générique).
- Retrait de NextURI déclenché (F13 - lecture sans blanc désactivée pour la session sur un appareil instable).
- Échec / partiel de l'analyse de la bibliothèque.
- Connexion du rendu perdue en cours de session.
- Toute autre condition où "s'est produit une fois, l'utilisateur devrait savoir" l'emporte sur "enregistré silencieusement parmi 1000 autres lignes".
Conventions d'implémentation :
- Les étiquettes de badge se trouvent au niveau de la boîte de dialogue (pas à l'intérieur d'un panneau) afin qu'elles soient visibles quelle que soit la section sur laquelle l'utilisateur se trouve.
- Positionnées le long de la rangée inférieure près de Enregistrer/Annuler (actuel : y=410 empilées horizontalement).
- Chaque badge a un indicateur de session persistant correspondant dans
Pluginqui passe à True lorsque la condition se produit et ne se réinitialise qu'au redémarrage de MusicBee. - Ressources :
<Condition>Badge(texte de l'étiquette, préfixé par ⚠) +<Condition>BadgeTip(info-bulle expliquant la cause + le remède). - Pour les badges "paramètre enregistré nécessite un redémarrage", prenez un instantané d'exécution à
Plugin.Initialise()et comparez-le àSettings.*aprèsSettings.SaveSettings()dans le gestionnaire de sauvegarde de la boîte de dialogue.
N27 - Annuler annule les modifications de chemin/modèle
Quoi : les modifications apportées aux chemins et aux modèles dans la boîte de dialogue des paramètres sont désormais annulées lorsque l'utilisateur clique sur Annuler, au lieu de rester appliquées silencieusement, et tout modèle réservé supprimé pendant la session est recréé. (Les modèles sont autrement enregistrés en direct au fur et à mesure qu'ils sont modifiés - il n'y a pas de bouton Enregistrer séparé dans l'onglet Chemins.)
Pourquoi : Annuler devrait signifier annuler. Auparavant, un utilisateur qui expérimentait des changements de chemin/modèle et revenait en arrière constatait que les changements étaient déjà validés, sans aucun moyen de les annuler autrement qu'en les refaisant à la main.
N28 - Ampersands littéraux dans le menu du sélecteur de champ
Quoi : un champ dont le nom contient "&" - par exemple "Mood & Context" - affiche l'esperluette littéralement dans le menu du sélecteur de champ au lieu de l'avaler comme préfixe de raccourci Alt.
Pourquoi : les noms de champ avec une esperluette s'affichaient mal (le caractère disparaissait et la lettre suivante devenait un accélérateur), rendant l'entrée de menu difficile à reconnaître.
N29 - Titre de la fenêtre de paramètres stable et non traduit
Quoi : le titre de la fenêtre des paramètres est fixé à la chaîne de marque "MusicBee UPnP Plugin" et ne varie plus avec la langue de l'interface ; la chaîne DialogTitle par langue a été supprimée de chaque bundle de locale.
Pourquoi : un titre de fenêtre qui changeait de formulation par langue était une surface traduisible sans avantage - le titre est une marque. Le fixer le maintient stable et cohérent partout.
Localisation
Le plugin original est uniquement en anglais. Ce fork est entièrement localisable - chaque chaîne visible par l'utilisateur passe par un bundle de ressources, et le plugin détecte automatiquement la langue de l'interface utilisateur de MusicBee.
N30 - Interface utilisateur multilingue (traductions en attente)
Quoi : le mécanisme de localisation est complet et livré. Localisation.vb lit la langue sélectionnée de MusicBee à partir de MusicBee3Settings.ini (endonyme <SystemLanguage>) et applique la culture .NET correspondante au thread, de sorte que My.Resources.Resources.* renvoie la chaîne localisée. Chaque étiquette/bouton/message visible par l'utilisateur est lié à une clé de ressource (contrôles du concepteur via ApplyDesignerExtras + sync-en-locale.js ; chaînes d'exécution comme WarnPortInUse ajoutées manuellement). Ce qui n'est pas encore fait, c'est la traduction réelle : seul le bundle source anglais (Resources.resx) existe - les bundles satellites pour les autres langues sont produits en un seul passage par lots lorsque le plugin est complet en fonctionnalités (traduire par morceaux pendant que les chaînes changent encore gaspille des efforts).
Langues cibles (l'ensemble que MusicBee lui-même propose, mis en correspondance 1:1 par endonymToCulture afin que le plugin suive automatiquement la langue de MusicBee) :
Arabe (ar) |
Tchèque (cs) |
Allemand (de) |
Grec (el) |
Espagnol (es) |
Français (fr) |
Hongrois (hu) |
Italien (it) |
Coréen (ko) |
Néerlandais (nl) |
Norvégien (nb) |
Polonais (pl) |
Portugais BR (pt-BR) |
Portugais PT (pt-PT) |
Suédois (sv) |
Turc (tr) |
Ukrainien (uk) |
Russe (ru) |
Japonais (ja) |
Chinois simplifié (zh-CN) |
Chinois traditionnel (zh-TW) |
Anglais (en, source) |
Politique de variante (selon la règle de locale de l'espace de travail) : PT et ZH sont divisés en bundles distincts car le vocabulaire/script diverge réellement (pt-BR/pt-PT, zh-CN/zh-TW). EN est un bundle unique - "English(US)" (en-US) de MusicBee revient à en via la chaîne de culture de .NET, donc aucun bundle US séparé n'est produit. ES et FR sont également des locales uniques.
Pourquoi : les paramètres d'un plugin UPnP ("ne pas utiliser de PCM brut", "forcer le PCM petit-boutiste", avertissements de repli de port) sont assez cryptiques dans sa langue maternelle. Suivre la langue de l'interface utilisateur de MusicBee - plutôt que de forcer l'anglais - est la différence entre un outil qu'un utilisateur non anglophone peut configurer et un autre qu'il ne peut pas. Aucune version amont n'a tenté cela.
N31 - Le lien d'aide s'ouvre dans la langue d'interface complète
Quoi : l'ouverture du lien d'aide depuis le plugin respecte la langue d'interface complète de l'utilisateur (par exemple, pt-BR, zh-CN) au lieu de se réduire à la langue de base, et envoie un identifiant de vérification de mise à jour plus clair.
Pourquoi : un utilisateur exécutant MusicBee dans une variante régionale (portugais brésilien, chinois simplifié) était envoyé à la page d'aide en langue de base. Le fait de conserver la culture complète les dirige vers la page d'aide dans la langue exacte qu'ils utilisent.
Corrections et améliorations du plugin original
Protocole et lecture de base
F01 - Profils d'appareil DLNA par défaut mis à jour
Quoi : livre de nouveaux profils par défaut pour PlayStation 4, Xbox 360/One et BubbleUPnP moderne, avec des indicateurs de capacité (fréquences d'échantillonnage, profondeurs de bits, codecs) qui reflètent ce que ces appareils prennent réellement en charge aujourd'hui.
Pourquoi : les valeurs par défaut du plugin original étaient figées vers 2014. PS4/Xbox/BubbleUPnP ont depuis acquis la prise en charge de l'audio haute résolution. Dès la sortie de la boîte, une nouvelle installation offre la meilleure qualité sur ces appareils sans que l'utilisateur n'ait à toucher aux paramètres du profil de l'appareil.
F02 - Contrôler les appareils qui annoncent MediaRenderer:3
Quoi : le plugin sonde la description du service UPnP d'un rendu pour décider si MusicBee peut le piloter. L'original ne correspondait qu'à urn:schemas-upnp-org:device:MediaRenderer:1. Les appareils modernes annoncent :2 ou :3. F02 élargit la correspondance.
Pourquoi : sans cela, les unités Sonos / WiiM / Eversolo récentes n'apparaissent tout simplement pas comme cibles dans la liste des appareils "Lire vers" de MusicBee - même si elles parlent le même protocole. Une simple correction de correspondance de préfixe de chaîne débloque toute la génération d'appareils modernes.
F03 - Option "Forcer le flux natif" par profil (activée par défaut)
Quoi : lorsque cette option est cochée, le plugin envoie les octets du fichier original à l'appareil sans transcodage, sans DSP, sans traitement ReplayGain appliqué. Juste le fichier brut que l'utilisateur a choisi, octet par octet (modulo le cadrage HTTP).
Pourquoi : selon les témoignages des forums, c'est le plus grand gain de qualité de lecture. Les utilisateurs hi-fi qui achètent des rendus coûteux veulent explicitement une sortie bit-perfect ; toute touche DSP annule l'intérêt. Activé par défaut car la plupart des appareils modernes gèrent n'importe quel codec que l'utilisateur leur a envoyé, et ReplayGain/EQ devrait être facultatif. C'est par profil afin que vous puissiez conserver le transcodage pour une ancienne Xbox tout en envoyant du natif à un DAC hi-fi.
F04 - "Forcer le transcodage" par profil
Quoi : remplacement par profil qui force chaque flux vers cet appareil à passer par le transcodeur, quel que soit le support du codec natif. L'inverse de F03 (ForceNativeStream). Mutuellement exclusif avec F03 - l'interface utilisateur décoche automatiquement l'autre lorsque l'un est activé.
Pourquoi : un seul interrupteur global serait contradictoire avec le ForceNativeStream par profil (F03). Cas réel : l'appareil A est un DAC hi-fi qui veut des flux natifs bit-perfect ; l'appareil B est un ancien récepteur AV qui s'étouffe avec le FLAC. Avec un interrupteur global, l'utilisateur doit choisir - au détriment de l'autre appareil. Avec le par profil, chaque appareil obtient la bonne réponse.
Implémentation :
StreamingProfile.ForceTranscoding As Boolean = False.- Le schéma de persistance est passé à la v9. Les fichiers antérieurs à la v9 chargent la valeur globale héritée une fois et la copient dans tous les profils, préservant l'ancien comportement lors de la mise à niveau.
- Interface utilisateur : supprimé du panneau Diagnostics, ajouté à la section Profils d'appareil à côté de ForceNativeStream. Gestionnaires d'exclusion mutuelle bidirectionnels (
CheckedChangedsur chacun désabonne l'autre avant de basculer, pour éviter une boucle infinie). - Site de décision :
Settings.ForceTranscoding→streamingProfile.ForceTranscodingdansWriteAudioFileDIDL.
F05 - "Forcer le PCM petit-boutiste" par profil
Quoi : les flux PCM (types MIME L16/L24) sont big-endian par spécification. Certains appareils s'attendent à tort à du petit-boutiste et lisent du bruit blanc lorsqu'on leur fournit des données big-endian correctes. F05 bascule l'ordre des octets par profil.
Pourquoi : sans cela, certains appareils produisent un mur de statique. Le symptôme est dramatique et la cause invisible sans connaissance de l'encodage PCM - l'interrupteur offre aux utilisateurs une solution par tâtonnement.
F06 - "Ne pas utiliser de PCM brut" par profil
Quoi : lorsque l'appareil déclare prendre en charge le PCM brut, le plugin l'utilise. Certains appareils mentent - ils acceptent le handshake SOAP mais corrompent les données PCM brutes réelles, tout en gérant correctement le PCM encapsulé dans un conteneur WAVE. F06 force le PCM-over-Wave, quelle que soit la publicité de l'appareil.
Pourquoi : certains modèles Marantz en particulier - ils annoncent le PCM brut mais seul le WAVE fonctionne. Sans cela, les flux PCM bruts sortent déformés, sans message d'erreur pour l'indiquer.
F07 - "Longueur du contenu" par profil
Quoi : quelle valeur envoyer dans l'en-tête HTTP Content-Length. Quatre options :
- Par défaut - nombre d'octets réel lorsqu'il est connu, omis lorsqu'il est inconnu.
- Aucun - ne jamais envoyer l'en-tête (encodage par blocs uniquement).
- PCM uniquement - n'envoyer que pour le PCM brut ; omettre pour tout le reste.
- Fixe - envoyer
UInt32.MaxValue - 8192(une sentinelle pour "longueur inconnue énorme").
Pourquoi : les appareils UPnP/DLNA varient énormément dans leur réaction à Content-Length. Certains ont besoin d'un nombre exact, certains le détestent sur les flux, certains ont besoin d'une sentinelle "vraiment grande" pour maintenir la mise en mémoire tampon. Cela a ensuite été étendu du PCM uniquement à tous les formats de sortie car les mêmes problèmes sont apparus dans les flux MP3/AAC transcodés.
F08 - "Ne pas effacer NextURI" par profil
Quoi : normalement, le plugin efface le NextURI en file d'attente de l'appareil lorsque la file d'attente se vide (en envoyant SetNextAVTransportURI avec une URL vide). Certains appareils (notamment Denon) interprètent un NextURI vide comme "tout arrêter" et interrompent immédiatement la lecture. F08 empêche le plugin de l'effacer.
Pourquoi : sans cela, les propriétaires de Denon voient l'appareil couper la piste en cours lorsque la file d'attente se vide. Avec F08 coché, l'appareil conserve le NextURI obsolète en mémoire (inoffensif - il est simplement écrasé la prochaine fois que quelque chose est mis en file d'attente).
F09 - FLAC comme format de sortie de transcodage
Quoi : le menu déroulant du format de transcodage dans les profils d'appareil propose désormais le FLAC aux côtés du PCM 16/24, MP3, AAC, Ogg. Le choisir achemine l'encodeur BASS via la ligne de commande de conversion FLAC standard de MusicBee (même mécanisme que celui déjà utilisé par MP3/AAC/Ogg).
Pourquoi : pour les appareils qui gèrent bien le FLAC mais ne peuvent pas décoder le codec source (par exemple, un Eversolo recevant la bibliothèque WMA de MusicBee convertie en FLAC), cela préserve la qualité sans perte là où MP3/AAC jetterait des données audio. Débloque N02 (contrôle du downmix 5.1), qui ne pouvait pas être traité sans une option de transcodage sans perte.
Implémentation : ajout d'une ligne au bloc Select Case Codec de Encoder.StartEncode - le FLAC rejoint MP3/AAC/Ogg dans la branche pilotée par ligne de commande. Le menu déroulant de l'interface utilisateur obtient "FLAC" comme 6ème option. Le mappage de chargement/enregistrement dans SettingsDialog s'étend pour reconnaître FileCodec.Flac ↔ SelectedIndex = 5. Le type Mime, DLNA et la fonctionnalité d'encodage étaient déjà câblés dans ItemManager.GetMimes / GetDlnaType / GetEncodeFeature à partir de travaux antérieurs (F21, F26).
Sans blanc (SetNextAVTransportURI)
F10 - SetNextAVTransportURI / NextURI core
Quoi : véritable lecture sans blanc. Lorsque l'appareil annonce la prise en charge de SetNextAVTransportURI dans sa description de service UPnP, le plugin pré-met en file d'attente la piste suivante sur l'appareil avant la fin de la piste actuelle. L'appareil effectue une transition interne sans blanc audible entre les pistes - ce que vous entendez sur un lecteur CD. Ce n'est pas le hack du "flux continu" (qui concatène tout en un long flux et perd les métadonnées par piste).
Pourquoi : la fonctionnalité phare de niveau 2. Les albums enregistrés comme une performance live continue (enregistrements live, mouvements classiques, sets de DJ) sonnent mal lorsqu'il y a un demi-seconde de silence entre les pistes. Résoudre cela correctement est une fonctionnalité phare, maintenant dans ce fork.
Notes : l'audio mis en file d'attente est servi via le server HTTP du plugin en utilisant streamHandle=0 (mode de récupération de bibliothèque), ce qui signifie que le moteur audio de MusicBee n'est pas impliqué pour la piste mise en file d'attente. Compromis : les effets ReplayGain/DSP/EQ ne s'appliquent pas à la piste suivante. Acceptable lorsque "forcer le flux natif" est activé (par défaut).
F11 - "Désactiver le support NextURI" par profil
Quoi : même si un appareil annonce SetNextAVTransportURI, cette case à cocher force le plugin à ignorer cette annonce et à revenir à la lecture piste par piste.
Pourquoi : certains appareils annoncent NextURI mais ont une implémentation boguée (plantages, demi-transitions, blocages). Plutôt que de faire de la rétro-ingénierie pour chaque appareil défectueux, l'utilisateur dispose d'un interrupteur "il suffit de le désactiver ici".
F12 - Cycle de vie de NextURI sur la liste de lecture en cours
Quoi : lorsque MusicBee déclenche NowPlayingListChanged, le plugin réévalue ce qui doit être mis en file d'attente pour une transition sans blanc. Il demande à MusicBee la nouvelle piste "suivante" via NowPlayingList_GetNextIndex(1) + NowPlayingList_GetListFileUrl, compare avec ce qui est actuellement en file d'attente sur l'appareil (suivi via le nouveau champ nextPlaySourceUrl), et remet en file d'attente si cela a changé (ou vide la file d'attente si MusicBee indique qu'il n'y a pas de piste suivante).
Pourquoi : sans F12, l'appareil continuait à lire un NextURI obsolète lorsque l'utilisateur supprimait/réorganisait la piste en file d'attente. Cela a historiquement pris plusieurs itérations car chaque mutation de liste nécessite une gestion différente - nous avons simplifié en faisant confiance à NowPlayingList_GetNextIndex (qui respecte déjà le mode aléatoire et la répétition de toute la liste), de sorte que toutes les variantes passent par la même comparaison.
Implémentation :
- Nouveau champ
nextPlaySourceUrlstocke l'URL de la bibliothèque MusicBee de ce qui est en file d'attente (l'URL de streaming avec suffixe de handle n'est pas comparable à un chemin de bibliothèque). - Nouvelle
Public Sub RefreshQueuedNextUri()surMediaRendererDevice. Trois résultats : aucun NextURI en file d'attente → pas d'opération ; en file d'attente correspond au nouveau "suivant" → pas d'opération ; en file d'attente diffère → appel deQueueNextavec la nouvelle URL (ouQueueNext("")pour effacer - ce qui respecte F08 DoNotClearNextUri). - Câblé dans
Plugin.ReceiveNotificationsousNotificationType.NowPlayingListChanged.
F13 - Retrait en cas d'échec de NextURI
Quoi : après 4 échecs consécutifs de SetNextAVTransportURI sur le même appareil, le plugin désactive la lecture sans blanc pour cet appareil jusqu'au redémarrage de MusicBee.
Pourquoi : si un appareil est réellement défectueux pour NextURI (erreurs SOAP intermittentes, problèmes réseau), le plugin continuerait autrement à réessayer à chaque piste. F13 arrête le bruit et revient silencieusement à la lecture piste par piste.
F14 - Mode de répétition + intégration NextURI
Quoi : F14 se divise en deux cas gérés par le détecteur de transition F15 dans OnAvTransportStatusCheck :
- Répéter tout : MusicBee transmet lui-même l'URL de "boucle" correcte (piste 1 à la fin de la liste) à
Plugin.QueueNext. Aucune logique de plugin spéciale n'est nécessaire - l'appareil effectue la transition et le détecteur F15 appellePlayer_PlayNextTrackcomme d'habitude, ce qui ramène l'index NPL de MusicBee à 0. - Répéter un : MusicBee transmet la MÊME URL de piste à
Plugin.QueueNext. L'appareil effectue la transition (nouveau handle de flux, même source). Le détecteur F15 interroge maintenantPlayer_GetRepeat()- si c'estRepeatMode.One, il saute l'appelPlayer_PlayNextTrackafin que MusicBee n'avance pas l'index NPL loin de la piste en boucle.
Pourquoi : sans le saut de Répéter un, l'appel de Player_PlayNextTrack lors de la transition sans blanc ferait avancer MusicBee à la piste suivante dans la liste (Répéter un n'affecte que le comportement d'avance automatique en fin de piste dans l'interface utilisateur du lecteur - Piste suivante avance toujours), contredisant ce que Répéter un signifie.
Mise en garde sur le nombre de lectures : en mode Répéter un, l'incrémentation du nombre de lectures dépend de MusicBee 3.7.9563+ qui détecte la lecture en boucle. Les anciennes versions de MusicBee lisent correctement la répétition sans blanc mais manquent l'incrémentation du nombre de lectures. Documenté ; non bloquant.
F15 - Machine d'état de détection de transition de piste
Quoi : lorsque l'appareil passe en interne de la piste actuelle à NextURI, le plugin doit le remarquer et indiquer à MusicBee d'avancer son index de lecture en cours. Sinon, MusicBee pense qu'il est toujours sur la piste précédente et les nombres de lectures / l'interface utilisateur / le scrobbling se désynchronisent.
Implémentation : interroge GetPositionInfo.TrackURI à chaque tic du minuteur d'état. Lorsque l'URI signalé correspond à celui que nous avons mis en file d'attente via NextURI, nous appelons Player_PlayNextTrack sur MusicBee et définissons suppressNextSoapCall afin que le PlayToDevice résultant ne renvoie pas SetAVTransportURI (ce qui interromprait la lecture sans blanc).
Pourquoi : sans F15, l'appareil lit la piste suivante mais l'interface utilisateur de MusicBee indique qu'il est toujours sur la précédente. Déroutant, interrompt le scrobbling, interrompt le suivi du nombre de lectures. La détection de transition de piste nécessite une longue itération par rendu car chaque marque de rendu a ses propres particularités quant au moment où elle signale le changement d'URI (certains signalent d'abord TRANSITIONING, certains passent directement à PLAYING avec le nouvel URI, certains ont un bref STOPPED entre les deux).
Notes : notre première version fonctionne sur le rendu BubbleUPnP. Les cas limites par appareil restent dans B6.
F16 - Correction du pop lors de la transition sans blanc
Quoi : le pop se produit lorsque le format source (fréquence d'échantillonnage / canaux / codec) de la piste en file d'attente diffère de la piste en cours de lecture, forçant le DAC de l'appareil à se reverrouiller à la transition. F16 ajoute un diagnostic NextUri:FormatChange qui se déclenche au moment de la mise en file d'attente chaque fois que les formats diffèrent, nommant les deux côtés - afin que les utilisateurs entendant des pops puissent corréler.
Le diagnostic indique également l'atténuation : cochez ForceTranscoding sur le profil de l'appareil. Cela homogénéise chaque piste à un seul codec/fréquence d'échantillonnage/profondeur de bits de transcodage, éliminant entièrement la différence de format source.
Pourquoi différé pour la correction réelle de transcodage pour correspondre : la correction structurelle (transcoder la piste en file d'attente pour qu'elle corresponde au format de la piste en cours de lecture) nécessite des modifications du schéma d'URL du server HTTP du plugin - actuellement /encode/{id}0.{ext} sert le fichier en file d'attente nativement. Une future v2 de F16 ajouterait des routes /encode/{id}0_{rate}_{depth}.{ext} par format et les câblerait via l'encodeur. C'est un changement architectural plus important qui vaut la peine d'être fait si un appareil réel présente le pop après que ForceTranscoding ne suffit pas.
Implémentation aujourd'hui :
- Le champ
lastSourceUrlsuit l'URL source en cours de lecture. QueueNextlitFilePropertyType.SampleRate/Channels/Kindpour les pistes actuelle et en file d'attente et enregistreNextUri:FormatChangeen cas de non-concordance.
F17 - Resynchronisation de la barre de progression après une recherche
Quoi : la fonction Seek() appelait déjà GetPlayPositionInformation() après un SOAP de recherche réussi, ce qui corrige le cas de "pas de resynchronisation du tout". F17 corrige le décalage restant d'un maximum d'une seconde causé par la quantification RelTime d'une seconde d'UPnP : lorsque la position signalée par l'appareil est arrondie à moins d'une seconde de la cible demandée par l'utilisateur, le plugin fait désormais confiance à la valeur de l'utilisateur précise à la sous-seconde au lieu de la troncature de l'appareil. Ce n'est que lorsque l'appareil signale quelque chose de radicalement différent (plus d'une seconde de décalage) que nous utilisons sa valeur (la recherche a atterri ailleurs que demandé, par exemple, un alignement sur une image clé sur certains codecs).
Pourquoi : sans cela, la recherche à 2:30.500 ancrée par rapport au rapport "2:30" de l'appareil ferait afficher la barre de progression avec environ 500 ms de retard par rapport à la réalité. Après F17, la barre correspond à l'intention de l'utilisateur pour le cas courant de défilement dans la piste, et respecte toujours le rapport de l'appareil pour le cas exceptionnel d'alignement sur une image clé.
F18 - Interverrouillage flux continu / NextURI
Quoi : deux interverrouillages sont désormais en place :
- Exécution :
QueueNextrenvoieFalseprématurément lorsqueSettings.ContinuousOutputest activé. Le flux continu est son propre mécanisme sans blanc (un long flux concaténé) ; l'envoi deSetNextAVTransportURIen plus de cela perturbe l'appareil quant à savoir si chaque piste est une URI discrète ou fait partie du flux continu. - Interface utilisateur : lorsque l'utilisateur coche la case globale de flux continu,
forceNativeStreamdu profil actuellement affiché se décoche automatiquement. Le flux continu transcode toujours, donc le forçage natif n'a pas de sens en combinaison.
Pourquoi : empêche l'utilisateur d'activer simultanément deux mécanismes sans blanc conflictuels. Sans F18, l'appareil recevrait à la fois une URI de flux continu ET une NextURI pour chaque piste suivante, avec un comportement indéfini selon le rendu.
F19 - Erreurs NextURI vides ignorées
Quoi : lorsque SetNextAVTransportURI est appelé avec une URL vide (par exemple, dernière piste de la liste), certains appareils renvoient une erreur SOAP. F19 les ignore silencieusement - enregistrées mais non propagées comme erreurs.
Pourquoi : la condition "pas de piste suivante" est normale, pas une erreur. La traiter comme fatale pollue le journal et (dans certains flux) déclenche des tempêtes de réessais.
Types MIME et métadonnées DLNA
F20 - MIME MP3 → audio/mpeg
Quoi : le type MIME MP3 conforme aux normes est audio/mpeg, et non audio/mp3. Ce dernier est un terme impropre courant que la plupart des appareils tolèrent, mais les rendus plus stricts le rejettent.
Pourquoi : corrige silencieusement la lecture sur les appareils plus stricts qui suivent la norme. La base de code yaiol avait déjà cette correction ; aucun changement n'était nécessaire.
F21 - Ordre des types MIME : variante non-x- en premier
Quoi : lorsqu'un appareil annonce à la fois audio/flac et audio/x-flac, le plugin renvoie d'abord la variante non-x-. Idem pour tout codec avec des MIME standard et expérimentaux.
Pourquoi : le préfixe x- marque les MIME expérimentaux/non officiels. Certains rendus se comportent mieux avec la forme standard. Petit réarrangement, impact réel.
F22 - Prise en charge du type MIME Opus
Quoi : reconnaît Opus comme un codec audio streamable ; envoie le type MIME audio/opus lors de la diffusion de pistes Opus.
Pourquoi : Opus est désormais courant (codec de compromis moderne pour la parole/musique). Sans F22, le plugin refuserait de diffuser des fichiers Opus même vers des appareils qui les gèrent.
F23 - Prise en charge des fichiers source Monkey Audio (APE)
Quoi : reconnaît les fichiers .ape comme un codec source valide pour le streaming/transcodage.
Pourquoi : APE est un format sans perte avec une base d'utilisateurs de niche mais fidèle. L'ajouter coûte peu et débloque la bibliothèque pour ces utilisateurs.
F24 - Repli MIME AAC / ALAC
Quoi : si un appareil prend en charge l'AAC ou l'ALAC mais ne les annonce pas explicitement dans sa description de service UPnP, le plugin les propose quand même en tant que repli.
Pourquoi : plusieurs appareils qui gèrent bien l'AAC ont oublié de le lister dans leur XML de capacités. Sans F24, le plugin n'essaierait même pas, forçant le transcodage. Avec F24, le plugin essaie et laisse l'appareil le gérer nativement s'il le peut.
F25 - Indicateur de type DLNA pour les flux WAV natifs + encodés
Quoi : l'indicateur de type DLNA (un identifiant de profil comme LPCM, WAVE, MP3) doit correspondre à ce que l'appareil reçoit. F25 garantit que les flux natifs et les flux WAV encodés sont correctement signalés.
Pourquoi : un type DLNA non concordant entraîne certains appareils à refuser complètement la lecture ou à appliquer le mauvais décodeur.
F26 - En-tête DLNA pour les fichiers FLAC
Quoi : les flux FLAC obtiennent l'identifiant de profil DLNA approprié dans leurs en-têtes.
Pourquoi : sans cela, certains appareils qui prennent en charge le FLAC ne reconnaissent pas le flux comme tel.
F27 - Correction du calcul du débit binaire dans les métadonnées
Quoi : le res@bitrate du flux continu était calculé comme (sampleRate * channels * bitsPerSample) / 1000 - kbps, décalé d'un facteur d'environ 125 par rapport à la spécification UPnP DIDL qui définit l'attribut comme octets par seconde. Maintenant, il divise par 8 au lieu de 1000.
Pourquoi : affichage du débit binaire incorrect sur l'appareil - cosmétique sur la plupart des rendus, mais certains allouent des tampons de flux à partir de la valeur et bégayent sur des flux qui semblent environ 125 fois plus petits qu'ils ne le sont. Le chemin du fichier source non continu avait déjà cette correction ((bitrate_kbps * 1000) \ 8 = octets/sec) ; seul le chemin du flux continu était incorrect.
F28 - Correction du format de l'heure des métadonnées (Marantz)
Quoi : res@duration dans DIDL était formaté en H:MM:SS (par exemple, 0:03:42). La spécification UPnP DIDL définit le format comme H+:MM:SS[.F+] - strictement avec des secondes fractionnaires facultatives mais recommandées ; certains appareils Marantz considèrent la forme nue comme invalide et laissent leur affichage de durée vide. Maintenant formaté en H:MM:SS.fff (par exemple, 0:03:42.000).
Pourquoi : problème d'affichage spécifique à une marque ; le format conforme au style ISO avec des secondes fractionnaires le corrige sans affecter aucun autre appareil. Appliqué aux deux sites d'émission DIDL (chemin du fichier source + chemin du flux encodé dans WriteAudioFileDIDL).
Correction bonus dans le même passage : pv:addedTime et pv:lastPlayedTime utilisaient hh (horloge 12 heures) dans leurs chaînes de format DateTime au lieu de HH (24 heures). Toute piste ajoutée ou lue entre 13:00 et 23:59 s'afficherait avec une heure incorrecte (par exemple, 17:42 → "05:42") sur les appareils qui affichent le champ. Utilise maintenant HH.
F29 - Prise en charge de la recherche MP3 encodée (CBR)
Quoi : les flux MP3 transcodés annoncent désormais DLNA.ORG_OP=11 (recherche par octet et par temps) au lieu de DLNA.ORG_OP=10 (par octet uniquement). Les appareils qui refusaient auparavant la recherche par temps sur les MP3 transcodés peuvent désormais piloter leur barre de progression/interface utilisateur de recherche normalement.
Pourquoi : le transcodeur de MusicBee produit du MP3 à débit constant avec le préréglage HighQuality, de sorte que le mappage octet ↔ temps est linéaire - l'appareil peut convertir une demande de recherche par temps en une recherche par octet HTTP Range lui-même sans aucun support côté encodeur. L'annonce de OP=11 débloque cette interface utilisateur sur l'appareil. Sans F29, les utilisateurs qui recherchaient dans un MP3 transcodé voyaient la recherche ignorée silencieusement ou étaient ramenés au début de la piste.
Implémentation : restructuration de GetEncodeFeature dans ItemManager.vb pour décomposer le If en ligne en une chaîne If/ElseIf/Else lisible. Le MP3 obtient OP=11 explicitement ; les autres codecs non-PCM conservent OP=10. Aucun changement pour AAC/FLAC/etc. - ceux-ci nécessiteraient une vérification spécifique au codec de la nature CBR que MusicBee ne garantit pas.
F30 - Extension de fichier .mpeg gérée
Quoi : les fichiers avec l'extension .mpeg (et le plus rare .mpe) sont désormais reconnus comme FileCodec.Mp3 dans GetCodec. Avant F30, ils renvoyaient FileCodec.Unknown et étaient silencieusement rejetés de la bibliothèque / incapables d'être des sources de transcodage.
Pourquoi : les anciennes archives MPEG-1 Layer 3 utilisaient parfois .mpeg au lieu de .mp3 (la spécification autorise les deux). Une poignée de fichiers dans une bibliothèque de 300 000 suffit pour ressentir "MusicBee les affiche mais le plugin non" - déroutant pour l'utilisateur.
Comportement de lecture
F31 - Les flux radio utilisent automatiquement le mode continu
Quoi : WriteAudioFileDIDL sonde désormais la propriété Kind de l'URL source via Library_GetFileProperty et traite tout fichier dont le Kind se termine par "Stream" (MusicBee signale "MP3 Stream", "Internet Stream", etc. pour la radio) comme continu, quel que soit le commutateur global Settings.ContinuousOutput. La branche DIDL de flux continu (Titre : "Flux continu", id="continuousstream", sortie PCM/Wave fixe) est utilisée ; l'appareil voit un seul flux de style infini.
Pourquoi : les flux radio n'ont pas de limites de piste, pas de longueur fixe, pas de recherche. Les traiter comme des fichiers discrets dans le DIDL entraînait le plugin à annoncer des plages d'octets et des durées qui n'existent pas. Le basculement automatique lorsque MusicBee nous a déjà dit "c'est un flux" supprime un piège que l'utilisateur ne devrait pas avoir à considérer.
Portée : s'applique uniquement lorsque MusicBee pilote la lecture (musicBeePlayToMode). Le chemin de récupération de la bibliothèque (navigation du client UPnP) est inchangé - les URL radio y sont rares et le comportement visible par l'utilisateur ne devrait pas changer sans test explicite.
F32 - Repli de l'annonce de codec
Quoi : si un appareil n'annonce pas certains codecs (ou si le plugin ne peut pas analyser le XML de capacité de l'appareil), le plugin ne rejette pas immédiatement le flux. Au lieu de cela, il essaie de le servir et laisse l'appareil décider.
Pourquoi : de nombreux appareils ont un XML de capacité incomplet ou illisible mais gèrent en fait très bien le codec. F32 échange un petit "meilleure estimation et essai" contre un refus pur et simple.
F33 - Amélioration de la synchronisation de la barre de progression
Quoi : la position entre les sondages est déjà extrapolée par horloge murale à partir d'une seule ancre (currentPlayStartTicks), de sorte que la barre de progression se met à jour en douceur à une vitesse inférieure à la seconde. La source de gigue restante était l'ancre initiale pour une piste fraîchement démarrée : le code précédent supposait position=0 au moment où le minuteur d'état a remarqué pour la première fois que l'état était passé à Lecture, mais à ce moment-là, l'appareil pouvait avoir joué pendant 100 à 500 ms (un intervalle de sondage). La barre de progression de MusicBee commencerait à 0, puis sauterait en avant lorsque la réalité rattraperait son retard.
Correction F33 : lors de la transition vers Lecture pour la première fois sur une nouvelle piste (currentPlayStartTimeEstimated=True), appelez GetPlayPositionInformation() pour obtenir la position réelle actuelle de l'appareil, puis ancrez-vous sur celle-ci. UPnP ne signale qu'une résolution d'une seconde, donc l'ancre est toujours quantifiée, mais elle est beaucoup plus proche de la vérité que de supposer 0.
Pourquoi : affichage de la progression plus fluide et plus précis, surtout juste après un changement de piste. Il n'y a aucun moyen de contourner la résolution de rapport UPnP d'une seconde elle-même - c'est une spécification.
F34 - Gigue de la barre de progression après un changement de piste
Quoi : lorsque PlayToDevice est appelé pour une nouvelle piste, le plugin laissait currentPlayPositionMs et currentPlayStartTicks à leurs valeurs de la piste précédente pendant la fenêtre d'environ 100 ms entre SOAP-Play et le premier sondage du minuteur d'état détectant le nouvel état de lecture. La barre de progression de MusicBee affichait brièvement la fin de la piste précédente, puis revenait à 0, puis montait. F34 met les deux à zéro à l'entrée de PlayToDevice - au moment où nous savons qu'un changement de piste se produit, avant tout travail SOAP.
Pourquoi : problème visuel sur les cas d'utilisation de saut rapide (suivant manuel ou transition sans blanc). Maintenant, la première requête PlayPositionMs de MusicBee après Play renvoie 0 proprement, puis GetPlayPositionInformation de F33 l'affine à la position réelle de l'appareil lors du premier tic de changement d'état.
Implémentation : quatre lignes en haut de PlayToDevice, associées à l'ancrage précis du temps de transition de F33.
F35 - Bug "Forcer le transcodage"
Quoi : le forçage du transcodage pouvait encore ignorer le transcodage dans certaines combinaisons. Après la refonte par profil F04, deux lacunes spécifiques ont été comblées :
- Priorité avec ForceNativeStream. Lorsque les deux étaient True (ce qui peut arriver lors d'une migration de schéma ou d'un fichier de paramètres partiel), ForceTranscoding l'emporte désormais (
If streamingProfile.ForceTranscoding Then forceEncode = True ElseIf streamingProfile.ForceNativeStream Then forceEncode = False). L'exclusion mutuelle de l'interface utilisateur empêche l'utilisateur de cocher les deux, mais la garde d'exécution gère tout état qui a chargé de manière incohérente depuis le disque. - Logique bypassTranscodeDecision. Auparavant :
streamingProfile.ForceNativeStream AndAlso Not Settings.ForceTranscoding. Maintenant :streamingProfile.ForceNativeStream AndAlso Not streamingProfile.ForceTranscoding- même règle de priorité mais au même niveau par profil.
Pourquoi : "forcer" devrait signifier forcer. Si l'utilisateur a explicitement activé ForceTranscoding pour un appareil, le plugin ne doit jamais passer silencieusement au streaming natif, quelle que soit la combinaison des autres indicateurs.
F36 - Exception de rendu fermé
Quoi : a enveloppé Plugin.ReceiveNotification dans un Try/Catch de niveau supérieur qui enregistre toute exception non gérée au lieu de la laisser se propager vers la pompe de notification de MusicBee.
Pourquoi : les notifications de MusicBee (PlayStateChanged, VolumeMuteChanged, etc.) sont distribuées à ControlPointManager qui communique avec le rendu via SOAP. Les sites d'appel individuels avaient déjà Try/Catch autour de leurs appels SOAP, mais un cas de synchronisation suffisamment étrange (par exemple, le rendu mourant entre deux appels SOAP dans le même gestionnaire de notification) pouvait encore s'échapper. L'enveloppe de niveau supérieur est le filet de sécurité final afin que l'utilisateur ne voie jamais une fenêtre contextuelle générique "TargetInvocationException" de MusicBee.
Implémentation : a renommé le corps existant en ReceiveNotificationInternal et a ajouté un mince wrapper ReceiveNotification qui fait Try { ReceiveNotificationInternal(...) } Catch { LogError(...) }. L'infrastructure Try/Catch préexistante par méthode dans ControlPointManager (autour de chaque appel PostSoapRequest) reste - F36 est ceinture + bretelles.
F37 - Recherche de piste longue déclenchant une fausse transition
Quoi : la recherche dans une piste longue peut produire un bref cycle Arrêté→Lecture sur certains rendus. Sans discrimination, ProcessNewPlayState.Stopped le traite comme une fin de piste naturelle et appelle Player_PlayNextTrack, faisant avancer MusicBee alors que l'utilisateur voulait juste faire défiler. F37 horodate lastUserInitiatedSeek dans Seek() et ajoute une garde de 5 secondes dans le gestionnaire Arrêté (reflétant la fenêtre lastUserInitiatedStop existante).
Pourquoi : le saut silencieux à la piste suivante pendant une recherche est l'un de ces bugs dont personne ne peut deviner la cause - l'utilisateur pense "bizarre, j'ai essayé de faire défiler en avant et maintenant il joue la chanson suivante". La correction est mécanique : même modèle que la discrimination d'arrêt utilisateur déjà en place.
F38 - Amélioration de la gestion de la recherche pour les codecs sujets aux plantages
Quoi : le plantage de BubbleUPnP lors de la recherche MP3 était le symptôme canonique. Après audit, le code de recherche yaiol actuel fait déjà les bonnes choses - le chemin natif gère correctement HTTP Range (206, Content-Range, AcceptRanges), le chemin encodé annonce X-AvailableSeekRange et analyse les en-têtes entrants timeSeekRange.dlna.org / npt, les indicateurs DLNA.ORG_OP reflètent les capacités réelles du flux (avec DisablePcmTimeSeek opt-out pour les appareils Platinum problématiques). Testé par l'utilisateur sur BubbleUPnP 4.6.4 actuel : aucun plantage observé.
Pourquoi : le plantage de la recherche MP3 de BubbleUPnP a été signalé vers 2024 et l'application a bénéficié d'environ 16 mois de corrections depuis. F29 (MP3 encodé OP=11) était la nouvelle variable qui aurait pu le réexposer ; ce n'est pas le cas sur les versions testées.
Si un plantage revient : la forme de la correction serait un commutateur "recherche limitée" par profil qui force DLNA.ORG_OP=10 (octet uniquement) sur les codecs signalés - reflétant le fonctionnement actuel de DisablePcmTimeSeek pour le PCM. À ajouter alors, pas de manière préventive.
Interface utilisateur et journalisation
F39 - Le bouton "Ajouter" sélectionne le nouveau profil
Quoi : cliquer sur "Ajouter" dans la liste des profils d'appareil crée un nouveau profil ET le sélectionne automatiquement afin que l'utilisateur puisse immédiatement modifier les champs. Notre refactorisation de la boîte de dialogue sectionnée le fait déjà - les chemins d'ajout direct et à partir d'un modèle se terminent par Me.activeStreamingProfiles.SelectedIndex = Me.activeStreamingProfiles.Items.Count - 1. Une vérification a confirmé que notre fork gère déjà cela - rien à changer.
Pourquoi : petite imperfection de l'expérience utilisateur qui s'est avérée ne pas en être une ici.
F40 - Connexions maximales plus grandes + journal d'avertissement
Quoi : la limite de flux concurrents du plugin (SemaphoreSlim autour de Sockets_Stream_File / Sockets_Encoder_Start) était codée en dur à 4. F40 la rend configurable par l'utilisateur dans la page des paramètres généraux (par défaut 16, plage 1-256), ajoute une ligne de journal MaxConnections lorsqu'une requête doit attendre un emplacement, ET affiche un badge rouge ⚠ Conn Max en bas à gauche de la boîte de dialogue des paramètres si la limite a été atteinte au moins une fois depuis le démarrage de MusicBee.
Pourquoi : lorsqu'un appareil déclenche des requêtes parallèles (certains Marantz/Linn lors de scans d'illustrations, les sondes de métadonnées de BubbleUPnP en même temps que la lecture active), des requêtes supplémentaires étaient bloquées silencieusement derrière le sémaphore - l'utilisateur voyait "appareil lent" sans cause visible. La ligne de journal est bonne pour le débogage technique mais les utilisateurs non techniques ne lisent jamais les journaux. Le badge visible dans la boîte de dialogue des paramètres rend la condition de dépassement de limite détectable par quiconque ouvre les préférences du plugin.
Implémentation :
- Centralisation de l'attente dans
WaitOnSendBarrier(logTag)dansMusicBeeUpnp.vb; les deux sites d'appel (MediaServerDevice.GetFile,Encoder.StartEncode) l'utilisent. Settings.MaxConnectionspersisté dans la v8 du schéma des paramètres.Plugin.MaxConnectionsHitest un indicateur de session persistant défini dansWaitOnSendBarrier; ne se réinitialise qu'au redémarrage de MusicBee.SettingsDialog.maxConnectionsBadgeest une étiquette rouge en gras à(16, 410)qui ne s'affiche que lorsquePlugin.MaxConnectionsHitest True. Possède une info-bulle expliquant la cause et le remède.- Le sémaphore est initialisé une fois au chargement du type, donc la modification du paramètre nécessite un redémarrage de MusicBee (noté dans l'étiquette du champ).
F41 - Journal "encodage dû à ReplayGain/DSP"
Quoi : au lieu de lignes de journal séparées "encodage pour RG" / "encodage pour DSP", la ligne unique StreamDecision de F42 inclut MB-DSP/EQ, MB-ReplayGain, Profile-DSP/EQ, Profile-ReplayGain comme raisons accumulées. Même valeur diagnostique, moins de bruit.
Pourquoi : les utilisateurs voient toutes les raisons pour lesquelles le transcodage se produit pour une piste donnée sur une seule ligne de journal, et non dispersées. Voir F42 pour tous les détails.
F42 - Journal "le rendu ne prend pas en charge le codec source"
Quoi : ajout d'une ligne de journal StreamDecision par piste lue vers l'appareil qui indique soit "CODEC natif" soit "transcodage CODEC→CODEC raison=…". Le champ raison accumule toutes les conditions qui ont déclenché le transcodage : MB-DSP/EQ, MB-ReplayGain, Profile-DSP/EQ, Profile-ReplayGain, WebFile, VirtualFile, ForceTranscoding(global), SampleRate<min/SampleRate>max, DownmixToStereo, DeviceLacksCodec(X), BandwidthConstrained.
Pourquoi : les utilisateurs étaient confus par des pics de CPU inattendus sur des fichiers qu'ils s'attendaient à diffuser nativement. Une ligne de journal par piste leur indique exactement quelle condition a provoqué le transcodage - et si le champ affiche DeviceLacksCodec(Flac), ils savent immédiatement que les informations de protocole de l'appareil étaient incomplètes et peuvent vouloir que le repli de F32 se déclenche.
Implémentation : une seule chaîne d'accumulateur construite de manière incrémentielle tout au long de la chaîne de décision ; enregistrée une fois à la fin. Protégée par Settings.LogDebugInfo pour éviter le bruit de journal en production.
F43 - Le journal SetNextAVTransport affiche l'URL source
Quoi : les entrées de journal QueueNext incluent désormais source=<chemin de la bibliothèque MusicBee> à côté de stream=<URL de streaming HTTP>. Même changement appliqué au chemin de succès et au chemin d'échec (QueueNext:Failed).
Pourquoi : lors du débogage d'un problème de piste en file d'attente, l'URL de streaming (/encode/aabbccdd0.flac) est opaque en soi - la même pour chaque piste. L'URL source est le chemin de la bibliothèque lisible par l'homme qui vous indique exactement quel fichier MusicBee a essayé de mettre en file d'attente.
F44 - Meilleure journalisation des erreurs de type MIME
Quoi : deux nouvelles entrées de journal pendant Activate :
Activate:MimeUnverified- se déclenche par entrée mal formée dans la réponseGetProtocolInfode l'appareil, nommant l'entrée qui n'a pas pu être analysée (afin que l'utilisateur puisse voir par exemple "le Marantz a renvoyéhttp-get:*::*pour un codec - la capacité n'est pas vérifiée, le repli de F32 devinera").Activate:NoSinkInfo- se déclenche une fois si l'appareil n'a renvoyé aucun élément<Sink>. Signifie queSupportedMimeTypesreste Nothing etIsCodecSupportedse dégrade en "supposer que tout fonctionne" - contexte utile lorsque des erreurs ultérieures "l'appareil a refusé le flux" apparaissent.
Pourquoi : avant F44, ces replis silencieux de capacité laissaient les utilisateurs deviner pourquoi leurs pistes étaient soit transcodées contre toute attente, soit refusées par l'appareil. Maintenant, une simple recherche de Activate: montre si les informations de capacité de l'appareil étaient utilisables.
F45 - Meilleure journalisation des erreurs de métadonnées
Quoi : le journal d'exceptions Browse dans ContentDirectoryService.vb était déjà enrichi dans un travail yaiol antérieur (la session de bug Alia Vox) avec ObjectID et la trace de pile. F45 l'étend davantage avec BrowseFlag (métadonnées vs enfants), Filter (quels attributs le client a demandés), sortCriteria, et partialResultLength (combien d'octets de DIDL ont été produits avant l'échec - indique à quel point la mauvaise piste se trouve dans le lot).
Pourquoi : lorsque quelque chose ne va pas au milieu du DIDL, la valeur de longueur partielle vous indique si l'échec s'est produit sur la première piste du lot (partiel=0) ou en cours de route (partiel=N) - combiné avec le startingIndex du lot, vous pouvez identifier l'index de la piste incriminée. Filter et BrowseFlag expliquent quel type de navigation le client voulait ; parfois une navigation uniquement de métadonnées échoue là où une navigation d'enfants pour le même ID réussit.
Réseau
F46 - Le mode Automatique ne s'annonce que sur les adaptateurs de réseau réels
Quoi : en mode d'interface Automatique, le plugin s'annonçait auparavant (SSDP) sur chaque adaptateur IPv4 opérationnel. Sur une machine qui exécute aussi un tunnel VPN (NordLynx) ou un commutateur virtuel (Hyper-V / WSL / Docker), la même bibliothèque était annoncée sur chacun de ces adaptateurs également, de sorte que le point de contrôle depuis lequel vous diffusez découvrait le server deux ou trois fois et listait la bibliothèque en copies dupliquées. Le mode Automatique ne conserve désormais que les adaptateurs disposant d'une véritable passerelle IPv4 par défaut (HasIPv4Gateway) - ce que les adaptateurs de tunnel et de commutateur virtuel n'ont pas - ceux-ci sont donc retirés de la liste d'annonce. Une adresse épinglée par l'utilisateur l'emporte toujours (annonce sur cette interface uniquement), et si aucun adaptateur ne signale de passerelle, le sélecteur revient à tous les adaptateurs, de sorte que la liste des adresses annoncées n'est jamais vide et que le plugin ne peut pas devenir invisible.
Pourquoi : le doublon n'est pas causé par le fait "d'être sur un VPN" - il est causé par l'annonce simultanée sur l'adaptateur LAN et sur l'adaptateur tunnel/virtuel, de sorte qu'un même point de contrôle voit le même server à deux adresses. Un VPN grand public (NordVPN/NordLynx) ne tunnelise que le trafic à destination d'Internet ; le rendu DLNA vit sur le LAN et le trafic du sous-réseau local contourne le tunnel, si bien que l'adaptateur tunnel n'atteint de toute façon jamais un rendu - le retirer supprime une copie fantôme, jamais un chemin fonctionnel. Le test de passerelle est le signal simple et fiable qui distingue un vrai adaptateur LAN/Wi-Fi d'un tunnel ou d'un commutateur virtuel. Complète N05 (qui corrigeait comment les annonces sont envoyées sur de tels liens - multicast au lieu de broadcast) ; F46 régit quels adaptateurs font l'objet d'annonces.
Limite connue : un VPN maillé / d'accès distant (Tailscale, ZeroTier, WireGuard vers le domicile) dont les rendus vivent réellement de l'autre côté du tunnel présente généralement un adaptateur sans passerelle par défaut, le mode Automatique l'écarte donc aussi. Ces utilisateurs épinglent alors l'adresse VPN, qui a priorité sur le filtre de passerelle.