MusicBee UPnP Plugin Aide

Nouveautés

2.0.9 - 2026-08-23

L'avis de mise à jour ouvre ses pages dans votre langue

Quoi : les liens Nouveautés et Télécharger dans l'avis de mise à jour ouvrent désormais les pages du plugin dans la langue de MusicBee, plutôt qu'en anglais.

Pourquoi : ces deux liens limitaient la langue à l'une des quatre suivantes — anglais, français, espagnol ou allemand — avant de l'envoyer au site web, de sorte que tous les autres recevaient la page en anglais même lorsqu'une traduction existait. Ils transmettent désormais la langue de MusicBee sans modification et laissent le site web décider quoi servir, ce que le bouton Aide à côté d'eux a toujours fait.

2.0.8 - 2026-08-22

Le plugin se présente désormais correctement aux applications et appareils qui le découvrent sur votre réseau, et les paramètres des profils d'appareil sont à nouveau alignés.

Vos appareils affichent le bon fabricant, modèle et version

Quoi : lorsqu'une application de contrôle, un téléphone ou un téléviseur trouve MusicBee sur votre réseau, il présente désormais ce plugin comme étant fabriqué par yaiol, pointe vers le site propre du plugin, le décrit comme couvrant les trois rôles — server, lecteur et rendu — et signale la version que vous avez réellement installée.

Pourquoi : chaque appareil UPnP annonce qui l'a fabriqué et ce qu'il est, et les applications de contrôle l'affichent comme l'identité de l'appareil. Ce plugin annonçait toujours les détails du plugin original dont il était issu : le nom d'un autre auteur, le site web de MusicBee au lieu du sien, et un numéro de modèle figé à "1.0" depuis la toute première version. Depuis votre téléphone, il n'y avait aucun moyen de savoir à quel plugin vous parliez, et encore moins quelle version. Ces détails proviennent maintenant du plugin lui-même, de sorte que la version affichée à côté de l'appareil reste correcte à chaque mise à jour.

Les paramètres des profils d'appareil sont à nouveau alignés

Quoi : sur l'onglet Profils d'appareil, les étiquettes et leurs cases partagent un même bord gauche et sont espacées uniformément, et la plage de fréquences d'échantillonnage s'affiche sur une seule ligne.

Pourquoi : les champs s'étaient décalés au fur et à mesure que des options étaient ajoutées à l'onglet, et l'étiquette "à" de la plage de fréquences d'échantillonnage s'était retrouvée au-dessus de la case adjacente — lisible une fois que vous saviez ce qu'elle disait, déroutante la première fois que vous la regardiez.

2.0.7 - 2026-08-08

Les pistes plus longues conservent leur titre et leur curseur de position

Quoi : une piste plus volumineuse envoyée depuis un téléphone — un long fichier FLAC, un fichier haute résolution ou DSD — affiche désormais son titre correct et peut être parcourue, comme une petite. Auparavant, certaines d'entre elles étaient lues sur le réseau, avec une adresse web à la place du titre et un curseur qui ne faisait rien.

Pourquoi : le plugin attend sa copie locale avant de démarrer, mais il décidait à l'avance si un fichier valait la peine d'être attendu, en fonction de sa taille. C'était en fait une estimation de la vitesse de votre réseau, qu'il n'a aucun moyen de connaître : une piste de 65 Mo était jugée trop volumineuse, puis terminait son téléchargement une seconde plus tard — confortablement dans le délai qu'il avait déjà abandonné. Il surveille maintenant simplement le téléchargement. Tant qu'il arrive, le plugin continue d'attendre, quelle que soit la durée ; il n'abandonne que lorsque le transfert stagne réellement, ce qu'il remarque maintenant plus rapidement que l'ancien délai fixe.

2.0.6 - 2026-08-03

Les albums envoyés depuis un téléphone sont désormais lus sans blanc entre les pistes, et la radio internet est reconnue comme de la radio au lieu d'être traitée comme une chanson exceptionnellement longue.

Les albums sont lus sans blanc

Quoi : lorsque vous envoyez un album entier depuis votre téléphone, MusicBee passe désormais d'une piste à l'autre sans pause — ainsi, les enregistrements en direct, les sets de DJ et les œuvres classiques continues restent cohérents.

Pourquoi : la norme permet à une application de contrôle de dire "voici ce qui vient ensuite", ce qui rend possible une jointure transparente. Cette instruction n'était pas du tout acceptée, de sorte que l'application n'avait nulle part où placer la piste suivante et l'annonçait comme si c'était la piste actuelle — la cause du problème d'album corrigé dans la version précédente. Elle est maintenant acceptée correctement : la piste suivante est récupérée pendant que la piste actuelle est encore en lecture, et le lecteur de MusicBee franchit la limite.

La radio internet est reconnue comme de la radio

Quoi : une station en direct envoyée à MusicBee est lue en tant que flux et n'est jamais téléchargée.

Pourquoi : télécharger une émission n'a aucun sens — elle n'a pas de fin, et il n'y a rien à parcourir — mais le plugin n'avait auparavant aucun moyen de la distinguer d'un fichier de musique, il commençait donc à la récupérer et s'arrêtait une fois que le téléchargement dépassait une taille fixe. L'application de contrôle indique lequel des deux elle envoie, et cela est maintenant lu directement. Pas de réglage, pas de devinette.

Les longues pistes haute résolution conservent leur copie

Quoi : les fichiers DSD et les longs enregistrements 24 bits peuvent désormais être parcourus comme n'importe quelle autre piste.

Pourquoi : ils étaient soumis à la limite de taille mentionnée ci-dessus — un mouvement haute résolution de 20 minutes ou une piste DSD de 10 minutes la dépassaient tous deux — de sorte que leur copie était abandonnée et que le curseur de position cessait de fonctionner précisément pour le matériel le plus susceptible d'être intéressant à parcourir. La radio étant correctement identifiée, aucune limite de taille n'est nécessaire.

2.0.5 - 2026-08-03

Deux correctifs pour piloter MusicBee depuis un téléphone : le volume signifie désormais la même chose aux deux extrémités, et la diffusion d'un album entier continue de fonctionner après le premier morceau.

Le volume de votre téléphone correspond au volume de MusicBee

Quoi : mettre le volume au maximum sur votre téléphone atteint désormais le maximum dans MusicBee, et le réglage de MusicBee est correctement affiché sur le téléphone.

Pourquoi : le plugin n'a jamais indiqué à l'application de contrôle quel était son volume maximal, de sorte que chaque application devait deviner. L'une d'elles s'est fixée à 69, ce qui signifiait que son 100 % n'atteignait que 69 % dans MusicBee, tandis que le 100 % de MusicBee revenait à 144 % sur le téléphone — et les boutons de volume du téléphone ne pouvaient jamais tout à fait atteindre le maximum. Le rendu indique désormais clairement la plage, de sorte que les deux extrémités parlent de la même échelle.

La diffusion d'un album conserve ses titres et son curseur de position

Quoi : chaque morceau d'un album envoyé depuis un téléphone affiche désormais son titre correct et peut être parcouru, pas seulement le premier.

Pourquoi : une application de contrôle annonce le morceau suivant une fraction de seconde après le morceau actuel, et cette annonce annulait la copie en cours de récupération pour le morceau sur le point d'être lu — de sorte que la plupart des morceaux revenaient silencieusement à la lecture sur le réseau, ce qui entraînait la perte du titre et de la possibilité de sauter. Les copies de plusieurs morceaux sont désormais conservées côte à côte, de sorte qu'une annonce ne peut plus annuler celle en cours d'utilisation.

2.0.4 - 2026-08-02

La musique envoyée à MusicBee depuis un téléphone ou un autre server se comporte désormais comme une vraie piste : vous pouvez vous y déplacer, et elle affiche son titre correct immédiatement. Plus un correctif pour les streamers hi-fi qui se présentent comme un seul appareil combiné.

Se déplacer dans une piste envoyée d'ailleurs

Quoi : faire glisser le curseur de position fonctionne désormais pour une piste envoyée depuis votre téléphone, un NAS ou un autre server multimédia. Pour rendre cela possible, MusicBee télécharge une copie de la piste dans un dossier temporaire pendant qu'elle commence à jouer, et lit cette copie. Cela prend environ une seconde sur un réseau domestique, la copie est supprimée dès que vous envoyez une autre piste, et tout reste est effacé au prochain démarrage de MusicBee.

Pourquoi : MusicBee peut démarrer et arrêter quelque chose qu'il écoute sur le réseau, mais il ne peut pas s'y déplacer — le curseur semblait donc sauter puis revenir directement à sa position initiale, sans explication. La lecture d'un fichier ordinaire sur votre propre disque supprime entièrement la limitation plutôt que de la contourner.

Titre et longueur corrects dès la première note

Quoi : une piste envoyée par une application qui ne donne pas d'extension de fichier ordinaire à ses fichiers affiche désormais son vrai titre et sa longueur dès qu'elle commence, au lieu d'apparaître comme une longue adresse web.

Pourquoi : MusicBee identifie une piste — et trouve ses tags — à partir de l'extension du fichier, et certains lecteurs distribuent des adresses sans aucune extension. La copie locale porte toujours la bonne, de sorte que la piste est reconnue quelle que soit la façon dont l'application d'envoi l'appelle.

Un saut impossible est désormais signalé

Quoi : si le rendu ne peut pas réellement se déplacer vers le point demandé, l'application de contrôle en est informée et le signale.

Pourquoi : elle répondait auparavant "terminé" quoi qu'il arrive, de sorte que le curseur revenait une seconde plus tard sans explication. Un refus honnête est plus facile à gérer qu'un refus silencieux.

Les streamers hi-fi combinés sont lus correctement

Quoi : lorsque MusicBee lit sur un appareil qui se présente comme une unité combinée — un streamer Marantz ou Denon, où le lecteur se trouve dans un wrapper du fabricant aux côtés d'un server multimédia — le plugin lit désormais les détails propres au lecteur au lieu de ceux du server multimédia.

Pourquoi : il demandait à la mauvaise moitié de l'appareil quels formats audio il pouvait gérer, n'obtenant aucune réponse utilisable, et continuait sans jamais vérifier — exactement le matériel où la gestion des formats doit être la plus juste. La description du modèle d'un appareil est également désormais prise en compte lors de sa correspondance avec un profil d'appareil ; elle était lue au mauvais endroit et ignorée.

2.0.3 - 2026-08-02

Le rôle de lecture évolue : MusicBee peut désormais recevoir de la musique qui ne se trouve pas déjà dans sa bibliothèque — un fichier sur votre téléphone, sur un NAS, sur un autre server — au lieu de se limiter aux pistes qu'il possède déjà.

Écoutez la musique envoyée depuis votre téléphone, pas seulement votre propre bibliothèque

Quoi : lorsque vous utilisez une application de contrôle telle que Symfonium ou BubbleUPnP pour envoyer de la musique à MusicBee, la piste n'a plus besoin de provenir de la propre bibliothèque de MusicBee. Un fichier stocké sur le téléphone lui-même, sur un NAS ou sur un autre server multimédia est désormais lu. Le titre et la durée proviennent de l'application qui l'a envoyé, de sorte que la piste s'affiche correctement même si MusicBee n'a jamais vu le fichier.

Pourquoi : le rôle de lecture a été conçu pour le cas où vous parcourez la bibliothèque de ce PC depuis votre téléphone et appuyez sur une chanson — la piste était déjà sur le PC, donc MusicBee a simplement lu son propre fichier. Tout ce qui arrivait d'ailleurs était discrètement ignoré, ce qui rendait la fonctionnalité inutile pour le cas tout aussi naturel de pousser de la musique depuis le téléphone vers les bonnes enceintes.

Une piste qui ne peut pas être lue le signale

Quoi : si le rendu ne peut pas réellement lire ce qui lui a été envoyé, il le signale désormais à l'application qui l'a envoyé.

Pourquoi : il répondait auparavant "reçu" à tout, de sorte que l'application de contrôle continuait et appuyait sur lecture. Sans rien de réellement chargé, MusicBee redémarrait la piste qui avait été laissée auparavant — et si ce fichier avait disparu, se plaignait que sa source n'avait pas pu être trouvée. L'erreur nommait une piste sans rapport et ne pointait nulle part près du vrai problème.

L'envoi d'une nouvelle piste en pause lit désormais cette piste

Quoi : si MusicBee est en pause et que votre application de contrôle lui envoie quelque chose de nouveau, la nouvelle piste démarre.

Pourquoi : la reprise prenait la priorité sur le chargement, de sorte que la piste en pause reprenait là où elle s'était arrêtée et la piste que vous veniez de choisir était abandonnée sans un mot.

2.0.2 - 2026-08-01

La recherche est le thème : elle fonctionne désormais par artiste, renvoie les bons résultats et est rapide sur une grande bibliothèque. De plus, une nouvelle façon d'orienter la lecture aléatoire de votre application de contrôle, et un correctif pour trois boutons qui ne menaient nulle part.

La recherche par artiste fonctionne réellement

Quoi : la recherche d'un artiste depuis votre application de contrôle renvoie désormais la musique de cet artiste. La recherche correspond à la fois à l'artiste de la piste et à l'artiste de l'album, de sorte qu'une compilation est trouvée, que vous tapiez le nom de l'interprète ou le nom sous lequel l'album est classé.

Pourquoi : une recherche d'artiste était auparavant confondue avec « donnez-moi tout de ce genre » — l'artiste que vous aviez tapé était ignoré et toute la bibliothèque était renvoyée, de sorte qu'une recherche qui aurait dû correspondre à quelques centaines de pistes en renvoyait des dizaines de milliers. Ne faire correspondre qu'un seul des deux champs d'artiste aurait discrètement perdu la moitié des résultats, c'est pourquoi les deux sont vérifiés.

La page des résultats de recherche est correcte

Quoi : faire défiler une longue liste de résultats de recherche permet désormais de la parcourir. Chaque page sur laquelle vous faites défiler est la page que vous obtenez.

Pourquoi : le server répondait auparavant à chaque requête avec la première poignée de résultats tout en signalant le nombre total de correspondances, de sorte qu'une application qui cherchait plus continuait à recevoir les mêmes éléments et n'atteignait jamais la fin.

La recherche est beaucoup plus rapide sur une grande bibliothèque

Quoi : une recherche exécute désormais une seule requête pour l'ensemble des résultats et ne lit que les balises de la page que vous consultez.

Pourquoi : chaque page réexécutait auparavant la requête sur la bibliothèque, puis chargeait les balises de chaque correspondance — des milliers d'entre elles — pour en afficher une douzaine. Sur une grande collection, cela provoquait une pause à chaque défilement. Les résultats sont également supprimés chaque fois que la bibliothèque est actualisée, de sorte qu'une modification n'est jamais servie périmée.

Orientez votre lecture aléatoire vers un filtre

Quoi : un nouveau paramètre Lectures aléatoires depuis dans l'onglet Options de la bibliothèque. Laissez-le sur Toute la musique et les dossiers Pistes aléatoires / Albums aléatoires de votre application de contrôle se comportent comme avant ; choisissez l'un de vos filtres MusicBee et chaque demande aléatoire sera tirée de ce filtre à la place. Les filtres cachés sont également proposés.

Pourquoi : un dossier de lecture aléatoire demande une tranche de « tout », et c'est la seule requête qui ne contient aucun indice sur ce que vous vouliez dire — elle puisait donc toujours dans toute la bibliothèque, paroles et tout. C'est ici que vous dites ce que « tout » signifie. L'ouverture d'un dossier de lecture aléatoire à l'intérieur d'un filtre sur l'appareil mélange toujours ce filtre : un choix que vous faites en naviguant l'emporte sur le paramètre.

Aide, GitHub et vérification des mises à jour mènent à de vraies pages

Quoi : les boutons Aide et GitHub dans la boîte de dialogue des paramètres, ainsi que la vérification automatique d'une nouvelle version, ouvrent désormais les pages qu'ils nomment.

Pourquoi : les trois étaient construits à partir d'une forme abrégée du nom du plugin pour laquelle aucune page n'a jamais existé, de sorte que chacun échouait silencieusement — les boutons semblaient ne rien faire et la vérification des mises à jour ne signalait jamais rien, quelle que soit la durée depuis laquelle une nouvelle version était sortie.

2.0.1 - 2026-07-26

Une série de correctifs de lecture et de navigation, axés sur les podcasts et les applications de contrôle (telles que BubbleUPnP) qui gèrent la lecture et la lecture aléatoire.

Les podcasts commencent à jouer sans pause

Quoi : un épisode de podcast téléchargé indique désormais sa longueur réelle et sa taille de fichier dès le départ, directement du fichier sur le disque vers les métadonnées multimédias.

Pourquoi : sans durée indiquée, un contrôleur comme BubbleUPnP rescannait tout le flux audio chaque fois que vous appuyiez sur lecture, juste pour déterminer la durée de l'épisode — la lecture ne commençait donc qu'après une pause notable. La durée étant maintenant annoncée, la lecture démarre proprement.

Les podcasts apparaissent lorsque vous naviguez par artiste

Quoi : le nom d'une émission de podcast est désormais classé comme son artiste — reflété dans les champs Artiste et Artiste de l'album (et leurs variantes de tri), exactement comme il remplit déjà le champ Album.

Pourquoi : un chemin de navigation qui regroupe par un champ d'artiste trouvait auparavant l'artiste de chaque podcast vide et aboutissait à un niveau vide. Traiter l'émission elle-même comme l'artiste — ce qui est cohérent avec le fait de la traiter comme l'album — signifie que ces chemins atteignent désormais les épisodes au lieu de rien.

"Récemment joués" et le casting fonctionnent correctement après un redémarrage

Quoi : la lecture d'un podcast, d'un audiolivre, d'un inbox ou d'une piste radio directement — sans y naviguer d'abord, comme le font la liste "Récemment joués" de BubbleUPnP et les cibles de casting — n'échoue plus. Le plugin charge désormais la piste à la demande lorsqu'elle est demandée par son identifiant.

Pourquoi : ces listes demandent une piste juste après le redémarrage de MusicBee, avant que quoi que ce soit n'ait été parcouru, de sorte que le plugin n'avait jamais vu l'identifiant et répondait "Mauvais identifiant". Il charge maintenant de force les sources pertinentes lors de cette première demande directe et trouve la piste.

"Pistes aléatoires" et "Albums aléatoires" renvoient des résultats

Quoi : les dossiers de lecture aléatoire "Pistes aléatoires" et "Albums aléatoires" de BubbleUPnP, qui demandent une tranche aléatoire de toute la bibliothèque, sont désormais remplis.

Pourquoi : ce sont des recherches sans titre à faire correspondre, et le plugin y répondait auparavant à partir d'une liste interne vide, de sorte qu'elles ne montraient jamais rien. Elles sont maintenant servies — correctement paginées — à partir du même chemin de requête à la demande que le reste de la navigation utilise.

Les albums avec une balise vide listent leurs pistes

Quoi : l'ouverture d'un album qui était regroupé sur une valeur vide — par exemple des pistes d'inbox qui ne portent pas d'année — affiche désormais ses pistes.

Pourquoi : la correspondance qui rassemble les pistes d'un album traitait "cette balise est vide" comme "aucune correspondance", de sorte que tout album formé à partir d'un champ vide ne naviguait vers rien. Une valeur de groupe vide correspond désormais correctement aux pistes qui la partagent.


2.0.0 - 2026-07-22

Voici la traduction en français de vos notes de version, en respectant toutes vos règles :

Ceci est la première version publique du fork open-source yaiol du plugin MusicBee UPnP. 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 afin que la justification de chaque changement soit 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, saut en avant ou en arrière, saut à un point de la piste, et modification du volume ou mise en sourdine.

Pourquoi : cela transforme votre téléphone en 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é en état de marche.

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), laisser les autres 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'il lui est demandé 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 - FLAC 5.1 non automatiquement mixé

Quoi : la limitation du nombre de canaux dans MediaServerDevice.GetEncodedFile était If StereoOnly OrElse Not isPcmData Then channelCount = 2. La clause Not isPcmData mixait silencieusement toutes les transcodifications non-PCM (FLAC, MP3, AAC, Ogg) en stéréo, quel que soit le nombre de canaux source, ce qui rendait 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 mixage silencieux rendait l'option de transcodage FLAC inutile pour l'écoute surround. Avec N02, cela fait ce qu'il faut.


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érer chaque piste, Library_GetFileTags complet par fichier, assembler 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 (LazyBrowseEnsureLazyEndpointInMemory → 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 :

  1. Restauration automatique en cas d'échec de liaison. HttpServer.Start essaie le port configuré, et en cas de SocketException, scanne jusqu'à 20 ports vers le haut pour trouver le premier disponible. Le port réellement lié est enregistré dans un nouveau Plugin.boundServerPort, et tout ce qui annonce le server - les URL LOCATION SSDP (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ésormais boundServerPort au lieu de Settings.ServerPort. Les clients UPnP découvrent le vrai port via SSDP, donc un port déplacé est transparent pour les rendus.
  2. Notification à l'utilisateur. Lorsqu'une restauration se produit (le port enregistré n'est pas celui utilisé), une MessageBox localisé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.
  3. Récupération après redémarrage. RestartServer (le chemin de redémarrage de la sauvegarde des paramètres) déréférençait Plugin.controller / Plugin.server aveuglément. Si l'Initialise initial levait une exception avant de les créer (exactement ce qu'un échec de liaison provoquait), la prochaine sauvegarde des paramètres entraînait une NullReferenceException - laissant un plugin à moitié mort. Il les recrée et les démarre désormais lorsque Nothing, 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 échouait donc 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 entrait alors 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 à 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éclarations ServerPort + la restauration en cas d'échec d'analyse des paramètres.
  • Plugin.boundServerPort (nouveau champ partagé) contient le port d'écoute actif ; activeServerPort reste 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 premier TcpListener.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 sur le groupe de multidiffusion (VPN / point à point)

Quoi : les annonces SSDP sont envoyées au groupe de multidiffusion UPnP (239.255.255.250) au lieu d'une adresse de diffusion IP. L'erreur inoffensive "cannot access a disposed object" 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'ancien envoi de diffusion échouait avec "invalid argument" et les annonces étaient manquées, de sorte que le plugin était invisible pour les clients sur ces liens. L'annonce au groupe de multidiffusion 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 MusicBee de l'utilisateur) 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 organisés (par exemple, "Pistes 5 étoiles", "Récemment ajoutées", "Classique → Baroque") s'attendent à les retrouver lors de la navigation dans le plugin à partir d'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 le MetaDataType 165 de MusicBee (Sort Album Artist) 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'artistes 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 AlbumArtist à 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 - Illustration 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 tous les sous-dossiers, se retrouvaient orphelins au niveau racine.

Pourquoi : présent dans le plugin original depuis 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 XML illégaux

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 → retour tronquant 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 un. 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 "Random Albums" 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 (regroupés par AlbumArtist+Album) et émettent chacun comme un conteneur musicAlbum approprié avec 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/radio/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" à des résultats utiles, ciblés et lisibles. 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 du 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 (donc 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 / ResetCacheBumpSystemUpdateId).

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 - Illustration d'abonnement aux podcasts

Quoi : les vignettes de podcast 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 route 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ûr pour les URL qui survit intact à la couche HTTP (PodcastSlug / podcastSubIdBySlug).

Pourquoi : l'illustration d'abonnement s'affiche 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éfinissez 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 tagué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 dupliquées côte à côte à la racine.

Pourquoi : un utilisateur ayant plusieurs vues liées 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 liées 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 sur le 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 perdait 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 la navigation, et une liste de lecture épinglée directement sous le dossier Listes de lecture, au lieu que toutes les épingles se regroupent en un seul bloc à la fin de la racine. Chaque raccourci épinglé se trouve avec son propre type.

Pourquoi : à mesure qu'un utilisateur épingle davantage de raccourcis, un seul bloc final de filtres et de listes de lecture mélangés devient plus difficile à parcourir et sépare chaque raccourci du dossier auquel il appartient. Le regroupement des éléments épinglés sous leur propre catégorie maintient 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 reçu une mise en page de navigation gauche avec des sections : Général / Lecture / Bibliothèque / Profils d'appareil / Diagnostics.

Pourquoi : l'original était une seule 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 d'être davantage des paramètres d'application modernes.


Renommage de l'assemblage + plugin (pas d'ID F - note d'empaquetage)

Quoi : la DLL compilée est nommée mb_UPnP_yaiol.dll et le plugin se déclare comme "MusicBee UPnP (yaiol)". Distinct de l'original mb_Upnp.dll.

Pourquoi : les utilisateurs peuvent installer yaiol aux côtés 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. Drapeau de session persistant Plugin.MaxConnectionsHit. Défini dans WaitOnSendBarrier lorsqu'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. Drapeau 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 réellement ê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 bien pour les utilisateurs techniques qui déboguent, mais un utilisateur non technique confronté à "le son de l'appareil est mauvais" 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'User-Agent de l'appareil n'a jamais correspondu à aucun profil, est revenu à Générique).
  • Retrait NextURI déclenché (F13 - sans blanc désactivé 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 sur la ligne du bas près de Enregistrer/Annuler (actuel : y=410 empilées horizontalement).
  • Chaque badge a un drapeau de session persistant correspondant dans Plugin qui 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 "le paramètre enregistré nécessite un redémarrage", prenez un instantané d'exécution à Plugin.Initialise() et comparez-le à Settings.* après Settings.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 de leur modification - 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 champs 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 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 de 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 lorsque le plugin est complet en fonctionnalités (traduire par morceaux pendant que les chaînes sont encore en mouvement 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 seul bundle - "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 restauration 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 qu'il ne peut pas. Aucun des deux projets 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 de base et lecture

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 drapeaux 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 de profil d'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 choisi par l'utilisateur, octet par octet (modulo l'encadrement HTTP).

Pourquoi : selon les témoignages du forum, 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 une option. 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, quelle que soit la prise en charge 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 (CheckedChanged sur chacun désabonne l'autre avant de basculer, pour éviter une boucle infinie).
  • Site de décision : Settings.ForceTranscodingstreamingProfile.ForceTranscoding dans WriteAudioFileDIDL.

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 donne des données big-endian correctes. F05 bascule l'ordre des octets par profil.

Pourquoi : sans cela, certains appareils émettent un mur de statique. Le symptôme est dramatique et la cause invisible sans connaissance de l'encodage PCM - l'interrupteur donne aux utilisateurs une solution par tâtonnement.


F06 - "Ne pas utiliser de PCM brut" par profil

Quoi : lorsque l'appareil prétend 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 énorme inconnue").

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 valeur 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 au milieu de la piste 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 la 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.FlacSelectedIndex = 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 la prise en charge de 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 dans 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 dit 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 nextPlaySourceUrl stocke 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() sur MediaRendererDevice. 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 → appeler QueueNext avec la nouvelle URL (ou QueueNext("") pour effacer - ce qui respecte F08 DoNotClearNextUri).
  • Câblé dans Plugin.ReceiveNotification sous NotificationType.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 le 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 au détecteur de transition F15 dans OnAvTransportStatusCheck :

  • Répéter tout : MusicBee transmet l'URL de "boucle" correcte (piste 1 à la fin de la liste) à Plugin.QueueNext lui-même. Aucune logique de plugin spéciale n'est nécessaire - l'appareil y passe et le détecteur F15 appelle Player_PlayNextTrack comme 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 y passe (nouveau handle de flux, même source). Le détecteur F15 interroge maintenant Player_GetRepeat() - si c'est RepeatMode.One, il saute l'appel Player_PlayNextTrack afin que MusicBee n'avance pas l'index NPL loin de la piste en boucle.

Pourquoi : sans le saut Répéter un, l'appel de Player_PlayNextTrack à la transition sans blanc ferait avancer MusicBee à la piste suivante de 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 remarque 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. Confus, 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 resynchroniser à 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 lastSourceUrl suit l'URL source en cours de lecture.
  • QueueNext lit FilePropertyType.SampleRate/Channels/Kind pour les pistes actuelle et en file d'attente et enregistre NextUri:FormatChange en 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 Seek réussi, ce qui corrige le cas de "pas de resynchronisation du tout". F17 corrige le décalage restant allant jusqu'à 1s 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, une recherche à 2:30.500 ancrée sur le rapport "2:30" de l'appareil ferait afficher la barre de progression avec environ 500ms de retard sur 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 aberrant d'alignement sur une image clé.


F18 - Verrouillage du flux continu / NextURI

Quoi : deux verrous sont désormais en place :

  1. Exécution : QueueNext retourne False tôt lorsque Settings.ContinuousOutput est activé. Le flux continu est son propre mécanisme sans blanc (un long flux concaténé) ; l'envoi de SetNextAVTransportURI en plus de cela perturbe l'appareil quant à savoir si chaque piste est une URI discrète ou fait partie du flux continu.
  2. Interface utilisateur : lorsque l'utilisateur coche la case globale de flux continu, le forceNativeStream du profil actuellement affiché est automatiquement décoché. 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 un 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, la 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 - Type mime MP3 → audio/mpeg

Quoi : le type mime MP3 conforme aux normes est audio/mpeg, et non audio/mp3. Ce dernier est un nom erroné 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'est 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 mimes standard et expérimentaux.

Pourquoi : le préfixe x- marque les mimes expérimentaux/non officiels. Certains rendus se comportent mieux avec la forme standard. Petit réordonnancement, impact réel.


F22 - Prise en charge du type mime Opus

Quoi : reconnaît Opus comme un codec audio streamable ; envoie le 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 - Restauration du mime AAC / ALAC

Quoi : si un appareil prend en charge AAC ou ALAC mais ne les annonce pas explicitement dans sa description de service UPnP, le plugin les propose quand même en tant que solution de secours.

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 - Drapeau de type DLNA pour les flux WAV natifs + encodés

Quoi : le drapeau 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. Divise maintenant 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 de fichier source non continu avait déjà cette correction ((bitrate_kbps * 1000) \ 8 = octets/sec) ; seul le chemin de 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 de fichier source + chemin de 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 requête 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 cherchaient dans un MP3 transcodé voyaient la recherche ignorée silencieusement ou étaient ramenés au début de la piste.

Implémentation : GetEncodeFeature dans ItemManager.vb a été restructuré pour diviser 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 l'interrupteur global Settings.ContinuousOutput. La branche DIDL du 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 "ceci est un flux" supprime un piège auquel l'utilisateur ne devrait pas avoir à penser.

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 - Restauration de l'annonce du 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 en lecture, mais à ce moment-là, l'appareil aurait pu jouer 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 la 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 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 dans les cas d'utilisation de saut rapide (suivant manuel ou transition sans blanc). Désormais, 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 au premier tic de changement d'état.

Implémentation : quatre lignes en haut de PlayToDevice, associées à l'ancrage précis au moment de la 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 :

  1. 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.
  2. 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 drapeaux.


F36 - Exception de rendu fermé

Quoi : a enveloppé Plugin.ReceiveNotification dans un Try/Catch de haut niveau qui enregistre toute exception non intercepté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 haut niveau est le dernier filet de sécurité 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 un défilement. F37 marque 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 lors d'une recherche est l'un de ces bugs dont personne ne peut deviner la cause - l'utilisateur pense "bizarre, j'ai essayé de faire un défilement en avant et maintenant il joue la chanson suivante". La correction est mécanique : le 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 timeSeekRange.dlna.org / npt entrants, les drapeaux DLNA.ORG_OP reflètent les capacités réelles du flux (avec DisablePcmTimeSeek en option 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 reçu 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 interrupteur "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 deux chemins d'ajout direct et d'ajout à 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 UX qui s'est avérée ne pas en être une ici.


F40 - Plus de connexions maximales + journal d'avertissement

Quoi : la limite de flux simultanés du plugin (SemaphoreSlim autour de Sockets_Stream_File / Sockets_Encoder_Start) était un 4 codé en dur. 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 des 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) dans MusicBeeUpnp.vb ; les deux sites d'appel (MediaServerDevice.GetFile, Encoder.StartEncode) l'utilisent.
  • Settings.MaxConnections persisté dans la v8 du schéma des paramètres.
  • Plugin.MaxConnectionsHit est un drapeau de session persistant défini dans WaitOnSendBarrier ; ne se réinitialise qu'au redémarrage de MusicBee.
  • SettingsDialog.maxConnectionsBadge est une étiquette rouge en gras à (16, 410) qui ne s'affiche que lorsque Plugin.MaxConnectionsHit est 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 - Journalisation "encodage dû à ReplayGain/DSP"

Quoi : au lieu de lignes de journal séparées "encodage pour RG" / "encodage pour DSP", la seule ligne 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 - Journalisation "le rendu ne prend pas en charge le codec source"

Quoi : ajout d'une ligne de journal StreamDecision par piste lue vers un appareil qui indique soit "codec natif" soit "transcodage CODEC→CODEC raison=…". Le champ de 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 pourraient vouloir que la restauration 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>. Le même changement est 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 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éponse GetProtocolInfo de 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, la restauration de F32 devinera").
  • Activate:NoSinkInfo - se déclenche une fois si l'appareil n'a renvoyé aucun élément <Sink>. Signifie que SupportedMimeTypes reste Nothing et IsCodecSupported se dégrade en "supposer que tout fonctionne" - contexte utile lorsque des erreurs ultérieures de "l'appareil a refusé le flux" apparaissent.

Pourquoi : avant F44, ces retours silencieux de capacité laissaient les utilisateurs deviner pourquoi leurs pistes étaient soit transcodées contre toute attente, soit refusées par l'appareil. Désormais, une simple recherche de Activate: indique 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é), 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 n'annonce que sur les adaptateurs réseau réels

Quoi : en mode d'interface Automatique, le plugin s'annonçait (SSDP) sur chaque adaptateur IPv4 opérationnel. Sur une machine qui exécute également un tunnel VPN (NordLynx) ou un commutateur virtuel (Hyper-V / WSL / Docker), la même bibliothèque était également annoncée sur chacun de ces adaptateurs, de sorte que le point de contrôle à partir duquel vous diffusiez découvrait le server deux ou trois fois et listait la bibliothèque comme des copies en double. Le mode automatique ne conserve désormais que les adaptateurs qui ont une passerelle par défaut IPv4 réelle (HasIPv4Gateway) - ce que les adaptateurs de tunnel et de commutateur virtuel n'ont pas - de sorte que ceux-ci sont supprimés de la liste d'annonce. Une adresse épinglée par l'utilisateur l'emporte toujours (annonce uniquement sur cette interface), et si aucun adaptateur ne signale une passerelle, le sélecteur revient à tous les adaptateurs, de sorte que la liste d'adresses annoncées n'est jamais vide et le plugin ne peut pas devenir invisible.

Pourquoi : le doublon n'est pas causé par "être sur un VPN" - il est causé par l'annonce simultanée sur l'adaptateur LAN et l'adaptateur de tunnel/virtuel, de sorte qu'un point de contrôle voit le même server à deux adresses. Un VPN grand public (NordVPN/NordLynx) ne tunnelise que le trafic Internet ; le rendu DLNA vit sur le LAN et le trafic du sous-réseau local contourne le tunnel, de sorte que l'adaptateur de tunnel n'atteint de toute façon jamais un rendu - le supprimer supprime une copie fantôme, jamais un chemin fonctionnel. Le test de passerelle est le signal bon marché et fiable qui sépare un véritable adaptateur LAN/Wi-Fi d'un tunnel ou d'un commutateur virtuel. Complète N05 (qui a corrigé comment les annonces sont envoyées sur de tels liens - multidiffusion au lieu de diffusion) ; F46 régit quels adaptateurs sont annoncés.

Limite connue : un VPN maillé / d'accès à distance (Tailscale, ZeroTier, WireGuard-to-home) dont les rendus vivent réellement à travers le tunnel présente généralement un adaptateur sans passerelle par défaut, de sorte que le mode automatique le supprime également. Ces utilisateurs épinglent plutôt l'adresse VPN, qui a la priorité sur le filtre de passerelle.

Sommaire