MusicBee UPnP Plugin المساعدة

الجديد

2.0.2 - 2026-07-20

  • لم يعد بالإمكان حذف قالب بينما يتبعه أي عقدة - يظل زر الحذف معطلاً ببساطة، لذلك لا يمكن ترك عقدة يتيمة أبدًا. يعرض كل صف قالب الآن عددًا مباشرًا (ن) لمتابعيه، مما يفسر تعطيل الحذف في لمحة؛ كما أن حذف قالب غير مستخدم يثبت الآن، بدلاً من ظهور الإعدادات الافتراضية التي تم شحنها بهدوء عند التحميل التالي.
  • يظهر زر قمع جديد بجانب شجرة العرض بالضبط أي العقد تتبع قالبًا: يقوم بتصفية الشجرة لتقتصر على متابعي القالب المحدد فقط، ويعيد التصفية عند تحديد قوالب أخرى، ويعيد الشجرة الكاملة عند إيقاف تشغيله.
  • أصبحت عقدتا الراديو والبودكاست مقترنتين بشكل دائم بقالب فئتهما - أعد تشكيل القالب في علامة التبويب "المسارات" وتتبع العقدة تلقائيًا؛ لا يوجد شيء لتطبيقه، لذا فإن زر "تطبيق" معطل لهما. تعكس قائمة القوالب ذلك بنطاقين، قياسي (حيث يتم إنشاء جميع القوالب الجديدة) ومحجوز (الراديو + البودكاست).

2.0.1 - 2026-07-20

  • أصبحت قوالب المسار الآن مرتبطة مباشرة بالعقد التي تستخدمها. يؤدي تطبيق قالب إلى جعل العقدة تتبعه: قم بتحرير القالب لاحقًا وستتم إعادة تشكيل كل عقدة تتبعه على الفور — لا داعي للبحث عنه وإعادة تطبيقه عقدة تلو الأخرى. تعرض شجرة العرض القالب الذي تتبعه كل عقدة مباشرة بعد اسمها، ويظهر تغيير الاسم هناك على الفور، ويخبرك حذف القالب أولاً بعدد العقد التي تتبعه (تحتفظ بتخطيطها الحالي وتتوقف ببساطة عن اتباع أي شيء).
  • يؤدي تطبيق قالب على عقدة مخفية إلى جعلها مرئية مرة أخرى — التطبيق هو إيماءة "أرني هذا، بهذا الشكل"، بينما يبقى الإخفاء مع مربع الاختيار "مرئي". لقد اختفى قالب "مخفي" المحجوز الذي يحل محله.
  • لم تعد شجرة العرض تفقد مكانك: تبقى علامات التحديد والمجلدات الموسعة وموضع التمرير جميعها بعد تطبيق القوالب والتحديثات الأخرى.

2.0.0 - 2026-06-16

هذا هو الإصدار العام الأول من شوكة yaiol مفتوحة المصدر لمكون MusicBee UPnP الإضافي. يتم تقديمه في جزأين: كل ما هو جديد في هذه الشوكة، ثم الإصلاحات والتحسينات التي أجريت على المكون الإضافي الأصلي. يحتفظ كل عنصر بنموذج ماذا / لماذا من كتالوج الميزات الداخلية للمشروع بحيث يكون المنطق وراء كل تغيير موجودًا في الصفحة، وليس مجرد التغيير.

الجديد في هذه الشوكة

MediaRenderer - التشغيل إلى MusicBee

N01 - MusicBee كعارض للتشغيل إليه

ماذا: عادةً ما يعمل هذا المكون الإضافي في اتجاه واحد: يتصفح الهاتف أو أي جهاز آخر مكتبة MusicBee ويشغل الموسيقى على نفسه. تضيف هذه الميزة الاتجاه المعاكس - فهي تتيح لـ MusicBee أن يكون المشغل. من تطبيق تحكم على هاتفك (مثل BubbleUPnP) يمكنك اختيار MusicBee على سطح المكتب الخاص بك كشيء يقوم بالتشغيل، ثم التحكم فيه من يدك: تشغيل، إيقاف مؤقت، إيقاف، تخطي للأمام أو الخلف، الانتقال إلى نقطة في المسار، وتغيير مستوى الصوت أو كتمه.

لماذا: يحول هاتفك إلى جهاز تحكم عن بعد للموسيقى الموجودة بالفعل على جهاز الكمبيوتر الخاص بك. اجلس على الأريكة، وتصفح مكتبتك على الهاتف، وانقر على مسار، وسيخرج الصوت من مكبرات الصوت المتصلة بسطح المكتب الخاص بك - مع تحكم كامل من مكان جلوسك. لم يتم شحن المكون الإضافي الأصلي لهذه الميزة كخاصية عاملة.

تشغيله: يكون معطلاً افتراضيًا، لأن تشغيله يسمح لأي شيء على شبكتك المنزلية ببدء التشغيل على جهاز الكمبيوتر الخاص بك. يمكنك تمكينه عن طريق تحديد مربع اختيار في علامة التبويب General في مربع حوار الإعدادات. لكل دور من أدوار المكون الإضافي الثلاثة مربع اختيار خاص به هناك - مشاركة مكتبتي (Server)، السماح للآخرين بالتشغيل إليّ (Renderer)، والتشغيل على أجهزة أخرى (Control Point) - ويعرض مربع الحوار فقط علامات تبويب الإعدادات التي تحتاجها الأدوار التي قمت بتشغيلها بالفعل، لذلك لن تواجه أبدًا خيارات لا تنطبق عليك.

التمييز بين أجهزتك: يمكنك إعطاء العارض أي اسم تريده (يبدأ بـ "MusicBee (yaiol)"). هذا الاسم هو ما يظهر في قائمة أهداف التشغيل على هاتفك، لذلك عندما يكون هناك أكثر من جهاز كمبيوتر واحد يشغل MusicBee، يمكنك معرفة أيها هو. يسري تغيير الاسم فورًا، دون الحاجة إلى إعادة تشغيل.

أفضل صوت ممكن عند التشغيل على نفسه: عندما تتصفح مكتبة MusicBee الخاصة من هاتفك وترسل مسارًا مرة أخرى إلى نفس MusicBee، يتعرف المكون الإضافي على أنه يُطلب منه تشغيل أحد ملفاته الخاصة ويقوم ببساطة بتشغيله مباشرة من القرص الخاص بك. تكون النتيجة دقيقة وفورية - مثالية بت، مع تطبيق معادل MusicBee الخاص وتعديل مستوى الصوت - بدلاً من دفع الصوت بلا فائدة إلى الشبكة ثم إعادته مباشرة إلى نفسه.

تشغيله بشكل خاص: تعمل الأدوار الثلاثة بشكل مستقل، لذا يمكنك تشغيل العارض مع ترك مشاركة المكتبة معطلة. في إعداد "عارض فقط" هذا، تظل مكتبتك مخفية تمامًا عن الشبكة - يتم الإعلان عن هدف التشغيل فقط - ولن يعرض MusicBee أبدًا التشغيل على نفسه.


سلوك التشغيل

N02 - عدم خلط FLAC 5.1 تلقائيًا

ماذا: كان قيد عدد القنوات في MediaServerDevice.GetEncodedFile هو If StereoOnly OrElse Not isPcmData Then channelCount = 2. كانت عبارة Not isPcmData تقوم بخلط كل تحويل غير PCM (FLAC، MP3، AAC، Ogg) إلى ستريو بصمت بغض النظر عن عدد قنوات المصدر، مما يعطل العارضات القادرة على 5.1 عندما كان المصدر FLAC 5.1. الآن تستثني العبارة الثانية FLAC: Not isPcmData AndAlso encoder.Codec <> FileCodec.Flac. يمر FLAC 5.1 دون تغيير؛ بينما لا يزال MP3/AAC/Ogg يفرض الستريو لأن مشفرات MusicBee سطر الأوامر لتلك التنسيقات تتوقع إدخال قناتين.

لماذا: الهدف الأساسي من تحويل مصدر FLAC 5.1 إلى إخراج FLAC هو الحفاظ على المزيج متعدد القنوات. جعل الخلط الصامت خيار تحويل FLAC عديم الفائدة للاستماع المحيطي. مع N02، يقوم بالشيء الصحيح.


البنية

التغيير الهيكلي الذي يجعل الشوكة قابلة للتطبيق على مكتبة كبيرة - غائب عن المكون الإضافي الأصلي.

N03 - شجرة تصفح كسولة (حسب الطلب)

ماذا: قام المكون الإضافي الأصلي ببناء شجرة التصفح بأكملها عند بدء تشغيل MusicBee - تعداد كل مسار، Library_GetFileTags كامل لكل ملف، تجميع التسلسل الهرمي للحاويات بالكامل - قبل فتح منفذ HTTP. في مكتبة حقيقية (أكثر من 50 ألف مسار، 5400 حلقة بودكاست، مئات المحطات) يستغرق ذلك دقائق من التشغيل البارد، وتبقى الشجرة في ذاكرة الوصول العشوائي (RAM) إلى الأبد بما في ذلك الفروع التي لا يفتحها أي عميل أبدًا. لا تبني هذه الشوكة أي شيء مقدمًا: يعرض الجذر عنصرًا نائبًا واحدًا مسبوقًا بـ L: لكل نقطة نهاية (L:music، L:podcast، L:filter:…)؛ يتم حساب كل مستوى فقط عندما يتصفح العميل فيه (LazyBrowseEnsureLazyEndpointInMemory ← ذاكرات تخزين مؤقتة لكل مستوى)، وتقوم إشعارات تغيير المكتبة بمسح ذاكرات التخزين المؤقتة (SetLibraryDirty).

لماذا: التشغيل البارد فوري بشكل أساسي - يكون منفذ HTTP مفتوحًا بحلول الوقت الذي ينتهي فيه MusicBee من تهيئة المكون الإضافي - وتبقى الذاكرة متناسبة مع ما تم تصفحه، وليس مع حجم المكتبة. المقايضة: يدفع التصفح الأول لنقطة نهاية تكلفة التحميل الخاصة به؛ يتم تخزين إعادة الدخول مؤقتًا حتى التغيير التالي للمكتبة. هذا هو الأساس الذي يعتمد عليه كل شيء آخر. ملاحظات كاملة: FIXES.md.


الشبكات والمتانة

تعزيز مسار ربط خادم HTTP. يتعطل المكون الإضافي الأصلي بصمت عندما يكون منفذه غير متاح.

N04 - ربط منفذ HTTP ذاتي الإصلاح

ماذا: لم يعد خادم HTTP الخاص بالمكون الإضافي يتعطل عندما يكون منفذه المكون غير متاح. ثلاثة تغييرات مرتبطة:

  1. العودة التلقائية عند فشل الربط. يحاول HttpServer.Start المنفذ المكون، وعند حدوث SocketException، يقوم بمسح ما يصل إلى 20 منفذًا للأعلى بحثًا عن أول منفذ مجاني. يتم تسجيل المنفذ المربوط الفعلي في Plugin.boundServerPort جديد، وكل ما يعلن عن الخادم - عناوين URL الخاصة بـ SSDP LOCATION (استجابة NOTIFY + M-SEARCH)، وعنوان URL للجهاز (PrimaryHostUrl)، وإعادة توجيه منفذ الموجه، ومرشحات SSDP/نقطة التحكم الذاتية - يقرأ الآن boundServerPort بدلاً من Settings.ServerPort. تكتشف عملاء UPnP المنفذ الحقيقي عبر SSDP، لذا فإن المنفذ المنقول يكون شفافًا للعارضات.
  2. إشعار المستخدم. عندما يحدث تراجع (المنفذ المحفوظ ليس هو المستخدم)، يخبر MessageBox مترجم (WarnPortInUse) المستخدم بالمنفذ الذي يعمل بالفعل وأن الأجهزة ستظل تجده - لأن المكون الإضافي يعمل بدون واجهة رسومية ولن يرى رسالة داخل مربع الحوار إلا شخص يشتبه بالفعل في وجود مشكلة.
  3. استعادة بعد إعادة التشغيل. كان RestartServer (مسار إعادة التشغيل بعد حفظ الإعدادات) يقوم بإلغاء مرجعية Plugin.controller / Plugin.server بشكل أعمى. إذا ألقى Initialise الأولي خطأ قبل إنشائهما (وهو بالضبط ما تسبب فيه فشل الربط)، فإن حفظ الإعدادات التالي كان يؤدي إلى NullReferenceException - تاركًا مكونًا إضافيًا شبه ميت. يقوم الآن بإعادة إنشائهما وتشغيلهما عندما يكون Nothing، لذا فإن حفظ منفذ يعمل يعيد إحياء المكون الإضافي دون إعادة تشغيل MusicBee بالكامل.

لماذا: كان المحفز حادثة مستخدم حقيقية. كان المنفذ الافتراضي القديم 49382 يقع ضمن النطاق الديناميكي لنظام Windows (49152-65535)، حيث تقوم Hyper-V/WSL2/Docker/WinNAT بحجز كتل كبيرة تتغير مع كل تمهيد - لذلك فشل الربط مع WSAEACCES ("الوصول محظور") على جهاز كان يعمل عليه لأشهر. ثم اصطدم تغيير الافتراضي إلى منفذ مجاني مع Serviio (خادم DLNA منفصل موجود بالفعل على المنفذ الجديد)، مما أدى إلى الفشل مع WSAEADDRINUSE. تم ابتلاع كل فشل في Initialise، مما ترك المكون الإضافي ميتًا بصمت ثم أدى إلى NRE عند حفظ الإعدادات التالي. بعد N04، يُصلح تصادم المنفذ نفسه - يستمر الخادم في العمل على المنفذ المجاني التالي، ويتم إبلاغ المستخدم، ويعيد العملاء اكتشافه - بدلاً من تعطيل المكون الإضافي بالكامل.

التنفيذ:

  • تم نقل المنفذ الافتراضي 493829779 (أقل من النطاق الديناميكي، لذلك لا يقوم Windows بحجزه تلقائيًا؛ ليس افتراضيًا معروفًا لخادم الوسائط) في جميع إعلانات ServerPort الثلاثة + احتياطي فشل تحليل الإعدادات.
  • Plugin.boundServerPort (حقل مشترك جديد) يحمل منفذ الاستماع المباشر؛ يبقى activeServerPort هو اللقطة المكونة بحيث لا يتم تشغيل منطق شارة "Restart Required" بشكل خاطئ عند التراجع.
  • HttpServer.PortScanRange = 20؛ يتوقف المسح عند أول TcpListener.Start() ناجح ويرمي الاستثناء الأخير فقط إذا فشلت جميع المحاولات.
  • مفتاح مورد EN جديد WarnPortInUse (تتبع الترجمات تمرير اللغة المحلية وقت النشر).

N05 - إعلانات SSDP عبر مجموعة البث المتعدد (VPN / نقطة إلى نقطة)

ماذا: يتم إرسال إعلانات SSDP إلى مجموعة البث المتعدد UPnP (239.255.255.250) بدلاً من عنوان بث IP. يتم أيضًا كبح خطأ "لا يمكن الوصول إلى كائن تم التخلص منه" غير الضار الذي يتم تسجيله عندما تتسابق استجابة بحث SSDP مع إعادة تشغيل الخادم.

لماذا: على محولات الشبكة من نقطة إلى نقطة / VPN، لا ينطبق بث IP - فشل إرسال البث القديم مع "وسيطة غير صالحة" وتم تفويت الإعلانات، لذلك كان المكون الإضافي غير مرئي للعملاء على تلك الروابط. الإعلان إلى مجموعة البث المتعدد الصحيحة يصلح الاكتشاف على هذه المحولات بالضبط.

تصفح المكتبة

تم شحن هذه الميزات في هذه الشوكة وليست موجودة في المكون الإضافي الأصلي. وقد جاءت من تصفح مخرجات المكون الإضافي نفسه من عملاء UPnP حقيقيين.

N06 - عرض المكتبة بناءً على الفلاتر

ماذا: تصبح علامات تبويب الفلاتر في MusicBee (ملفات .xautopf في مجلد MusicBee الخاص بالمستخدم) حاويات جذر UPnP في مكتبة المكون الإضافي. يمكن بعد ذلك تصفح مسارات كل فلتر في تسلسل هرمي من AlbumArtistSort → Album → Tracks.

لماذا: يتوقع المستخدمون الذين لديهم فلاتر MusicBee منسقة (مثل "مسارات 5 نجوم"، "المضافة مؤخرًا"، "كلاسيكي ← باروك") العثور عليها عند تصفح المكون الإضافي من عميل UPnP. المكون الإضافي الأصلي كان يعرض فقط شجرة المكتبة الخام.


N07 - ربط حقل SortAlbumArtist

ماذا: يقرأ المكون الإضافي الآن MetaDataType 165 (Sort Album Artist) من MusicBee ويستخدمه لتجميع/فرز الفنانين في طرق عرض التصفح.

لماذا: يستخدم متصفحو الصوت عالي الدقة وعشاق الصوت أسماء الفنانين المصنفة ("Beethoven, Ludwig van" بدلاً من "Ludwig van Beethoven") لتنظيم المكتبات. توقع قياسي للمستمعين الجادين. مفقود من كلا المصدرين.


N08 - معالجة AlbumArtist متعدد القيم

ماذا: عندما يحتوي حقل AlbumArtist لألبوم على فنانين متعددين مفصولين بـ "; " (مثل "yaiol; Ars Ricercata")، يظهر المسار الآن تحت كل فنان في طرق عرض التصفح، وليس تحت فنان واحد مجمع يجمع الأسماء.

لماذا: تحتاج الألبومات التعاونية والتجميعات إلى الظهور تحت كل متعاون. بدون هذا، تكون نصف مسارات البحث للعثور على الألبوم معطلة.


N09 - غلاف الألبوم للحاويات (upnp:albumArtURI)

ماذا: تتضمن عقد حاوية الألبوم في استجابات تصفح DIDL الآن عنصر upnp:albumArtURI يشير إلى غلاف الألبوم.

لماذا: بدون هذا، يعرض كل ألبوم في عرض تصفح عميل UPnP أيقونة عامة بدلاً من غلاف الألبوم. إشارة بصرية للتنقل؛ متوقع من كل متصفح صوتي حديث عالي الدقة.


N10 - ترتيب المسارات داخل ألبومات الفلاتر

ماذا: يتم الآن فرز المسارات داخل ألبوم مكشوف بفلتر حسب رقم القرص، ثم رقم المسار.

لماذا: ترتيب الألبوم القياسي. بدون فرز صريح، كانت المسارات تعود بأي ترتيب يصادف أن يعيدها الفلتر - عادة ما تبدو عشوائية.


N11 - إصلاح شجرة مجلدات قوائم التشغيل

ماذا: فشلت دالة LoadLibraryPlaylists (التي كتبها ستيفن مايال في حوالي عام 2014) في النزول إلى مجلدات قوائم التشغيل التي تم إنشاؤها حديثًا. انتهى الأمر بأول قائمة تشغيل في كل مجلد، بالإضافة إلى أي مجلدات فرعية، كيتيمة في المستوى الجذر.

لماذا: كانت موجودة في المكون الإضافي الأصلي لمدة أحد عشر عامًا. مرئية في غضون 30 ثانية من فتح BubbleUPnP والنقر على قوائم التشغيل. تم إصلاحها في yaiol عن طريق التكرار الصحيح في المجلدات التي تم إنشاؤها حديثًا أثناء بناء الشجرة.


N12 - تنقية أحرف التحكم غير القانونية في XML

ماذا: أي مسار يحتوي على علامة تتضمن حرف تحكم C0 (مثل 0x19 من تمرير ترميز سيء - UTF-8 ← Latin-1 ← ثم تقطيع 0x99 إلى 0x19) تسبب في فشل استجابة التصفح بأكملها مع Action Failed بمجرد دخول المسار السيء إلى دفعة مقسمة إلى صفحات.

لماذا: يمنع XML 1.0 معظم أحرف التحكم C0، ويرمي XmlWriter استثناءً عند طلب كتابة أي منها. كانت موجودة في المكون الإضافي الأصلي. تم إصلاحها عن طريق إزالة الأحرف غير الصالحة عند كل نقطة خروج Library_GetFileTags عبر XmlConvert.IsXmlChar.


N13 - قائمة الراديو حتمية عبر التصفح المقسم إلى صفحات

ماذا: تصفح حاوية الراديو وقع في فرع قائمة الملفات العامة، والذي استدعى files.Sort(AlbumFileComparer) في كل استدعاء. تحتوي إدخالات الراديو على علامات ألبوم/قرص/مسار فارغة، لذا أعادت كل مقارنة 0 - List(Of T).Sort غير مستقر، مما ينتج ترتيبًا مختلفًا في كل استدعاء. تقوم نقاط تحكم UPnP بتقسيم الصفحات (BubbleUPnP يجلب 0..15 ثم 16..النهاية)؛ بين الاستدعاءين، تم إعادة ترتيب القائمة، لذلك ظهرت بعض المحطات في كلتا الصفحتين (مكررة) وبعضها لم يظهر في أي منهما (مفقودة) - تبدو عشوائية مع كل تحديث.

لماذا: كانت موجودة في المكون الإضافي الأصلي (لم يتصفح مؤلفه الراديو عبر UPnP أبدًا). تم إصلاحها هنا بفرع ContainerCategory.Radio مخصص في Browse، بدون فرز لكل استدعاء؛ يتم فرز radioFiles مرة واحدة عند التحميل حسب العنوان (مستقر). يرى التصفح المقسم إلى صفحات الآن ترتيبًا حتميًا؛ الصفحة 1 والصفحة 2 منفصلتان.


N14 - بحث UPnP عن فئة الألبوم يعيد حاويات الألبوم

ماذا: بحث UPnP عن استعلامات فئة الألبوم (upnp:class = "object.container.album.musicAlbum"، على سبيل المثال "Random Albums" في BubbleUPnP) أعاد قائمة المسارات الكاملة بدلاً من حاويات الألبوم، لذلك عرض العميل صفر ألبومات. كان المعالج الأصلي يحلل فقط المعايير الموضوعة بين قوسين، ثم يفرغ جميع المسارات بغض النظر عن الفئة المطلوبة.

لماذا: تم إصلاحها هنا - تقوم استعلامات فئة الألبوم الآن بتعداد الألبومات المميزة (مجمعة حسب AlbumArtist+Album) وتصدر كل منها كحاوية musicAlbum مناسبة مع غلاف فني، يمكن الوصول إليها عبر مساحة المعرف الافتراضي Salb<idx> حتى يتمكن العميل من التعمق في النتيجة وتشغيلها.


N15 - بحث UPnP عامل وواعٍ بالنطاق مع إمكانية النقر

ماذا: لم يعلن الأصل عن أي قدرات بحث (GetSearchCapabilities أعاد فارغًا)، لذلك رفض العملاء حتى إرسال بحث؛ وقرأ الواجهة الخلفية القديمة من musicFiles، والتي كانت فارغة بشكل دائم في عصر الشجرة الكسولة. تعلن هذه الشوكة عن الخصائص القابلة للبحث الحقيقية، وتنفذ البحث عن المسارات حسب العنوان والألبومات حسب العنوان مقابل المكتبة الكسولة (HandleLazySearch)، وتحدد نطاق الاستعلام إلى الفرع الحالي للعميل عند إرسال معرف حاوية حقيقي (وإلا تستبدل L:music حتى لا تجلب عمليات البحث في الشريط العلوي ضوضاء البودكاست/الراديو/الكتب الصوتية)، وتجعل نتائج الألبوم قابلة للنقر عبر معرفات Ssrch_alb_* الاصطناعية التي يقوم فرع Browse المبكر بربطها بمسارات الألبوم. (جزء نتائج فئة الألبوم كحاويات هو N14).

لماذا: تحول البحث في BubbleUPnP من "المكتبة لا تدعم البحث" إلى إرجاع نتائج مفيدة، محددة النطاق، وقابلة للتشغيل. التصميم الكامل + الأساليب المرفوضة: SEARCH.md.


N16 - إبطال ذاكرة التخزين المؤقت لـ UPnP (SystemUpdateID)

ماذا: أعاد الأصل SystemUpdateID=0 ثابتًا - وهو عقد إبطال ذاكرة التخزين المؤقت لـ UPnP ContentDirectory - لذلك تعامل العملاء المتوافقون مع المواصفات (BubbleUPnP) المكتبة على أنها لا تتغير أبدًا: نتائج تصفح قديمة، صور مصغرة 404 بعد تغيير مخطط URL، ورقصة "أعد تشغيل MusicBee مرتين لرؤية التغييرات". تقوم هذه الشوكة بتهيئة SystemUpdateID من ثواني الحقبة عند التحميل (لذا فإن كل إعادة تشغيل تسبق الأخيرة تمامًا) وتزيدها عند كل تغيير في المكتبة وتغيير في الإعدادات (SetLibraryDirty / ResetCacheBumpSystemUpdateId).

لماذا: يلتقط العملاء التعديلات والملفات الجديدة وتغييرات الإعدادات بشكل موثوق في تصفحهم التالي. حد معروف: لا يتم دفع القيمة الجديدة للعملاء المشتركين بشكل نشط عبر GENA (تم تأجيلها كعمل مستقبلي)؛ لا يزالون يرونها في تصفحهم التالي.


N17 - غلاف اشتراك البودكاست

ماذا: لم تظهر مربعات البودكاست أي صور - كل طلب /PodcastThumbnail/ كان يعود بـ 404. خطأان متراكمان: لم تتحقق سلسلة الحل أبدًا من ذاكرة التخزين المؤقت الفعلية لغلاف MusicBee (%LocalAppData%\MusicBee\InternalCache\Subscriptions\<name>.jpg، حيث يتم التحميل من واجهة المستخدم لسطح المكتب)، وقامت طبقة HTTP بإلغاء الهروب + تحويل الأحرف إلى أحرف صغيرة بتشويه مفتاح مسار URL الخاص بالخلاصة وصولاً إلى آخر جزء من المسار. تحل هذه الشوكة الغلاف الفني من InternalCache الخاص بـ MB وتوجه عمليات البحث عبر معرف URL آمن يبقى سليمًا في طبقة HTTP (PodcastSlug / podcastSubIdBySlug).

لماذا: يتم الآن عرض غلاف الاشتراك في طرق عرض التصفح (تم حل جميع الطلبات الـ 22 التي كانت تعود بـ 404 سابقًا).


N18 - تصفح العلامات الهرمي (المحدد)

ماذا: يمكن تحديد أي حقل على أنه هرمي في علامة التبويب Library Options وإعطاؤه محددًا من حرف واحد (محدد حقل + مربع محدد مع إضافة/إزالة، يتم حفظه في إعدادات المكون الإضافي). اضبط Grouping على / وقيمة مثل Jazz/Cool Jazz ثم يتم تصفحها كـ Jazz › Cool Jazz بدلاً من إدخال مسطح واحد. تحصل المسارات التي تم وضع علامة عليها بالضبط عند فرع (فقط Jazz) على عقدة [Jazz] خاصة بها حتى لا يتم إخفاء أي شيء، وينهار الفرع الذي يحتوي على طفل واحد من تلقاء نفسه، ويتم رفض ; كمحدد لأنه هو فاصل القيم المتعددة الخاص بـ MusicBee.

لماذا: تصبح تصنيفات العلامات العميقة التي قام المستخدم بالفعل بترميزها في حقل واحد (أشجار الأنواع، تسلسلات الحالة المزاجية، "كلاسيكي/باروك/كونشيرتو") قابلة للتصفح كالشجرة التي تصفها العلامة، بدلاً من جدار مسطح من السلاسل المفصولة بشرطة مائلة يجب على المستخدم قراءتها من البداية إلى النهاية.


N19 - مسار جذر واحد مُسمى بحقل التجميع الخاص به

ماذا: يتم تسمية مسار تصفح واحد في الجذر بحقل التجميع الخاص به (مثل "Genre") بدلاً من مساره القصير الكامل، مما يطابق كيفية تسمية مجموعات الحقل الأول المدمجة.

لماذا: تقرأ شجرة التصفح بشكل متسق - قاعدة تسمية واحدة سواء كان إدخال الجذر قائمًا بذاته أو تم دمجه مع الأشقاء (N20) - بدلاً من إدخال جذر وحيد يعرض مسارًا داخليًا مطولًا بينما يعرض جيرانه المدمجون اسم حقل نظيفًا.


N20 - دمج مسارات التصفح التي تشترك في حقل أول

ماذا: مساران للتصفح يشتركان في نفس الحقل الأول - "Genre / Sort Album Artist" و "Genre / Podcast People" - يندمجان في مجلد جذر واحد Genre يسرد قيم النوع أولاً ثم ينقسم إلى العرضين، بدلاً من إدخالين شبه متطابقين "Genre / …" جنبًا إلى جنب في الجذر.

لماذا: رأى مستخدم لديه عدة طرق عرض ذات صلة متداخلة تحت حقل مشترك أن الجذر مليء بإدخالات علوية متطابقة تقريبًا. دمجها يحافظ على قابلية قراءة الجذر ويجمع طرق العرض ذات الصلة حيث تنتمي - تحت حقلها المشترك.


N21 - مسارات التصفح المصنفة حسب الفئة (قياسي / راديو / بودكاست)

ماذا: يتم تصنيف كل مسار تصفح حسب الفئة - Standard أو Radio أو Podcast. يتم تجميع قائمة القوالب في هذه الأقسام الثلاثة، ويوفر محدد الحقول لكل قالب فقط الحقول التي يمكن لبيانات تلك الفئة تقديمها بالفعل، ولا يمكن تطبيق القالب إلا على العقد المطابقة في شجرة العرض (تصبح العقد غير المتوافقة رمادية ولا يمكن تحديدها). لا يمكن حذف قوالب الراديو والبودكاست المحجوزة، لذلك لا يختفي قسم الفئة الخاص بها أبدًا.

لماذا: بدون التصنيف، يمكن للمستخدم إنشاء تخطيط يظهر فارغًا بصمت - محطة راديو ليس لديها "ألبوم"، حلقة بودكاست ليس لديها "فنان ألبوم" - ولا يكتشف ذلك إلا عن طريق التصفح إلى مجلد ميت من عميل UPnP. تقييد قائمة الحقول وأهداف التطبيق ببيانات الفئة الحقيقية يجعل التخطيطات الفارغة غير قابلة للبناء.


N22 - تجميع البودكاست حسب سنة النشر

ماذا: يتم قراءة تاريخ نشر كل حلقة بودكاست، لذلك يقوم مسار تصفح البودكاست بمستوى السنة بتجميع الحلقات حسب السنة بدلاً من طيها تحت "غير معروف" واحد.

لماذا: تصبح اشتراكات البودكاست الكبيرة قابلة للتصفح حسب السنة مثل بقية المكتبة، بدلاً من أن تهبط كل حلقة في كومة واحدة غير مؤرخة لأن المكون الإضافي لم ينظر أبدًا إلى تاريخ نشر كل حلقة.


N23 - طي مستويات التجميع ذات النتيجة الواحدة

ماذا: يتم تخطي مستوى تجميع يحل إلى قيمة واحدة - مستوى نوع التسجيل الذي يعرض "LP" فقط لفنان صنع LPs فقط، أو مستوى حرف بحرف واحد - تلقائيًا، مما ينقل المستخدم مباشرة إلى محتوياته.

لماذا: التصفح عبر مجلد يحتوي على مجلد واحد بالضبط هو احتكاك محض. يؤدي طي مستوى الاختيار الفردي إلى إزالة النقر الميت دون تغيير ما يمكن للمستخدم الوصول إليه.


N24 - تجميع/بحث السنة مقابل حقل التاريخ في MusicBee

ماذا: لم يعد شرط السنة يستعلم حقل "Year" الخاص بالتاريخ الكامل في MusicBee بقيمة مكونة من أربعة أرقام فقط، وقد اختفى التسمية المستعارة لحقل السنة المبرمجة بشكل ثابت، لذا فإن كل حقل تجميع يحل الآن بشكل عام من تعريف المسار.

لماذا: بالنسبة للمكتبات التي تحتوي علامة السنة فيها على تاريخ كامل، كان التجميع أو البحث حسب السنة يعيد لا شيء سابقًا - لم يتطابق الاستعلام المكون من أربعة أرقام أبدًا مع حقل التاريخ الكامل. استعلام الحقل الصحيح يجعل تجميع السنة والبحث يجدان المسارات مرة أخرى.


N25 - فصل حقول التجميع "Year" و "Year (yyyy)"

ماذا: تعرض مسارات تجميع الألبومات والتصفح الآن حقلي السنة الخاصين بـ MusicBee - Year (علامة التاريخ الكامل) و Year (yyyy) (السنة المكونة من أربعة أرقام فقط) - بحيث يمكن للمستخدم اختيار أي منهما عند تحديد تجميع ألبوم أو مسار تصفح.

لماذا: يعني الحقلان أشياء مختلفة في MusicBee، وفقد دمجها هذا التمييز. إظهار كليهما يتيح للمستخدم جمع كل إصدارات سنة معًا (yyyy) أو الحفاظ على ترتيب التاريخ الدقيق (علامة السنة الكاملة)، حسب قصدهم.


N32 - الفلاتر وقوائم التشغيل المثبتة مجمعة حسب نوعها في الجذر

ماذا: يظهر الفلتر المثبت الآن مباشرة أسفل مجلد الفلاتر في جذر التصفح، وتظهر قائمة التشغيل المثبتة مباشرة أسفل مجلد قوائم التشغيل، بدلاً من تجمع كل العناصر المثبتة في كتلة واحدة في نهاية الجذر. كل اختصار مثبت يقع مع نوعه الخاص.

لماذا: كلما ثبّت المستخدم المزيد من الاختصارات، تصبح الكتلة الواحدة المختلطة من الفلاتر وقوائم التشغيل في نهاية الجذر أصعب في التصفح وتفصل كل اختصار عن المجلد الذي ينتمي إليه. تجميع العناصر المثبتة تحت فئتها الخاصة يبقي الجذر سهل القراءة ويبقي كل اختصار بجانب الأشياء التي ينتمي إليها.

مربع حوار الإعدادات والتعبئة

N26 - مربع حوار الإعدادات المقسم

ماذا: حصلت صفحة التفضيلات على تخطيط تنقل جانبي مع أقسام: General / Playback / Library / Device Profiles / Diagnostics.

لماذا: كان الأصل عبارة عن قائمة مسطحة طويلة واحدة لكل إعداد - جيد للمطور الذي بناه، ومحير للجميع. يؤدي تقسيم الخيارات إلى تجميع الخيارات ذات الصلة ويجعل مربع الحوار يبدو أشبه بإعدادات التطبيقات الحديثة.


إعادة تسمية التجميع + المكون الإضافي (لا يوجد معرف F - ملاحظة التعبئة)

ماذا: يتم تسمية ملف DLL المترجم mb_UPnP_yaiol.dll ويبلغ المكون الإضافي عن نفسه باسم "MusicBee UPnP (yaiol)". يختلف عن mb_Upnp.dll الأصلي.

لماذا: يمكن للمستخدمين تثبيت yaiol جنبًا إلى جنب مع المكون الإضافي الأصلي ومقارنة السلوك جنبًا إلى جنب.


نظام الشارات - إظهار حالة وقت التشغيل (الآلية وراء F40)

ماذا: نمط واجهة مستخدم عام لإظهار ظروف وقت التشغيل المهمة كشارات ملونة مرئية في مربع حوار الإعدادات. الحالات الحالية:

  • ⚠ Max Conn (N04) - يتم تشغيله عندما تم الوصول إلى حد أقصى للاتصالات مرة واحدة على الأقل منذ بدء تشغيل MusicBee. علامة جلسة لاصقة Plugin.MaxConnectionsHit. يتم تعيينها داخل WaitOnSendBarrier عندما لا تكون هناك فتحة متاحة.
  • ⚠ Restart Required - يتم تشغيله عندما يحتاج إعداد محفوظ إلى إعادة تشغيل MusicBee ليصبح ساري المفعول. علامة جلسة لاصقة Plugin.RestartRequired. يتم تعيينها في معالج حفظ مربع الحوار عندما تختلف القيمة الثابتة الجديدة عن لقطة وقت التشغيل (Plugin.activeMaxConnections، Plugin.activeServerPort، Plugin.activeIpAddress). تقتصر الإعدادات التي تتطلب إعادة التشغيل على تلك التي لا يمكن إعادة تحميلها فعليًا - معلمات ربط خادم HTTP و SemaphoreSlim الذي تم إنشاؤه مرة واحدة عند Initialise.

لماذا: ملف سجل المكون الإضافي جيد للمستخدمين التقنيين الذين يقومون بالتصحيح، ولكن المستخدم غير التقني الذي يحدق في "صوت الجهاز خاطئ" أو "التشغيل بطيء" لن يفتح أبدًا Diagnostics → View log. تلتقط الشارات الحالات التي يحتاج فيها المستخدم إلى معرفة أن شيئًا ما حدث وتظهره في المرة التالية التي يفتح فيها المكون الإضافي - يمكن اكتشافها دون قراءة أي شيء.

قابلة لإعادة الاستخدام في المستقبل:

  • تم اكتشاف عدم تطابق الملف الشخصي (لم يتطابق وكيل مستخدم الجهاز مع أي ملف شخصي، وتم التراجع إلى Generic).
  • تم تشغيل تراجع NextURI (F13 - تم تعطيل التشغيل بدون فجوات للجلسة على جهاز متقلب).
  • فشل مسح المكتبة / جزئي.
  • فقدان اتصال العارض في منتصف الجلسة.
  • أي حالة أخرى حيث "حدث مرة واحدة، يجب أن يعرف المستخدم" يتفوق على "تم تسجيله بصمت بين 1000 سطر آخر".

اصطلاحات التنفيذ:

  • توجد تسميات الشارات على مستوى مربع الحوار (وليس داخل أي لوحة) بحيث تكون مرئية بغض النظر عن القسم الذي يتواجد فيه المستخدم.
  • موضوعة على طول الصف السفلي بالقرب من Save/Cancel (الحالي: y=410 مكدسة أفقيًا).
  • تحتوي كل شارة على علامة جلسة لاصقة مقابلة في Plugin تتحول إلى True عند حدوث الشرط وتُعاد تعيينها فقط عند إعادة تشغيل MusicBee.
  • الموارد: <Condition>Badge (نص التسمية، مسبوق بـ ⚠) + <Condition>BadgeTip (تلميح يشرح السبب + العلاج).
  • بالنسبة لشارات "الإعداد المحفوظ يحتاج إلى إعادة تشغيل"، خذ لقطة وقت التشغيل عند Plugin.Initialise() وقارنها بـ Settings.* بعد Settings.SaveSettings() في معالج حفظ مربع الحوار.

N27 - الإلغاء يتجاهل تعديلات المسار/القالب

ماذا: يتم الآن تجاهل التعديلات التي تم إجراؤها على المسارات والقوالب داخل مربع حوار الإعدادات عندما ينقر المستخدم على Cancel، بدلاً من أن تظل مطبقة بصمت، ويتم إعادة إنشاء أي قالب محجوز تمت إزالته أثناء الجلسة. (يتم حفظ القوالب بخلاف ذلك مباشرة أثناء تحريرها - لا يوجد زر Save منفصل في علامة التبويب Paths.)

لماذا: يجب أن يعني Cancel إلغاء. في السابق، كان المستخدم الذي جرب تغييرات المسار/القالب وتراجع يجد التغييرات قد تم تطبيقها بالفعل، دون أي طريقة للتراجع عنها سوى إعادة القيام بكل منها يدويًا.


N28 - علامات العطف الحرفية في قائمة اختيار الحقول

ماذا: حقل يحتوي اسمه على "&" - مثل "Mood & Context" - يعرض علامة العطف حرفيًا في قائمة اختيار الحقول بدلاً من ابتلاعها كبادئة Alt-mnemonic.

لماذا: أسماء الحقول التي تحتوي على علامة عطف كانت تُعرض بشكل خاطئ (اختفت الحرف وأصبح الحرف التالي مسرعًا)، مما جعل إدخال القائمة صعب التعرف عليه.


N29 - عنوان نافذة الإعدادات ثابت وغير مترجم

ماذا: تم تثبيت عنوان نافذة الإعدادات على سلسلة العلامة التجارية "MusicBee UPnP Plugin" ولم يعد يتغير مع لغة الواجهة؛ تم إسقاط سلسلة DialogTitle لكل لغة من كل حزمة لغة.

لماذا: كان عنوان النافذة الذي يتغير صياغته حسب اللغة سطحًا قابلاً للترجمة بدون فائدة - العنوان هو علامة تجارية. تثبيته يحافظ على استقراره واتساقه في كل مكان.

الترجمة

المكون الإضافي الأصلي باللغة الإنجليزية فقط. هذه الشوكة قابلة للترجمة بالكامل - كل سلسلة موجهة للمستخدم تمر عبر حزمة موارد، ويكتشف المكون الإضافي تلقائيًا لغة واجهة مستخدم MusicBee الخاصة.

N30 - واجهة مستخدم متعددة اللغات (الترجمات قيد الانتظار)

ماذا: آلية الترجمة كاملة ويتم شحنها. يقرأ Localisation.vb اللغة المحددة لـ MusicBee من MusicBee3Settings.ini (الاسم الداخلي <SystemLanguage>) ويطبق ثقافة .NET المطابقة على الخيط، بحيث يعيد My.Resources.Resources.* السلسلة المترجمة. كل تسمية/زر/رسالة موجهة للمستخدم مرتبطة بمفتاح مورد (عناصر تحكم المصمم عبر ApplyDesignerExtras + sync-en-locale.js؛ سلاسل وقت التشغيل مثل WarnPortInUse مضافة يدويًا). ما لم يتم بعد هو الترجمة الفعلية: توجد فقط حزمة المصدر الإنجليزية (Resources.resx) - يتم إنتاج حزم الأقمار الصناعية للغات الأخرى في دفعة واحدة عندما يكون المكون الإضافي مكتمل الميزات (الترجمة الجزئية بينما لا تزال السلاسل تتغير تهدر الجهد).

اللغات المستهدفة (المجموعة التي يقدمها MusicBee نفسه، مطابقة 1:1 بواسطة endonymToCulture بحيث يتبع المكون الإضافي لغة 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، لذلك لا يتم إنتاج حزمة أمريكية منفصلة. ES و FR هما أيضًا لغة محلية واحدة.

لماذا: إعدادات مكون UPnP الإضافي ("عدم استخدام PCM الخام"، "فرض PCM ذي الترتيب الصغير للبايتات"، تحذيرات تراجع المنفذ) غامضة بما يكفي بلغتك الأم. اتباع لغة واجهة مستخدم MusicBee الخاصة - بدلاً من فرض الإنجليزية - هو الفرق بين أداة يمكن للمستخدم غير الإنجليزي تهيئتها وأداة لا يمكنه ذلك. لم يحاول أي من المصدرين الأصليين هذا.


N31 - رابط المساعدة يفتح بلغة الواجهة الكاملة

ماذا: فتح رابط المساعدة من المكون الإضافي يحترم لغة الواجهة الكاملة للمستخدم (مثل pt-BR، zh-CN) بدلاً من الانهيار إلى اللغة الأساسية، ويرسل معرف تحقق تحديث أوضح.

لماذا: تم إرسال مستخدم يشغل MusicBee بمتغير إقليمي (البرتغالية البرازيلية، الصينية المبسطة) إلى صفحة المساعدة باللغة الأساسية. حمل الثقافة الكاملة يوصلهم إلى صفحة المساعدة باللغة الدقيقة التي يستخدمونها.

إصلاحات وتحسينات على المكون الإضافي الأصلي

البروتوكول الأساسي والتشغيل

F01 - تحديث ملفات تعريف أجهزة DLNA الافتراضية

ماذا: يشحن ملفات تعريف افتراضية جديدة لـ PlayStation 4 و Xbox 360/One و BubbleUPnP الحديث، مع علامات قدرة (معدلات العينة، عمق البت، برامج الترميز) تعكس ما تدعمه هذه الأجهزة بالفعل اليوم.

لماذا: كانت الإعدادات الافتراضية للمكون الإضافي الأصلي مجمدة حوالي عام 2014. اكتسبت PS4/Xbox/BubbleUPnP منذ ذلك الحين دعمًا للصوت عالي الدقة. عند التثبيت الجديد، يعمل الجهاز بأفضل أداء على هذه الأجهزة دون أن يلمس المستخدم إعدادات ملف تعريف الجهاز.


F02 - التحكم في الأجهزة التي تعلن عن MediaRenderer:3

ماذا: يستكشف المكون الإضافي وصف خدمة UPnP للعارض ليقرر ما إذا كان MusicBee يمكنه تشغيله. كان الأصل يطابق فقط urn:schemas-upnp-org:device:MediaRenderer:1. تعلن الأجهزة الحديثة عن :2 أو :3. يوسع F02 المطابقة.

لماذا: بدون هذا، لا تظهر وحدات Sonos / WiiM / Eversolo الحديثة ببساطة كأهداف في قائمة أجهزة "Play To" في MusicBee - على الرغم من أنها تتحدث نفس البروتوكول. إصلاح مطابقة بادئة سلسلة واحدة يفتح جيل الأجهزة الحديث بالكامل.


F03 - خيار "فرض البث الأصلي" لكل ملف تعريف (افتراضيًا ON)

ماذا: عند تحديده، يرسل المكون الإضافي بايتات الملف الأصلي إلى الجهاز بدون تحويل ترميز، بدون معالجة DSP، بدون معالجة ReplayGain. فقط الملف الخام الذي اختاره المستخدم، بايتًا ببايت (باستثناء إطار HTTP).

لماذا: وفقًا لشهادات المنتديات، هذا هو أكبر مكسب في جودة التشغيل. يرغب مستخدمو Hi-fi الذين يشترون عارضات باهظة الثمن صراحة في إخراج مثالي للبت؛ أي لمسة DSP تبطل الغرض. افتراضيًا ON لأن معظم الأجهزة الحديثة تتعامل مع أي برنامج ترميز يرميه المستخدم عليها، ويجب أن يكون ReplayGain/EQ اختياريًا. إنه لكل ملف تعريف حتى تتمكن من الاستمرار في تحويل الترميز لجهاز Xbox قديم أثناء إرسال البث الأصلي إلى DAC عالي الدقة.


F04 - "فرض تحويل الترميز" لكل ملف تعريف

ماذا: تجاوز لكل ملف تعريف يفرض مرور كل بث إلى هذا الجهاز عبر المحول، بغض النظر عن دعم برنامج الترميز الأصلي. عكس F03 (ForceNativeStream). حصري متبادل مع F03 - تقوم واجهة المستخدم بإلغاء تحديد الآخر تلقائيًا عند تشغيل أحدهما.

لماذا: سيكون مفتاح تبديل عالمي واحد متناقضًا مع ForceNativeStream لكل ملف تعريف (F03). حالة واقعية: الجهاز A هو DAC عالي الدقة يريد تدفقات أصلية مثالية للبت؛ الجهاز B هو جهاز استقبال AV قديم يتعثر في FLAC. باستخدام مفتاح تبديل عالمي، يتعين على المستخدم الاختيار - على حساب الجهاز الآخر. مع كل ملف تعريف، يحصل كل جهاز على الإجابة الصحيحة.

التنفيذ:

  • StreamingProfile.ForceTranscoding As Boolean = False.
  • تم رفع مخطط الثبات إلى الإصدار 9. تقوم الملفات قبل الإصدار 9 بتحميل القيمة العالمية القديمة مرة واحدة ونسخها إلى جميع الملفات الشخصية، مع الحفاظ على السلوك القديم خلال الترقية.
  • واجهة المستخدم: تمت إزالتها من لوحة Diagnostics، وتمت إضافتها إلى قسم Device Profiles بجانب ForceNativeStream. معالجات استبعاد متبادل ثنائية الاتجاه (CheckedChanged على كل منها يلغي اشتراك الآخر قبل التبديل، لتجنب حلقة لا نهائية).
  • موقع القرار: Settings.ForceTranscodingstreamingProfile.ForceTranscoding في WriteAudioFileDIDL.

F05 - "فرض PCM ذي الترتيب الصغير للبايتات" لكل ملف تعريف

ماذا: تدفقات PCM (أنواع mime L16/L24) هي big-endian حسب المواصفات. تتوقع بعض الأجهزة خطأً little-endian وتشغل ضوضاء بيضاء عند تزويدها ببيانات big-endian صحيحة. يقوم F05 بتبديل ترتيب البايتات لكل ملف تعريف.

لماذا: بدون هذا، تنتج بعض الأجهزة جدارًا من الضوضاء الساكنة. العرض دراماتيكي والسبب غير مرئي بدون معرفة ترميز PCM - يمنح المفتاح المستخدمين إصلاحًا بالتخمين والتحقق.


F06 - "عدم استخدام PCM الخام" لكل ملف تعريف

ماذا: عندما يدعي الجهاز أنه يدعم PCM الخام، يستخدم المكون الإضافي ذلك. بعض الأجهزة تكذب - فهي تقبل مصافحة SOAP ولكنها تشوه بيانات PCM الخام الفعلية، بينما تتعامل بشكل صحيح مع PCM الملفوف في حاوية WAVE. يفرض F06 PCM-over-Wave بغض النظر عما يعلن عنه الجهاز.

لماذا: بعض موديلات Marantz على وجه التحديد - تعلن عن PCM الخام ولكن WAVE فقط يعمل. بدون هذا، تخرج تدفقات PCM الخام مشوهة، بدون رسالة خطأ تشير إلى المشكلة.


F07 - "طول المحتوى" لكل ملف تعريف

ماذا: القيمة التي يجب إرسالها في رأس HTTP Content-Length. أربعة خيارات:

  • Default - عدد البايتات الفعلي عند معرفته، يتم حذفه عند عدم معرفته.
  • None - لا ترسل الرأس أبدًا (الترميز المجزأ فقط).
  • PCM Only - أرسل فقط لـ PCM الخام؛ احذفه لكل شيء آخر.
  • Fixed - أرسل UInt32.MaxValue - 8192 (علامة لـ "طول غير معروف ضخم").

لماذا: تختلف أجهزة UPnP/DLNA بشكل كبير في كيفية تفاعلها مع Content-Length. يحتاج البعض إلى رقم دقيق، والبعض يكرهه في التدفقات، والبعض يحتاج إلى قيمة "كبيرة جدًا" للحفاظ على التخزين المؤقت. تم توسيع هذا لاحقًا من PCM فقط إلى جميع تنسيقات الإخراج لأن نفس المشاكل ظهرت في تدفقات MP3/AAC المحولة.


F08 - "عدم مسح NextURI" لكل ملف تعريف

ماذا: عادةً ما يقوم المكون الإضافي بمسح NextURI المضاف إلى قائمة الانتظار بالجهاز عندما تفرغ قائمة الانتظار (عن طريق إرسال SetNextAVTransportURI بعنوان URL فارغ). تفسر بعض الأجهزة (خاصة Denon) NextURI الفارغ على أنه "إيقاف كل شيء" وتوقف التشغيل فورًا. يمنع F08 المكون الإضافي من مسحه على الإطلاق.

لماذا: بدون هذا، يواجه مالكو Denon توقف الجهاز في منتصف المسار عندما تفرغ قائمة الانتظار. مع تحديد F08، يحتفظ الجهاز بـ NextURI القديم في الذاكرة (غير ضار - يتم فقط الكتابة فوقه في المرة التالية التي يتم فيها إضافة شيء إلى قائمة الانتظار).


F09 - FLAC كتنسيق إخراج للتحويل

ماذا: توفر قائمة تنسيقات التحويل المنسدلة في Device Profiles الآن 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 في الفرع الذي يعمل بسطر الأوامر. تحصل القائمة المنسدلة لواجهة المستخدم على "FLAC" كخيار سادس. يمتد تعيين التحميل/الحفظ في SettingsDialog للتعرف على FileCodec.FlacSelectedIndex = 5. تم بالفعل ربط Mime ونوع DLNA وميزة الترميز في ItemManager.GetMimes / GetDlnaType / GetEncodeFeature من عمل سابق (F21، F26).


بدون فجوات (SetNextAVTransportURI)

F10 - SetNextAVTransportURI / NextURI الأساسي

ماذا: تشغيل حقيقي بدون فجوات. عندما يعلن الجهاز عن دعم SetNextAVTransportURI في وصف خدمة UPnP الخاص به، يقوم المكون الإضافي بوضع المسار التالي في قائمة انتظار الجهاز قبل انتهاء المسار الحالي. ينتقل الجهاز داخليًا بدون فجوة مسموعة بين المسارات - ما تسمعه على مشغل الأقراص المضغوطة. هذا ليس خدعة "التدفق المستمر" (التي تربط كل شيء في تدفق طويل واحد وتفقد البيانات الوصفية لكل مسار).

لماذا: الميزة الرائدة من المستوى الثاني. تبدو الألبومات المسجلة كأداء حي مستمر (تسجيلات حية، حركات كلاسيكية، مجموعات DJ) خاطئة عندما يكون هناك صمت لمدة نصف ثانية بين المسارات. حل هذه المشكلة بشكل صحيح هو ميزة رائدة، وهي الآن في هذه الشوكة.

ملاحظات: يتم تقديم الصوت المضاف إلى قائمة الانتظار عبر خادم HTTP الخاص بالمكون الإضافي باستخدام streamHandle=0 (وضع جلب المكتبة)، مما يعني أن محرك الصوت في MusicBee ليس في الحلقة للمسار المضاف إلى قائمة الانتظار. المقايضة: لا تنطبق تأثيرات ReplayGain/DSP/EQ على المسار التالي. مقبول عندما يكون "فرض البث الأصلي" قيد التشغيل (الافتراضي).


F11 - "تعطيل دعم NextURI" لكل ملف تعريف

ماذا: حتى لو أعلن الجهاز عن SetNextAVTransportURI، فإن مربع الاختيار هذا يجبر المكون الإضافي على تجاهل هذا الإعلان والعودة إلى التشغيل مسارًا واحدًا في كل مرة.

لماذا: تعلن بعض الأجهزة عن NextURI ولكن لديها تطبيق معيب (تعطل، انتقالات جزئية، تعليق). بدلاً من الهندسة العكسية لكل جهاز معطل، يحصل المستخدم على مفتاح تبديل "فقط قم بإيقاف تشغيله هنا".


F12 - دورة حياة NextURI في قائمة التشغيل الحالية

ماذا: عندما يشغل MusicBee NowPlayingListChanged، يعيد المكون الإضافي تقييم ما يجب وضعه في قائمة الانتظار للانتقال بدون فجوات. يطلب من MusicBee المسار "التالي" الجديد عبر NowPlayingList_GetNextIndex(1) + NowPlayingList_GetListFileUrl، ويقارنه بما هو موجود حاليًا في قائمة انتظار الجهاز (يتم تتبعه عبر حقل nextPlaySourceUrl الجديد)، ويعيد وضعه في قائمة الانتظار إذا تغير (أو يمسح قائمة الانتظار إذا قال MusicBee أنه لا يوجد مسار تالي).

لماذا: بدون F12، استمر الجهاز في تشغيل NextURI قديم عندما أزال المستخدم/أعاد ترتيب المسار المضاف إلى قائمة الانتظار. استغرق هذا تاريخيًا عدة تكرارات لأن كل تغيير في القائمة يحتاج إلى معالجة مختلفة - لقد قمنا بالتبسيط عن طريق الثقة في NowPlayingList_GetNextIndex (الذي يحترم بالفعل التبديل والتكرار الشامل)، لذلك تمر جميع المتغيرات عبر نفس المقارنة.

التنفيذ:

  • حقل nextPlaySourceUrl جديد يخزن عنوان URL لمكتبة MusicBee لما هو في قائمة الانتظار (عنوان URL للتدفق مع لاحقة المعرف غير قابل للمقارنة بمسار مكتبة).
  • Public Sub RefreshQueuedNextUri() جديد على MediaRendererDevice. ثلاث نتائج: لا يوجد NextURI في قائمة الانتظار ← لا يوجد إجراء؛ ما في قائمة الانتظار يطابق "التالي" الجديد ← لا يوجد إجراء؛ ما في قائمة الانتظار يختلف ← استدعاء QueueNext مع عنوان URL الجديد (أو QueueNext("") للمسح - والذي يحترم F08 DoNotClearNextUri).
  • تم ربطه في Plugin.ReceiveNotification تحت NotificationType.NowPlayingListChanged.

F13 - تراجع فشل NextURI

ماذا: بعد 4 حالات فشل متتالية لـ SetNextAVTransportURI على نفس الجهاز، يقوم المكون الإضافي بتعطيل التشغيل بدون فجوات لهذا الجهاز حتى يتم إعادة تشغيل MusicBee.

لماذا: إذا كان الجهاز معطلاً حقًا لـ NextURI (أخطاء SOAP متقطعة، مشاكل في الشبكة)، فإن المكون الإضافي سيستمر في المحاولة مع كل مسار. يوقف F13 الضوضاء ويعود إلى تشغيل مسار واحد في كل مرة بصمت.


F14 - وضع التكرار + تكامل NextURI

ماذا: ينقسم F14 إلى حالتين يتم التعامل معهما عند كاشف الانتقال F15 في OnAvTransportStatusCheck:

  • Repeat-All: يمرر MusicBee عنوان URL "التفاف" الصحيح (المسار 1 في نهاية القائمة) إلى Plugin.QueueNext نفسه. لا توجد حاجة لمنطق مكون إضافي خاص - ينتقل الجهاز إليه ويستدعي كاشف F15 Player_PlayNextTrack كالمعتاد، والذي يعيد فهرس NPL الخاص بـ MusicBee إلى 0.
  • Repeat-One: يمرر MusicBee نفس عنوان URL للمسار إلى Plugin.QueueNext. ينتقل الجهاز إليه (معرف تدفق جديد، نفس المصدر). يستعلم كاشف F15 الآن Player_GetRepeat() - إذا كان RepeatMode.One، فإنه يتخطى استدعاء Player_PlayNextTrack حتى لا يتقدم MusicBee بفهرس NPL بعيدًا عن المسار المتكرر.

لماذا: بدون تخطي Repeat-One، فإن استدعاء Player_PlayNextTrack عند الانتقال بدون فجوات سيقدم MusicBee إلى المسار التالي في القائمة (يؤثر Repeat-One فقط على سلوك التقديم التلقائي في نهاية المسار في واجهة مستخدم المشغل - Next Track يتحرك دائمًا للأمام)، مما يتناقض مع ما يعنيه Repeat-One.

تحذير عدد التشغيل: في Repeat-One، تعتمد زيادة عدد التشغيل على ملاحظة MusicBee 3.7.9563+ للتشغيل المتكرر. تشغل إصدارات MusicBee الأقدم التكرار بدون فجوات بشكل صحيح ولكنها تفوت زيادة عدد التشغيل. موثق؛ لا يمنع.


F15 - آلة حالة اكتشاف انتقال المسار

ماذا: عندما ينتقل الجهاز داخليًا من المسار الحالي إلى NextURI، يحتاج المكون الإضافي إلى ملاحظة ذلك وإخبار MusicBee بتقديم فهرس التشغيل الحالي الخاص به. وإلا فإن MusicBee يعتقد أنه لا يزال على المسار السابق وتتأخر أعداد التشغيل / واجهة المستخدم / scrobbling عن التزامن.

التنفيذ: يستعلم GetPositionInfo.TrackURI عند كل نبضة مؤقت الحالة. عندما يتطابق URI المبلغ عنه مع الذي وضعناه في قائمة الانتظار عبر NextURI، نستدعي Player_PlayNextTrack على MusicBee ونعين suppressNextSoapCall بحيث لا يعيد PlayToDevice الناتج إرسال SetAVTransportURI (مما سيقطع التشغيل بدون فجوات).

لماذا: بدون F15، يشغل الجهاز المسار التالي ولكن واجهة مستخدم MusicBee تقول إنه لا يزال على المسار السابق. مربك، يكسر scrobbling، يكسر تتبع عدد التشغيل. يتطلب اكتشاف انتقال المسار تكرارًا طويلاً لكل عارض لأن كل علامة تجارية للعارض لها خصوصياتها الخاصة في متى تبلغ عن تغيير URI (بعضها يبلغ عن TRANSITIONING أولاً، وبعضها ينتقل مباشرة إلى PLAYING مع URI جديد، وبعضها يتوقف لفترة وجيزة بينهما).

ملاحظات: يعمل إصدارنا الأول على عارض BubbleUPnP. تبقى الحالات الخاصة لكل جهاز في B6.


F16 - إصلاح صوت الفرقعة عند الانتقال بدون فجوات

ماذا: تحدث الفرقعة عندما يختلف تنسيق المصدر (معدل العينة / القنوات / برنامج الترميز) للمسار المضاف إلى قائمة الانتظار عن المسار الذي يتم تشغيله حاليًا، مما يجبر DAC الخاص بالجهاز على إعادة القفل عند الانتقال. يضيف F16 تشخيصًا NextUri:FormatChange يتم تشغيله في وقت قائمة الانتظار كلما اختلفت التنسيقات، مع تسمية كلا الجانبين - حتى يتمكن المستخدمون الذين يسمعون فرقعات من الربط.

يشير التشخيص أيضًا إلى التخفيف: حدد ForceTranscoding في ملف تعريف الجهاز. يؤدي ذلك إلى تجانس كل مسار إلى برنامج ترميز/معدل عينة/عمق بت واحد للتحويل، مما يلغي اختلاف تنسيق المصدر تمامًا.

لماذا تم تأجيل الإصلاح الفعلي للتحويل للمطابقة: يتطلب الإصلاح الهيكلي (تحويل المسار المضاف إلى قائمة الانتظار لمطابقة تنسيق المسار الذي يتم تشغيله) تغييرات في مخطط URL لخادم HTTP الخاص بالمكون الإضافي - حاليًا /encode/{id}0.{ext} يقدم الملف المضاف إلى قائمة الانتظار بشكل أصلي. سيضيف الإصدار الثاني المستقبلي من F16 مسارات /encode/{id}0_{rate}_{depth}.{ext} لكل تنسيق وربطها عبر المشفر. هذا تغيير معماري أكبر يستحق القيام به إذا أظهر جهاز حقيقي الفرقعة بعد عدم كفاية ForceTranscoding.

التنفيذ اليوم:

  • حقل lastSourceUrl يتتبع عنوان URL للمصدر الذي يتم تشغيله حاليًا.
  • يقرأ QueueNext FilePropertyType.SampleRate/Channels/Kind لكل من المسارات الحالية والمضافة إلى قائمة الانتظار ويسجل NextUri:FormatChange عند عدم التطابق.

F17 - إعادة مزامنة شريط التقدم بعد البحث

ماذا: استدعت دالة Seek() بالفعل GetPlayPositionInformation() بعد SOAP بحث ناجح، مما يصلح حالة "عدم إعادة المزامنة على الإطلاق". يغلق F17 الانحراف المتبقي الذي يصل إلى ثانية واحدة والناتج عن تكميم RelTime لـ UPnP لمدة ثانية واحدة: عندما يتم تقريب الموضع المبلغ عنه للجهاز إلى ما لا يزيد عن ثانية واحدة من الهدف المطلوب من قبل المستخدم، يثق المكون الإضافي الآن بقيمة المستخدم الدقيقة لأقل من ثانية بدلاً من اقتطاع الجهاز. فقط عندما يبلغ الجهاز عن شيء مختلف بشكل كبير (أكثر من ثانية واحدة) نستخدم قيمته (هبط البحث في مكان آخر غير المطلوب، على سبيل المثال، الانتقال إلى إطار مفتاح على بعض برامج الترميز).

لماذا: بدون هذا، فإن البحث عن 2:30.500 المرتبط بتقرير الجهاز "2:30" سيجعل شريط التقدم يظهر متأخرًا بحوالي 500 مللي ثانية عن الواقع. بعد F17، يتطابق الشريط مع نية المستخدم لحالة التمرير الشائعة داخل المسار، ولا يزال يحترم تقرير الجهاز للحالة الشاذة للانتقال إلى إطار مفتاح.


F18 - قفل متبادل للتدفق المستمر / NextURI

ماذا: يوجد الآن قفلان متبادلان:

  1. وقت التشغيل: QueueNext يعود مبكرًا بـ False عندما يكون Settings.ContinuousOutput قيد التشغيل. التدفق المستمر هو آلية خاصة به بدون فجوات (تدفق واحد طويل متسلسل)؛ إرسال SetNextAVTransportURI فوقه يربك الجهاز حول ما إذا كان كل مسار هو URI منفصل أو جزء من التدفق المستمر.
  2. واجهة المستخدم: عندما يحدد المستخدم مربع اختيار التدفق المستمر العالمي، يتم إلغاء تحديد forceNativeStream للملف الشخصي المعروض حاليًا تلقائيًا. التدفق المستمر يقوم دائمًا بالتحويل، لذا فإن فرض الأصلي لا معنى له في هذه الحالة.

لماذا: يمنع المستخدم من تمكين آليتين متضاربتين بدون فجوات في وقت واحد. بدون F18، سيتلقى الجهاز كلاً من URI تدفق مستمر و NextURI لكل مسار لاحق، مع سلوك غير محدد يعتمد على العارض.


F19 - تجاهل أخطاء NextURI الفارغة

ماذا: عندما يتم استدعاء SetNextAVTransportURI بعنوان URL فارغ (مثل آخر مسار في القائمة)، تعيد بعض الأجهزة خطأ SOAP. يبتلع F19 هذه الأخطاء بصمت - يتم تسجيلها ولكن لا يتم نشرها كأخطاء.

لماذا: حالة "لا يوجد مسار تالي" طبيعية، وليست خطأ. معاملتها على أنها قاتلة تلوث السجل وتؤدي (في بعض التدفقات) إلى عواصف إعادة المحاولة.


أنواع Mime وبيانات DLNA الوصفية

F20 - نوع MP3 mime ← audio/mpeg

ماذا: نوع MP3 mime المتوافق مع المعايير هو audio/mpeg، وليس audio/mp3. هذا الأخير هو تسمية خاطئة شائعة تتسامح معها معظم الأجهزة، لكن العارضات الأكثر صرامة ترفضها.

لماذا: يصلح التشغيل بصمت على الأجهزة الأكثر صرامة التي تتبع المعيار. كان رمز yaiol يحتوي على هذا التصحيح بالفعل؛ لا حاجة للتغيير.


F21 - ترتيب أنواع Mime: المتغير غير المسبوق بـ x- أولاً

ماذا: عندما يعلن جهاز عن كل من audio/flac و audio/x-flac، يعيد المكون الإضافي المتغير غير المسبوق بـ x- أولاً. وينطبق الشيء نفسه على أي برنامج ترميز يحتوي على كل من أنواع mime القياسية والتجريبية.

لماذا: تشير البادئة x- إلى أنواع mime التجريبية/غير الرسمية. تتصرف بعض العارضات بشكل أفضل مع الشكل القياسي. إعادة ترتيب صغيرة، تأثير حقيقي في العالم.


F22 - دعم نوع Opus mime

ماذا: يتعرف على Opus كبرنامج ترميز صوتي قابل للتدفق؛ يرسل audio/opus mime عند تقديم مسارات Opus.

لماذا: أصبح Opus شائعًا الآن (برنامج ترميز حديث للتسوية بين الكلام والموسيقى). بدون F22، سيرفض المكون الإضافي بث ملفات Opus حتى للأجهزة التي تتعامل معها.


F23 - دعم ملفات مصدر Monkey Audio (APE)

ماذا: يتعرف على ملفات .ape كبرنامج ترميز مصدر صالح للتدفق/التحويل.

لماذا: APE هو تنسيق بدون فقدان مع قاعدة مستخدمين متخصصة ولكن مخلصة. إضافته تكلف القليل وتفتح المكتبة لهؤلاء المستخدمين.


F24 - تراجع نوع AAC / ALAC mime

ماذا: إذا كان الجهاز يدعم AAC أو ALAC ولكنه لا يعلن عنهما صراحة في وصف خدمة UPnP الخاص به، فإن المكون الإضافي يقدمهما على أي حال كخيار احتياطي.

لماذا: نسيت عدة أجهزة تتعامل مع AAC بشكل جيد إدراجه في ملف XML الخاص بقدراتها. بدون F24، لن يحاول المكون الإضافي حتى، مما يفرض التحويل. مع F24، يحاول المكون الإضافي ويترك الجهاز يتعامل معه بشكل أصلي إذا استطاع.


F25 - علامة نوع DLNA لتدفقات WAV الأصلية + المشفرة

ماذا: يجب أن تتطابق علامة نوع DLNA (معرف ملف تعريف مثل LPCM، WAVE، MP3) مع ما يتلقاه الجهاز. يضمن F25 وضع علامة صحيحة على التدفقات الأصلية وتدفقات WAV المشفرة.

لماذا: يؤدي عدم تطابق نوع DLNA إلى رفض بعض الأجهزة للتشغيل بالكامل أو تطبيق فك التشفير الخاطئ.


F26 - رأس DLNA لملفات FLAC

ماذا: تحصل تدفقات FLAC على معرف ملف تعريف DLNA الصحيح في رؤوسها.

لماذا: بدون ذلك، لا تتعرف بعض الأجهزة التي تدعم FLAC على التدفق على هذا النحو.


F27 - إصلاح حساب معدل البت في البيانات الوصفية

ماذا: كان res@bitrate للتدفق المستمر يتم حسابه كـ (sampleRate * channels * bitsPerSample) / 1000 - كيلوبت في الثانية، وهو يختلف بعامل ~125 عن مواصفات UPnP DIDL التي تحدد السمة على أنها بايت في الثانية. الآن يقسم على 8 بدلاً من 1000.

لماذا: عرض معدل البت الخاطئ على الجهاز - تجميلي على معظم العارضات، ولكن بعضها يخصص مخازن مؤقتة للتدفق من القيمة ويتعثر في التدفقات التي تبدو أصغر بحوالي 125 مرة مما هي عليه. كان مسار ملف المصدر غير المستمر صحيحًا بالفعل ((bitrate_kbps * 1000) \ 8 = بايت/ثانية)؛ كان مسار التدفق المستمر فقط هو الخاطئ.


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 (البحث بالبايت والوقت) بدلاً من DLNA.ORG_OP=10 (البحث بالبايت فقط). يمكن للأجهزة التي كانت ترفض سابقًا البحث بالوقت في MP3 المحول الآن تشغيل شريط التقدم/واجهة مستخدم البحث بشكل طبيعي.

لماذا: ينتج محول MusicBee MP3 بمعدل بت ثابت عند إعداد HighQuality المسبق، لذا فإن تعيين البايت ↔ الوقت خطي - يمكن للجهاز تحويل طلب البحث بالوقت إلى بحث بايت HTTP Range بنفسه دون أي دعم من جانب المشفر. الإعلان عن OP=11 يفتح واجهة المستخدم هذه على الجهاز. بدون F29، كان المستخدمون الذين يبحثون داخل MP3 محول إما يتم تجاهل البحث بصمت أو يتم إسقاطهم إلى بداية المسار.

التنفيذ: تمت إعادة هيكلة GetEncodeFeature في ItemManager.vb لتقسيم If المضمن إلى سلسلة If/ElseIf/Else قابلة للقراءة. يحصل MP3 على OP=11 صراحة؛ تحتفظ برامج الترميز الأخرى غير PCM بـ OP=10. لا يوجد تغيير لـ AAC/FLAC/إلخ. - ستحتاج هذه إلى تحقق خاص ببرنامج الترميز من كونها CBR وهو ما لا يضمنه MusicBee.


F30 - معالجة امتداد الملف .mpeg

ماذا: يتم الآن التعرف على الملفات ذات الامتداد .mpeg (والأندر .mpe) كـ FileCodec.Mp3 في GetCodec. قبل F30، كانت تعيد FileCodec.Unknown ويتم رفضها بصمت من المكتبة / غير قادرة على أن تكون مصادر تحويل.

لماذا: استخدمت أرشيفات MPEG-1 Layer 3 القديمة أحيانًا .mpeg بدلاً من .mp3 (تسمح المواصفات بكلاهما). عدد قليل من الملفات في مكتبة بحجم 300 ألف يكفي للشعور بأن "MusicBee يعرضها ولكن المكون الإضافي لا يفعل" - وهو أمر مربك للمستخدم.


سلوك التشغيل

F31 - تدفقات الراديو تستخدم الوضع المستمر تلقائيًا

ماذا: يقوم WriteAudioFileDIDL الآن بفحص خاصية Kind لعنوان URL المصدر عبر Library_GetFileProperty ويعامل أي ملف ينتهي نوعه بـ "Stream" (يبلغ MusicBee عن "MP3 Stream"، "Internet Stream"، إلخ للراديو) على أنه مستمر بغض النظر عن مفتاح التبديل العالمي Settings.ContinuousOutput. يتم استخدام فرع DIDL للتدفق المستمر (العنوان: "Continuous Stream"، id="continuousstream"، إخراج PCM/Wave ثابت)؛ يرى الجهاز تدفقًا واحدًا بنمط لا نهائي.

لماذا: لا تحتوي تدفقات الراديو على حدود للمسارات، ولا طول ثابت، ولا بحث. معاملتها كملفات منفصلة في DIDL تسببت في إعلان المكون الإضافي عن نطاقات بايت ومدد غير موجودة. التبديل التلقائي عندما أخبرنا MusicBee بالفعل "هذا تدفق" يزيل مشكلة لا ينبغي للمستخدم أن يفكر فيها.

النطاق: ينطبق فقط عندما يقوم MusicBee بتشغيل التشغيل (musicBeePlayToMode). مسار جلب المكتبة (تصفح عميل UPnP) لم يتغير - عناوين URL للراديو هناك نادرة ولا ينبغي أن يتغير السلوك الموجه للمستخدم بدون اختبار صريح.


F32 - تراجع إعلان برنامج الترميز

ماذا: إذا لم يعلن جهاز عن برامج ترميز معينة (أو لم يتمكن المكون الإضافي من تحليل ملف XML الخاص بقدرات الجهاز)، فإن المكون الإضافي لا يرفض التدفق على الفور. بدلاً من ذلك، يحاول تقديمه ويترك الجهاز يقرر.

لماذا: العديد من الأجهزة لديها ملف XML غير مكتمل أو غير قابل للقراءة ولكنها تتعامل مع برنامج الترميز بشكل جيد. يستبدل F32 "أفضل تخمين ومحاولة" صغيرة بالرفض التام.


F33 - تحسين مزامنة شريط التقدم

ماذا: يتم بالفعل استقراء الموضع بين الاستطلاعات من مرساة واحدة (currentPlayStartTicks)، لذلك يتم تحديث شريط التقدم بسلاسة بمعدل أقل من الثانية. كان مصدر الاهتزاز المتبقي هو المرساة الأولية لمسار بدأ حديثًا: افترض الكود السابق position=0 في اللحظة التي لاحظ فيها مؤقت الحالة لأول مرة أن الحالة أصبحت Playing، ولكن بحلول ذلك الوقت قد يكون الجهاز قد شغل لمدة 100-500 مللي ثانية (فترة استطلاع واحدة). سيبدأ شريط تقدم MusicBee من 0، ثم يقفز للأمام عندما يلحق الواقع.

إصلاح F33: عند الانتقال إلى Playing لأول مرة في مسار جديد (currentPlayStartTimeEstimated=True)، استدعِ GetPlayPositionInformation() للحصول على الموضع الحالي الفعلي للجهاز، ثم اربط به. يبلغ UPnP عن دقة ثانية واحدة فقط، لذا فإن المرساة لا تزال مكممة، لكنها أقرب بكثير إلى الحقيقة من افتراض 0.

لماذا: عرض تقدم أكثر سلاسة ودقة، خاصة بعد تغيير المسار مباشرة. لا توجد طريقة لتجاوز دقة تقارير UPnP نفسها التي تبلغ ثانية واحدة - هذه مواصفات.


F34 - اهتزاز شريط التقدم بعد تغيير المسار

ماذا: عندما يتم استدعاء PlayToDevice لمسار جديد، كان المكون الإضافي يترك currentPlayPositionMs و currentPlayStartTicks عند قيمهما للمسار السابق لمدة ~100 مللي ثانية بين SOAP-Play وأول استطلاع لمؤقت الحالة يكتشف حالة Playing الجديدة. سيعرض شريط تقدم MusicBee لفترة وجيزة نهاية المسار السابق، ثم يعود إلى 0، ثم يتصاعد. يقوم F34 بتصفير كليهما عند دخول PlayToDevice - اللحظة التي نعرف فيها أن تغيير المسار يحدث، قبل أي من عمل SOAP.

لماذا: خلل بصري في حالات الاستخدام السريع للتخطي (التالي اليدوي أو الانتقال بدون فجوات). الآن، تعيد استعلام PlayPositionMs الأول لـ MusicBee بعد Play 0 بشكل نظيف، ثم تقوم GetPlayPositionInformation في F33 بتحسينه إلى الموضع الفعلي للجهاز عند أول نبضة لتغيير الحالة.

التنفيذ: أربعة أسطر في الجزء العلوي من PlayToDevice، مقترنة بالربط الدقيق لوقت الانتقال في F33.


F35 - خطأ "فرض تحويل الترميز"

ماذا: لا يزال فرض تحويل الترميز يمكن أن يتخطى التحويل في مجموعات معينة. بعد إعادة صياغة F04 لكل ملف تعريف، تم سد فجوتين محددتين:

  1. الأسبقية مع ForceNativeStream. عندما كان كلاهما True (وهو ما يمكن أن يحدث عبر ترحيل مخطط أو ملف إعدادات جزئي)، يفوز ForceTranscoding الآن بشكل مطلق (If streamingProfile.ForceTranscoding Then forceEncode = True ElseIf streamingProfile.ForceNativeStream Then forceEncode = False). يمنع الاستبعاد المتبادل لواجهة المستخدم المستخدم من تحديد كليهما، لكن الحارس في وقت التشغيل يتعامل مع أي حالة تم تحميلها بشكل غير متسق من القرص.
  2. منطق bypassTranscodeDecision. سابقًا: streamingProfile.ForceNativeStream AndAlso Not Settings.ForceTranscoding. الآن: streamingProfile.ForceNativeStream AndAlso Not streamingProfile.ForceTranscoding - نفس قاعدة الأسبقية ولكن في نفس نطاق كل ملف تعريف.

لماذا: "فرض" يجب أن يعني فرض. إذا قام المستخدم بتمكين ForceTranscoding لجهاز بشكل صريح، يجب ألا يتراجع المكون الإضافي أبدًا بصمت إلى التدفق الأصلي، بغض النظر عن كيفية دمج العلامات الأخرى.


F36 - استثناء إغلاق العارض

ماذا: تم تغليف Plugin.ReceiveNotification في Try/Catch على مستوى أعلى يسجل أي استثناء غير ملتقط بدلاً من السماح له بالانتشار مرة أخرى إلى مضخة إشعارات MusicBee.

لماذا: يتم إرسال الإشعارات من MusicBee (PlayStateChanged، VolumeMuteChanged، إلخ) إلى ControlPointManager الذي يتحدث إلى العارض عبر SOAP. كانت مواقع الاستدعاء الفردية تحتوي بالفعل على Try/Catch حول استدعاءات SOAP الخاصة بها، ولكن حالة توقيت غريبة بما فيه الكفاية (مثل تعطل العارض بين استدعاءين SOAP في نفس معالج الإشعارات) يمكن أن تفلت. الغلاف العلوي هو شبكة الأمان النهائية حتى لا يرى المستخدم أبدًا نافذة منبثقة عامة "TargetInvocationException" من MusicBee.

التنفيذ: تمت إعادة تسمية الجسم الحالي إلى ReceiveNotificationInternal وتمت إضافة غلاف رفيع ReceiveNotification يقوم بـ Try { ReceiveNotificationInternal(...) } Catch { LogError(...) }. تبقى البنية التحتية Try/Catch الموجودة مسبقًا لكل طريقة داخل ControlPointManager (حول كل استدعاء PostSoapRequest) - F36 هو حزام + حمالات.


F37 - البحث في المسارات الطويلة يؤدي إلى انتقال خاطئ

ماذا: يمكن أن يؤدي البحث داخل مسار طويل إلى دورة قصيرة من Stopped→Playing على بعض العارضات. بدون تمييز، يعامل ProcessNewPlayState.Stopped ذلك على أنه نهاية طبيعية للمسار ويستدعي Player_PlayNextTrack، مما يقدم MusicBee عندما أراد المستخدم فقط التمرير. يختم F37 lastUserInitiatedSeek في Seek() ويضيف حارسًا لمدة 5 ثوانٍ في معالج Stopped (يعكس نافذة lastUserInitiatedStop الموجودة).

لماذا: التخطي الصامت إلى المسار التالي أثناء البحث هو أحد تلك الأخطاء التي لا يمكن لأحد تخمين سببها - يعتقد المستخدم "غريب، حاولت التمرير للأمام والآن يتم تشغيل الأغنية التالية". الإصلاح ميكانيكي: نفس النمط مثل تمييز إيقاف المستخدم الموجود بالفعل.


F38 - تحسين معالجة البحث لبرامج الترميز المعرضة للتعطل

ماذا: كان تعطل BubbleUPnP عند البحث في MP3 هو العرض الكلاسيكي. بعد التدقيق، يقوم رمز البحث الحالي في yaiol بالفعل بالأشياء الصحيحة - المسار الأصلي يتعامل مع HTTP Range بشكل صحيح (206، Content-Range، AcceptRanges)، المسار المشفر يعلن عن X-AvailableSeekRange ويحلل رؤوس timeSeekRange.dlna.org / npt الواردة، تعكس علامات DLNA.ORG_OP قدرات التدفق الفعلية (مع DisablePcmTimeSeek كخيار إلغاء الاشتراك لأجهزة Platinum التي بها مشاكل). تم اختباره من قبل المستخدم على BubbleUPnP 4.6.4 الحالي: لم يتم ملاحظة أي تعطل.

لماذا: تم الإبلاغ عن تعطل BubbleUPnP عند البحث في MP3 حوالي عام 2024 وقد تلقى التطبيق حوالي 16 شهرًا من الإصلاحات منذ ذلك الحين. كان F29 (MP3 المشفر OP=11) هو المتغير الجديد الذي كان يمكن أن يعيد كشفه؛ لم يحدث ذلك، في الإصدارات التي تم اختبارها.

إذا عاد التعطل: سيكون شكل الإصلاح هو مفتاح تبديل "بحث محدود" لكل ملف تعريف يفرض DLNA.ORG_OP=10 (بايت فقط) على برامج الترميز المعلمة - مما يعكس كيفية عمل DisablePcmTimeSeek بالفعل لـ PCM. أضف ذلك حينئذٍ، وليس بشكل استباقي.


واجهة المستخدم والتسجيل

F39 - زر "إضافة" يحدد الملف الشخصي الجديد

ماذا: يؤدي النقر على "Add" في قائمة ملفات تعريف الجهاز إلى إنشاء ملف تعريف جديد وتحديده تلقائيًا حتى يتمكن المستخدم من تحرير الحقول على الفور. يقوم إعادة هيكلة مربع الحوار المقسم لدينا بذلك بالفعل - ينتهي كل من مسار الإضافة المباشرة ومسار القالب بـ Me.activeStreamingProfiles.SelectedIndex = Me.activeStreamingProfiles.Items.Count - 1. أكد الفحص أن شوكتنا تتعامل مع هذا بالفعل - لا يوجد شيء لتغييره.

لماذا: مشكلة صغيرة في تجربة المستخدم تبين أنها ليست مشكلة هنا بالفعل.


F40 - اتصالات قصوى أكبر + سجل تحذير

ماذا: كان الحد الأقصى للتدفقات المتزامنة للمكون الإضافي (SemaphoreSlim حول Sockets_Stream_File / Sockets_Encoder_Start) ثابتًا عند 4. يجعل F40 هذا قابلًا للتكوين من قبل المستخدم في صفحة إعدادات General (الافتراضي 16، النطاق 1-256)، ويضيف سطر سجل MaxConnections عندما يضطر طلب إلى الانتظار لفتحة، ويعرض شارة حمراء ⚠ Max Conn في أسفل يسار مربع حوار الإعدادات إذا تم الوصول إلى الحد الأقصى مرة واحدة على الأقل منذ بدء تشغيل MusicBee.

لماذا: عندما يطلق جهاز طلبات متوازية (بعض أجهزة Marantz/Linn أثناء مسح الأعمال الفنية، استكشافات بيانات BubbleUPnP الوصفية جنبًا إلى جنب مع التشغيل النشط)، يتم حظر الطلبات الإضافية بصمت خلف الإشارة - رأى المستخدم "الجهاز بطيء" بدون سبب مرئي. سطر السجل جيد لتصحيح الأخطاء التقنية ولكن المستخدمين غير التقنيين لا يقرأون السجلات أبدًا. الشارة المرئية في مربع حوار الإعدادات تجعل حالة الوصول إلى الحد الأقصى قابلة للاكتشاف لأي شخص يفتح تفضيلات المكون الإضافي.

التنفيذ:

  • تم مركزة الانتظار في WaitOnSendBarrier(logTag) في MusicBeeUpnp.vb؛ يستخدمه كلا موقعي الاستدعاء (MediaServerDevice.GetFile، Encoder.StartEncode).
  • Settings.MaxConnections تم حفظه في الإصدار 8 من مخطط الإعدادات.
  • Plugin.MaxConnectionsHit هي علامة جلسة لاصقة يتم تعيينها داخل WaitOnSendBarrier؛ تُعاد تعيينها فقط عند إعادة تشغيل MusicBee.
  • SettingsDialog.maxConnectionsBadge هي تسمية حمراء غامقة عند (16, 410) تظهر فقط عندما يكون Plugin.MaxConnectionsHit صحيحًا. تحتوي على تلميح يشرح السبب والعلاج.
  • يتم تهيئة Semaphore مرة واحدة عند تحميل النوع، لذا يتطلب تغيير الإعداد إعادة تشغيل MusicBee (ملاحظة في تسمية الحقل).

F41 - سجل "الترميز بسبب ReplayGain/DSP"

ماذا: بدلاً من سطور سجل منفصلة "encoding for RG" / "encoding for DSP"، يتضمن سطر StreamDecision الواحد من F42 MB-DSP/EQ، MB-ReplayGain، Profile-DSP/EQ، Profile-ReplayGain كأسباب متراكمة. نفس القيمة التشخيصية، ضوضاء أقل.

لماذا: يرى المستخدمون جميع الأسباب التي تجعل التحويل يحدث لمسار معين في سطر سجل واحد، وليس متناثرة. راجع F42 للحصول على التفاصيل الكاملة.


F42 - سجل "العارض لا يدعم برنامج الترميز المصدر"

ماذا: تمت إضافة سطر سجل StreamDecision لكل مسار تشغيل إلى جهاز يقول إما "native CODEC" أو "transcode 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.

لماذا: كان المستخدمون مرتبكين بسبب ارتفاعات غير متوقعة في استخدام وحدة المعالجة المركزية على الملفات التي توقعوا بثها بشكل أصلي. يخبرهم سطر سجل واحد لكل مسار بالضبط أي شرط تسبب في التحويل - وإذا أظهر الحقل 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 (كم عدد بايتات DIDL التي تم إنتاجها قبل الفشل - يشير إلى مدى تقدم المسار السيء في الدفعة).

لماذا: عندما يحدث خطأ ما في منتصف DIDL، تخبرك قيمة الطول الجزئي ما إذا كان الفشل في المسار الأول من الدفعة (partial=0) أو في منتصفها (partial=N) - بالاشتراك مع startingIndex للدفعة، يمكنك تحديد فهرس المسار المخالف. يشرح Filter و BrowseFlag أي نوع من التصفح أراده العميل؛ أحيانًا يفشل تصفح البيانات الوصفية فقط حيث ينجح تصفح الأطفال لنفس المعرف.


الشبكات

F46 - الوضع التلقائي يعلن فقط على محولات الشبكة الحقيقية

ماذا: في وضع الواجهة التلقائي كان المكون الإضافي يعلن عن نفسه (SSDP) على كل محول IPv4 عامل. على جهاز يشغّل أيضًا نفق VPN (NordLynx) أو محولًا افتراضيًا (Hyper-V / WSL / Docker)، كانت المكتبة نفسها تُعلن على كل من هذه المحولات أيضًا، فيكتشف تطبيق التحكم الذي تبث منه الـ server مرتين أو ثلاث مرات ويعرض المكتبة كنسخ مكررة. يحتفظ الوضع التلقائي الآن فقط بالمحولات التي لديها بوابة IPv4 افتراضية حقيقية (HasIPv4Gateway) - وهو ما لا تملكه محولات الأنفاق والمحولات الافتراضية - لذا تُستبعد تلك من قائمة الإعلان. العنوان الذي يثبته المستخدم يفوز دائمًا (الإعلان على تلك الواجهة فقط)، وإذا لم يبلّغ أي محول عن بوابة، يعود المحدد إلى كل المحولات، فلا تكون قائمة العناوين المعلنة فارغة أبدًا ولا يمكن أن يصبح المكون الإضافي غير مرئي.

لماذا: التكرار ليس سببه "الاتصال بشبكة VPN" - بل سببه الإعلان على محول الشبكة المحلية LAN ومحول النفق/المحول الافتراضي في الوقت نفسه، فترى نقطة التحكم الواحدة الـ server نفسه على عنوانين. شبكات VPN الاستهلاكية (NordVPN/NordLynx) تمرر عبر النفق حركة الإنترنت فقط؛ مشغل DLNA يعيش على الشبكة المحلية وحركة الشبكة الفرعية المحلية تتجاوز النفق، لذا لا يصل محول النفق أبدًا إلى أي مشغل - واستبعاده يزيل نسخة وهمية، ولا يزيل أبدًا مسارًا عاملًا. اختبار البوابة هو الإشارة الرخيصة والموثوقة التي تميز محول LAN/Wi-Fi حقيقيًا عن نفق أو محول افتراضي. يكمّل N05 (الذي أصلح كيفية إرسال الإعلانات على مثل هذه الروابط - multicast بدلاً من broadcast)؛ بينما يحكم F46 أي المحولات يجري الإعلان عليها أصلًا.

قيد معروف: شبكة VPN شبكية / للوصول عن بُعد (Tailscale أو ZeroTier أو WireGuard إلى المنزل) تعيش مشغلاتها فعلًا عبر النفق تقدم عادةً محولًا بلا بوابة افتراضية، فيستبعده الوضع التلقائي أيضًا. هؤلاء المستخدمون يثبتون عنوان VPN بدلاً من ذلك، وهو ما له الأسبقية على مرشح البوابة.