What's new
2.0.2 - 2026-07-20
- Ένα πρότυπο δεν μπορεί πλέον να διαγραφεί όσο οποιοσδήποτε κόμβος το ακολουθεί — το κουμπί διαγραφής παραμένει απενεργοποιημένο, οπότε ένας κόμβος δεν μπορεί ποτέ να μείνει ορφανός. Κάθε γραμμή προτύπου δείχνει τώρα έναν ζωντανό αριθμό (n) των ακολούθων του, ο οποίος εξηγεί μια απενεργοποιημένη διαγραφή με μια ματιά. Η διαγραφή ενός αχρησιμοποίητου προτύπου επίσης παραμένει τώρα, αντί οι προεπιλεγμένες ρυθμίσεις να επανεμφανίζονται σιωπηλά στην επόμενη φόρτωση.
- Ένα νέο κουμπί funnel δίπλα στο δέντρο προβολής δείχνει ακριβώς ποιοι κόμβοι ακολουθούν ένα πρότυπο: φιλτράρει το δέντρο μόνο στους ακόλουθους του επιλεγμένου προτύπου, επανα-φιλτράρει καθώς επιλέγετε άλλα πρότυπα και επαναφέρει το πλήρες δέντρο όταν απενεργοποιηθεί.
- Οι κόμβοι Radio και Podcasts είναι πλέον μόνιμα ζευγαρωμένοι με το πρότυπο της κατηγορίας τους — αναδιαμορφώστε το πρότυπο στην καρτέλα Paths και ο κόμβος ακολουθεί από μόνος του. Δεν υπάρχει τίποτα να εφαρμοστεί, οπότε το κουμπί Apply είναι απενεργοποιημένο για αυτούς. Η λίστα προτύπων το αντικατοπτρίζει αυτό με δύο ζώνες, Standard (όπου δημιουργούνται όλα τα νέα πρότυπα) και Reserved (Radio + Podcasts).
2.0.1 - 2026-07-20
- Τα πρότυπα διαδρομής είναι πλέον ζωντανά συνδεδεμένα με τους κόμβους που τα χρησιμοποιούν. Η εφαρμογή ενός προτύπου κάνει τον κόμβο να το ακολουθεί: επεξεργαστείτε το πρότυπο αργότερα και κάθε κόμβος που το ακολουθεί αναδιαμορφώνεται αμέσως — χωρίς να χρειάζεται να το αναζητήσετε και να το εφαρμόσετε ξανά κόμβο προς κόμβο. Το δέντρο προβολής δείχνει ποιο πρότυπο ακολουθεί κάθε κόμβος αμέσως μετά το όνομά του, μια μετονομασία εμφανίζεται εκεί αμέσως και η διαγραφή ενός προτύπου σας λέει πρώτα πόσοι κόμβοι το ακολουθούν (διατηρούν την τρέχουσα διάταξή τους και απλώς σταματούν να ακολουθούν οτιδήποτε).
- Η εφαρμογή ενός προτύπου σε έναν κρυφό κόμβο τον κάνει επίσης ορατό ξανά — η εφαρμογή είναι η χειρονομία "δείξε μου αυτό, διαμορφωμένο έτσι", ενώ η απόκρυψη παραμένει με το πλαίσιο ελέγχου Ορατό. Το δεσμευμένο πρότυπο "Κρυφό" που αντικαθιστά αυτό έχει φύγει.
- Το δέντρο προβολής δεν χάνει πλέον τη θέση σας: τα σημάδια επιλογής, οι επεκτεταμένοι φάκελοι και η θέση κύλισης επιβιώνουν από την εφαρμογή προτύπων και άλλες ανανεώσεις.
2.0.0 - 2026-06-16
Αυτή είναι η πρώτη δημόσια έκδοση του open-source fork yaiol του MusicBee UPnP plugin. Παρουσιάζεται σε δύο μέρη: όλα όσα είναι νέα σε αυτό το fork, και στη συνέχεια οι διορθώσεις και βελτιώσεις που έγιναν στο αρχικό plugin. Κάθε στοιχείο διατηρεί τη μορφή Τι / Γιατί του εσωτερικού καταλόγου χαρακτηριστικών του έργου, έτσι ώστε η λογική πίσω από κάθε αλλαγή να είναι εμφανής, όχι μόνο η αλλαγή.
Νέα σε αυτό το fork
MediaRenderer - αναπαραγωγή στο MusicBee
N01 - MusicBee ως renderer αναπαραγωγής
Τι: κανονικά αυτό το plugin λειτουργεί μονόδρομα: ένα τηλέφωνο ή άλλη συσκευή περιηγείται στη βιβλιοθήκη του MusicBee και αναπαράγει τη μουσική στον εαυτό της. Αυτή η λειτουργία προσθέτει την αντίθετη κατεύθυνση - επιτρέπει στο MusicBee να είναι ο αναπαραγωγέας. Από μια εφαρμογή ελεγκτή στο τηλέφωνό σας (όπως το BubbleUPnP) μπορείτε να επιλέξετε το MusicBee του υπολογιστή σας ως τη συσκευή αναπαραγωγής, και στη συνέχεια να το ελέγχετε από το χέρι σας: αναπαραγωγή, παύση, διακοπή, παράλειψη εμπρός ή πίσω, μετάβαση σε ένα σημείο του κομματιού, και αλλαγή της έντασης ή σίγαση.
Γιατί: μετατρέπει το τηλέφωνό σας σε τηλεχειριστήριο για τη μουσική που βρίσκεται ήδη στον υπολογιστή σας. Καθίστε στον καναπέ, περιηγηθείτε στη βιβλιοθήκη σας στο τηλέφωνο, πατήστε ένα κομμάτι, και αυτό θα ακουστεί από τα ηχεία που είναι συνδεδεμένα στον υπολογιστή σας - με πλήρη έλεγχο από εκεί που κάθεστε. Το αρχικό plugin δεν το παρείχε ποτέ ως λειτουργικό χαρακτηριστικό.
Ενεργοποίηση: είναι απενεργοποιημένο από προεπιλογή, επειδή η ενεργοποίησή του επιτρέπει σε οτιδήποτε στο οικιακό σας δίκτυο να ξεκινήσει την αναπαραγωγή στον υπολογιστή σας. Το ενεργοποιείτε με ένα πλαίσιο επιλογής στην καρτέλα Γενικά του παραθύρου ρυθμίσεων. Οι τρεις ρόλοι του plugin έχουν ο καθένας το δικό του πλαίσιο επιλογής εκεί - κοινή χρήση της βιβλιοθήκης μου (Server), άσε τους άλλους να παίξουν σε μένα (Renderer), και αναπαραγωγή σε άλλες συσκευές (Control Point) - και το παράθυρο διαλόγου εμφανίζει μόνο τις καρτέλες ρυθμίσεων που χρειάζονται οι ρόλοι που ενεργοποιήσατε, οπότε δεν αντιμετωπίζετε ποτέ επιλογές που δεν σας αφορούν.
Διαχωρισμός των μηχανών σας: μπορείτε να δώσετε στον renderer οποιοδήποτε όνομα θέλετε (ξεκινά ως "MusicBee (yaiol)"). Αυτό το όνομα είναι αυτό που εμφανίζεται στη λίστα στόχων αναπαραγωγής του τηλεφώνου σας, οπότε όταν περισσότεροι από ένας υπολογιστές τρέχουν το MusicBee μπορείτε να ξεχωρίσετε ποιος είναι ποιος. Μια αλλαγή ονόματος τίθεται σε ισχύ αμέσως, χωρίς επανεκκίνηση.
Καλύτερος δυνατός ήχος όταν παίζει στον εαυτό του: όταν περιηγείστε στην ίδια τη βιβλιοθήκη του MusicBee από το τηλέφωνό σας και στέλνετε ένα κομμάτι πίσω στο ίδιο MusicBee, το plugin αναγνωρίζει ότι του ζητείται να αναπαράγει ένα από τα δικά του αρχεία και απλώς το αναπαράγει απευθείας από το δίσκο σας. Το αποτέλεσμα είναι ακριβές και άμεσο - bit-perfect, με τον δικό του ισοσταθμιστή και την εξισορρόπηση έντασης του MusicBee - αντί να ωθεί άσκοπα τον ήχο στο δίκτυο και κατευθείαν πίσω στον εαυτό του.
Λειτουργία ιδιωτικά: οι τρεις ρόλοι λειτουργούν ανεξάρτητα, οπότε μπορείτε να ενεργοποιήσετε τον renderer ενώ αφήνετε απενεργοποιημένη την κοινή χρήση βιβλιοθήκης. Σε αυτή τη ρύθμιση "μόνο renderer" η βιβλιοθήκη σας παραμένει εντελώς κρυμμένη από το δίκτυο - ανακοινώνεται μόνο ο στόχος αναπαραγωγής - και το MusicBee δεν θα προσφερθεί ποτέ να παίξει στον εαυτό του.
Συμπεριφορά αναπαραγωγής
N02 - 5.1 FLAC δεν γίνεται αυτόματη υπομίξη
Τι: ο περιορισμός του αριθμού καναλιών στο MediaServerDevice.GetEncodedFile ήταν If StereoOnly OrElse Not isPcmData Then channelCount = 2. Η ρήτρα Not isPcmData έκανε σιωπηλά υπομίξη κάθε μη-PCM μετακωδικοποίηση (FLAC, MP3, AAC, Ogg) σε στερεοφωνικό ανεξάρτητα από τον αριθμό καναλιών της πηγής, ακυρώνοντας τους 5.1-ικανούς renderers όταν η πηγή ήταν 5.1 FLAC. Τώρα η δεύτερη ρήτρα εξαιρεί το FLAC: Not isPcmData AndAlso encoder.Codec <> FileCodec.Flac. Το FLAC 5.1 περνάει· τα MP3/AAC/Ogg εξακολουθούν να αναγκάζονται σε στερεοφωνικό επειδή οι κωδικοποιητές γραμμής εντολών του MusicBee για αυτές τις μορφές αναμένουν είσοδο 2 καναλιών.
Γιατί: ολόκληρο το νόημα της μετακωδικοποίησης μιας πηγής 5.1 FLAC σε έξοδο FLAC είναι η διατήρηση της πολυκάναλης μίξης. Η σιωπηλή υπομίξη καθιστούσε την επιλογή μετακωδικοποίησης FLAC άχρηστη για ακρόαση surround. Με το N02 κάνει το σωστό.
Αρχιτεκτονική
Η δομική αλλαγή που καθιστά το fork βιώσιμο σε μια μεγάλη βιβλιοθήκη - απουσιάζει από το αρχικό plugin.
N03 - Lazy (κατά παραγγελία) δέντρο περιήγησης
Τι: το αρχικό plugin δημιούργησε ολόκληρο το δέντρο περιήγησης κατά την εκκίνηση του MusicBee - απαρίθμηση κάθε κομματιού, πλήρης Library_GetFileTags ανά αρχείο, συναρμολόγηση ολόκληρης της ιεραρχίας κοντέινερ - πριν ανοίξει τη θύρα HTTP. Σε μια πραγματική βιβλιοθήκη (50k+ κομμάτια, 5400 επεισόδια podcast, εκατοντάδες σταθμοί) αυτό είναι λεπτά κρύας εκκίνησης, και το δέντρο παραμένει στη RAM για πάντα, συμπεριλαμβανομένων κλάδων που κανένας πελάτης δεν ανοίγει ποτέ. Αυτό το fork δεν δημιουργεί τίποτα εκ των προτέρων: η ρίζα εκθέτει έναν placeholder με πρόθεμα L: ανά τελικό σημείο (L:music, L:podcast, L:filter:…)· κάθε επίπεδο υπολογίζεται μόνο όταν ένας πελάτης περιηγείται σε αυτό (LazyBrowse → EnsureLazyEndpointInMemory → caches ανά επίπεδο), και οι ειδοποιήσεις αλλαγής βιβλιοθήκης καθαρίζουν τις caches (SetLibraryDirty).
Γιατί: η κρύα εκκίνηση είναι ουσιαστικά άμεση - η θύρα HTTP είναι ανοιχτή μέχρι να ολοκληρώσει το MusicBee την αρχικοποίηση του plugin - και η μνήμη παραμένει ανάλογη με ό,τι έχει περιηγηθεί, όχι με το μέγεθος της βιβλιοθήκης. Αντιστάθμιση: η πρώτη περιήγηση σε ένα τελικό σημείο πληρώνει το κόστος φόρτωσής του· η επανείσοδος είναι cached μέχρι την επόμενη αλλαγή βιβλιοθήκης. Αυτό είναι το θεμέλιο από το οποίο εξαρτώνται όλα τα άλλα. Πλήρεις σημειώσεις: FIXES.md.
Δικτύωση & ανθεκτικότητα
Ενίσχυση της διαδρομής σύνδεσης του HTTP server. Το αρχικό plugin πεθαίνει σιωπηλά όταν η θύρα του είναι μη διαθέσιμη.
N04 - Αυτο-επιδιορθούμενη σύνδεση θύρας HTTP
Τι: ο HTTP server του plugin δεν πεθαίνει πλέον όταν η ρυθμισμένη θύρα του είναι μη διαθέσιμη. Τρεις συνδεδεμένες αλλαγές:
- Αυτόματη εφεδρεία σε αποτυχία σύνδεσης. Το
HttpServer.Startδοκιμάζει τη ρυθμισμένη θύρα, και σεSocketExceptionσαρώνει προς τα πάνω έως και 20 θύρες για την πρώτη ελεύθερη. Η πραγματική συνδεδεμένη θύρα καταγράφεται σε ένα νέοPlugin.boundServerPort, και όλα όσα διαφημίζουν τον server - SSDPLOCATIONURLs (NOTIFY + M-SEARCH response), το URL της συσκευής (PrimaryHostUrl), η προώθηση θύρας του router, και τα SSDP/control-point self-filters - διαβάζουν τώρα τοboundServerPortαντί για τοSettings.ServerPort. Οι πελάτες UPnP ανακαλύπτουν την πραγματική θύρα μέσω SSDP, οπότε μια μετακινημένη θύρα είναι διαφανής για τους renderers. - Ειδοποίηση χρήστη. Όταν συμβαίνει μια εφεδρεία (η αποθηκευμένη θύρα δεν είναι αυτή που χρησιμοποιείται), ένα τοπικοποιημένο
MessageBox(WarnPortInUse) ενημερώνει τον χρήστη ποια θύρα εξυπηρετεί πραγματικά και ότι οι συσκευές θα την βρουν ακόμα - επειδή το plugin τρέχει χωρίς γραφικό περιβάλλον και ένα μήνυμα εντός του παραθύρου διαλόγου θα το έβλεπε μόνο κάποιος που ήδη υποψιαζόταν πρόβλημα. - Ανάκτηση επανεκκίνησης. Το
RestartServer(η διαδρομή επανεκκίνησης αποθήκευσης ρυθμίσεων) συνήθιζε να αποαναφοροποιεί ταPlugin.controller/Plugin.serverτυφλά. Εάν η αρχικήInitialiseέριχνε εξαίρεση πριν τα δημιουργήσει (ακριβώς αυτό που προκαλούσε μια αποτυχημένη σύνδεση), η επόμενη αποθήκευση ρυθμίσεων χτυπούσε έναNullReferenceException- αφήνοντας ένα μισο-νεκρό plugin. Τώρα τα αναδημιουργεί και τα ξεκινά όταν είναιNothing, οπότε η αποθήκευση μιας λειτουργικής θύρας αναβιώνει το plugin χωρίς πλήρη επανεκκίνηση του MusicBee.
Γιατί: η αιτία ήταν ένα πραγματικό περιστατικό χρήστη. Η παλιά προεπιλεγμένη θύρα 49382 βρίσκεται στο δυναμικό εύρος των Windows (49152-65535), όπου τα Hyper-V/WSL2/Docker/WinNAT δεσμεύουν μεγάλα μπλοκ που μετατοπίζονται σε κάθε εκκίνηση - οπότε η σύνδεση απέτυχε με WSAEACCES ("απαγορευμένη πρόσβαση") σε ένα μηχάνημα όπου λειτουργούσε για μήνες. Η αλλαγή της προεπιλογής σε μια ελεύθερη θύρα συγκρούστηκε στη συνέχεια με το Serviio (ένας ξεχωριστός DLNA server ήδη στην νέα θύρα), αποτυγχάνοντας με WSAEADDRINUSE. Κάθε αποτυχία καταπνίγηκε στο Initialise, αφήνοντας το plugin σιωπηλά νεκρό και στη συνέχεια NRE-ing στην επόμενη αποθήκευση ρυθμίσεων. Μετά το N04 μια σύγκρουση θύρας αυτο-επιδιορθώνεται - ο server συνεχίζει να τρέχει στην επόμενη ελεύθερη θύρα, ο χρήστης ενημερώνεται, και οι πελάτες τον ανακαλύπτουν ξανά - αντί να καταρρίπτει ολόκληρο το plugin.
Υλοποίηση:
- Η προεπιλεγμένη θύρα μετακινήθηκε
49382→9779(κάτω από το δυναμικό εύρος, οπότε τα Windows δεν την δεσμεύουν ποτέ αυτόματα· δεν είναι γνωστή προεπιλογή media-server) και στις τρεις δηλώσειςServerPort+ την εφεδρεία αποτυχίας ανάλυσης ρυθμίσεων. - Το
Plugin.boundServerPort(νέο κοινό πεδίο) κρατά την ενεργή θύρα ακρόασης· τοactiveServerPortπαραμένει το ρυθμισμένο στιγμιότυπο ώστε η λογική της ετικέτας "Απαιτείται Επανεκκίνηση" να μην ενεργοποιείται ψευδώς σε μια εφεδρεία. HttpServer.PortScanRange = 20· η σάρωση σταματά στην πρώτη επιτυχημένηTcpListener.Start()και ρίχνει την τελευταία εξαίρεση μόνο αν όλες οι προσπάθειες αποτύχουν.- Νέο κλειδί πόρου EN
WarnPortInUse(οι μεταφράσεις ακολουθούν την τοπική ρύθμιση κατά τη δημοσίευση).
N05 - Ανακοινώσεις SSDP μέσω της ομάδας multicast (VPN / σημείο-προς-σημείο)
Τι: οι ανακοινώσεις SSDP αποστέλλονται στην ομάδα multicast UPnP (239.255.255.250) αντί για μια διεύθυνση IP broadcast. Το αβλαβές σφάλμα "δεν είναι δυνατή η πρόσβαση σε ένα αντικείμενο που έχει απορριφθεί" που καταγράφεται όταν μια απάντηση αναζήτησης SSDP ανταγωνίζεται μια επανεκκίνηση διακομιστή καταστέλλεται επίσης.
Γιατί: σε προσαρμογείς δικτύου σημείο-προς-σημείο / VPN, η IP broadcast δεν εφαρμόζεται - η παλιά αποστολή broadcast απέτυχε με "μη έγκυρο όρισμα" και οι ανακοινώσεις χάθηκαν, οπότε το plugin ήταν αόρατο για τους πελάτες σε αυτές τις συνδέσεις. Η ανακοίνωση στην κατάλληλη ομάδα multicast διορθώνει την ανακάλυψη ακριβώς σε αυτούς τους προσαρμογείς.
Πλοήγηση βιβλιοθήκης
Αυτά κυκλοφόρησαν σε αυτό το fork και δεν υπάρχουν στο αρχικό plugin. Προέκυψαν από την πραγματική περιήγηση στην έξοδο του plugin από πραγματικούς πελάτες UPnP.
N06 - Έκθεση βιβλιοθήκης βάσει φίλτρων
Τι: οι καρτέλες φίλτρων του MusicBee (αρχεία .xautopf στον φάκελο MusicBee του χρήστη) γίνονται ριζικά κοντέινερ UPnP στη βιβλιοθήκη του plugin. Τα κομμάτια κάθε φίλτρου είναι στη συνέχεια περιηγήσιμα σε μια ιεραρχία AlbumArtistSort → Album → Tracks.
Γιατί: οι χρήστες με επιμελημένα φίλτρα MusicBee (π.χ. "Κομμάτια 5 αστέρων", "Πρόσφατα προστιθέμενα", "Κλασική → Μπαρόκ") αναμένουν να τα βρουν όταν περιηγούνται στο plugin από έναν πελάτη UPnP. Το αρχικό plugin εξέθετε μόνο το ακατέργαστο δέντρο της βιβλιοθήκης.
N07 - Καλωδίωση πεδίου SortAlbumArtist
Τι: το plugin διαβάζει τώρα το MetaDataType 165 του MusicBee (Sort Album Artist) και το χρησιμοποιεί για ομαδοποίηση/ταξινόμηση καλλιτεχνών σε προβολές περιήγησης.
Γιατί: οι hi-fi browsers και οι audiophiles χρησιμοποιούν ονόματα καλλιτεχνών ταξινόμησης ("Beethoven, Ludwig van" αντί για "Ludwig van Beethoven") για την οργάνωση βιβλιοθηκών. Τυπική προσδοκία για σοβαρούς ακροατές. Λείπει και από τις δύο upstream εκδόσεις.
N08 - Χειρισμός πολλαπλών τιμών AlbumArtist
Τι: όταν το πεδίο AlbumArtist ενός άλμπουμ περιέχει πολλούς καλλιτέχνες διαχωρισμένους με "; " (π.χ. "yaiol; Ars Ricercata"), το κομμάτι εμφανίζεται τώρα κάτω από κάθε καλλιτέχνη στις προβολές περιήγησης, όχι κάτω από έναν ενιαίο καλλιτέχνη Frankenstein που συνδυάζει τα ονόματα.
Γιατί: τα συνεργατικά άλμπουμ και οι συλλογές πρέπει να εμφανίζονται κάτω από κάθε συνεργάτη. Χωρίς αυτό, οι μισές διαδρομές αναζήτησης για την εύρεση του άλμπουμ είναι σπασμένες.
N09 - Εξώφυλλο κοντέινερ άλμπουμ (upnp:albumArtURI)
Τι: οι κόμβοι κοντέινερ άλμπουμ στις απαντήσεις DIDL Browse περιλαμβάνουν τώρα ένα στοιχείο upnp:albumArtURI που δείχνει το εξώφυλλο του άλμπουμ.
Γιατί: χωρίς αυτό, κάθε άλμπουμ στην προβολή περιήγησης ενός πελάτη UPnP εμφανίζει ένα γενικό εικονίδιο αντί για το εξώφυλλο του άλμπουμ. Οπτική ένδειξη για πλοήγηση· αναμενόμενο από κάθε σύγχρονο hi-fi browser.
N10 - Σειρά κομματιών μέσα σε άλμπουμ φίλτρων
Τι: τα κομμάτια μέσα σε ένα άλμπουμ που εκτίθεται από φίλτρο ταξινομούνται τώρα κατά Αριθμό Δίσκου, και στη συνέχεια κατά Αριθμό Κομματιού.
Γιατί: τυπική σειρά άλμπουμ. Χωρίς ρητή ταξινόμηση, τα κομμάτια επέστρεφαν με όποια σειρά τα επέστρεφε το φίλτρο - συνήθως φαινόταν τυχαία.
N11 - Διόρθωση δέντρου φακέλων λίστας αναπαραγωγής
Τι: η συνάρτηση LoadLibraryPlaylists (αρχικά από τον Steven Mayall, ~2014) απέτυχε να εισέλθει σε νεοδημιουργημένους φακέλους λίστας αναπαραγωγής. Η πρώτη λίστα αναπαραγωγής σε κάθε φάκελο, συν τυχόν υποφακέλους, κατέληγε ορφανή στο ριζικό επίπεδο.
Γιατί: υπήρχε στο αρχικό plugin για έντεκα χρόνια. Ορατό μέσα σε 30 δευτερόλεπτα από το άνοιγμα του BubbleUPnP και το κλικ στις Λίστες Αναπαραγωγής. Διορθώθηκε στο yaiol με σωστή αναδρομή σε νεοδημιουργημένους φακέλους κατά τη δημιουργία του δέντρου.
N12 - Καθαρισμός χαρακτήρων ελέγχου που είναι παράνομοι για XML
Τι: οποιοδήποτε κομμάτι με ετικέτα που περιέχει χαρακτήρα ελέγχου C0 (π.χ. 0x19 από μια κακή διέλευση κωδικοποίησης - UTF-8 → Latin-1 → πίσω περικοπή 0x99 σε 0x19) προκαλούσε την αποτυχία ολόκληρης της απάντησης Browse με Action Failed μόλις το κακό κομμάτι εισερχόταν σε μια σελιδοποιημένη παρτίδα.
Γιατί: το XML 1.0 απαγορεύει τους περισσότερους χαρακτήρες ελέγχου C0, και ο XmlWriter ρίχνει εξαίρεση όταν του ζητηθεί να γράψει οποιονδήποτε. Υπήρχε στο αρχικό plugin. Διορθώθηκε με την αφαίρεση μη έγκυρων χαρακτήρων σε κάθε σημείο εξόδου Library_GetFileTags μέσω του XmlConvert.IsXmlChar.
N13 - Λίστα ραδιοφώνου ντετερμινιστική σε σελιδοποιημένη περιήγηση
Τι: η περιήγηση για το κοντέινερ Ραδιοφώνου έπεσε στον γενικό κλάδο λίστας αρχείων, ο οποίος καλούσε files.Sort(AlbumFileComparer) σε κάθε κλήση. Οι καταχωρήσεις ραδιοφώνου έχουν κενές ετικέτες Άλμπουμ/Δίσκου/Κομματιού, οπότε κάθε σύγκριση επέστρεφε 0 - το List(Of T).Sort είναι ασταθές, παράγοντας μια διαφορετική σειρά σε κάθε κλήση. Τα σημεία ελέγχου UPnP σελιδοποιούν (το BubbleUPnP ανακτά 0..15 και μετά 16..τέλος)· μεταξύ των δύο κλήσεων η λίστα ανακατεύτηκε, οπότε κάποιοι σταθμοί εμφανίστηκαν και στις δύο σελίδες (διπλότυπα) και κάποιοι σε καμία (λείπουν) - φαινόταν τυχαία σε κάθε ανανέωση.
Γιατί: υπήρχε στο αρχικό plugin (ο συγγραφέας του δεν περιηγείται ποτέ στο ραδιόφωνο μέσω UPnP). Διορθώθηκε εδώ με έναν ειδικό κλάδο ContainerCategory.Radio στο Browse, χωρίς ταξινόμηση ανά κλήση· το radioFiles ταξινομείται μία φορά κατά τη φόρτωση κατά Τίτλο (σταθερό). Η σελιδοποιημένη περιήγηση βλέπει τώρα μια ντετερμινιστική σειρά· η σελίδα 1 και η σελίδα 2 είναι ασύνδετες.
N14 - Αναζήτηση UPnP κλάσης άλμπουμ επιστρέφει κοντέινερ άλμπουμ
Τι: η αναζήτηση UPnP για ερωτήματα κλάσης άλμπουμ (upnp:class = "object.container.album.musicAlbum", π.χ. "Τυχαία Άλμπουμ" του BubbleUPnP) επέστρεφε την πλήρη λίστα κομματιών αντί για κοντέινερ άλμπουμ, οπότε ο πελάτης εμφάνιζε μηδέν άλμπουμ. Ο αρχικός χειριστής ανέλυε μόνο κριτήρια σε παρένθεση, και στη συνέχεια απέρριπτε όλα τα κομμάτια ανεξάρτητα από την αιτούμενη κλάση.
Γιατί: διορθώθηκε εδώ - τα ερωτήματα κλάσης άλμπουμ απαριθμούν τώρα διακριτά άλμπουμ (ομαδοποιημένα κατά AlbumArtist+Album) και εκπέμπουν το καθένα ως ένα σωστό κοντέινερ musicAlbum με εξώφυλλο, προσβάσιμο μέσω του χώρου εικονικών αναγνωριστικών Salb<idx> ώστε ο πελάτης να μπορεί να εμβαθύνει σε ένα αποτέλεσμα και να το αναπαράγει.
N15 - Λειτουργική, ευαίσθητη στο πεδίο αναζήτηση UPnP με κλικ-μέσω
Τι: το πρωτότυπο δεν διαφήμιζε δυνατότητες αναζήτησης (GetSearchCapabilities επέστρεφε κενό), οπότε οι πελάτες αρνούνταν ακόμη και να στείλουν μια αναζήτηση· και το παλιό backend διάβαζε από το musicFiles, μόνιμα κενό στην εποχή του lazy-tree. Αυτό το fork διαφημίζει τις πραγματικές αναζητήσιμες ιδιότητες, υλοποιεί αναζήτηση κομματιών κατά τίτλο και άλμπουμ κατά τίτλο έναντι της lazy βιβλιοθήκης (HandleLazySearch), περιορίζει το ερώτημα στον τρέχοντα κλάδο του πελάτη όταν αποστέλλεται ένα πραγματικό αναγνωριστικό κοντέινερ (αλλιώς αντικαθιστά το L:music ώστε οι αναζητήσεις στην επάνω γραμμή να μην συμπεριλαμβάνουν θόρυβο από podcast/ραδιόφωνο/audiobook), και καθιστά τα αποτελέσματα άλμπουμ κλικαριστά μέσω συνθετικών αναγνωριστικών Ssrch_alb_* που ένας πρώιμος κλάδος Browse αντιστοιχίζει πίσω στα κομμάτια του άλμπουμ. (Το κομμάτι των αποτελεσμάτων κλάσης άλμπουμ ως κοντέινερ είναι το N14.)
Γιατί: η αναζήτηση στο BubbleUPnP πήγε από "Η βιβλιοθήκη δεν υποστηρίζει αναζήτηση" στην επιστροφή χρήσιμων, περιορισμένων, αναπαραγώγιμων αποτελεσμάτων. Πλήρης σχεδιασμός + απορριφθείσες προσεγγίσεις: SEARCH.md.
N16 - Ακύρωση cache UPnP (SystemUpdateID)
Τι: το πρωτότυπο επέστρεφε ένα σταθερό SystemUpdateID=0 - τη σύμβαση ακύρωσης cache του UPnP ContentDirectory - οπότε οι συμβατοί με τις προδιαγραφές πελάτες (BubbleUPnP) αντιμετώπιζαν τη βιβλιοθήκη ως αμετάβλητη: παρωχημένα αποτελέσματα περιήγησης, 404 μικρογραφίες μετά από αλλαγή σχήματος URL, και ο χορός "επανεκκίνηση του MusicBee δύο φορές για να δείτε αλλαγές". Αυτό το fork αρχικοποιεί το SystemUpdateID από δευτερόλεπτα εποχής κατά τη φόρτωση (οπότε κάθε επανεκκίνηση είναι αυστηρά μπροστά από την τελευταία) και το αυξάνει σε κάθε τροποποίηση βιβλιοθήκης και αλλαγή ρυθμίσεων (SetLibraryDirty / ResetCache → BumpSystemUpdateId).
Γιατί: οι πελάτες λαμβάνουν αξιόπιστα τις επεξεργασίες, τα νέα αρχεία και τις αλλαγές ρυθμίσεων στην επόμενη περιήγησή τους. Γνωστό όριο: οι εγγεγραμμένοι πελάτες δεν λαμβάνουν ενεργά τη νέα τιμή μέσω GENA (παρκαρισμένο ως μελλοντική εργασία)· εξακολουθούν να τη βλέπουν στην επόμενη περιήγησή τους.
N17 - Εξώφυλλο συνδρομής podcast
Τι: τα πλακίδια podcast δεν εμφάνιζαν εικόνες - κάθε αίτημα /PodcastThumbnail/ επέστρεφε 404. Δύο αλληλεπικαλυπτόμενα σφάλματα: η αλυσίδα ανάλυσης δεν έλεγχε ποτέ την πραγματική cache εξωφύλλων του MusicBee (%LocalAppData%\MusicBee\InternalCache\Subscriptions\<name>.jpg, από όπου φορτώνει το UI του desktop), και το unescape+lowercase του HTTP layer παραμόρφωνε το κλειδί διαδρομής του feed-URL μέχρι το τελευταίο του τμήμα διαδρομής. Αυτό το fork αναλύει το εξώφυλλο από το InternalCache του MB και δρομολογεί τις αναζητήσεις μέσω ενός URL-safe slug που επιβιώνει ανέπαφο στο HTTP layer (PodcastSlug / podcastSubIdBySlug).
Γιατί: το εξώφυλλο συνδρομής εμφανίζεται τώρα στις προβολές περιήγησης (όλα τα 22 αιτήματα που προηγουμένως επέστρεφαν 404 επιλύονται).
N18 - Ιεραρχική (οριοθετημένη) περιήγηση ετικετών
Τι: οποιοδήποτε πεδίο μπορεί να επισημανθεί ως ιεραρχικό στην καρτέλα Επιλογές Βιβλιοθήκης και να του δοθεί ένας χαρακτήρας οριοθέτησης (ένας επιλογέας πεδίου + πλαίσιο οριοθέτησης με προσθήκη/αφαίρεση, που διατηρείται στις ρυθμίσεις του plugin). Ορίστε την Ομαδοποίηση σε / και μια τιμή όπως Jazz/Cool Jazz τότε περιηγείται ως Jazz › Cool Jazz αντί για μια επίπεδη καταχώρηση. Τα κομμάτια που έχουν ετικέτα ακριβώς σε έναν κλάδο (μόνο Jazz) αποκτούν τον δικό τους κόμβο [Jazz] ώστε τίποτα να μην κρύβεται, ένας κλάδος με ένα μόνο παιδί συμπτύσσεται από μόνος του, και το ; απορρίπτεται ως οριοθέτης επειδή είναι ο διαχωριστής πολλαπλών τιμών του MusicBee.
Γιατί: οι βαθιές ταξινομίες ετικετών που ένας χρήστης έχει ήδη κωδικοποιήσει σε ένα μόνο πεδίο (δέντρα ειδών, ιεραρχίες διάθεσης, "Κλασική/Μπαρόκ/Κοντσέρτο") επιτέλους περιηγούνται ως το δέντρο που περιγράφει η ετικέτα, αντί για έναν επίπεδο τοίχο από συμβολοσειρές διαχωρισμένες με κάθετο που ο χρήστης πρέπει να διαβάσει από άκρη σε άκρη.
N19 - Μία διαδρομή ρίζας με ετικέτα από το πεδίο ομαδοποίησής της
Τι: μια ενιαία διαδρομή περιήγησης στη ρίζα επισημαίνεται από το πεδίο ομαδοποίησής της (π.χ. "Είδος") αντί για την πλήρη σύντομη διαδρομή της, ταιριάζοντας με τον τρόπο που ονομάζονται οι συγχωνευμένες ομάδες πρώτου πεδίου.
Γιατί: το δέντρο περιήγησης διαβάζεται με συνέπεια - ένας κανόνας ονομασίας είτε μια ριζική καταχώρηση στέκεται μόνη της είτε συγχωνεύτηκε με αδέλφια (N20) - αντί για μια μοναχική ριζική καταχώρηση που εμφανίζει μια λεπτομερή εσωτερική διαδρομή ενώ οι συγχωνευμένοι γείτονές της εμφανίζουν ένα καθαρό όνομα πεδίου.
N20 - Συγχώνευση διαδρομών περιήγησης που μοιράζονται ένα πρώτο πεδίο
Τι: δύο διαδρομές περιήγησης που μοιράζονται το ίδιο πρώτο πεδίο - "Είδος / Ταξινόμηση Καλλιτέχνη Άλμπουμ" και "Είδος / Άτομα Podcast" - συμπτύσσονται σε έναν ενιαίο ριζικό φάκελο Είδος που παραθέτει πρώτα τις τιμές του είδους και στη συνέχεια χωρίζεται στις δύο προβολές, αντί για δύο σχεδόν διπλότυπες καταχωρήσεις "Είδος / ..." δίπλα-δίπλα στη ρίζα.
Γιατί: ένας χρήστης με πολλές σχετικές προβολές φωλιασμένες κάτω από ένα κοινό πεδίο έβλεπε τη ρίζα γεμάτη με σχεδόν πανομοιότυπες καταχωρήσεις ανώτερου επιπέδου. Η συγχώνευσή τους διατηρεί τη ρίζα ευανάγνωστη και ομαδοποιεί τις σχετικές προβολές εκεί που ανήκουν - κάτω από το κοινό τους πεδίο.
N21 - Διαδρομές περιήγησης με κατηγορία (Standard / Radio / Podcast)
Τι: κάθε διαδρομή περιήγησης είναι τύπου κατηγορίας - Standard, Radio ή Podcast. Η λίστα προτύπων ομαδοποιείται σε αυτές τις τρεις ενότητες, ο επιλογέας πεδίου κάθε προτύπου προσφέρει μόνο τα πεδία που μπορούν πραγματικά να παραδώσουν τα δεδομένα αυτής της κατηγορίας, και ένα πρότυπο μπορεί να εφαρμοστεί μόνο σε αντίστοιχους κόμβους στο δέντρο View (οι ασύμβατοι κόμβοι γκριζάρουν και δεν μπορούν να επιλεγούν). Τα δεσμευμένα πρότυπα Radio και Podcasts δεν μπορούν να διαγραφούν, οπότε η ενότητα κατηγορίας τους δεν εξαφανίζεται ποτέ.
Γιατί: χωρίς την τυποποίηση ένας χρήστης θα μπορούσε να δημιουργήσει μια διάταξη που να εμφανίζεται σιωπηλά κενή - ένας ραδιοφωνικός σταθμός δεν έχει "άλμπουμ", ένα επεισόδιο podcast δεν έχει "καλλιτέχνη άλμπουμ" - και να το ανακαλύψει μόνο περιηγούμενος σε έναν νεκρό φάκελο από έναν πελάτη UPnP. Ο περιορισμός του μενού πεδίων και των στόχων εφαρμογής στα πραγματικά δεδομένα της κατηγορίας καθιστά τις κενές διατάξεις μη κατασκευάσιμες.
N22 - Ομαδοποίηση podcast ανά έτος δημοσίευσης
Τι: η ημερομηνία δημοσίευσης κάθε επεισοδίου podcast διαβάζεται, οπότε μια διαδρομή περιήγησης podcast με επίπεδο Έτους ομαδοποιεί τα επεισόδια ανά έτος αντί να τα συμπτύσσει κάτω από ένα ενιαίο "Άγνωστο".
Γιατί: οι μεγάλες συνδρομές podcast γίνονται περιηγήσιμες ανά έτος όπως και η υπόλοιπη βιβλιοθήκη, αντί κάθε επεισοδίου να καταλήγει σε έναν αχρονολόγητο σωρό επειδή το plugin δεν κοίταξε ποτέ την ημερομηνία δημοσίευσης ανά επεισόδιο.
N23 - Σύμπτυξη επιπέδων ομαδοποίησης με ένα αποτέλεσμα
Τι: ένα επίπεδο ομαδοποίησης που επιλύεται σε μία μόνο τιμή - ένα επίπεδο Τύπου Εγγραφής που εμφανίζει μόνο "LP" για έναν καλλιτέχνη που έκανε μόνο LPs, ή ένα επίπεδο γραμμάτων με ένα μόνο γράμμα - παραλείπεται αυτόματα, οδηγώντας τον χρήστη απευθείας στο περιεχόμενό του.
Γιατί: η περιήγηση σε έναν φάκελο που περιέχει ακριβώς έναν φάκελο είναι καθαρή τριβή. Η σύμπτυξη του επιπέδου μονής επιλογής αφαιρεί το νεκρό κλικ χωρίς να αλλάζει αυτό που μπορεί να φτάσει ο χρήστης.
N24 - Ομαδοποίηση/αναζήτηση έτους έναντι του πεδίου ημερομηνίας του MusicBee
Τι: η συνθήκη έτους δεν αναζητά πλέον το πεδίο "Έτος" πλήρους ημερομηνίας του MusicBee με μια απλή τετραψήφια τιμή, και η σκληρά κωδικοποιημένη ψευδωνυμία πεδίου έτους έχει φύγει, οπότε κάθε πεδίο ομαδοποίησης επιλύεται πλέον γενικά από τον ορισμό της διαδρομής.
Γιατί: για βιβλιοθήκες των οποίων η ετικέτα Έτους περιέχει μια πλήρη ημερομηνία, η ομαδοποίηση ή η αναζήτηση κατά έτος προηγουμένως δεν επέστρεφε τίποτα - το τετραψήφιο ερώτημα δεν ταίριαζε ποτέ με το πεδίο πλήρους ημερομηνίας. Η αναζήτηση στο σωστό πεδίο κάνει την ομαδοποίηση και την αναζήτηση κατά έτος να βρίσκουν ξανά τα κομμάτια.
N25 - Ξεχωριστά πεδία ομαδοποίησης "Έτος" και "Έτος (yyyy)"
Τι: οι διαδρομές ομαδοποίησης και περιήγησης άλμπουμ εκθέτουν τώρα και τα δύο πεδία έτους του MusicBee - Έτος (η ετικέτα πλήρους ημερομηνίας) και Έτος (yyyy) (μόνο το τετραψήφιο έτος) - ώστε ο χρήτης να μπορεί να επιλέξει οποιοδήποτε κατά τον ορισμό μιας ομαδοποίησης άλμπουμ ή μιας διαδρομής περιήγησης.
Γιατί: τα δύο πεδία σημαίνουν διαφορετικά πράγματα στο MusicBee, και η σύμπτυξή τους έχανε αυτή τη διάκριση. Η εμφάνιση και των δύο επιτρέπει στον χρήστη να συγκεντρώσει κάθε κυκλοφορία ενός έτους μαζί (yyyy) ή να διατηρήσει την ακριβή σειρά ημερομηνίας (πλήρης ετικέτα Έτους), όπως σκοπεύει.
N32 - Καρφιτσωμένα φίλτρα και λίστες αναπαραγωγής ομαδοποιημένα κατά είδος στη ρίζα
Τι: ένα καρφιτσωμένο φίλτρο εμφανίζεται πλέον ακριβώς κάτω από τον φάκελο Φίλτρα στη ρίζα περιήγησης, και μια καρφιτσωμένη λίστα αναπαραγωγής ακριβώς κάτω από τον φάκελο Λίστες αναπαραγωγής, αντί όλα τα καρφιτσωμένα να συγκεντρώνονται σε έναν σωρό στο τέλος της ρίζας. Κάθε καρφιτσωμένη συντόμευση βρίσκεται με το είδος της.
Γιατί: όσο περισσότερες συντομεύσεις καρφιτσώνει ο χρήστης, τόσο δυσκολότερα διαβάζεται ένας ενιαίος σωρός από ανάμεικτα φίλτρα και λίστες αναπαραγωγής, ενώ χωρίζει κάθε συντόμευση από τον φάκελο στον οποίο ανήκει. Η ομαδοποίηση των καρφιτσωμένων στοιχείων κάτω από τη δική τους κατηγορία κρατά τη ρίζα ευανάγνωστη και κάθε συντόμευση δίπλα στα πράγματα στα οποία ανήκει.
Παράθυρο ρυθμίσεων & συσκευασία
N26 - Παράθυρο ρυθμίσεων με ενότητες
Τι: η σελίδα Προτιμήσεις απέκτησε μια διάταξη αριστερής πλοήγησης με ενότητες: Γενικά / Αναπαραγωγή / Βιβλιοθήκη / Προφίλ Συσκευών / Διαγνωστικά.
Γιατί: το πρωτότυπο ήταν μια ενιαία ψηλή επίπεδη λίστα κάθε ρύθμισης - μια χαρά για τον προγραμματιστή που το έφτιαξε, μπερδευτική για όλους τους άλλους. Η ομαδοποίηση των σχετικών επιλογών σε ενότητες κάνει το παράθυρο διαλόγου να μοιάζει περισσότερο με τις ρυθμίσεις σύγχρονων εφαρμογών.
Μετονομασία Assembly + plugin (χωρίς F-id - σημείωση συσκευασίας)
Τι: η μεταγλωττισμένη DLL ονομάζεται mb_UPnP_yaiol.dll και το plugin αναφέρει τον εαυτό του ως "MusicBee UPnP (yaiol)". Διαφορετικό από το αρχικό mb_Upnp.dll.
Γιατί: οι χρήστες μπορούν να εγκαταστήσουν το yaiol παράλληλα με το αρχικό plugin και να συγκρίνουν τη συμπεριφορά τους δίπλα-δίπλα.
Σύστημα ετικετών - εμφάνιση κατάστασης εκτέλεσης (μηχανισμός πίσω από το F40)
Τι: ένα γενικό μοτίβο UI για την εμφάνιση σημαντικών συνθηκών εκτέλεσης ως ορατές χρωματιστές ετικέτες στο παράθυρο διαλόγου Ρυθμίσεις. Τρέχουσες περιπτώσεις:
- ⚠ Μέγ. Συνδ. (N04) - ενεργοποιείται όταν το όριο μέγιστων συνδέσεων χτυπήθηκε τουλάχιστον μία φορά από την εκκίνηση του MusicBee. Κολλητή σημαία συνεδρίας
Plugin.MaxConnectionsHit. Ορίζεται μέσα στοWaitOnSendBarrierόταν δεν υπάρχει διαθέσιμη θέση. - ⚠ Απαιτείται Επανεκκίνηση - ενεργοποιείται όταν μια αποθηκευμένη ρύθμιση χρειάζεται επανεκκίνηση του MusicBee για να τεθεί σε ισχύ. Κολλητή σημαία συνεδρίας
Plugin.RestartRequired. Ορίζεται στον χειριστή Αποθήκευσης του παραθύρου διαλόγου όταν η νέα διατηρημένη τιμή διαφέρει από το στιγμιότυπο εκτέλεσης (Plugin.activeMaxConnections,Plugin.activeServerPort,Plugin.activeIpAddress). Οι ρυθμίσεις που απαιτούν επανεκκίνηση περιορίζονται σε αυτές που πραγματικά δεν μπορούν να φορτωθούν εν θερμώ - παράμετροι σύνδεσης HTTP server και το SemaphoreSlim που δημιουργείται μία φορά στην αρχικοποίηση.
Γιατί: το αρχείο καταγραφής του plugin είναι μια χαρά για τεχνικούς χρήστες που κάνουν debugging, αλλά ένας μη τεχνικός χρήστης που κοιτάζει "η συσκευή ακούγεται λάθος" ή "η αναπαραγωγή είναι αργή" δεν θα ανοίξει ποτέ Διαγνωστικά → Προβολή καταγραφής. Οι ετικέτες καταγράφουν τις περιπτώσεις όπου ο χρήστης πρέπει να γνωρίζει ότι κάτι συνέβη και το εμφανίζουν την επόμενη φορά που θα ανοίξει το plugin - ανακαλύψιμο χωρίς να διαβάσει τίποτα.
Επαναχρησιμοποιήσιμο για το μέλλον:
- Εντοπίστηκε αναντιστοιχία προφίλ (ο user-agent της συσκευής δεν ταίριαξε ποτέ με κανένα προφίλ, επέστρεψε σε Γενικό).
- Ενεργοποιήθηκε η αναστολή NextURI (F13 - απρόσκοπτη αναπαραγωγή απενεργοποιήθηκε για τη συνεδρία σε μια ασταθή συσκευή).
- Η σάρωση βιβλιοθήκης απέτυχε / ήταν μερική.
- Η σύνδεση του renderer χάθηκε εν μέσω συνεδρίας.
- Οποιαδήποτε άλλη συνθήκη όπου "συνέβη μία φορά, ο χρήστης πρέπει να γνωρίζει" είναι καλύτερη από "καταγράφηκε σιωπηλά μεταξύ 1000 άλλων γραμμών".
Συμβάσεις υλοποίησης:
- Οι ετικέτες των ετικετών βρίσκονται στο επίπεδο του παραθύρου διαλόγου (όχι μέσα σε κανένα πάνελ) ώστε να είναι ορατές ανεξάρτητα από την ενότητα στην οποία βρίσκεται ο χρήστης.
- Τοποθετημένες στην κάτω σειρά κοντά στο Αποθήκευση/Ακύρωση (τρέχουσα: y=410 οριζόντια στοιβασμένες).
- Κάθε ετικέτα έχει μια αντίστοιχη κολλητή σημαία συνεδρίας στο
Pluginπου γίνεται True όταν συμβεί η συνθήκη και επαναφέρεται μόνο με επανεκκίνηση του MusicBee. - Πόροι:
<Condition>Badge(κείμενο ετικέτας, με πρόθεμα ⚠) +<Condition>BadgeTip(συμβουλή εργαλείου που εξηγεί την αιτία + την αποκατάσταση). - Για τις ετικέτες "αποθηκευμένη ρύθμιση χρειάζεται επανεκκίνηση", λάβετε ένα στιγμιότυπο εκτέλεσης στο
Plugin.Initialise()και συγκρίνετε με τοSettings.*μετά τοSettings.SaveSettings()στον χειριστή Αποθήκευσης του παραθύρου διαλόγου.
N27 - Η ακύρωση απορρίπτει τις επεξεργασίες διαδρομής/προτύπου
Τι: οι επεξεργασίες που έγιναν σε διαδρομές και πρότυπα μέσα στο παράθυρο διαλόγου ρυθμίσεων απορρίπτονται τώρα όταν ο χρήστης κάνει κλικ στο Ακύρωση, αντί να παραμένουν σιωπηλά εφαρμοσμένες, και οποιοδήποτε δεσμευμένο πρότυπο αφαιρέθηκε κατά τη διάρκεια της συνεδρίας αναδημιουργείται. (Τα πρότυπα διαφορετικά αποθηκεύονται ζωντανά καθώς επεξεργάζονται - δεν υπάρχει ξεχωριστό κουμπί Αποθήκευσης στην καρτέλα Διαδρομές.)
Γιατί: το Ακύρωση πρέπει να σημαίνει ακύρωση. Προηγουμένως ένας χρήστης που πειραματίστηκε με αλλαγές διαδρομής/προτύπου και υποχώρησε διαπίστωσε ότι οι αλλαγές είχαν ήδη δεσμευτεί, χωρίς τρόπο να τις αναιρέσει εκτός από το να τις ξανακάνει μία-μία με το χέρι.
N28 - Κυριολεκτικές αμπερσάν στο μενού επιλογής πεδίου
Τι: ένα πεδίο του οποίου το όνομα περιέχει "&" - π.χ. "Διάθεση & Πλαίσιο" - αποδίδει το αμπερσάν κυριολεκτικά στο μενού επιλογής πεδίου αντί να το καταπίνει ως πρόθεμα Alt-mnemonic.
Γιατί: τα ονόματα πεδίων με αμπερσάν εμφανίζονταν λανθασμένα (ο χαρακτήρας εξαφανιζόταν και το επόμενο γράμμα γινόταν επιταχυντής), καθιστώντας την καταχώρηση του μενού δύσκολο να αναγνωριστεί.
N29 - Σταθερός, μη μεταφρασμένος τίτλος παραθύρου ρυθμίσεων
Τι: ο τίτλος του παραθύρου ρυθμίσεων είναι σταθερός στην επωνυμία "MusicBee UPnP Plugin" και δεν ποικίλλει πλέον με τη γλώσσα της διεπαφής· η συμβολοσειρά DialogTitle ανά γλώσσα αφαιρέθηκε από κάθε πακέτο τοπικής προσαρμογής.
Γιατί: ένας τίτλος παραθύρου που άλλαζε διατύπωση ανά γλώσσα ήταν μια μεταφράσιμη επιφάνεια χωρίς όφελος - ο τίτλος είναι ένα σήμα επωνυμίας. Η σταθεροποίησή του τον διατηρεί σταθερό και συνεπή παντού.
Τοπική προσαρμογή
Το αρχικό plugin είναι μόνο στα Αγγλικά. Αυτό το fork είναι πλήρως τοπικοποιήσιμο - κάθε συμβολοσειρά που βλέπει ο χρήστης περνάει μέσα από ένα πακέτο πόρων, και το plugin ανιχνεύει αυτόματα τη γλώσσα UI του MusicBee.
N30 - Πολυγλωσσικό UI (αναμένονται μεταφράσεις)
Τι: ο μηχανισμός τοπικοποίησης είναι πλήρης και κυκλοφορεί. Το Localisation.vb διαβάζει την επιλεγμένη γλώσσα του MusicBee από το MusicBee3Settings.ini (ενδώνυμο <SystemLanguage>) και εφαρμόζει την αντίστοιχη κουλτούρα .NET στο νήμα, οπότε το My.Resources.Resources.* επιστρέφει την τοπικοποιημένη συμβολοσειρά. Κάθε ετικέτα/κουμπί/μήνυμα που βλέπει ο χρήστης είναι συνδεδεμένο με ένα κλειδί πόρου (στοιχεία ελέγχου σχεδιαστή μέσω ApplyDesignerExtras + sync-en-locale.js· συμβολοσειρές χρόνου εκτέλεσης όπως WarnPortInUse προστίθενται χειροκίνητα). Αυτό που δεν έχει γίνει ακόμα είναι η πραγματική μετάφραση: υπάρχει μόνο το αγγλικό πακέτο πηγής (Resources.resx) - τα δορυφορικά πακέτα για τις άλλες γλώσσες παράγονται σε μια ενιαία παρτίδα όταν το plugin είναι πλήρες σε χαρακτηριστικά (η τμηματική μετάφραση ενώ οι συμβολοσειρές εξακολουθούν να αλλάζουν σπαταλάει προσπάθεια).
Γλώσσες στόχοι (το σύνολο που προσφέρει το ίδιο το MusicBee, που αντιστοιχεί 1:1 με το endonymToCulture ώστε το plugin να ακολουθεί αυτόματα τη γλώσσα του MusicBee):
Αραβικά (ar) |
Τσέχικα (cs) |
Γερμανικά (de) |
Ελληνικά (el) |
Ισπανικά (es) |
Γαλλικά (fr) |
Ουγγρικά (hu) |
Ιταλικά (it) |
Κορεάτικα (ko) |
Ολλανδικά (nl) |
Νορβηγικά (nb) |
Πολωνικά (pl) |
Πορτογαλικά Βραζιλίας (pt-BR) |
Πορτογαλικά Πορτογαλίας (pt-PT) |
Σουηδικά (sv) |
Τουρκικά (tr) |
Ουκρανικά (uk) |
Ρωσικά (ru) |
Ιαπωνικά (ja) |
Απλοποιημένα Κινεζικά (zh-CN) |
Παραδοσιακά Κινεζικά (zh-TW) |
Αγγλικά (en, πηγή) |
Πολιτική παραλλαγών (σύμφωνα με τον κανόνα τοπικής ρύθμισης του χώρου εργασίας): τα PT και ZH χωρίζονται σε διακριτά πακέτα επειδή το λεξιλόγιο/σενάριο αποκλίνει πραγματικά (pt-BR/pt-PT, zh-CN/zh-TW). Το EN είναι ένα ενιαίο πακέτο - το "English(US)" (en-US) του MusicBee επιστρέφει στο en μέσω της αλυσίδας κουλτούρας του .NET, οπότε δεν παράγεται ξεχωριστό πακέτο US. Τα ES και FR είναι ομοίως μονής τοπικής ρύθμισης.
Γιατί: οι ρυθμίσεις ενός plugin UPnP ("μη χρησιμοποιείτε ακατέργαστο PCM", "επιβάλλετε little-endian PCM", προειδοποιήσεις εφεδρείας θύρας) είναι αρκετά κρυπτικές στη μητρική γλώσσα κάποιου. Η παρακολούθηση της γλώσσας UI του ίδιου του MusicBee - αντί της επιβολής των Αγγλικών - είναι η διαφορά μεταξύ ενός εργαλείου που ένας μη αγγλόφωνος χρήστης μπορεί να διαμορφώσει και ενός που δεν μπορεί. Κανένα upstream δεν το επιχείρησε αυτό.
N31 - Ο σύνδεσμος βοήθειας ανοίγει στην πλήρη γλώσσα διεπαφής
Τι: το άνοιγμα του συνδέσμου Βοήθειας από το plugin σέβεται την πλήρη γλώσσα διεπαφής του χρήστη (π.χ. pt-BR, zh-CN) αντί να συμπτύσσεται στην βασική γλώσσα, και στέλνει ένα σαφέστερο αναγνωριστικό ελέγχου ενημερώσεων.
Γιατί: ένας χρήστης που χρησιμοποιούσε το MusicBee σε μια περιφερειακή παραλλαγή (Πορτογαλικά Βραζιλίας, Απλοποιημένα Κινεζικά) οδηγούνταν στη σελίδα βοήθειας της βασικής γλώσσας. Η μεταφορά της πλήρους κουλτούρας τους οδηγεί στη σελίδα βοήθειας στην ακριβή γλώσσα που χρησιμοποιούν.
Διορθώσεις και βελτιώσεις στο αρχικό plugin
Βασικό πρωτόκολλο & αναπαραγωγή
F01 - Ενημερωμένα προεπιλεγμένα προφίλ συσκευών DLNA
Τι: παρέχει νέα προεπιλεγμένα προφίλ για PlayStation 4, Xbox 360/One, και σύγχρονο BubbleUPnP, με σημαίες δυνατοτήτων (ρυθμοί δειγματοληψίας, βάθη bit, κωδικοποιητές) που αντικατοπτρίζουν τι υποστηρίζουν πραγματικά αυτές οι συσκευές σήμερα.
Γιατί: οι προεπιλογές του αρχικού plugin ήταν παγωμένες περίπου το 2014. Τα PS4/Xbox/BubbleUPnP έχουν έκτοτε αποκτήσει υποστήριξη ήχου υψηλής ανάλυσης. Εκτός συσκευασίας, μια νέα εγκατάσταση παίζει την καλύτερη δυνατή ποιότητα σε αυτές τις συσκευές χωρίς ο χρήστης να αγγίξει τις ρυθμίσεις προφίλ συσκευής.
F02 - Συσκευές ελέγχου που διαφημίζουν MediaRenderer:3
Τι: το plugin ελέγχει την περιγραφή υπηρεσίας UPnP ενός renderer για να αποφασίσει αν το MusicBee μπορεί να το οδηγήσει. Το αρχικό ταίριαζε μόνο με urn:schemas-upnp-org:device:MediaRenderer:1. Οι σύγχρονες συσκευές διαφημίζουν :2 ή :3. Το F02 διευρύνει την αντιστοίχιση.
Γιατί: χωρίς αυτό, οι πρόσφατες μονάδες Sonos / WiiM / Eversolo απλά δεν εμφανίζονται ως στόχοι στη λίστα συσκευών "Αναπαραγωγή σε" του MusicBee - παρόλο που μιλούν το ίδιο πρωτόκολλο. Μια απλή διόρθωση αντιστοίχισης προθέματος συμβολοσειράς ξεκλειδώνει ολόκληρη τη σύγχρονη γενιά συσκευών.
F03 - Επιλογή "Επιβολή εγγενούς ροής" ανά προφίλ (προεπιλογή ΕΝΕΡΓΟ)
Τι: όταν επιλεγεί, το plugin στέλνει τα αρχικά bytes του αρχείου στη συσκευή χωρίς μετακωδικοποίηση, χωρίς DSP, χωρίς επεξεργασία ReplayGain. Απλώς το αρχικό αρχείο που επέλεξε ο χρήστης, byte-προς-byte (εκτός από το HTTP framing).
Γιατί: σύμφωνα με μαρτυρίες σε φόρουμ, αυτό είναι το μεγαλύτερο κέρδος στην ποιότητα αναπαραγωγής. Οι χρήστες hi-fi που αγοράζουν ακριβούς renderers θέλουν ρητά bit-perfect έξοδο· οποιαδήποτε παρέμβαση DSP ακυρώνει το νόημα. Προεπιλογή ΕΝΕΡΓΟ επειδή οι περισσότερες σύγχρονες συσκευές χειρίζονται όποιον κωδικοποιητή τους έριξε ο χρήστης, και το ReplayGain/EQ πρέπει να είναι προαιρετικό. Είναι ανά προφίλ, οπότε μπορείτε να διατηρήσετε τη μετακωδικοποίηση για ένα παλιό Xbox ενώ στέλνετε εγγενή ροή σε έναν hi-fi DAC.
F04 - "Επιβολή μετακωδικοποίησης" ανά προφίλ
Τι: παράκαμψη ανά προφίλ που αναγκάζει κάθε ροή σε αυτή τη συσκευή να περάσει από τον μετακωδικοποιητή, ανεξάρτητα από την υποστήριξη εγγενούς κωδικοποιητή. Το αντίστροφο του F03 (ForceNativeStream). Αμοιβαία αποκλειστικό με το F03 - το UI απενεργοποιεί αυτόματα το άλλο όταν ενεργοποιείται ένα από τα δύο.
Γιατί: ένας ενιαίος καθολικός διακόπτης θα ήταν αντιφατικός με το ForceNativeStream ανά προφίλ (F03). Πραγματική περίπτωση: η συσκευή Α είναι ένας hi-fi DAC που θέλει bit-perfect εγγενείς ροές· η συσκευή Β είναι ένας παλιός AV receiver που κολλάει στο FLAC. Με έναν καθολικό διακόπτη ο χρήστης πρέπει να επιλέξει - εις βάρος της άλλης συσκευής. Με ανά προφίλ κάθε συσκευή παίρνει τη σωστή απάντηση.
Υλοποίηση:
StreamingProfile.ForceTranscoding As Boolean = False.- Το σχήμα διατήρησης αυξήθηκε στην έκδοση 9. Τα αρχεία προ-v9 φορτώνουν την παλιά καθολική τιμή μία φορά και την αντιγράφουν σε όλα τα προφίλ, διατηρώντας την παλιά συμπεριφορά μέσω της αναβάθμισης.
- UI: αφαιρέθηκε από το πάνελ Διαγνωστικά, προστέθηκε στην ενότητα Προφίλ Συσκευών δίπλα στο ForceNativeStream. Χειριστές αμοιβαίας αποκλεισμού δύο κατευθύνσεων (
CheckedChangedσε κάθε ένα καταργεί την εγγραφή του άλλου πριν την αναστροφή, για να αποφευχθεί ένας άπειρος βρόχος). - Σημείο απόφασης:
Settings.ForceTranscoding→streamingProfile.ForceTranscodingστοWriteAudioFileDIDL.
F05 - "Επιβολή little-endian PCM" ανά προφίλ
Τι: οι ροές PCM (τύποι mime L16/L24) είναι big-endian σύμφωνα με τις προδιαγραφές. Ορισμένες συσκευές λανθασμένα αναμένουν little-endian και αναπαράγουν λευκό θόρυβο όταν τους δίνονται σωστά δεδομένα big-endian. Το F05 αλλάζει τη σειρά byte ανά προφίλ.
Γιατί: χωρίς αυτό, ορισμένες συσκευές παράγουν έναν τοίχο στατικού θορύβου. Το σύμπτωμα είναι δραματικό και η αιτία αόρατη χωρίς γνώση της κωδικοποίησης PCM - ο διακόπτης δίνει στους χρήστες μια λύση δοκιμής και λάθους.
F06 - "Μη χρήση Raw PCM" ανά προφίλ
Τι: όταν η συσκευή ισχυρίζεται ότι υποστηρίζει raw PCM, το plugin το χρησιμοποιεί. Ορισμένες συσκευές ψεύδονται - δέχονται το SOAP handshake αλλά παραμορφώνουν τα πραγματικά raw PCM δεδομένα, ενώ χειρίζονται σωστά το PCM τυλιγμένο σε ένα κοντέινερ WAVE. Το F06 επιβάλλει PCM-over-Wave ανεξάρτητα από το τι διαφημίζει η συσκευή.
Γιατί: συγκεκριμένα μοντέλα Marantz - διαφημίζουν raw PCM αλλά μόνο το WAVE λειτουργεί. Χωρίς αυτό, οι raw PCM ροές βγαίνουν παραμορφωμένες, χωρίς μήνυμα σφάλματος για να το επισημάνουν.
F07 - "Μήκος περιεχομένου" ανά προφίλ
Τι: ποια τιμή να στείλετε στην κεφαλίδα HTTP Content-Length. Τέσσερις επιλογές:
- Προεπιλογή - πραγματικός αριθμός byte όταν είναι γνωστός, παράλειψη όταν είναι άγνωστος.
- Κανένα - ποτέ μην στέλνετε την κεφαλίδα (μόνο κωδικοποίηση σε κομμάτια).
- Μόνο PCM - στέλνετε μόνο για raw PCM· παράλειψη για όλα τα άλλα.
- Σταθερό - στέλνετε
UInt32.MaxValue - 8192(ένας φρουρός για "τεράστιο άγνωστο μήκος").
Γιατί: οι συσκευές UPnP/DLNA διαφέρουν πολύ στον τρόπο που αντιδρούν στο Content-Length. Κάποιες χρειάζονται έναν ακριβή αριθμό, κάποιες το μισούν στις ροές, κάποιες χρειάζονται έναν φρουρό "πραγματικά μεγάλη" τιμή για να συνεχίσουν την προσωρινή αποθήκευση. Αυτό αργότερα διευρύνθηκε από PCM-only σε όλες τις μορφές εξόδου επειδή τα ίδια προβλήματα εμφανίστηκαν σε μετακωδικοποιημένες ροές MP3/AAC.
F08 - "Μη διαγραφή NextURI" ανά προφίλ
Τι: κανονικά το plugin καθαρίζει το NextURI που βρίσκεται στην ουρά της συσκευής όταν η ουρά αδειάζει (στέλνοντας SetNextAVTransportURI με κενό URL). Ορισμένες συσκευές (ιδίως η Denon) ερμηνεύουν ένα κενό NextURI ως "σταμάτα τα πάντα" και διακόπτουν αμέσως την αναπαραγωγή. Το F08 εμποδίζει το plugin να το καθαρίσει ποτέ.
Γιατί: χωρίς αυτό, οι κάτοχοι Denon βιώνουν τη συσκευή να διακόπτει εν μέσω κομματιού όταν η ουρά αδειάζει. Με το F08 επιλεγμένο, η συσκευή διατηρεί το παλιό NextURI στη μνήμη (αβλαβές - απλώς αντικαθίσταται την επόμενη φορά που κάτι μπαίνει στην ουρά).
F09 - FLAC ως μορφή εξόδου μετακωδικοποίησης
Τι: το αναπτυσσόμενο μενού μορφής μετακωδικοποίησης στα Προφίλ Συσκευών προσφέρει τώρα FLAC μαζί με PCM 16/24, MP3, AAC, Ogg. Η επιλογή του δρομολογεί τον κωδικοποιητή BASS μέσω της τυπικής γραμμής εντολών μετατροπής FLAC του MusicBee (ο ίδιος μηχανισμός που χρησιμοποιούν ήδη τα MP3/AAC/Ogg).
Γιατί: για συσκευές που χειρίζονται καλά το FLAC αλλά δεν μπορούν να αποκωδικοποιήσουν τον κωδικοποιητή πηγής (π.χ. ένα Eversolo που λαμβάνει τη βιβλιοθήκη WMA του MusicBee μετατρεπόμενη σε FLAC), αυτό διατηρεί την ποιότητα χωρίς απώλειες όπου τα MP3/AAC θα απέρριπταν δεδομένα ήχου. Ξεκλειδώνει το N02 (έλεγχος υπομίξης 5.1), το οποίο δεν μπορούσε να αντιμετωπιστεί χωρίς μια επιλογή μετακωδικοποίησης χωρίς απώλειες.
Υλοποίηση: προσθήκη μιας γραμμής στο μπλοκ Select Case Codec του Encoder.StartEncode - το FLAC ενώνεται με τα MP3/AAC/Ogg στον κλάδο που οδηγείται από τη γραμμή εντολών. Το αναπτυσσόμενο μενού UI αποκτά το "FLAC" ως 6η επιλογή. Η αντιστοίχιση φόρτωσης/αποθήκευσης στο SettingsDialog επεκτείνεται για να αναγνωρίζει FileCodec.Flac ↔ SelectedIndex = 5. Ο τύπος Mime, ο τύπος DLNA και η λειτουργία κωδικοποίησης είχαν ήδη συνδεθεί στο ItemManager.GetMimes / GetDlnaType / GetEncodeFeature από προηγούμενη εργασία (F21, F26).
Απρόσκοπτη αναπαραγωγή (SetNextAVTransportURI)
F10 - SetNextAVTransportURI / NextURI core
Τι: πραγματική απρόσκοπτη αναπαραγωγή. Όταν η συσκευή διαφημίζει υποστήριξη για SetNextAVTransportURI στην περιγραφή υπηρεσίας UPnP, το plugin προ-προωθεί το επόμενο κομμάτι στη συσκευή πριν τελειώσει το τρέχον. Η συσκευή μεταβαίνει εσωτερικά χωρίς ακουστικό κενό μεταξύ των κομματιών - αυτό που ακούτε σε ένα CD player. Αυτό δεν είναι το hack "συνεχής ροή" (το οποίο συνενώνει τα πάντα σε μια μακρά ροή και χάνει τα μεταδεδομένα ανά κομμάτι).
Γιατί: το κορυφαίο χαρακτηριστικό Tier-2. Τα άλμπουμ που ηχογραφούνται ως συνεχής ζωντανή παράσταση (ζωντανές ηχογραφήσεις, κλασικές κινήσεις, DJ sets) ακούγονται λάθος όταν υπάρχει μισό δευτερόλεπτο σιωπής μεταξύ των κομματιών. Η σωστή επίλυση αυτού είναι ένα κορυφαίο χαρακτηριστικό, τώρα σε αυτό το fork.
Σημειώσεις: ο ήχος που βρίσκεται στην ουρά εξυπηρετείται μέσω του HTTP server του plugin χρησιμοποιώντας streamHandle=0 (λειτουργία ανάκτησης βιβλιοθήκης), πράγμα που σημαίνει ότι ο μηχανισμός ήχου του MusicBee δεν συμμετέχει στο κομμάτι που βρίσκεται στην ουρά. Αντιστάθμιση: τα εφέ ReplayGain/DSP/EQ δεν εφαρμόζονται στο επόμενο κομμάτι. Αποδεκτό όταν είναι ενεργοποιημένη η "επιβολή εγγενούς ροής" (η προεπιλογή).
F11 - "Απενεργοποίηση υποστήριξης NextURI" ανά προφίλ
Τι: ακόμα κι αν μια συσκευή διαφημίζει SetNextAVTransportURI, αυτό το πλαίσιο ελέγχου αναγκάζει το plugin να αγνοήσει αυτή τη διαφήμιση και να επιστρέψει στην αναπαραγωγή ενός κομματιού κάθε φορά.
Γιατί: ορισμένες συσκευές διαφημίζουν NextURI αλλά έχουν μια buggy υλοποίηση (σφάλματα, μισές μεταβάσεις, κολλήματα). Αντί να κάνουμε reverse-engineer κάθε χαλασμένη συσκευή, ο χρήστης αποκτά έναν διακόπτη "απλώς απενεργοποίησέ το εδώ".
F12 - Κύκλος ζωής NextURI στη λίστα αναπαραγωγής
Τι: όταν το MusicBee ενεργοποιεί το NowPlayingListChanged, το plugin επανεκτιμά τι πρέπει να μπει στην ουρά για απρόσκοπτη μετάβαση. Ζητά από το MusicBee το νέο "επόμενο" κομμάτι μέσω NowPlayingList_GetNextIndex(1) + NowPlayingList_GetListFileUrl, συγκρίνει με αυτό που βρίσκεται αυτή τη στιγμή στην ουρά της συσκευής (παρακολουθείται μέσω του νέου πεδίου nextPlaySourceUrl), και το ξαναβάζει στην ουρά αν άλλαξε (ή καθαρίζει την ουρά αν το MusicBee λέει ότι δεν υπάρχει επόμενο κομμάτι).
Γιατί: χωρίς το F12, η συσκευή συνέχιζε να αναπαράγει ένα παλιό NextURI όταν ο χρήστης αφαιρούσε/αναδιάτασσε το κομμάτι που βρισκόταν στην ουρά. Αυτό ιστορικά χρειάστηκε αρκετές επαναλήψεις επειδή κάθε τροποποίηση λίστας χρειάζεται διαφορετικό χειρισμό - εμείς απλοποιήσαμε εμπιστευόμενοι το NowPlayingList_GetNextIndex (το οποίο ήδη σέβεται την τυχαία αναπαραγωγή και την επανάληψη όλων), οπότε όλες οι παραλλαγές διοχετεύονται μέσω της ίδιας σύγκρισης.
Υλοποίηση:
- Νέο πεδίο
nextPlaySourceUrlαποθηκεύει το URL της βιβλιοθήκης MusicBee του τι βρίσκεται στην ουρά (το URL ροής με επίθημα χειρισμού δεν είναι συγκρίσιμο με μια διαδρομή βιβλιοθήκης). - Νέα
Public Sub RefreshQueuedNextUri()στοMediaRendererDevice. Τρία αποτελέσματα: κανένα NextURI στην ουρά → no-op· το NextURI στην ουρά ταιριάζει με το νέο "επόμενο" → no-op· το NextURI στην ουρά διαφέρει → καλέστε τοQueueNextμε το νέο URL (ήQueueNext("")για να καθαρίσετε - το οποίο σέβεται το F08 DoNotClearNextUri). - Συνδεδεμένο στο
Plugin.ReceiveNotificationκάτω από τοNotificationType.NowPlayingListChanged.
F13 - Αναστολή αποτυχίας NextURI
Τι: μετά από 4 διαδοχικές αποτυχίες SetNextAVTransportURI στην ίδια συσκευή, το plugin απενεργοποιεί την απρόσκοπτη αναπαραγωγή για αυτή τη συσκευή μέχρι να επανεκκινηθεί το MusicBee.
Γιατί: αν μια συσκευή είναι πραγματικά χαλασμένη για το NextURI (διαλείποντα σφάλματα SOAP, δυσλειτουργίες δικτύου), το plugin θα συνέχιζε να προσπαθεί σε κάθε κομμάτι. Το F13 σταματά τον θόρυβο και επιστρέφει σιωπηλά στην αναπαραγωγή ενός κομματιού κάθε φορά.
F14 - Λειτουργία επανάληψης + ενσωμάτωση NextURI
Τι: το F14 χωρίζεται σε δύο περιπτώσεις που αντιμετωπίζονται στον ανιχνευτή μετάβασης F15 στο OnAvTransportStatusCheck:
- Επανάληψη Όλων: Το MusicBee περνάει το σωστό URL "wrap" (κομμάτι 1 στο τέλος της λίστας) στο
Plugin.QueueNextαπό μόνο του. Δεν χρειάζεται ειδική λογική plugin - η συσκευή μεταβαίνει σε αυτό και ο ανιχνευτής F15 καλεί τοPlayer_PlayNextTrackως συνήθως, το οποίο επαναφέρει τον δείκτη NPL του MusicBee στο 0. - Επανάληψη Ενός: Το MusicBee περνάει το ΙΔΙΟ URL κομματιού στο
Plugin.QueueNext. Η συσκευή μεταβαίνει σε αυτό (νέος χειριστής ροής, ίδια πηγή). Ο ανιχνευτής F15 τώρα ερωτά τοPlayer_GetRepeat()- αν είναιRepeatMode.One, παραλείπει την κλήσηPlayer_PlayNextTrackώστε το MusicBee να μην προωθήσει τον δείκτη NPL μακριά από το κομμάτι που επαναλαμβάνεται.
Γιατί: χωρίς την παράλειψη Επανάληψης Ενός, η κλήση Player_PlayNextTrack κατά την απρόσκοπτη μετάβαση θα προωθούσε το MusicBee στο επόμενο κομμάτι της λίστας (η Επανάληψη Ενός επηρεάζει μόνο την αυτόματη προώθηση στο τέλος του κομματιού στο UI του αναπαραγωγέα - το Επόμενο Κομμάτι κινείται πάντα προς τα εμπρός), αντιφάσκοντας με το τι σημαίνει Επανάληψη Ενός.
Προειδοποίηση μετρητή αναπαραγωγών: στην Επανάληψη Ενός, η αύξηση του μετρητή αναπαραγωγών εξαρτάται από το MusicBee 3.7.9563+ που παρατηρεί την επαναλαμβανόμενη αναπαραγωγή. Οι παλαιότερες εκδόσεις του MusicBee αναπαράγουν σωστά την απρόσκοπτη επανάληψη αλλά χάνουν την αύξηση του μετρητή αναπαραγωγών. Τεκμηριωμένο· δεν είναι εμπόδιο.
F15 - Μηχανή κατάστασης ανίχνευσης μετάβασης κομματιού
Τι: όταν η συσκευή μεταβαίνει εσωτερικά από το τρέχον κομμάτι στο NextURI, το plugin πρέπει να το παρατηρήσει και να πει στο MusicBee να προωθήσει τον δείκτη αναπαραγωγής του. Διαφορετικά, το MusicBee νομίζει ότι βρίσκεται ακόμα στο προηγούμενο κομμάτι και οι μετρήσεις αναπαραγωγής / UI / scrobbling αποκλίνουν.
Υλοποίηση: ελέγχει το GetPositionInfo.TrackURI σε κάθε χτύπο του χρονομέτρου κατάστασης. Όταν το αναφερόμενο URI ταιριάζει με αυτό που βάλαμε στην ουρά μέσω NextURI, καλούμε το Player_PlayNextTrack στο MusicBee και ορίζουμε το suppressNextSoapCall ώστε το προκύπτον PlayToDevice να μην ξαναστείλει το SetAVTransportURI (το οποίο θα διέκοπτε την απρόσκοπτη αναπαραγωγή).
Γιατί: χωρίς το F15 η συσκευή αναπαράγει το επόμενο κομμάτι αλλά το UI του MusicBee λέει ότι βρίσκεται ακόμα στο προηγούμενο. Μπερδευτικό, χαλάει το scrobbling, χαλάει την παρακολούθηση του μετρητή αναπαραγωγών. Η ανίχνευση μετάβασης κομματιού χρειάζεται μακρά, ανά renderer επανάληψη επειδή κάθε μάρκα renderer έχει τις δικές της ιδιορρυθμίες στο πότε αναφέρει την αλλαγή URI (κάποιες αναφέρουν πρώτα TRANSITIONING, κάποιες πηδούν κατευθείαν στο PLAYING με νέο URI, κάποιες έχουν ένα σύντομο STOPPED ενδιάμεσα).
Σημειώσεις: η πρώτη μας προσπάθεια λειτουργεί στον renderer BubbleUPnP. Οι περιπτώσεις άκρων ανά συσκευή παραμένουν στο B6.
F16 - Διόρθωση pop-on-gapless-transition
Τι: το pop συμβαίνει όταν η μορφή πηγής (ρυθμός δειγματοληψίας / κανάλια / κωδικοποιητής) του κομματιού που βρίσκεται στην ουρά διαφέρει από το κομμάτι που αναπαράγεται αυτή τη στιγμή, αναγκάζοντας τον DAC της συσκευής να κλειδώσει ξανά κατά τη μετάβαση. Το F16 προσθέτει ένα διαγνωστικό NextUri:FormatChange που ενεργοποιείται κατά την ώρα της ουράς κάθε φορά που οι μορφές διαφέρουν, ονομάζοντας και τις δύο πλευρές - έτσι οι χρήστες που ακούν pops μπορούν να συσχετίσουν.
Το διαγνωστικό επισημαίνει επίσης την αντιμετώπιση: επιλέξτε ForceTranscoding στο προφίλ της συσκευής. Αυτό ομογενοποιεί κάθε κομμάτι σε έναν ενιαίο κωδικοποιητή/ρυθμό δειγματοληψίας/βάθος bit μετακωδικοποίησης, εξαλείφοντας εντελώς τη διαφορά μορφής πηγής.
Γιατί αναβλήθηκε για την πραγματική διόρθωση μετακωδικοποίησης για αντιστοίχιση: η δομική διόρθωση (μετακωδικοποίηση του κομματιού που βρίσκεται στην ουρά για να ταιριάζει με τη μορφή του κομματιού που αναπαράγεται) απαιτεί αλλαγές στο σχήμα URL του HTTP server του plugin - επί του παρόντος το /encode/{id}0.{ext} εξυπηρετεί το αρχείο που βρίσκεται στην ουρά εγγενώς. Μια μελλοντική v2 του F16 θα πρόσθετε διαδρομές /encode/{id}0_{rate}_{depth}.{ext} ανά μορφή και θα τις συνέδεε μέσω του κωδικοποιητή. Αυτή είναι μια μεγαλύτερη αρχιτεκτονική αλλαγή που αξίζει να γίνει αν μια πραγματική συσκευή εμφανίζει το pop αφού το ForceTranscoding δεν επαρκεί.
Υλοποίηση σήμερα:
- Το πεδίο
lastSourceUrlπαρακολουθεί το URL της πηγής που αναπαράγεται αυτή τη στιγμή. - Το
QueueNextδιαβάζει τοFilePropertyType.SampleRate/Channels/Kindτόσο για το τρέχον όσο και για τα κομμάτια που βρίσκονται στην ουρά και καταγράφει τοNextUri:FormatChangeσε περίπτωση αναντιστοιχίας.
F17 - Επανασυγχρονισμός γραμμής προόδου μετά από αναζήτηση
Τι: η συνάρτηση Seek() καλούσε ήδη το GetPlayPositionInformation() μετά από ένα επιτυχημένο Seek SOAP, το οποίο διορθώνει την περίπτωση "καθόλου επανασυγχρονισμός". Το F17 κλείνει την υπόλοιπη απόκλιση έως και 1 δευτερόλεπτο που προκαλείται από την κβάντιση 1 δευτερολέπτου του RelTime του UPnP: όταν η αναφερόμενη θέση της συσκευής στρογγυλοποιείται εντός 1 δευτερολέπτου από τον ζητούμενο στόχο του χρήστη, το plugin εμπιστεύεται τώρα την υπο-δευτερολέπτου ακριβή τιμή του χρήστη αντί για την περικοπή της συσκευής. Μόνο όταν η συσκευή αναφέρει κάτι δραματικά διαφορετικό (>1 δευτερόλεπτο απόκλιση) χρησιμοποιούμε την τιμή της (η αναζήτηση προσγειώθηκε κάπου αλλού από ό,τι ζητήθηκε, π.χ. snap-to-keyframe σε ορισμένους κωδικοποιητές).
Γιατί: χωρίς αυτό, η αναζήτηση στο 2:30.500 αγκυροβολημένη έναντι της αναφοράς "2:30" της συσκευής θα έκανε τη γραμμή προόδου να εμφανίζει ~500ms πίσω από την πραγματικότητα. Μετά το F17 η γραμμή ταιριάζει με την πρόθεση του χρήστη για την κοινή περίπτωση σάρωσης εντός κομματιού, και εξακολουθεί να σέβεται την αναφορά της συσκευής για την εξαίρεση snap-to-keyframe.
F18 - Συνεχής ροή / NextURI interlock
Τι: δύο αλληλεπιδράσεις είναι τώρα σε ισχύ:
- Χρόνος εκτέλεσης: Το
QueueNextεπιστρέφει νωρίςFalseόταν τοSettings.ContinuousOutputείναι ενεργοποιημένο. Η συνεχής ροή είναι ο δικός της μηχανισμός απρόσκοπτης αναπαραγωγής (μία μακρά συνενωμένη ροή)· η αποστολήSetNextAVTransportURIπάνω από αυτήν μπερδεύει τη συσκευή σχετικά με το αν κάθε κομμάτι είναι ένα διακριτό URI ή μέρος της συνεχούς ροής. - UI: όταν ο χρήστης επιλέγει το καθολικό πλαίσιο ελέγχου συνεχούς ροής, το
forceNativeStreamτου προφίλ που εμφανίζεται αυτή τη στιγμή απενεργοποιείται αυτόματα. Η συνεχής ροή μετακωδικοποιεί πάντα, οπότε η επιβολή εγγενούς ροής είναι άσκοπη σε συνδυασμό.
Γιατί: αποτρέπει τον χρήστη από το να ενεργοποιήσει δύο αντικρουόμενους μηχανισμούς απρόσκοπτης αναπαραγωγής ταυτόχρονα. Χωρίς το F18, η συσκευή θα λάμβανε τόσο ένα URI συνεχούς ροής όσο και ένα NextURI για κάθε επόμενο κομμάτι, με απροσδιόριστη συμπεριφορά ανάλογα με τον renderer.
F19 - Αγνοούνται τα κενά σφάλματα NextURI
Τι: όταν καλείται το SetNextAVTransportURI με κενό URL (π.χ. τελευταίο κομμάτι στη λίστα), ορισμένες συσκευές επιστρέφουν ένα σφάλμα SOAP. Το F19 τα καταπίνει σιωπηλά - καταγράφονται αλλά δεν διαδίδονται ως σφάλματα.
Γιατί: η συνθήκη "δεν υπάρχει επόμενο κομμάτι" είναι φυσιολογική, όχι σφάλμα. Η αντιμετώπισή της ως θανατηφόρας μολύνει το αρχείο καταγραφής και (σε ορισμένες ροές) ενεργοποιεί καταιγίδες επανάληψης.
Τύποι Mime & μεταδεδομένα DLNA
F20 - MP3 mime → audio/mpeg
Τι: ο συμβατός με τα πρότυπα τύπος mime MP3 είναι audio/mpeg, όχι audio/mp3. Ο τελευταίος είναι μια κοινή λανθασμένη ονομασία που οι περισσότερες συσκευές ανέχονται, αλλά οι αυστηρότεροι renderers τον απορρίπτουν.
Γιατί: διορθώνει σιωπηλά την αναπαραγωγή σε αυστηρότερες συσκευές που ακολουθούν το πρότυπο. Η βάση κώδικα yaiol το είχε ήδη σωστά· δεν χρειάστηκε αλλαγή.
F21 - Σειρά τύπων Mime: μη-x- παραλλαγή πρώτη
Τι: όταν μια συσκευή διαφημίζει τόσο audio/flac όσο και audio/x-flac, το plugin επιστρέφει πρώτα την μη-x- παραλλαγή. Το ίδιο ισχύει για οποιονδήποτε κωδικοποιητή με τυπικούς και πειραματικούς τύπους mime.
Γιατί: το πρόθεμα x- σηματοδοτεί πειραματικούς/ανεπίσημους τύπους mime. Ορισμένοι renderers συμπεριφέρονται καλύτερα με την τυπική μορφή. Μικρή αναδιάταξη, πραγματικός αντίκτυπος.
F22 - Υποστήριξη τύπου mime Opus
Τι: αναγνωρίζει το Opus ως κωδικοποιητή ήχου με δυνατότητα ροής· στέλνει τύπο mime audio/opus κατά την εξυπηρέτηση κομματιών Opus.
Γιατί: το Opus είναι πλέον κοινό (σύγχρονος κωδικοποιητής συμβιβασμού ομιλίας/μουσικής). Χωρίς το F22 το plugin θα αρνούνταν να μεταδώσει αρχεία Opus ακόμη και σε συσκευές που τα χειρίζονται.
F23 - Υποστήριξη αρχείων πηγής Monkey Audio (APE)
Τι: αναγνωρίζει τα αρχεία .ape ως έγκυρο κωδικοποιητή πηγής για ροή/μετακωδικοποίηση.
Γιατί: το APE είναι μια μορφή χωρίς απώλειες με μια εξειδικευμένη αλλά πιστή βάση χρηστών. Η προσθήκη του κοστίζει λίγο και ξεκλειδώνει τη βιβλιοθήκη για αυτούς τους χρήστες.
F24 - Εφεδρεία mime AAC / ALAC
Τι: αν μια συσκευή υποστηρίζει AAC ή ALAC αλλά δεν τα διαφημίζει ρητά στην περιγραφή υπηρεσίας UPnP, το plugin τα προσφέρει ούτως ή άλλως ως εφεδρεία.
Γιατί: αρκετές συσκευές που χειρίζονται καλά το AAC ξέχασαν να το αναφέρουν στο XML δυνατοτήτων τους. Χωρίς το F24 το plugin δεν θα προσπαθούσε καν, αναγκάζοντας τη μετακωδικοποίηση. Με το F24 το plugin προσπαθεί και αφήνει τη συσκευή να το χειριστεί εγγενώς αν μπορεί.
F25 - Σημαία τύπου DLNA για εγγενείς + κωδικοποιημένες ροές WAV
Τι: η σημαία τύπου DLNA (ένας αναγνωριστικός προφίλ όπως LPCM, WAVE, MP3) πρέπει να ταιριάζει με αυτό που λαμβάνει η συσκευή. Το F25 διασφαλίζει ότι οι εγγενείς ροές και οι κωδικοποιημένες ροές WAV επισημαίνονται σωστά.
Γιατί: ο μη ταιριαστός τύπος DLNA προκαλεί σε ορισμένες συσκευές να αρνηθούν εντελώς την αναπαραγωγή ή να εφαρμόσουν τον λάθος αποκωδικοποιητή.
F26 - Κεφαλίδα DLNA για αρχεία FLAC
Τι: οι ροές FLAC λαμβάνουν τον κατάλληλο αναγνωριστικό προφίλ DLNA στις κεφαλίδες τους.
Γιατί: χωρίς αυτό, ορισμένες συσκευές που υποστηρίζουν FLAC δεν αναγνωρίζουν τη ροή ως τέτοια.
F27 - Διόρθωση υπολογισμού bitrate στα μεταδεδομένα
Τι: το res@bitrate της συνεχούς ροής υπολογιζόταν ως (sampleRate * channels * bitsPerSample) / 1000 - kbps, με απόκλιση κατά έναν παράγοντα ~125 από την προδιαγραφή UPnP DIDL που ορίζει το χαρακτηριστικό ως bytes ανά δευτερόλεπτο. Τώρα διαιρείται με το 8 αντί για το 1000.
Γιατί: λανθασμένη εμφάνιση bitrate στη συσκευή - κοσμητική στους περισσότερους renderers, αλλά κάποιοι διαθέτουν buffer ροής από την τιμή και κολλάνε σε ροές που φαίνονται ~125× μικρότερες από ό,τι είναι. Η διαδρομή αρχείου πηγής χωρίς συνεχή ροή το είχε ήδη σωστά ((bitrate_kbps * 1000) \ 8 = bytes/sec)· μόνο η διαδρομή συνεχούς ροής ήταν λάθος.
F28 - Διόρθωση μορφής χρόνου μεταδεδομένων (Marantz)
Τι: το res@duration στο DIDL είχε μορφοποιηθεί ως H:MM:SS (π.χ. 0:03:42). Η προδιαγραφή UPnP DIDL ορίζει τη μορφή ως H+:MM:SS[.F+] - αυστηρά με κλασματικά δευτερόλεπτα προαιρετικά αλλά συνιστώμενα· ορισμένες συσκευές Marantz αντιμετωπίζουν την απλή μορφή ως μη έγκυρη και αφήνουν την εμφάνιση διάρκειας κενή. Τώρα μορφοποιείται ως H:MM:SS.fff (π.χ. 0:03:42.000).
Γιατί: πρόβλημα εμφάνισης ειδικό για μια μάρκα· η συμβατή μορφή ISO με κλασματικά δευτερόλεπτα το διορθώνει χωρίς να επηρεάζει καμία άλλη συσκευή. Εφαρμόζεται και στα δύο σημεία εκπομπής DIDL (διαδρομή αρχείου πηγής + διαδρομή κωδικοποιημένης ροής στο WriteAudioFileDIDL).
Επιπλέον διόρθωση στην ίδια διέλευση: τα pv:addedTime και pv:lastPlayedTime χρησιμοποιούσαν hh (ρολόι 12 ωρών) στις συμβολοσειρές μορφής DateTime αντί για HH (24 ωρών). Οποιοδήποτε κομμάτι προστέθηκε ή αναπαράχθηκε μεταξύ 13:00 και 23:59 θα εμφανιζόταν με λάθος ώρα (π.χ. 17:42 → "05:42") σε συσκευές που εμφανίζουν το πεδίο. Τώρα χρησιμοποιεί HH.
F29 - Υποστήριξη αναζήτησης κωδικοποιημένου MP3 (CBR)
Τι: οι μετακωδικοποιημένες ροές MP3 διαφημίζουν τώρα DLNA.ORG_OP=11 (αναζήτηση τόσο κατά byte όσο και κατά χρόνο) αντί για DLNA.ORG_OP=10 (μόνο κατά byte). Οι συσκευές που προηγουμένως αρνούνταν την αναζήτηση κατά χρόνο σε μετακωδικοποιημένο MP3 μπορούν τώρα να οδηγήσουν κανονικά τη γραμμή προόδου/UI αναζήτησης.
Γιατί: ο μετακωδικοποιητής του MusicBee παράγει MP3 σταθερού bitrate στην προεπιλογή HighQuality, οπότε η αντιστοίχιση byte ↔ χρόνου είναι γραμμική - η συσκευή μπορεί να μετατρέψει ένα αίτημα αναζήτησης κατά χρόνο σε ένα αίτημα αναζήτησης κατά byte HTTP Range από μόνη της χωρίς καμία υποστήριξη από την πλευρά του κωδικοποιητή. Η διαφήμιση OP=11 ξεκλειδώνει αυτό το UI στη συσκευή. Χωρίς το F29, οι χρήστες που αναζητούσαν μέσα σε ένα μετακωδικοποιημένο MP3 είτε αγνοούσαν σιωπηλά την αναζήτηση είτε επέστρεφαν στην αρχή του κομματιού.
Υλοποίηση: αναδιαρθρώθηκε το GetEncodeFeature στο ItemManager.vb για να σπάσει το inline If σε μια ευανάγνωστη αλυσίδα If/ElseIf/Else. Το MP3 αποκτά OP=11 ρητά· άλλοι μη-PCM κωδικοποιητές διατηρούν OP=10. Καμία αλλαγή για AAC/FLAC/κλπ. - αυτά θα χρειάζονταν επαλήθευση CBR-ness ειδικά για τον κωδικοποιητή, την οποία το MusicBee δεν εγγυάται.
F30 - Χειρισμός επέκτασης αρχείου .mpeg
Τι: τα αρχεία με επέκταση .mpeg (και το ακόμα πιο σπάνιο .mpe) αναγνωρίζονται τώρα ως FileCodec.Mp3 στο GetCodec. Πριν από το F30 επέστρεφαν FileCodec.Unknown και απορρίπτονταν σιωπηλά από τη βιβλιοθήκη / δεν μπορούσαν να είναι πηγές μετακωδικοποίησης.
Γιατί: παλιά αρχεία MPEG-1 Layer 3 χρησιμοποιούσαν μερικές φορές .mpeg αντί για .mp3 (η προδιαγραφή επιτρέπει και τα δύο). Μια χούφτα αρχείων σε μια βιβλιοθήκη 300k είναι αρκετή για να αισθανθεί κανείς "το MusicBee τα δείχνει αλλά το plugin όχι" - μπερδευτικό για τον χρήστη.
Συμπεριφορά αναπαραγωγής
F31 - Οι ραδιοφωνικές ροές χρησιμοποιούν αυτόματα τη συνεχή λειτουργία
Τι: το WriteAudioFileDIDL ελέγχει τώρα την ιδιότητα Kind του URL πηγής μέσω Library_GetFileProperty και αντιμετωπίζει οποιοδήποτε αρχείο του οποίου το Kind τελειώνει σε "Stream" (το MusicBee αναφέρει "MP3 Stream", "Internet Stream", κ.λπ. για το ραδιόφωνο) ως συνεχές ανεξάρτητα από τον καθολικό διακόπτη Settings.ContinuousOutput. Χρησιμοποιείται ο κλάδος DIDL συνεχούς ροής (Τίτλος: "Continuous Stream", id="continuousstream", σταθερή έξοδος PCM/Wave)· η συσκευή βλέπει μια ενιαία ροή άπειρου τύπου.
Γιατί: οι ραδιοφωνικές ροές δεν έχουν όρια κομματιών, σταθερό μήκος, ούτε αναζήτηση. Η αντιμετώπισή τους ως διακριτών αρχείων στο DIDL προκάλεσε το plugin να διαφημίζει εύρη byte και διάρκειες που δεν υπάρχουν. Η αυτόματη εναλλαγή όταν το MusicBee μας είπε ήδη "αυτό είναι μια ροή" αφαιρεί ένα πρόβλημα που ο χρήστης δεν θα έπρεπε να σκέφτεται.
Πεδίο εφαρμογής: εφαρμόζεται μόνο όταν το MusicBee οδηγεί την αναπαραγωγή (musicBeePlayToMode). Η διαδρομή ανάκτησης βιβλιοθήκης (περιήγηση πελάτη UPnP) παραμένει αμετάβλητη - τα URL ραδιοφώνου εκεί είναι σπάνια και η συμπεριφορά που βλέπει ο χρήστης δεν πρέπει να αλλάξει χωρίς ρητή δοκιμή.
F32 - Εφεδρεία διαφήμισης κωδικοποιητή
Τι: αν μια συσκευή δεν διαφημίζει ορισμένους κωδικοποιητές (ή το plugin δεν μπορεί να αναλύσει το XML δυνατοτήτων της συσκευής), το plugin δεν απορρίπτει αμέσως τη ροή. Αντίθετα, προσπαθεί να την εξυπηρετήσει και αφήνει τη συσκευή να αποφασίσει.
Γιατί: πολλές συσκευές έχουν ελλιπές ή μη αναγνώσιμο XML δυνατοτήτων αλλά στην πραγματικότητα χειρίζονται καλά τον κωδικοποιητή. Το F32 ανταλλάσσει μια μικρή "καλύτερη εικασία και δοκιμή" με μια πλήρη άρνηση.
F33 - Βελτίωση συγχρονισμού γραμμής προόδου
Τι: η θέση μεταξύ των ερωτήσεων είναι ήδη εκτιμώμενη με βάση το ρολόι από ένα μόνο σημείο αναφοράς (currentPlayStartTicks), οπότε η γραμμή προόδου ενημερώνεται ομαλά σε υπο-δευτερολέπτου ρυθμό. Η υπόλοιπη πηγή τρεμοπαίγματος ήταν το αρχικό σημείο αναφοράς για ένα κομμάτι που μόλις ξεκίνησε: ο προηγούμενος κώδικας υπέθετε position=0 τη στιγμή που ο χρονοδιακόπτης κατάστασης παρατήρησε για πρώτη φορά ότι η κατάσταση έγινε Playing, αλλά μέχρι τότε η συσκευή μπορεί να έπαιζε για 100-500ms (ένα διάστημα ερωτήσεων). Η γραμμή προόδου του MusicBee θα ξεκινούσε από το 0, και στη συνέχεια θα πηδούσε μπροστά όταν η πραγματικότητα την προλάβαινε.
Διόρθωση F33: κατά τη μετάβαση σε Playing για πρώτη φορά σε ένα νέο κομμάτι (currentPlayStartTimeEstimated=True), καλέστε το GetPlayPositionInformation() για να λάβετε την πραγματική τρέχουσα θέση της συσκευής, και στη συνέχεια αγκυροβολήστε σε αυτήν. Το UPnP αναφέρει μόνο ανάλυση 1 δευτερολέπτου, οπότε το σημείο αναφοράς είναι ακόμα κβαντισμένο, αλλά είναι πολύ πιο κοντά στην αλήθεια από το να υποθέσουμε 0.
Γιατί: ομαλότερη + ακριβέστερη εμφάνιση προόδου, ειδικά αμέσως μετά την αλλαγή κομματιού. Δεν υπάρχει τρόπος να παρακαμφθεί η ανάλυση αναφοράς 1 δευτερολέπτου του UPnP - αυτό είναι προδιαγραφή.
F34 - Τρεμόπαιγμα γραμμής προόδου μετά την αλλαγή κομματιού
Τι: όταν καλείται το PlayToDevice για ένα νέο κομμάτι, το plugin συνήθιζε να αφήνει τα currentPlayPositionMs και currentPlayStartTicks στις προηγούμενες τιμές τους για το παράθυρο ~100ms μεταξύ του SOAP-Play και της πρώτης ερώτησης του χρονοδιακόπτη κατάστασης που ανιχνεύει τη νέα κατάσταση Playing. Η γραμμή προόδου του MusicBee θα έδειχνε για λίγο το τέλος του προηγούμενου κομματιού, και στη συνέχεια θα επέστρεφε στο 0, και μετά θα ανέβαινε. Το F34 μηδενίζει και τα δύο στην είσοδο του PlayToDevice - τη στιγμή που γνωρίζουμε ότι συμβαίνει μια αλλαγή κομματιού, πριν από οποιαδήποτε εργασία SOAP.
Γιατί: οπτικό σφάλμα σε περιπτώσεις γρήγορης παράλειψης (χειροκίνητη επόμενη ή απρόσκοπτη μετάβαση). Τώρα η πρώτη ερώτηση PlayPositionMs του MusicBee μετά το Play επιστρέφει καθαρά 0, και στη συνέχεια το GetPlayPositionInformation του F33 την βελτιώνει στην πραγματική θέση της συσκευής στον πρώτο χτύπο αλλαγής κατάστασης.
Υλοποίηση: τέσσερις γραμμές στην κορυφή του PlayToDevice, σε συνδυασμό με την ακριβή αγκύρωση χρόνου μετάβασης του F33.
F35 - Σφάλμα "Επιβολή μετακωδικοποίησης"
Τι: η επιβολή μετακωδικοποίησης μπορούσε ακόμα να παρακάμψει τη μετακωδικοποίηση σε ορισμένους συνδυασμούς. Μετά την αναδιάρθρωση ανά προφίλ του F04, δύο συγκεκριμένα κενά σφραγίστηκαν:
- Προτεραιότητα με το ForceNativeStream. Όταν και τα δύο ήταν True (κάτι που μπορεί να συμβεί σε μια μετανάστευση σχήματος ή ένα μερικό αρχείο ρυθμίσεων), το ForceTranscoding κερδίζει τώρα απευθείας (
If streamingProfile.ForceTranscoding Then forceEncode = True ElseIf streamingProfile.ForceNativeStream Then forceEncode = False). Ο αμοιβαίος αποκλεισμός του UI εμποδίζει τον χρήστη να επιλέξει και τα δύο, αλλά ο έλεγχος χρόνου εκτέλεσης χειρίζεται οποιαδήποτε κατάσταση που φορτώθηκε ασυνεπής από το δίσκο. - Λογική bypassTranscodeDecision. Προηγουμένως:
streamingProfile.ForceNativeStream AndAlso Not Settings.ForceTranscoding. Τώρα:streamingProfile.ForceNativeStream AndAlso Not streamingProfile.ForceTranscoding- ο ίδιος κανόνας προτεραιότητας αλλά στο ίδιο πεδίο ανά προφίλ.
Γιατί: "επιβολή" πρέπει να σημαίνει επιβολή. Αν ο χρήστης ενεργοποίησε ρητά το ForceTranscoding για μια συσκευή, το plugin δεν πρέπει ποτέ να επιστρέψει σιωπηλά σε εγγενή ροή, ανεξάρτητα από το πώς συνδυάζονται άλλες σημαίες.
F36 - Εξαίρεση κλεισίματος renderer
Τι: το Plugin.ReceiveNotification τυλίχθηκε σε ένα ανώτερο επίπεδο Try/Catch που καταγράφει οποιαδήποτε μη αναμενόμενη εξαίρεση αντί να την αφήσει να διαδοθεί πίσω στον μηχανισμό ειδοποιήσεων του MusicBee.
Γιατί: οι ειδοποιήσεις από το MusicBee (PlayStateChanged, VolumeMuteChanged, κ.λπ.) αποστέλλονται στο ControlPointManager το οποίο επικοινωνεί με τον renderer μέσω SOAP. Οι μεμονωμένες τοποθεσίες κλήσης είχαν ήδη Try/Catch γύρω από τις κλήσεις SOAP τους, αλλά μια αρκετά περίεργη περίπτωση χρονισμού (π.χ. ο renderer να πεθαίνει μεταξύ δύο κλήσεων SOAP στον ίδιο χειριστή ειδοποιήσεων) μπορούσε ακόμα να διαφύγει. Ο ανώτερος περιτύλιγμα είναι το τελικό δίχτυ ασφαλείας ώστε ο χρήστης να μην βλέπει ποτέ ένα γενικό αναδυόμενο μήνυμα "TargetInvocationException" από το MusicBee.
Υλοποίηση: μετονομάστηκε το υπάρχον σώμα σε ReceiveNotificationInternal και προστέθηκε ένας λεπτός περιτύλιγμα ReceiveNotification που κάνει Try { ReceiveNotificationInternal(...) } Catch { LogError(...) }. Η προϋπάρχουσα υποδομή Try/Catch ανά μέθοδο μέσα στο ControlPointManager (γύρω από κάθε κλήση PostSoapRequest) παραμένει - το F36 είναι ζώνη + τιράντες.
F37 - Αναζήτηση μακράς διάρκειας κομματιού που ενεργοποιεί ψευδή μετάβαση
Τι: η αναζήτηση μέσα σε ένα μακρύ κομμάτι μπορεί να προκαλέσει έναν σύντομο κύκλο Stopped→Playing σε ορισμένους renderers. Χωρίς διάκριση, το ProcessNewPlayState.Stopped το αντιμετωπίζει ως φυσικό τέλος κομματιού και καλεί το Player_PlayNextTrack, προωθώντας το MusicBee όταν ο χρήστης ήθελε απλώς να κάνει scrub. Το F37 καταγράφει το lastUserInitiatedSeek στο Seek() και προσθέτει μια προστασία 5 δευτερολέπτων στον χειριστή Stopped (αντικατοπτρίζοντας το υπάρχον παράθυρο lastUserInitiatedStop).
Γιατί: η σιωπηλή παράλειψη στο επόμενο κομμάτι κατά τη διάρκεια μιας αναζήτησης είναι ένα από αυτά τα σφάλματα που κανείς δεν μπορεί να μαντέψει την αιτία - ο χρήστης σκέφτεται "περίεργο, προσπάθησα να κάνω scrub προς τα εμπρός και τώρα παίζει το επόμενο τραγούδι". Η διόρθωση είναι μηχανική: το ίδιο μοτίβο με τη διάκριση διακοπής χρήστη που υπάρχει ήδη.
F38 - Βελτιωμένος χειρισμός αναζήτησης για κωδικοποιητές επιρρεπείς σε σφάλματα
Τι: η συντριβή του BubbleUPnP κατά την αναζήτηση MP3 ήταν το κανονικό σύμπτωμα. Μετά από έλεγχο, ο τρέχων κώδικας αναζήτησης yaiol κάνει ήδη τα σωστά πράγματα - η εγγενής διαδρομή χειρίζεται σωστά το HTTP Range (206, Content-Range, AcceptRanges), η κωδικοποιημένη διαδρομή διαφημίζει X-AvailableSeekRange και αναλύει τις εισερχόμενες κεφαλίδες timeSeekRange.dlna.org / npt, οι σημαίες DLNA.ORG_OP αντικατοπτρίζουν τις πραγματικές δυνατότητες ροής (με DisablePcmTimeSeek opt-out για προβληματικές συσκευές Platinum). Δοκιμασμένο από χρήστες στο τρέχον BubbleUPnP 4.6.4: δεν παρατηρήθηκαν σφάλματα.
Γιατί: η συντριβή αναζήτησης MP3 του BubbleUPnP αναφέρθηκε περίπου το 2024 και η εφαρμογή έχει λάβει ~16 μήνες διορθώσεων από τότε. Το F29 (κωδικοποιημένο MP3 OP=11) ήταν η νέα μεταβλητή που θα μπορούσε να το είχε εκθέσει ξανά· δεν το κάνει, στις δοκιμασμένες εκδόσεις.
Αν επιστρέψει ένα σφάλμα: η μορφή της διόρθωσης θα ήταν ένας διακόπτης "περιορισμένη αναζήτηση" ανά προφίλ που επιβάλλει DLNA.ORG_OP=10 (μόνο κατά byte) σε επισημασμένους κωδικοποιητές - αντικατοπτρίζοντας τον τρόπο λειτουργίας του DisablePcmTimeSeek για το PCM. Προσθέστε τότε, όχι προληπτικά.
UI & καταγραφή
F39 - Το κουμπί "Προσθήκη" επιλέγει το νέο προφίλ
Τι: το κλικ στο "Προσθήκη" στη λίστα προφίλ συσκευών δημιουργεί ένα νέο προφίλ ΚΑΙ το επιλέγει αυτόματα ώστε ο χρήστης να μπορεί να επεξεργαστεί αμέσως τα πεδία. Η αναδιάρθρωση του διαλόγου μας σε ενότητες το κάνει ήδη αυτό - τόσο η διαδρομή άμεσης προσθήκης όσο και η διαδρομή από πρότυπο καταλήγουν με Me.activeStreamingProfiles.SelectedIndex = Me.activeStreamingProfiles.Items.Count - 1. Ένας έλεγχος επιβεβαίωσε ότι το fork μας το χειρίζεται ήδη - τίποτα δεν χρειάζεται να αλλάξει.
Γιατί: μικρό UX papercut που αποδείχθηκε ότι δεν ήταν ήδη papercut εδώ.
F40 - Μεγαλύτερες μέγιστες συνδέσεις + προειδοποιητικό αρχείο καταγραφής
Τι: το όριο ταυτόχρονων ροών του plugin (SemaphoreSlim γύρω από Sockets_Stream_File / Sockets_Encoder_Start) ήταν σκληρά κωδικοποιημένο στο 4. Το F40 το καθιστά διαμορφώσιμο από τον χρήστη στη σελίδα γενικών ρυθμίσεων (προεπιλογή 16, εύρος 1-256), προσθέτει μια γραμμή καταγραφής MaxConnections όταν ένα αίτημα πρέπει να περιμένει για μια θέση, ΚΑΙ εμφανίζει μια κόκκινη ετικέτα ⚠ Max Conn κάτω αριστερά στο παράθυρο διαλόγου ρυθμίσεων αν το όριο χτυπήθηκε τουλάχιστον μία φορά από την εκκίνηση του MusicBee.
Γιατί: όταν μια συσκευή εκτελεί παράλληλα αιτήματα (ορισμένα Marantz/Linn κατά τη σάρωση εξωφύλλων, οι ανιχνεύσεις μεταδεδομένων του BubbleUPnP παράλληλα με την ενεργή αναπαραγωγή), πρόσθετα αιτήματα μπλοκαρίστηκαν σιωπηλά πίσω από το semaphore - ο χρήστης έβλεπε "αργή συσκευή" χωρίς ορατή αιτία. Η γραμμή καταγραφής είναι καλή για τεχνικό debugging αλλά οι μη τεχνικοί χρήστες δεν διαβάζουν ποτέ αρχεία καταγραφής. Η ορατή ετικέτα στο παράθυρο διαλόγου ρυθμίσεων καθιστά την κατάσταση υπέρβασης ορίου ανακαλύψιμη σε όποιον ανοίξει τις προτιμήσεις του plugin.
Υλοποίηση:
- Κεντροποιήθηκε η αναμονή στο
WaitOnSendBarrier(logTag)στοMusicBeeUpnp.vb· και οι δύο τοποθεσίες κλήσης (MediaServerDevice.GetFile,Encoder.StartEncode) το χρησιμοποιούν. - Το
Settings.MaxConnectionsδιατηρείται στην v8 του σχήματος ρυθμίσεων. - Το
Plugin.MaxConnectionsHitείναι μια κολλητή σημαία συνεδρίας που ορίζεται μέσα στοWaitOnSendBarrier· επαναφέρεται μόνο με επανεκκίνηση του MusicBee. - Το
SettingsDialog.maxConnectionsBadgeείναι μια κόκκινη έντονη ετικέτα στο(16, 410)που εμφανίζεται μόνο όταν τοPlugin.MaxConnectionsHitείναι True. Έχει μια συμβουλή εργαλείου που εξηγεί την αιτία και την αποκατάσταση. - Το Semaphore αρχικοποιείται μία φορά κατά τη φόρτωση τύπου, οπότε η αλλαγή της ρύθμισης απαιτεί επανεκκίνηση του MusicBee (σημειώνεται στην ετικέτα του πεδίου).
F41 - Καταγραφή "κωδικοποίηση λόγω ReplayGain/DSP"
Τι: αντί για ξεχωριστές γραμμές καταγραφής "κωδικοποίηση για RG" / "κωδικοποίηση για DSP", η ενιαία γραμμή StreamDecision από το F42 περιλαμβάνει MB-DSP/EQ, MB-ReplayGain, Profile-DSP/EQ, Profile-ReplayGain ως συσσωρευμένους λόγους. Ίδια διαγνωστική αξία, λιγότερος θόρυβος.
Γιατί: οι χρήστες βλέπουν όλους τους λόγους για τους οποίους γίνεται μετακωδικοποίηση για ένα δεδομένο κομμάτι σε μία γραμμή καταγραφής, όχι διάσπαρτους. Δείτε το F42 για πλήρεις λεπτομέρειες.
F42 - Καταγραφή "ο renderer δεν υποστηρίζει τον κωδικοποιητή πηγής"
Τι: προστέθηκε μια γραμμή καταγραφής StreamDecision ανά κομμάτι αναπαραγωγής σε συσκευή που λέει είτε "εγγενής CODEC" είτε "μετακωδικοποίηση CODEC→CODEC reason=…". Το πεδίο λόγου συσσωρεύει κάθε συνθήκη που ενεργοποίησε τη μετακωδικοποίηση: MB-DSP/EQ, MB-ReplayGain, Profile-DSP/EQ, Profile-ReplayGain, WebFile, VirtualFile, ForceTranscoding(global), SampleRate<min/SampleRate>max, DownmixToStereo, DeviceLacksCodec(X), BandwidthConstrained.
Γιατί: οι χρήστες μπερδεύονταν από απροσδόκητες αυξήσεις CPU σε αρχεία που περίμεναν να μεταδοθούν εγγενώς. Μια γραμμή καταγραφής ανά κομμάτι τους λέει ακριβώς ποια συνθήκη προκάλεσε τη μετακωδικοποίηση - και αν το πεδίο δείχνει DeviceLacksCodec(Flac) γνωρίζουν αμέσως ότι οι πληροφορίες πρωτοκόλλου της συσκευής ήταν ελλιπείς και μπορεί να θέλουν να ενεργοποιηθεί η εφεδρεία του F32.
Υλοποίηση: ενιαία συμβολοσειρά συσσωρευτή που δημιουργείται σταδιακά μέσω της αλυσίδας αποφάσεων· καταγράφεται μία φορά στο τέλος. Ελέγχεται από το Settings.LogDebugInfo για να αποφευχθεί ο θόρυβος καταγραφής στην παραγωγή.
F43 - Το αρχείο καταγραφής SetNextAVTransport εμφανίζει το URL πηγής
Τι: οι καταχωρήσεις αρχείου καταγραφής QueueNext περιλαμβάνουν τώρα source=<MusicBee library path> μαζί με stream=<HTTP streaming URL>. Η ίδια αλλαγή εφαρμόστηκε στη διαδρομή επιτυχίας και στη διαδρομή αποτυχίας (QueueNext:Failed).
Γιατί: κατά τον εντοπισμό σφαλμάτων ενός προβλήματος κομματιού στην ουρά, το URL ροής (/encode/aabbccdd0.flac) είναι αδιαφανές από μόνο του - το ίδιο για κάθε κομμάτι. Το URL πηγής είναι η αναγνώσιμη από άνθρωπο διαδρομή βιβλιοθήκης που σας λέει ακριβώς ποιο αρχείο προσπάθησε να βάλει στην ουρά το MusicBee.
F44 - Καλύτερη καταγραφή σφαλμάτων τύπου mime
Τι: δύο νέες καταχωρήσεις αρχείου καταγραφής κατά την Activate:
Activate:MimeUnverified- ενεργοποιείται ανά λανθασμένη καταχώρηση στην απάντησηGetProtocolInfoτης συσκευής, ονομάζοντας ποια καταχώρηση δεν μπορούσε να αναλυθεί (ώστε ο χρήστης να μπορεί να δει π.χ. "το Marantz επέστρεψεhttp-get:*::*για κάποιον κωδικοποιητή - η δυνατότητα είναι ανεπιβεβαίωτη, η εφεδρεία του F32 θα μαντέψει").Activate:NoSinkInfo- ενεργοποιείται μία φορά αν η συσκευή δεν επέστρεψε καθόλου στοιχείο<Sink>. Σημαίνει ότι τοSupportedMimeTypesπαραμένει Nothing και τοIsCodecSupportedυποβαθμίζεται σε "υποθέτουμε ότι όλα λειτουργούν" - χρήσιμο πλαίσιο όταν αργότερα εμφανίζονται σφάλματα "η συσκευή αρνήθηκε τη ροή".
Γιατί: πριν από το F44 αυτές οι σιωπηλές παρακάμψεις δυνατοτήτων άφηναν τους χρήστες να μαντεύουν γιατί τα κομμάτια τους είτε μετακωδικοποιούνταν ενάντια στις προσδοκίες είτε απορρίπτονταν από τη συσκευή. Τώρα μια απλή αναζήτηση για Activate: δείχνει αν οι πληροφορίες δυνατοτήτων της συσκευής ήταν χρησιμοποιήσιμες.
F45 - Καλύτερη καταγραφή σφαλμάτων μεταδεδομένων
Τι: το αρχείο καταγραφής εξαιρέσεων Browse στο ContentDirectoryService.vb είχε ήδη εμπλουτιστεί σε προηγούμενη εργασία yaiol (η συνεδρία σφαλμάτων Alia Vox) με ObjectID και ίχνος στοίβας. Το F45 το επεκτείνει περαιτέρω με BrowseFlag (μεταδεδομένα έναντι παιδιών), Filter (ποια χαρακτηριστικά ζήτησε ο πελάτης), sortCriteria, και partialResultLength (πόσα bytes DIDL παράχθηκαν πριν από την αποτυχία - δείχνει πόσο μακριά στην παρτίδα βρίσκεται το κακό κομμάτι).
Γιατί: όταν κάτι πάει στραβά εν μέσω DIDL, η τιμή μερικού μήκους σας λέει αν η αποτυχία ήταν στο πρώτο κομμάτι της παρτίδας (partial=0) ή ενδιάμεσα (partial=N) - σε συνδυασμό με το startingIndex της παρτίδας, μπορείτε να αναγνωρίσετε τον δείκτη του κομματιού που προκάλεσε το σφάλμα. Το Filter και το BrowseFlag εξηγούν τι είδους περιήγηση ήθελε ο πελάτης· μερικές φορές μια περιήγηση μόνο μεταδεδομένων αποτυγχάνει όπου μια περιήγηση παιδιών για το ίδιο ID επιτυγχάνει.
Δικτύωση
F46 - Η αυτόματη λειτουργία ανακοινώνει μόνο σε προσαρμογείς πραγματικού δικτύου
Τι: στη λειτουργία διεπαφής Αυτόματη, το πρόσθετο ανακοίνωνε παλαιότερα τον εαυτό του (SSDP) σε κάθε λειτουργικό προσαρμογέα IPv4. Σε μια μηχανή που τρέχει επίσης μια σήραγγα VPN (NordLynx) ή έναν εικονικό διακόπτη (Hyper-V / WSL / Docker), η ίδια βιβλιοθήκη ανακοινωνόταν και σε καθέναν από αυτούς τους προσαρμογείς, οπότε το control point από το οποίο μεταδίδετε ανακάλυπτε τον server δύο ή τρεις φορές και εμφάνιζε τη βιβλιοθήκη ως διπλά αντίγραφα. Η αυτόματη λειτουργία κρατά πλέον μόνο τους προσαρμογείς που έχουν πραγματική προεπιλεγμένη πύλη IPv4 (HasIPv4Gateway) - την οποία οι προσαρμογείς σηράγγων και εικονικών διακοπτών δεν διαθέτουν - οπότε αυτοί αφαιρούνται από τη λίστα ανακοίνωσης. Μια διεύθυνση καρφιτσωμένη από τον χρήστη εξακολουθεί να υπερισχύει απόλυτα (ανακοίνωση μόνο σε αυτή τη διεπαφή), και αν κανένας προσαρμογέας δεν αναφέρει πύλη, ο επιλογέας επιστρέφει σε όλους τους προσαρμογείς, ώστε η λίστα των ανακοινωμένων διευθύνσεων να μην είναι ποτέ κενή και το πρόσθετο να μην μπορεί να γίνει αόρατο.
Γιατί: το διπλό αντίγραφο δεν προκαλείται από το «να είσαι σε VPN» - προκαλείται από την ταυτόχρονη ανακοίνωση στον προσαρμογέα LAN και στον προσαρμογέα σήραγγας/εικονικό, οπότε ένα control point βλέπει τον ίδιο server σε δύο διευθύνσεις. Ένα καταναλωτικό VPN (NordVPN/NordLynx) περνά από τη σήραγγα μόνο την κίνηση προς το διαδίκτυο· ο renderer DLNA ζει στο LAN και η κίνηση του τοπικού υποδικτύου παρακάμπτει τη σήραγγα, οπότε ο προσαρμογέας της σήραγγας δεν φτάνει ούτως ή άλλως ποτέ σε renderer - η αφαίρεσή του αφαιρεί ένα αντίγραφο-φάντασμα, ποτέ μια λειτουργική διαδρομή. Ο έλεγχος πύλης είναι το φθηνό, αξιόπιστο σήμα που ξεχωρίζει έναν πραγματικό προσαρμογέα LAN/Wi-Fi από μια σήραγγα ή έναν εικονικό διακόπτη. Συμπληρώνει το N05 (που διόρθωσε το πώς στέλνονται οι ανακοινώσεις σε τέτοιες συνδέσεις - multicast αντί για broadcast)· το F46 ορίζει σε ποιους προσαρμογείς γίνεται καν ανακοίνωση.
Γνωστός περιορισμός: ένα mesh / απομακρυσμένης πρόσβασης VPN (Tailscale, ZeroTier, WireGuard προς το σπίτι) του οποίου οι renderers ζουν πραγματικά πέρα από τη σήραγγα συνήθως παρουσιάζει προσαρμογέα χωρίς προεπιλεγμένη πύλη, οπότε η αυτόματη λειτουργία τον απορρίπτει επίσης. Αυτοί οι χρήστες καρφιτσώνουν αντ' αυτού τη διεύθυνση VPN, η οποία υπερισχύει του φίλτρου πύλης.