سجل التغييرات
يُسجَّل هنا كل تغيير مهم في مكتبة. يتبع التنسيق
Keep a Changelog، ويتبع ترقيم الإصدارات
Semantic Versioning. يُحفظ هذا الملف العربي بجانب
CHANGELOG.md، وكل مُدخَل يُضاف باللغتين معًا.
يجمع قسم [لم يصدر بعد] المُدخَلات بين إصدار موسوم وآخر.
يمنع فحص سجل التغييرات في التكامل المستمر (القصة 22.5، معيار القبول AC-3) كل طلب دمج يمسّ
ما يراه المستخدم إن لم يُضف سطرًا هنا؛ وتُستثنى طلبات الدمج التي تقتصر على التوثيق.
[لم يصدر بعد]
تغيّر
- صار الاستماع الثاني مرحلة قائمة بذاتها، وصارت بطاقة 8 GB على Windows تشغّله. تحفظ مرحلة
Whisper
transcribeالنص مع كلمات كل جملة (بتوقيتاتها)، وتقرأ مرحلة جديدةsecond_listen(لها مجمّعهاlisten، لكل جهاز) ذلك النص وصوت الملف، فتشغّل Cohere Transcribe Arabic وتحفظ النص المصحَّح نصًّا نشطًا للفيديو؛ وتتبعها ملفات الترجمة وفهرس البحث والوسوم، فتعمل مرة واحدة على الكلمات المصحَّحة. ويبقى الجهاز الذي لا يشغّل الاستماع الثاني يفرّغ، ويصحّح تفريغاته جهازٌ يشغّله. وعلى بطاقة رسوميات تتسع للنموذجين معًا (9.5 GiB فأكثر على Windows و7.5 على غيره، بحسب الذاكرة الحرة التي يقيسها برنامج التشغيل لا بحسب نظام التشغيل) يبقيان محمَّلين؛ وعلى بطاقة أصغر (من 7.5 GiB على Windows و6 على غيره: بطاقة RTX 2080 SUPER) يتناوبان: نموذج واحد على البطاقة في كل مرة، لكلٍّ منهما دفعة من الملفات (30 دقيقة على الأكثر بينما ينتظر الآخر)، وتُنقل أوزان النموذج الخامل إلى ذاكرة الجهاز ثم تُعاد في ثوانٍ بدل تحميله من القرص من جديد. وتذكر صفحة الإدارة > الأجهزة كيف يشغّل الجهاز الاستماع الثاني (بجانب Whisper، أو بالتناوب معه، أو لماذا لا يشغّله) بالعربية والإنجليزية، وتعرض قائمة الانتظار المرحلة الجديدة. وصار عمّال التفريغ في المشرف يشغّلون المرحلتين، وتعطي أدوات التثبيت وتهيئة البيئة بطاقة 8 GB على Windows نسخة torch لـ CUDA، وصار خيار "التفريغ دون الاستماع الثاني" يتيح للجهاز الذي لا يشغّله أن يمرّر النص بعد 15 دقيقة، بعد أن يُترك أولًا لجهاز يشغّله. أما النصوص التي فُرّغت قبل ذلك (دون كلمات محفوظة) وينقصها الاستماع الثاني فيفرّغها من جديد جهاز يشغّله، بضعة نصوص في كل مرة.
أُصلح
Android: يبقى «المفضلة» أو «تمييز كمُشاهَد» كما نُقر عليه أثناء إعادة قراءة صفحة الفيديو. العودة إلى صفحة فيديو تعيد قراءتها خلف الصفحة المعروضة؛ وكانت النقرة على «المفضلة» أو «تمييز كمُشاهَد» قبل وصول تلك القراءة تُحفظ، لكن القراءة تعيد الحالة القديمة إلى الشاشة. صارت الصفحة تحتفظ بما نُقر عليه منذ بدء القراءة.
صار جهاز Windows أو Linux المزوَّد بمعالج رسومي من NVIDIA يُبلّغ عنه، ويذكر لماذا يُفرّغ النصوص أو لا يُفرّغها. كان تقرير قدراته يبحث عن المعالج الرسومي عبر
pynvmlوحدها، ولا يجلبها أي تثبيت (فأبلغ جهاز بمعالج RTX 2080 SUPER عن"gpus": [])، ولم يكن يحتسب الإضافةsttإلا عبر MLX-Whisper الخاص بأجهزة Mac. صار الآن يسأل برنامج التشغيل (NVML)، ثمnvidia-smi(فيPATHوSystem32وNVSMI)، ويُبلّغ عن اسم كل معالج رسومي وذاكرته وقدرته الحسابية وبرنامج تشغيله، أو عن سبب عدم عثوره على أي منها (gpu_problem، ويُسجَّل)، ويسمّي محرّك Whisper (stt_backend:mlxأوct2-cudaأوct2-cpu). والمرحلة التي لا يستطيع الجهاز تشغيلها تحتفظ بسبب خط المعالجة فيworkers.stages_off(unavailable: <why>)، وتعرض صفحة «المشرف ← الأجهزة» سطرًا للتفريغ النصي بالعربية والإنجليزية: جاهز على المعالج الرسومي، أو قيد التثبيت مع تحديث، أو السبب (على Windows تنتظر بطاقة سعتها 8 GB الاستماعَ الثاني، مع بيان كيف تسمح لها بالتفريغ عبر Whisper وحده). وصار مجمّعgpuخاصًا بكل جهاز: العامل الذي حدّد المشرف على العمليات حجم دفعته من معالجه الرسومي من NVIDIA يأخذ ذلك العدد من الملفات دفعة واحدة (MAKTABA_POOL_GPU، و8 على بطاقة سعتها 8 GB)، لا الحجم الذي ضبطته العنقودية للمعالج الرسومي في الخادم المنزلي. ولم تعد مهمةtranscribeتُطلَب حيث يفشل فحص القراءة من المجلد المشترك، والملف الذي لا تغطي خريطةُ المسارات مسارَه في الخادم المنزلي يفشل ذاكرًا ذلك. وتختار مثبّتات العُقَد الإضافةsttافتراضيًا على جهاز x86_64 مزوَّد بمعالج رسومي من NVIDIA بذاكرة 4 GiB أو أكثر، ويضيفها التحديث إلى مثل هذا الجهاز إن خلا منها.قائمة "يحتاج إلى انتباه" تصف كل مشكلة بلغتك على الويب وفي Maktaba Remote على iPhone وiPad، كما يفعل تطبيق Android: يأتي عنوان كل مجموعة من سببها، بالإنجليزية أو العربية، وتُعرض رسالة المشكلة بفواصل أسطرها الحقيقية، ويبقى اسم الملف العربي فيها بترتيبه الصحيح.
لم يعد جهاز Mac الذي يشغّل مكتبة يُعاد تسميته على الشبكة (
[name].local). كانت كل عملية من عمليات مكتبة تجيب باسم Bonjour الخاص بجهاز Mac إلى جانب macOS، فيعدّها macOS جهازًا آخر يستخدم الاسم. صارت مكتبة على Mac تعلن عن نفسها عبر المستجيب الخاص بـ macOS (dns-sd)، الذي ينشر الخدمة فقط. يستطيع جهاز Mac أُعيدت تسميته استعادة اسمه من إعدادات النظام ← عام ← المشاركة ← اسم المضيف المحلي.
الأمان
- لم تعد الخوادم تستمع ببروتوكول TCP متعدد المسارات. يفعّله Go 1.24 وما بعده افتراضيًا، وهو
المدخل إلى الثغرة CVE-2026-98432، وهي ثغرة حرجة في TCP بنواة Linux. ما زالت أجهزة Linux تحتاج إلى
تحديث النواة: راجع
docs/security/advisories/2026-11-01-linux-tcp-uaf.md(بالإنجليزية والعربية).
أُضيف
maktaba.dev، بوابة المطوّرين. أدلة الإضافات، وأدلة المساهمة، والأمان (السياسة، و
security.txt، والقواعد التي تلتزمها الإضافات والمساهمون)، ومرجع الواجهة البرمجية، وسجل التغييرات، في موقع واحد؛ أما الملاحم والمواصفات فتبقى على GitHub. كل صفحة بالإنجليزية، وبالعربية تحت/ar/(من اليمين إلى اليسار)، ولكل صفحة مبدّل للغة، ويفشل البناء إن غابت صفحة أو دليل أو مُدخَل في سجل التغييرات أو وصف في الواجهة البرمجية عن إحدى اللغتين. وهو Cloudflare Worker يُبنى من المستودع وفق قائمة سماح، ويُنشر من الفرعmain(web/packages/devportal).AirPlay، والصورة داخل الصورة، و"قيد التشغيل الآن" في Apple Player. تُشغَّل الفيديوهات والبث التلفزيوني المباشر على Apple TV من زر AirPlay، وتطفو في وضع الصورة داخل الصورة (وكذلك عند العودة إلى الشاشة الرئيسية)، وتظهر على شاشة القفل مع التشغيل والإيقاف المؤقت و±10 ثوانٍ والتالي (الملحمة 48، القصة 48.24، [#512]).
يمكن لـ AirPlay وCast التشغيل عبر منفذ الويب. يعبر الرابط الموقَّع للفيديو (
?sig=) منفذ الويب دون ملف تعريف ارتباط لتسجيل الدخول؛ أما قنوات التلفاز المباشر فما زالت تحتاج إلى جلسة مسجَّلة الدخول (الملحمة 48، القصة 48.12، [#474]).تطبيقات Apple ترتدي هوية العلامة. الوضع الداكن (Ink & Brass) افتراضيًا، وشاشات بداية بهوية العلامة بالإنجليزية والعربية، وشاشة إقلاع بلون الحبر عليها الشعار (48.10، [#480]).
تطبيقات Android ترتدي هوية العلامة. الوضع الداكن عند أول تشغيل، وشاشة إقلاع بلون الحبر عليها الشعار حتى حين يكون الهاتف في الوضع الفاتح (48.11، [#479]).
MaktabaKit: عميل التصفح في المشغّل (الملحمة 48، القصة 48.6). يضيف
Browse.swiftالاستدعاءات والنماذج لكل من: الرئيسية (الصفوف، والشرائط، و"غير مهتم")، والمكتبات (البطاقات، والتثبيت)، ومكتبة واحدة (الواجهة، والشبكة بالمؤشر مع المرشّحات والترتيب، ومسلسلاتها)، والمسلسلات، وصفحة الفيديو (التفاصيل، والعلامات، والفصول، والترجمات المصاحبة، والنص المفرّغ، ومعاينة التقديم، والملصق الصغير)، والمُشاهَد والمفضّل، والبحث (المجموعتان، والصفحات، والمقاطع، والاقتراحات، والأخيرة)، والملف الشخصي (السجل، والجلسات، والربط بالسحابة، وقوائم التشغيل). يقرأ كل نموذج إجابة الخادم المنزلي بتسامح؛ فالاستجابة 404 (و403 في مسار الفيديو) تصبحBrowseError.notAvailable، والاستجابة 401 تسلك مسار تسجيل الخروج القائم. اختُبر على إجابات مسجَّلة من الواجهة البرمجية على قاعدة بيانات تجريبية؛ ويفشل اختبار Go حين يُعاد تسمية حقل مسجَّل في المعالجات.عميل Android: التلفاز المباشر. يكتسب
apps/android/clientالملفLive.kt: ما يُعرض الآن، ودليل البرامج ودليل قناة واحدة، وشعارات القنوات، ورابط بث القناة، وGET /api/tv/info، والجلسة البعيدة خارج المنزل (الضبط، ونبضة كل 60 ثانية، والإنهاء). يختارLiveSourceبين بث الخادم المنزلي والمُرحِّل، ويبدّل الرمز (token) الجديد الذي تأتي به النبضة دون إعادة فتح المشغّل. ويُبنى عليه كل من مشغّل Android وAndroid TV (القصة 48.9).ملصقات صغيرة لشبكات الهاتف (الملحمة 48، G4). يقدّم
GET /api/videos/{id}/poster?size=smallنسخة خط المعالجة بعرض 320 بكسل من ذاكرة الصور المصغّرة المؤقتة، وإلا فالصورة الكاملة، وتُخزَّن مؤقتًا ساعة كالصورة الكاملة. صار المشاهد الذي لا يملك الوصول إلى مكتبة الفيديو يتلقّى 404 (كما في الفيديو غير الموجود) بدل 403، للحجمين كليهما.يرسل الخادم رموزًا لا عبارات إنجليزية لما تقوله شاشات المشرف. صار كل فحص لتغيير التلفاز يحمل
codeوparamsبجانب قيمته الإنجليزيةvalue(مثلslot.release_mismatchلعبارة "خانة التجهيز تشغّل إصدارًا آخر")، والخطوة التي تُفشل تغييرًا تحملreport_code/report_params. وسبب "لا يستطيع استقبال أجهزة" في الخادم المنزلي يحملroles_reason_code/roles_reason_params. تقول تطبيقات الويب وAndroid Remote وApple هذه الرموز بالإنجليزية والعربية، وتعرض العبارة الإنجليزية حين لا تعرف رمزًا (وتبقى التطبيقات الأقدم تقرأvalue). وصار لمن بدأ التحديث (auto،manual،catch_up،retry) كلمات على Android وiPhone، ولأسماء فحوص التلفاز كلمات في صفحة التحديثات وفي التطبيقات، وتقول شاشة العمليات على iPhone الرمزdetail_codeالخاص بالمشرف على العمليات بالكلمات بدل عبارته الإنجليزية. أُضيفت حقول فقط: يعمل تجهيز التلفاز كما كان.مشغّل Android: الهيكل الذي يضم أقسامه (الملحمة 48.15). يحصل Maktaba Player على Android على التنقّل التكيّفي من Material (شريط سفلي على الهاتف أو على جهاز قابل للطي نصف مطوي فوق طاولة، وشريط جانبي على جهاز قابل للطي مفتوح أو جهاز لوحي صغير، ودرج على الجهاز اللوحي الكبير) للرئيسية، والمكتبات، والتلفاز المباشر، والبحث، والملف الشخصي، ويحتفظ كل منها بشاشاته وموضع تمريره. لا يظهر القسم إلا بعد بناء شاشته، لذا يفتح التطبيق حاليًا على المكتبات كما كان. صار التكامل المستمر يشغّل اختبارات الوحدة والواجهة للمشغّل على خادم منزلي وهمي، مع لقطات شاشة داكنة بالإنجليزية والعربية تُقارن بصور مرجعية.
تظهر الترجمات المصاحبة على Apple TV وAirPlay وCast (القصة 48.4). صارت قائمة التشغيل الرئيسية لجلسة الفيديو تُدرج كل ملف ترجمة بجانب الفيديو كمسار ترجمة HLS (اللغة، واسمه الخاص، ومعطّل افتراضيًا). كل مسار قائمة تشغيل من ملف واحد فوق مسار الترجمة القائم، موقَّع كبقية الجلسة ومتزامن مع الصورة، فيستطيع AVPlayer وAirPlay وCast عرضه. ويُدرج hls.js على الويب المسارات أيضًا. والفيديو الذي لا ترجمات له يتلقّى قائمة التشغيل نفسها كما كان.
مواصفة الملحمة 48: المشغّلات الأصلية تلحق بالويب وPlex. يسرد
specs/epics/48-native-players/ما تركته الملحمة 42 خارج Maktaba Player على iPhone وiPad وMac وAndroid (هوية العلامة والبداية الداكنة، وشرائط الرئيسية، والمسلسلات، والبحث، والتلفاز المباشر، والملف الشخصي، ومشغّلًا كاملًا مع الحلقة التالية، والمسارات، والصورة داخل الصورة، وAirPlay وCast)، والتلفاز المباشر لتطبيقات التلفاز، ومصدرًا واحدًا لرموز التصميم، وأربع ثغرات في الخادم (يجب أن يتبع بث القناة صلاحية الوصول إلى المكتبة؛ وروابط بث موقَّعة لـ AirPlay وCast؛ والترجمات المصاحبة كنسخ HLS؛ والملصقات الصغيرة)، و36 قصة بفحوص قابلة للاختبار، والخطة التي تشغّلها بالتوازي.يستطيع عميل Android التصفح (الملحمة 48، القصة 48.8). يقرأ الملف الجديد
Browse.ktفيapps/android/clientشرائط الرئيسية وتوصياتها، والمكتبات وتثبيتاتها، وواجهة المكتبة وشبكتها ومرشّحاتها ومسلسلاتها، والمسلسلات وحلقاتها، وصفحة الفيديو (حالته، وفصوله، ونصه المفرّغ، وعلاماته)، والبحث (النتائج، والصفحات، والاقتراحات، والأخيرة)، بتسامح: الصف المفقود فارغ والبطاقة غير المقروءة تُستبعد. والاستجابة 404 تصبحNotAvailable. ويتحقق اختبار Go من بيانات الاختبار مقابل أنواع استجابات الواجهة البرمجية نفسها.MaktabaKit يقرأ التلفاز المباشر (القصة 48.7). تستطيع الحزمة المشتركة لتطبيقات Apple قراءة ما يُعرض الآن، ودليل البرامج لنافذة زمنية، ودليل قناة واحدة وشعارها، ومكان بث التلفاز. وتشغّل القناة من شبكة الخادم المنزلي داخل المنزل، أو عبر جلسة بعيدة خارجه، فتُبقي تلك الجلسة حيّة وتأخذ كل رمز (token) جديد دون إعادة فتح المشغّل. وسيستخدمها المشغّل وتطبيق Apple TV كلاهما.
مصدر واحد لرموز التصميم للويب وApple وAndroid (القصة 48.2). صار
web/design-system/tokens/يضم نظام Ink & Brass المنشور (الداكن: الحبر#0B111C، والنحاس#E3B252) ومجموعته الفاتحة، إضافة إلى أسطح التطبيقات (raised،nav،scrim،chosen). ويكتبmake build-tokensأيضًاTokens.swiftالخاص بـ MaktabaKit وTokens.ktفي نواة Android (أزواج الألوان، والمسافات، وأنصاف أقطار الزوايا، وأحجام الخط). ويفشلmake test-tokens، الذي صارت مهمة الوحدة في التكامل المستمر تشغّله، حين تكون قديمة، أو حين يحيدweb/src/styles/tokens.cssعن ملف JSON، أو حين يقل تباين لون نص عن 4.5:1 على سطحه في الوضع الفاتح أو الداكن. لا تقرأها أي شاشة بعد.صار لـ Maktaba Player (Apple) هيكل أقسامه (الملحمة 48، القصة 48.13). يضم الرئيسية، والمكتبات، والتلفاز المباشر، والبحث، والملف الشخصي في شريط تبويبات على iPhone يصبح شريطًا جانبيًا على iPad وMac وiPhone Duo مفتوح. لا يظهر القسم إلا بعد شحن شاشته، لذا ما زالت الإصدارات الحالية تفتح مباشرة على المكتبات. صار التكامل المستمر يشغّل اختبارات واجهة المشغّل، بالإنجليزية والعربية، على محاكيات iPhone وiPad مؤقتة مقابل خادم منزلي وهمي، ويحتفظ بلقطات شاشتها.
تطبيقات Apple تتحدث العربية. يُشحن Maktaba Remote (iPhone، iPad) وMaktaba Player (iPhone، iPad، Mac) وMaktaba TV (Apple TV) بالإنجليزية والعربية، من اليمين إلى اليسار، في كل شاشة: لكل تطبيق String Catalog، ولـ MaktabaKit كتالوج خاص للكلمات التي يصوغها (حالات التحديثات والعمليات وطابور المهام وخطواتها وأسبابها، وجملة الخطة)، بعربية الويب (
ar.json) حيثما يقول الويب الشيء نفسه. تأخذ الأعداد صيغ الجمع العربية؛ وتتبع الأرقام والتواريخ إعدادات اللغة؛ والقيمة التي يرسلها الخادم المنزلي بلا كلمات تُعرض كما أُرسلت. ويشغّل الفحص الجديد Apple apps في التكامل المستمر اختبارات MaktabaKit، ويبني التطبيقات، ويفشل عند نص بلا ترجمته العربية أو غائب عن كتالوجه (tools/apple/check-string-catalogs.py).تطبيقات Android تتحدث العربية. صار Maktaba Remote (كل شاشة: يحتاج إلى انتباه، وطابور المهام، والتحديثات، والمكتبات، والأجهزة وعملياتها) وMaktaba Player وقائمة الحساب تُشحن بالإنجليزية والعربية، من اليمين إلى اليسار، مع أرقام وتواريخ بلغة الجهاز. يقول Remote ما تقوله لوحة إدارة الويب، بعربية الويب نفسها لكل حالة وخطوة ورمز؛ والقيمة التي لا كلمات لها لديه تُعرض كما أرسلها الخادم المنزلي. وصار اختبار وحدة وفحص lint يُفشلان أي نص في Android أو Android TV بلا ترجمة عربية.
يعرض Maktaba Remote (iPhone، iPad) العمل الجاري في الخادم المنزلي. ترتبط الرئيسية بصفحتين جديدتين للقراءة فقط: التحديثات (الإصدار العامل وما يليه، بكلمات الويب: ما ينتظره الإصدار المُخطَّط، أو إصدار عبور، أو إصدار معلَّق، أو فحص قديم؛ والإصدارات التي ما زالت محظورة؛ والتلفاز المعلَّق عند تحديث مع فحوصه الفاشلة؛ والتشغيل الجاري أو الأخير بخطواته وحالة كل جهاز وخطئه؛ وإصدار كل جهاز وآخر تحديث له ووصوله إلى قاعدة بيانات الخادم المنزلي) والعمليات (كل عملية يشرف عليها المشرف على العمليات حسب الدور وخانة التلفاز، وحالتها، وإعادات تشغيلها، وآخر خروج لها، وحلقة الانهيار، وما تنتظره، كمنفذ مشغول؛ والأحداث الأخيرة للمشرف على العمليات؛ أو أن المشرف على العمليات غير متاح). ويسرد طابور المهام المهام العاملة الآن مع مدة عمل كل منها. وتُعرض الحقول الغائبة والحالات غير المعروفة لدى خادم منزلي أقدم أو أحدث كما تأتي بدل أن تُعطّل الصفحة.
تطبيقات iPhone على TestFlight. كل وسم إصدار يبني Maktaba Player وMaktaba Remote لـ iPhone، ويوقّعهما بالتوقيع السحابي في Xcode (مفتاح App Store Connect API) ويرفعهما إلى TestFlight (
testflight.yml،docs/testflight.md). إصدار التطبيقين هو إصدار الوسم، وكل رفع يحصل على رقم بناء جديد.يعرض Maktaba Remote لـ Android العمل الجاري في الخادم المنزلي، كما تفعل لوحة إدارة الويب. تبويب جديد للتحديثات: الإصدار العامل، وما ينتظره التحديث التالي، والإصدارات المحظورة (المقبل منها فقط)، والتلفاز المعلَّق وفحوصه الفاشلة، والتشغيل الجاري أو الأخير بخطواته وحالة كل جهاز وخطئه، وإصدار كل جهاز. ويسرد تبويب الأجهزة عمليات الخادم المنزلي (خانات التلفاز حسب اللون، وحلقات الانهيار، وإعادات التشغيل، وآخر خروج، والانهيارات الأخيرة؛ ويقول ذلك حين يتعذّر الوصول إلى المشرف على العمليات). ويعرض طابور المهام المهام العاملة الآن، وشريط الصحة، وسبب انتظار كل مرحلة، وصحتها، وإخفاقات المرحلة المختارة حسب السبب. للقراءة فقط.
تمرين التحديث: يُثبَت كل مسار تحديث في تطبيق Mac في التكامل المستمر قبل الإصدار. يحدّث سير عمل جديد (
update-rehearsal) خادمًا منزليًا مُهيّأ ببيانات من آخر إصدار منشور إلى الإيداع (commit) الحالي، بالطريقة التي يتبعها Maktaba.app (حزمته، ومُشغِّله، ومجلد حالته، وPostgres المضمَّن، وتثبيت التطبيق كما يؤديه الاختبار)، ثم يكسره بالطرق التي انكسر بها في 2026-10-28: تغيير تلفاز ينتظر، وعمليات تراجع بعد ترحيلات إضافية وأخرى هدّامة، واستعادة بينما تسجّل الأجهزة الدخول وأخرى تُقتل في منتصفها، وأدوار متوقفة، ونفاد المساحة، وحالة جهاز Mac الخاص بالمالك، وجهاز منضم يتحدّث قبل خادمه المنزلي. يُبلغ كل سيناريو عن مدة توقفه مقابل ميزانية محددة. ويعمل على طلبات الدمج التي تمس التحديثات، وعند كل دفع إلى main، وكل ليلة، ويرفض حارس وسم الإصدار أي إيداع لم ينجح عليه (docs/update-rehearsal.md).يغطي تمرين التحديث تغيير تلفاز يُجهَّز على خانة تشغّل الإصدار القديم. يحدّث السيناريو 11 خادمًا منزليًا بينما تعمل خانات تلفازه، مع خانة غير حيّة (الخضراء) تشغّل برنامج البث القديم عبر إعادة التشغيل. ثم يُجهَّز تغيير التلفاز الخاص بالتحديث عليها: يجب أن تبدأ الخانة من جديد على الإصدار الجديد وأن يبلغ التغيير مرحلة التحقق، ولا يُعلَّق أبدًا بسبب
slot.health/release_mismatchكما حدث للتغيير #58 في 2026-10-31. وصار التمرين يبني ملف البث التنفيذي مع تجهيز التلفاز، كما يفعل الإصدار.
أُصلح
عادت الفيديوهات تُشغَّل بالصوت. الفيديو الذي سجّل التفريغ النصي مسار صوته قبل تشغيل الفحص كان يُبث بلا صوت على كل المشغّلات (32,381 فيديو في الخادم المنزلي للمالك). صار الفحص يملأ المسار الموجود، وتُصلح الخانة 0171 المسارات المتأثرة سابقًا ([#477]).
الجهاز الذي يتصل بعد فحص الخادم المنزلي مباشرة يحصل على شهادة قاعدة بيانات الخادم المنزلي فورًا، بدل أن ينتظر حتى 30 ثانية ثم يعيد التشغيل ([#473]).
يسجّل المُرحِّل السحابي الخادم المنزلي متصلًا قبل التوجيه إليه، ولم يعد الخادم المنزلي الذي يعاود الاتصال سريعًا يبقى ظاهرًا غير متصل ([#475]).
تثبّت عقدة Windows تحديثات الخادم المنزلي حتى حين تكون نافذة مكتبة مغلقة. حتى الآن كان تطبيق النافذة هو الذي ينزّل كل تحديث ويثبّته. وكان الخادم يحاول فتحه برابط
maktaba://فقط، فيبقى التحديث منتظرًا حتى يمكن فتح نافذة. في 2026-10-31 بقي تحديث windows-pc إلى v0.3.44 منتظرًا ثماني ساعات، بينما يعرض Windows رسالة "احصل على تطبيق لفتح رابط 'maktaba' هذا" كل 90 ثانية. صار وكيل الخادم الذي يعمل عند تسجيل الدخول (Maktaba.exe --server-agent) يتولى ذلك بنفسه:- ينزّل مثبّت الإصدار من صفحة ذلك الإصدار نفسه؛
- يتحقق من المثبّت بمفتاح التطبيق، ويتحقق من أن التوقيع يحمل الإصدار المطلوب ويسمّي هذا المثبّت بعينه؛
- وعند إشارة الخادم المنزلي، يشغّل المثبّت.
لم يعد الخادم يرسل الرابط، وصار تطبيق النافذة يعرض التحديث فقط.
بث القناة يتبع صلاحية الوصول إلى المكتبة (الأمان، الملحمة 48، القصة 48.3). كان
/stream/channel/{id}/…في منفذ الويب يتحقق فقط من أن أحدًا سجّل الدخول، فكان المشاهد المقيَّد ببعض المكتبات يستطيع مشاهدة قناة مكتبة أخرى بمعرّفها. صارت البوابة تسألGET /api/channels/{id}ببيانات اعتماد المشاهد (قاعدة دليل البرامج): القناة التي لا يحق للمشاهد قراءتها، أو القناة غير الموجودة، تعطي 403. يتخطى المشرفون الفحص؛ ويُخزَّن الحكم مؤقتًا لكل مشاهد وقناة مدة 30 ثانية، فلا تكلّف المقاطع أي استدعاء إضافي للواجهة البرمجية؛ وإن لم تُجب الواجهة البرمجية تبقى الاستجابة 502. لم تتغير جلسات الفيديو ولا معاينة التجهيز للمشرفين.صار الاستئناف على Windows يبدأ الخادم فور توقفه، وأُصلحت ثلاثة اختبارات متقلّبة من جذورها. كان تطبيق سطح المكتب يعدّ الوكيل الذي أفلت قفله لكنه ما زال يخرج وكيلًا عاملًا، فكان الاستئناف يطلب من Task Scheduler مرة واحدة، فيُتجاهل الطلب، ويبقى الخادم متوقفًا. صار لا يُعدّ عاملًا إلا الوكيل الذي يحمل القفل. وصار اختبار الإثراء قرب منتصف الليل يسمح بالحد الأدنى للانتظار وهو 60 ثانية بالضبط، ويقرأ التاريخ قبل التشغيل، ويُبقي التشغيلين في يوم ميزانية واحد. وبعد تشغيل بطيء واحد، صار مستوى اختبارات الوحدة يعيد تشغيل الاختبار كاملًا ببيانات اختبار جديدة. في السابق كان يعيد تشغيل جسم الاختبار فقط على بيانات التشغيل الأول، فيصطدم بملفاته نفسها، ويفشل في كل مرة. وصار اختبار الاحتفاظ يكتب ملفات أقل.
تبدأ نسخة قاعدة بيانات الخادم المنزلي في العقدة الكاملة من جديد حتى حين يتعلّق عامل. في 2026-10-28 كانت النسخة الأولى لجدول في منتصفها حين أُعيد تشغيل قاعدة بيانات الخادم المنزلي. لم ينقطع اتصاله قط، فانتظر العامل 15 دقيقة صفوفًا لم تأتِ، وفشلت كل نسخة جديدة برسالة "the subscription's workers did not stop within 30s". صار العامل الذي يبقى بعد 30 ثانية من إيقاف النسخة يُنهى. ولا شيء يعيد تشغيل عامل نسخة متوقفة، فتمضي النسخة الجديدة.
لم تعد اختبارات التكامل للواجهة البرمجية والبث تتصادم على منفذ. كان كلاهما يشغّل خادمه على المنفذين الثابتين 18081/19101. وحين شغّل التكامل المستمر الحزمتين معًا، كان اختبار البث قد يصل إلى خادم الواجهة البرمجية: "admin /readyz service = api, want streaming"، وهو ما أفشل #450. صار كل اختبار يأخذ منافذ حرة من نظام التشغيل.
صار التكامل المستمر يشغّل اختبارات الوحدة في Android Remote. كانت مهمة Android تشغّل اختبارات العميل المشترك والنواة دون اختبارات Remote نفسه (
:remote:testPhoneDebugUnitTest)، فلم تعمل اختبارات صياغة Remote (SayTest) في التكامل المستمر قط.يُبقي المشرف على العمليات كل خانة تلفاز على الإصدار المرتبطة به. صار يسجّل الارتباط الذي بدأت به عملية كل خانة. وكلما رُبطت خانة (من أي مستدعٍ، لا من بدء البوابة الأمامية فقط) أو تُبنّيت بعد إعادة تشغيل، تُعاد الخانة المطلوبة التي تشغّل إصدارًا آخر إلى التشغيل من تلقاء نفسها. وهذا يوسّع إصلاح التغيير #58، الذي علّقه التلفاز في 2026-10-31 بسبب
release_mismatch.تسمّي بطاقة العمليات كل خانة تلفاز وتقول لماذا لا تعمل إحداها. كانت خانات التلفاز الثلاث (وعدة عمّال في خط المعالجة) تُفهرس وتُوسم بالدور وحده، فتظهر بطاقات "tv-slot" متطابقة. صار كل منها يُسمّى بنسخته ("خانة التلفاز · الزرقاء"). والخانة غير المطلوبة تقول الآن في وضع الاستعداد لا متوقفة. والعملية التي يعيقها شيء ما (كمنفذها الذي يستخدمه برنامج آخر) تقول ما هو، بكلمات التطبيق.
لم تعد عقدة Windows في منتصف كتابة إعدادات عقدتها تُقرأ كأنها لم تنضم قط. كان قارئ خادم البث يرى
node.jsonمفقودًا أثناء تبديل الكتابة، ثم يبحث عن قفل الكتابة بعد انتهائها، فيعدّ الأمرين معًا "غير منضمة". صار ينظر إلىnode.jsonمرة أخرى قبل أن يقرر، ويقرؤه من جديد إن عاد. وكان هذا سبب فشلTestNodeConfigPathWaitsOutAWriteنحو مرة كل 744 قراءة في التكامل المستمر.يبني التكامل المستمر على macOS 15. يُوقف GitHub مشغّلات macOS 14 في 2026-11-02 وبدأ يطفئها لساعات متواصلة، ما أفشل كل بناء darwin وكل مهمة مثبّت macOS في نحو ثانية. صارت مسارات عمل البناء وتحزيم العقدة والإصدار تعمل على
macos-15، كما كانت تفعل مسارات عمل تطبيق Mac وإصدار سطح المكتب.خانة التلفاز التي كانت تعمل تُعاد إلى التشغيل حين يُطلب منها تشغيل إصدار آخر. بعد أن أعاد تحديث 0.3.44 تشغيل جهاز Mac، عادت الخانة الخضراء على برنامج التلفاز القديم. ثم ربط تغيير التلفاز الخانة الخضراء بالإصدار 0.3.44، لكن المشرف على العمليات لا يستخدم الارتباط الجديد إلا عند البدء التالي للخانة، فبقيت الخضراء تجيب عن إصدارها القديم. ففشل فحص صحة التغيير (
slot.health:release_mismatch) وبقي التلفاز على v0.3.39. صار البدء الذي يأتي بإصدار آخر يعيد تشغيل الخانة العاملة عليه، وإرسال الارتباط نفسه مرة أخرى لا يغيّر شيئًا.لم تعد صفحة التحديثات تقول "محظور" حين يكون التحديث التالي منتظرًا فقط. كانت تسرد كل إصدار حُظر يومًا، ومنها إصدارات حلّ محلها إصدار أحدث منذ زمن (0.3.42، الذي تُراجع عنه، بعد صدور 0.3.43)، فكانت الصفحة التي ينتظر تحديثها نافذته الهادئة فقط تبدو محظورة. صار الإصدار المحظور يُعرض فقط ما دام هو الإصدار المقبل. والإصدار الذي تعذّر على التلفاز الانتقال إليه له سببه الخاص.
بعد تراجع أبقى على المخطط الأحدث، يُثبَّت الإصدار الذي بينهما. التراجع عن إصدار ترحيلاته إضافية يُبقي مخططه للإصدار الذي يعود إليه؛ وكان أي إصدار بين الاثنين يُرفض حينها بأنه "أقدم من قاعدة البيانات"، فلا يمكن تثبيت الإصلاح التالي (تمرين التحديث 10a، بعد أن شحن v0.3.43 المخطط المُبقى عليه). صار كل إصدار بدءًا من الإصدار الذي أُبقي المخطط له وحتى إصدار قاعدة البيانات يبدأ عليه.
تعود خانتا التلفاز الزرقاء والخضراء إلى العمل على جهاز Mac يجري التفريغ النصي بالتوازي. كان عمّال التفريغ النصي يأخذون المنافذ من 9111 فصاعدًا، وهي منافذ خانات التلفاز نفسها (9111–9113): على جهاز Mac الخاص بالمالك احتجز عاملان المنفذين 9111 و9112 منذ 2026-10-28، فدخلت الخانتان الزرقاء والخضراء في حلقة انهيار بسببهما (أكثر من 1,300 إعادة تشغيل)، ما حظر أيضًا كل تحديث. صار العمّال يستخدمون 9131–9146، ويفشل اختبار حين تتطابق المنافذ الافتراضية لأي عمليتين. ولم تعد خانة تلفاز عاجزة عن العمل تمنع بدء تحديث: التلفاز لا يمنع أي تحديث، وقد يكون التحديث هو إصلاحه.
المنفذ الذي يحتجزه برنامج آخر يُسمّى، ولا تدخل العملية في حلقة انهيار بسببه. صار كل منفذ تستمع عليه مكتبة مُعلنًا مرة واحدة، مع مالكه والغرض منه (
shared/go/ports)، وكل جزء يأخذ قيمته الافتراضية من هناك؛ ويفشل اختبار حين يتشارك جزءان منفذًا، بما في ذلك كل منفذ في نطاق. وعند البدء يرفض الخادم العناوين فيserver.tomlالتي يتعارض بعضها مع بعض، مسمّيًا الاثنين. وقبل أن يبدأ جزءًا، يتحقق المشرف على العمليات من أن منافذ ذلك الجزء حرة: المنفذ المحتجز يترك الجزء دون بدء، معport_takenفي/processesيقول أي منفذ ومن يحتجزه (البرنامج ومعرّف عمليته حيث يعرضهما النظام، أو أي جزء من أجزاء مكتبة نفسها)، ثم يحاول من جديد بالتأخير المعتاد.على Mac، يعود الموقع من تلقاء نفسه بعد قتل الخادم. كان
kill -9(أو انهيار) الخادم يترك Postgres المضمَّن يعمل، فيتبنّاه النظام؛ ثم يفشل كل بدء لاحق بسببpostmaster.pidويبقى الموقع متوقفًا. صار الخادم يوقف أي postmaster تابع لعنقوده الخاص لم يعد شيء يشغّله (إيقاف سريع، ثم قتل بعد 30 ثانية) قبل أن يبدأ Postgres. وكان Linux يفعل ذلك سلفًا عبر المُشغِّل.لم يعد التحديث يُتراجع عنه لأن تغيير التلفاز الخاص به ينتظر، ولم يعد التراجع يعيد قاعدة البيانات في الترحيلات الإضافية، وصارت الاستعادة اللازمة كاملة (انقطاع 2026-10-28). اجتاز التحديث إلى 0.3.42 كل فحص، لكن تغيير التلفاز الخاص به انتظر نصف ساعة موافقةً؛ وفي الأثناء نفدت مهلة المراقبة (
watch_timeout)، فأعاد التراجع قاعدة البيانات كلها من أجل صف واحد مضاف، وفشلت تلك الاستعادة في منتصفها (اصطدم جهاز منضم يطلب نسخته بصلاحيات الاستعادة:tuple concurrently updated)، فبقيت المكتبة متوقفة ربع ساعة. الآن: تحكم المراقبة على صحة الخادم المنزلي نفسه (خانة التلفاز التي تبدأ أو تتوقف بسبب تبديل من شأن خطوة التلفاز؛ أما الخانة الحية التي تدخل في حلقة انهيار فما زالت تسبب التراجع)، والإصدار السليم طوال مدة مراقبته يُعتمد فورًا مع تغيير تلفاز معلّق قيد الانتظار (يمضي من تلقاء نفسه؛ ولا يكون أبدًاwatch_timeoutأوtv_change_pendingأوtv_timeout)، أو محتجز مع زر إعادة المحاولة حين يتعذّر الحصول على أي تغيير. والتراجع الذي تكون ترحيلاته كلها آمنة للتراجع يُبقي المخطط ولا يعيد شيئًا، للإصدار السابق الذي يعلن أنه يعمل على مخطط مُبقى عليه (shared/db/migrations/meta/schema-keep.json؛ ويُبلَّغ خادم واجهته البرمجية بذلك عبرMAKTABA_SCHEMA_AHEAD_OK)؛ وأي شيء آخر قد يكسر الإصدار السابق (حذف من أي نوع، أو إعادة تسمية، أو تغيير نوع، أو إضافة قيد أو فهرس فريد، أو تغيير توقيع دالة) يعيد النسخة الاحتياطية. ويعيدها الإصدار الجديد بنفسه قبل أن يعود إلى السابق، فيبدأ الإصدار السابق على قاعدة بيانات مكتملة: في قاعدة بيانات خاصة لا يصل إليها شيء آخر، المخطط والصلاحيات واحدًا تلو الآخر، والصفوف والمتجهات والفهارس بالتوازي، مع التحقق مقابل النسخة الاحتياطية (كل جدول وفهرس ومفتاح ومشغّل ودالة وصلاحية وامتياز افتراضي، وعدد صفوف كل جدول: صارت النسخ الاحتياطية تسجّلها،counts.json)، ثم تُعاد تسميتها إلى مكانها في معاملة واحدة مع صلاحيات قاعدة البيانات نفسها (صلاحية CONNECT للأجهزة)؛ والفشل يترك قاعدة البيانات كما كانت، ويعيد المحاولة بعامل واحد، ويقول بالضبط ما الناقص. والأدوار التي أوقفتها خطوة قاعدة البيانات تبدأ من جديد من تلقاء نفسها حين يُصحَّح سجلها (migratedأوrestored)، ويعيد البدء إلى الأجهزة صلاحية CONNECT حين تكون الاستعادة قد أسقطتها، وإعادة التشغيل المكتوبة يدويًا (updates/restart) تُنفَّذ تحت مُشغِّل التطبيق أيضًا.
أُضيف
تبويبا السلاسل والمجموعات في المكتبة (الملحمة 45، القصة 45.24). صارت صفحة المكتبة نظرة عامة | تصفّح | السلاسل | المجموعات، ولكل تبويب عنوانه الخاص (زر الرجوع يعيدك إلى التبويب السابق). السلاسل: كل سلاسل المكتبة في شبكة ملصقات مع حلقاتها وتقدّمك فيها، مرتّبة بالاسم أو بأحدث حلقة أو بآخر ما شوهد. المجموعات: المجموعات التي تصنعها مكتبة بنفسها (الموضوعات «ملف»، والمقاطع «مقاطع»، والمستقلة «منفردات»، وهي ما يعرضه Plex، وتحمل علامة تلقائية وللقراءة فقط، ولكل منها صفحتها) بجانب المجموعات التي ينتقيها المشرفون، تحت عنوان لكل نوع مع مرشّح وأعداد. أُزيل زر المجموعات المنفصل، وصار
/library/:id/collectionsيفتح التبويب. الواجهة البرمجية:GET /api/libraries/{id}/series،GET /api/libraries/{id}/collections،GET /api/libraries/{id}/collections/{topic|clip}/{id}(يعيد 404 لمكتبة لا يحق لك رؤيتها).مشاهدة على طريقة Plex، المرحلة 2 (الملحمة 45، القصص 45.8 إلى 45.16). علّم الفيديو مشاهدًا أو غير مشاهد، واحدًا تلو الآخر أو عدة فيديوهات معًا (تحديد في شبكة المكتبة)؛ علامات صح وأشرطة تقدّم على بطاقات الشبكة؛ المفضلة (قلب في صفحة الفيديو وفي الشبكة، وصف للمفضلة في الصفحة الرئيسية، ومرشّح للمفضلة)؛ قوائم التشغيل (
/playlists: أنشئ، وأعد التسمية، وأعد الترتيب، واحذف؛ وأضف من فيديو أو من تحديد)؛ ومربع البحث في الشبكة، ومرشّحات الوسم والمتحدث واللغة، وشريط قفز من الألف إلى الياء (حروف عربية ولاتينية)؛ وتشغيل الكل وخلط والتشغيل من هنا، مع التالي في نهاية الفيديو (التالي في الطابور، أو الدرس التالي في السلسلة، بعد عدّ تنازلي من 10 ثوانٍ)؛ وعلى المنصة (الدرس التالي من كل سلسلة أنت في منتصفها) في الصفحة الرئيسية وصفحة المكتبة؛ وفي صفحة الفيديو: الاستئناف، وموضع الدرس في سلسلته («الدرس 4 من 40») مع السابق والتالي. علامات كل مستخدم ومفضلته وقوائم تشغيله خاصة به، ولا تتعدى المكتبات التي يحق له رؤيتها. الخانة 0162. الواجهة البرمجية:POST /api/me/watched،POST /api/me/favorites،GET /api/me/videos/{id}،/api/me/playlists...،GET /api/videos/facets،GET /api/me/videos(القائمة في الصفحة الرئيسية،favorite=true)، ويكتسبGET /api/videosالمعاملاتtagوspeakerوletterوsort=random.إعدادات النسخ الاحتياطي (الملحمة 36، القصة 36.16). مكان تفريغات قاعدة بيانات المكتبة، والجدول (تشغيل/إيقاف، يوميًا أو أسبوعيًا أو كل N يومًا، والساعة)، وعدد التفريغات المجدولة والسابقة للتحديث التي تُبقى، وتشغيل أو إيقاف التفريغ السابق للتحديث، والمساحة التي تتركها التفريغات على قرصها (5% / 2 GB / الخُمس افتراضيًا)، كلها في
[backups]داخل server.toml، وتُغيَّر من الإعدادات ← التخزين والاكتشاف ← النسخ الاحتياطية لقاعدة بيانات المكتبة (التي تعرض التفريغات وتحذفها أيضًا)، أو شاشة التخزين في الإعداد الأولي، أوmaktaba-server backups show|set|delete. نقل المجلد يسأل هل تُنقل التفريغات معه. يُسجَّل وقت آخر تفريغ مجدول على حدة، فلا يؤدي نقل التفريغات أو حذفها أبدًا إلى تفريغ إضافي. الواجهة البرمجية:GET/PUT /api/server/backups،DELETE /api/server/backups/dumps/{name}(للمشرف فقط، ومع سجل تدقيق).بيانات مكتبة على قرص آخر (الملحمة 36، القصة 36.15). في الإعداد الأولي شاشة أين تحفظ مكتبة بياناتها (الأقراص ومساحتها الحرة، مع اقتراح أوسعها، وتحذير حين تكون صغيرة) تضبط
[server] data_dirقبل إنشاء أي شيء. لاحقًا، ينسخmaktaba-server storage showوmaktaba-server storage move <dir> [--dry-run](ومكتبة متوقفة)، أو الإعدادات ← التخزين والاكتشاف ← النقل إلى قرص آخر (للمشرفين؛ يتوقف الخادم، وينسخ، ويتحقق، ثم يعود للعمل)، قاعدةَ البيانات والنسخ الاحتياطية والنماذج وذاكرات التخزين المؤقت إلى قرص محلي آخر، ويتحقق من النسخة (تبدأ قاعدة البيانات عليها)، ويوجّه server.toml إليها، ويُبقي المجلدات القديمة باسم*.moved-<stamp>. النقل الذي انقطع يُستأنف. وإذا بدأ التشغيل وقرصه المختار غير موصول، ينتظر ويُعلن ذلك (storage_waiting)، ولا ينشئ أبدًا قاعدة بيانات فارغة جديدة مكانه. الواجهة البرمجية:GET /api/server/storage،GET /api/server/storage/disks،POST /api/server/storage/check،POST /api/server/storage/move(للمشرف فقط، ومع سجل تدقيق).مكتبات تديرها وتتصفحها وتثبّتها (الملحمة 45). صفحة رئيسية (
/) تشمل المكتبات التي يحق لك رؤيتها: متابعة المشاهدة، وأُضيف مؤخرًا، وشوهد مؤخرًا، والبث التلفزيوني المباشر الآن، وصف لكل مكتبة مثبّتة. تعرض المكتبات (/library) كل مكتبة بطاقةً فيها ملصقاتها وعددها؛ ولصفحة المكتبة نظرة عامة (متابعة المشاهدة، وأُضيف مؤخرًا، وشوهد مؤخرًا، والأكثر مشاهدة، والسلاسل، وحسب السنة) وشبكة تصفّح ترتّب حسب تاريخ الإضافة، أو تاريخ النشر، أو العنوان، أو المدة، أو عدد المشاهدات، وترشّح حسب السنة والمشاهدة واللغة (يكتسب/api/videosالمعاملاتsort=published_at|duration_sec|playsوorderوyearوwatchedوseries). ثبّت المكتبات لتبقى في الشريط الجانبي بترتيبك أنت (تُحفظ لكل مستخدم على الخادم، الخانة 0159user_library_pins). واجهة برمجية جديدة:GET /api/home،GET /api/libraries/summary،GET /api/libraries/{id}/hub،GET/PUT /api/me/pinned-libraries؛ وكل صف وعدد يقتصر على مكتبات صاحب الطلب.إدارة المكتبات (
/admin/libraries، للمشرفين): كل مكتبة مع مجلداتها (وهل يصل إليها هذا الحاسوب)، وأعدادها، وآخر فحص لها، وفحص مباشر؛ أضف مكتبة باختيار مجلد من متصفح يستعرض قرص الخادم؛ أعد التسمية، وغيّر المجلدات، وأفرغ سلة المهملات (الفيديوهات التي اختفت ملفاتها)، وادمج في مكتبة أخرى، واحذف (لا تُمس الملفات على القرص أبدًا)، ويُؤكَّد كل ذلك مع بيان ما سيشمله. والمجلدات التي تتشاركها مكتبتان تُعرض مع دمج بنقرة واحدة في المكتبة التي تحوي الفيديوهات. واجهة برمجية جديدة:GET /api/libraries/manage،GET /api/libraries/folders،GET /api/libraries/{id}/impact،POST /api/libraries/{id}/merge،POST /api/libraries/{id}/empty-trash.الإعدادات ← التخزين والاكتشاف (للمشرفين): خريطة المسارات، ومجلدات الوسائط، والاكتشاف على الشبكة المحلية، وعناوين الاستماع والعناوين العامة التي كانت في
server.tomlوحده، تُفحص قبل حفظها، ويكتبها الخادم (مع إبقاء كل مفتاح آخر)، وتُطبَّق بإعادة تشغيل تبدأ من الصفحة؛ ويعرض جدول كل مجلد مكتبة وأين يجده هذا الحاسوب. واجهة برمجية جديدة:GET/PUT /api/server/settings،POST /api/server/restart(الخانة 0160: فئة التدقيقserver)؛ وتكتسب واجهة الإدارة المحلية (loopback) للمشرف على العمليات/config/.تحليلات الاستخدام، باختيارك. يسأل تطبيق الويب مرة واحدة لكل جهاز: «ساعد في تحسين مكتبة»، ويعرض ما يُشارَك (إصدار التطبيق ونظام التشغيل واللغة، وحجم المكتبة على شكل نطاق، ومدى استخدام كل قسم، وتقارير الأخطاء بلا مسارات) وما لا يُشارَك أبدًا (أسماء الملفات، والنصوص المفرّغة، وعمليات البحث، وهويتك، وعنوان IP الخاص بك). لا يُرسل شيء قبل الموافقة. في الإعدادات ← الخصوصية المفتاح وخيار «انسَ جهازي» الذي يحذف كل ما أرسله الجهاز. يخزّن الخادم الأحداث (الخانة 0156) تحت اسم مستعار عشوائي للجهاز فقط: تُرفض الأنواع غير المعروفة، وتُسقط الحقول غير المعروفة، وتُزال جذور المكتبات والمجلدات المنزلية، وبحد 1,000 حدث في الدقيقة لكل IP، ولمدة 90 يومًا (و30 لمقاييس Web Vitals). ومع
[telemetry] enabled = "off"في server.toml لا يُخزَّن شيء.للخادم المنزلي اسم. يسمّي المشرف الخادم المنزلي في الإعدادات ← الحساب (إن تُرك فارغًا: اسم الحاسوب، وهو الافتراضي). يعرض تطبيق Apple لكل مستخدم مسجّل الدخول هذا الاسم في قائمة الخوادم المنزلية وفي العنوان بدل العنوان الذي كُتب. ترفض الواجهة البرمجية إعادة التسمية من أي أحد غير المشرف، وتعطي الاسم لكل مستخدم مسجّل الدخول في
GET /api/auth/me(home_name).نتائج البحث لكل فيديو. تعرض صفحة البحث قائمتين، «في العناوين» و«في النصوص المفرّغة»، بمدخل واحد لكل فيديو بلا تكرار. يعرض تطابق النص المفرّغ ملصق الفيديو وعنوانه ومكتبته ومدته، والعدد الحقيقي للمقاطع المطابقة في النص المفرّغ النشط (لم يعد محدودًا بـ50 مقطعًا)، وأفضل مقاطعه مع طوابع زمنية تفتح المشغّل عندها، و«اعرض الكل» للباقي، وشارة «في العنوان أيضًا». تُرتَّب الفيديوهات حسب أفضل تطابق لها، ثم حسب عدد المقاطع المطابقة. للقائمتين عرض قائمة / شبكة (يُتذكَّر)، وأرقام صفحات خاصة بكل منهما في العنوان، وصفحة تطابقات الفيديو (
/search/video/{id}) تشغّل الفيديو مع علامة على شريط التقدّم عند كل تطابق، والتطابق السابق / التالي، وكل التطابقات بترتيبها الزمني. تُحفظ النتائج المرتّبة للبحث تحتsearch_id(الخانة 0148search_runs) مدة 15 دقيقة بعد آخر استخدام لها، فلا يتغير ترتيب الصفحات أبدًا؛ وبعد ذلك يُعاد البحث مع إشعار. تعرض عمليات البحث الأخيرة آخر 20 عملية بحث لكل مستخدم (سارية أو منتهية، مع حذف واحدة أو الكل). واجهة برمجية جديدة:GET /api/search/results/{sid}،GET /api/search/passages،GET/DELETE /api/search/recent؛ ويعيدPOST /api/searchالحقلtranscript_videosوالصفحات الأولى (ويبقى حقله المسطّحhitsلإصدار واحد، مُهملًا). القواعدrules.search(المقاطع لكل فيديو، وأحجام الصفحات، ومدة حفظ النتائج، وعدد عمليات البحث الأخيرة المحفوظة، وأقصى عدد فيديوهات في القائمة، وقيمة K العليا للبحث بالمعنى).كل جهاز كامل يحتفظ بنسخة حيّة من قاعدة بيانات خادمه المنزلي، ويعمل عليها أثناء غياب الخادم المنزلي (الملحمة 37،
specs/epics/37-node-replica). لا شيء لإعداده: الجهاز الذي ينضم، أو الذي انضم من قبل (عند أول تشغيل له على هذا الإصدار)، ينشئ Postgres الخاص به (بالإصدار الرئيسي نفسه للخادم المنزلي: إصدار موجود لديه، أو بناء PostgreSQL 16.15 المثبّت، يُجلب ويُتحقق منه مرة واحدة)، ويُرحّله، ويشترك به، عبر النسخ المتماثل المنطقي فوق TLS مع التحقق من شهادة الخادم المنزلي، في المنشور الذي يحتفظ به الخادم المنزلي له: كل جدول ما عدا الأسرار، وصفوف الأجهزة الأخرى، والحالة الخاصة بكل قاعدة بيانات. ينشئ الخادم المنزلي بنفسه دور النسخ المتماثل للجهاز ومنشوره وفتحته، ويحذف فتحة الجهاز الذي يتأخر كثيرًا (rules.cluster.replica_wal_keep_gb، والافتراضي 20 GB)؛ فيشترك الجهاز من جديد من تلقاء نفسه حينها، وكذلك حين تكون قاعدة بيانات الخادم المنزلي قاعدة أخرى، أو حين تُحدَّث مكتبة على الجهاز نفسه، أو حين يتكرر فشل تطبيق تغييرات الخادم المنزلي، وينتظر ما دام مخطط الخادم المنزلي أحدث من مخططه. كل Postgres مضمّن يعمل بـwal_level=logical. يقرأ الجهاز المكتبة من نسخته ما دامت النسخة متأخرة عن الخادم المنزلي بما لا يتجاوزcopy_reads_max_lag_seconds(الافتراضي 30). ومع غياب الخادم المنزلي (رافضًا الاتصال، أو مطفأً تاركًا الاتصالات معلّقة) يواصل العمل من تلك النسخة: تُقرأ الصفحات، وتُشغَّل الفيديوهات التي يمكنه قراءتها، وتُبث قنواته التلفزيونية، وإعادة تحميل الصفحة تُبقي الناس مسجّلي الدخول، وتسجيل الدخول يعمل (يُتحقق منه على النسخة، وصالح على ذلك الجهاز وحده؛offline_session_hours، والافتراضي 12، وينتهي بعدoffline_session_grace_minutes، والافتراضي 30، من عودة الخادم المنزلي)، وتسجيل الخروج يعمل (تنتهي الجلسة هناك فورًا؛ ويُبلَّغ الخادم المنزلي حين يستجيب). تُرفض التغييرات حتى يعود الخادم المنزلي. والنسخة التي كانت قد توقفت عن المتابعة قبل الانقطاع بوقت طويل لا تستجيب (offline_copy_max_lag_minutes، والافتراضي 10). تظهر حالة النسخة على بطاقة كل جهاز في صفحة الأجهزة وفي إشعار الانقطاع الخاص بالجهاز. إلغاء ترخيص جهاز ينهي اشتراكه ويمسح نسخته. يتوقف خط معالجة العقدة عن المطالبة بالمهام ما دامت قاعدة بيانات الخادم المنزلي لا تستجيب، ويوقف المهام الجارية قبل انقضاء مهلة مطالبتها (محسوبة من آخر نبضة استجاب لها الخادم المنزلي)، ويستأنف من تلقاء نفسه؛ والمهمة التي سلّمها الخادم المنزلي لجهاز آخر في الأثناء لا ينهيها الجهاز الأول أبدًا. الترحيل 0147.يمكن نقل الخادم المنزلي إلى جهاز آخر (الملحمة 37، «تبديل الخادم المنزلي»): «اجعل B الخادم المنزلي» في تبويب الأجهزة، ثم لا شيء يُفعل يدويًا. يوقف الخادم المنزلي المهام وتغييرات قاعدة بياناته مؤقتًا، ويسلّم الجهاز ما يستثنيه النسخ المتماثل (الأسرار ومفتاحها، والأجهزة وأدوار قواعد بياناتها مع كلمات مرورها، ومفتاح العنقود، وشهادة CA لقاعدة البيانات، ورمز Hugging Face، ومفاتيح JWT الخاصة بالمشغّل) مختومة لذلك الجهاز عبر قناة الأجهزة، فيتولى الجهاز المهمة دون نسخ قاعدة البيانات من جديد: يتبعه كل جهاز آخر من حيث وصلت نسخته، ويصبح الخادم المنزلي القديم أحد أجهزته، والجهاز الذي كان مطفأً يتبعه حين يعود (أي جهاز في العنقود يسلّمه إشعارًا مختومًا له). لا يكتب خادمان منزليان معًا أبدًا: فما إن تُطلب الترقية حتى يبقى الخادم المنزلي القديم للقراءة فقط إلى أن يعلن الخادم المنزلي الجديد أنه الخادم المنزلي أو يلغي التبديل، وكذلك حين تضيع إجابته (تفقد أدوار قاعدة بيانات تطبيقه صلاحيات الكتابة على جداوله، ولا يتجاوز ذلك وضعُ «قراءة وكتابة» الخاص بأي جلسة، ولا يحق لها تغيير مخططه ولا إعادة تلك الصلاحيات، وتُستثنى أدوار الأجهزة)؛ ويُكتب طلب الترقية على القرص قبل إرساله؛ ولا يجري إلا تبديل واحد في كل مرة؛ ويُستأنف التبديل بعد إعادة تشغيل الخادم المنزلي القديم، ويُعاد طلب فكّ التجميد الذي فشل، وينهي فكّ التجميد جلسات التطبيق التي فُتحت أثناء التجميد (كانت تحتفظ بوضع القراءة فقط افتراضيًا، فكانت الكتابة تفشل حتى نصف ساعة بعد تبديل أُلغي)؛ والترقية معاملة واحدة (فالفاشلة منها تترك النسخة تتبع الخادم المنزلي القديم)، ولا تقبل إلا نسخة فيها كل الجداول؛ ولا يتخلى الخادم المنزلي القديم عن بياناته إلا حين يتلقى تغييرات الخادم المنزلي الجديد، وعندها لا يحتفظ بأي من مفاتيح الخادم المنزلي (ولا بشهادة قاعدة بياناته). يستطيع المشرف إلغاء التبديل من التبويب: قبل الترقية فورًا، وبعدها فقط إن لم يكن الجهاز المتولي قد ترقّى (أو، إن لم يستجب، بكتابة اسمه: فيظل الخادم المنزلي القديم يسأله، ويترقب إشعاره، وإن تبيّن أنه الخادم المنزلي في النهاية توقف عن الكتابة؛ والترقية الملغاة لا تُستكمل أبدًا، وتُزال حزمتها، حتى حين لم يصلها الإلغاء؛ ويبدأ التبديل التالي بعد ولايتها). ينتقل البث التلفزيوني المباشر في Plex أيضًا (يُوجَّه الموالف ودليله إلى الخادم المنزلي الجديد، مع إبقاء مسجّل DVR في Plex وجداول تسجيله وقنواته)، ويُفحص Plex وdizqueTV من الخادم المنزلي الجديد (والعنوان الذي كان عنوان الخادم المنزلي القديم نفسه يُنقل إلى عنوانه على الشبكة)، وتنتقل إشعارات Cloudflare مع مُرحِّل السحابة، ويعرض التبويب كل جهاز وكل خدمة خارجية على أنها نُقلت، أو قيد النقل، أو فشلت، أو ما يلزم أن يفعله شخص (تسجيل الدخول إلى YouTube في متصفح على الخادم المنزلي الجديد؛ وبيئة تشغيل نماذج هناك، حيث كانت المكتبة تستخدم واحدة). والتبديل الذي لا يمكن إتمامه يُتراجع عنه. يحمل
GET /api/cluster/selfولاية العنقود.يمكن لخادم منزلي احتياطي أن يتولى المهمة حين يغيب الخادم المنزلي نهائيًا (الملحمة 37، «الترقية الطارئة»). في تبويب الأجهزة يعلّم المشرف أجهزة كاملة على أنها خوادم منزلية احتياطية. يحتفظ كل منها بأسرار الخادم المنزلي ومفاتيحه مختومة له وحده. يعيد الخادم المنزلي إرسالها خلال دقيقتين من أي تغيير، ويزيلها حين تُزال العلامة. ومع عدم استجابة الخادم المنزلي، يجعل مشرفُ الخادم الاحتياطي جهازَه الخادمَ المنزلي من صفحة الأجهزة على ذلك الجهاز نفسه، بعد تحذير وكتابة اسم الخادم المنزلي للتأكيد: ولا يكون ذلك إلا من نسخة كاملة كانت متأخرة عن الخادم المنزلي بما لا يتجاوز 10 دقائق حين استجاب آخر مرة، وبعد أن يطالب الخادم الاحتياطي بالولاية لدى الأجهزة الأخرى، فلا يتولى خادمان احتياطيان المهمة معًا أبدًا (وإن انقطع اثنان عن بعضهما وأخذ كلاهما الولاية نفسها، يتنحى صاحب معرّف الجهاز الأصغر، كما يفعل الخادم المنزلي القديم، ويتبع كل جهاز الآخر). تجد الأجهزة الأخرى الخادم المنزلي الجديد لدى الخوادم الاحتياطية بنفسها وتتبعه. وإن عاد الخادم المنزلي القديم، يتوقف عن الكتابة خلال دقيقة ويصبح أحد أجهزة الخادم المنزلي الجديد. ما كتبه بعد التبديل يُحفظ جانبًا ويُبلَّغ به الخادم المنزلي الجديد، حيث ينزّله المشرف أو يراجعه ويطبّق ما لا يتعارض، في معاملة واحدة. ولا يُطبَّق تلقائيًا أبدًا: المعاملة التي تمس صفًا غيّره الخادم المنزلي الجديد منذئذ، أو الأسرار أو بيانات العنقود الخاصة، أو بيانات اعتماد (جلسة، أو رمز، أو رابط إعادة تعيين، أو إقران، أو كلمة مرور تغيّرت)، أو معاملة كانت ستفشل. يحمل التقرير حتى 100,000 تغيير (ويبقى ما يزيد في قاعدة بيانات الخادم المنزلي القديم، التي يحفظها جانبًا). ويُسجَّل ما طُبّق. ولكل ختم من حزمة الخادم الاحتياطي مفتاحه الخاص. الترحيل 0147.
وكيل مُرحِّل السحابة ينتقل مع الخادم المنزلي. يشغّل المشرف على العمليات في الخادم المنزلي
maktaba-cloudlinkما دام[cloud]مفعّلًا. يوقفه التبديل على الخادم المنزلي القديم، وينقل بيانات اعتماده، ويشغّله على الجديد. ويُشحن مع تطبيقي Mac وLinux.بعد التبديل، قائمة مراجعة على الخادم المنزلي الجديد: ما يحتاج شخصًا هناك (YouTube في متصفح، وبيئة تشغيل نماذج حيث كانت المكتبة تستخدم واحدة، وموالف بث تلفزيوني مباشر يُضبط يدويًا، وعنوان إشعارات Cloudflare على خادم منزلي بلا ترحيل سحابي) يبعد نقرة واحدة عن مكان إنجازه في مركز التحكم، ويُؤشَّر عليه يدويًا، أو تلقائيًا حيث تستطيع مكتبة التحقق (بيئة تشغيل نماذج تستجيب، أو إشعار يصل).
لا تصل الأجهزة من قاعدة بيانات الخادم المنزلي إلا إلى ما تحتاجه (الملحمة 37). يستمع Postgres المضمّن خارج جهازه، ويسمح بدخول أي أحد من خارجه، فقط ما دام هناك جهاز يحق له الاتصال (جهاز مُعتمَد، أو باب الانضمام مفتوح). ويعيد تشغيل نفسه حين يتغير ذلك. وحتى حينها لا يسمح إلا بأدوار الأجهزة، من الشبكات التي يوجد عليها الخادم المنزلي وعنوان كل جهاز معتمد (ويمكن أن يسمّي
[postgres] machines_fromشبكات بدلًا من ذلك)، ولا يسمح أبدًا بالنسخ المتماثل الفيزيائي. يتحقق كل جهاز من قاعدة بيانات الخادم المنزلي بشهادة CA الخاصة بها. تأتي شهادة CA مع الانضمام، أو عبر قناة الأجهزة (للجهاز الذي انضم سابقًا)، أو مع التبديل؛ والجهاز الذي لا يملكها لا يوصل نسخته. كلمة مرور النسخ المتماثل للجهاز لا تغادره أبدًا (يحصل الخادم المنزلي على محقِّق SCRAM الخاص بها)، ولا تحصل على نسخة إلا الأجهزة الكاملة، ولا تحفظ النسخة جلسات الويب إلا ببصمتها. لا يقرأ دور النسخ المتماثل للجهاز إلا ما نُشر له: لا تحوي بناءات Postgres الخاصة بمكتبة أي إضافة فك ترميز منطقي سوىpgoutput، ودوال الفتحات حكر على الخادم المنزلي، وينهي الخادم المنزلي ويحذف أي فتحة ليست لمكتبة خلال ثوانٍ (ويقول مرة واحدة ما يجب فعله بشأن فتحة لا يستطيع ذلك معها). والخادم المنزلي العامل على Postgres خاص بالمشغّل، والذي قد يحوي إضافات أخرى، لا يحتفظ بنسخ ما لم يفعّل المشرفrules.cluster.copies_on_operator_postgres؛ وحتى ذلك الحين تُبلَّغ الأجهزة بالسبب. تسجيل الدخول على جهاز أثناء غياب خادمه المنزلي يرفض المستخدم الذي يلزمه تغيير كلمة مروره. والجلسات التي يفتحها تُكتب في سجل الخادم المنزلي، مع إغلاقها، حتى بعد إعادة تشغيل الجهاز. ولا يُحتفظ بتسجيل الخروج هناك إلا لجلسة يعرفها الجهاز، كاملًا (يُحتفظ بتسجيل الخروج من كل مكان مع جلسته أو لا يُحتفظ به أبدًا)، ويُسلَّم على دفعات يأخذها الخادم المنزلي، ولا ينهي الخادم المنزلي إلا الجلسات التي أُنشئت عبر ذلك الجهاز؛ والشخص الذي سجّل الدخول على الجهاز فقط أثناء غياب الخادم المنزلي ثم سجّل الخروج من كل مكان يُسجَّل خروجه من كل جلسة على ذلك الجهاز، ويُبلَّغ بأن الجلسات الأخرى تنتهي عبر الخادم المنزلي (الذي لن يقبل ذلك منه)؛ وعمليات الإبطال التي يجريها الخادم المنزلي تصمد أمام إعادة تشغيل الجهاز أثناء الانقطاع، والشخص الذي سُجّل خروجه من كل مكان يستطيع تسجيل الدخول هناك من جديد ما إن تصل ذلك إلى نسخة الجهاز.صحة النظام تجيب عن سؤال "هل كل شيء على ما يرام، وإن لم يكن فما الخلل وماذا أفعل؟". تقرأ الصفحة (
/admin/health) نقطة الوصول الجديدة الخاصة بالمشرفGET /api/admin/system/status: كل جزء من مكتبة سليم أو متراجع أو متوقف مع سبب واضح، ووقت فحصه، ورابط "اعرف المزيد" إلى المساعدة. الخدمات (الواجهة البرمجية مع إصدارها ومدة تشغيلها؛ وPostgres مع زمن استجابته، وهل هو أساسي أم نسخة متماثلة، ونسخه المتماثلة؛ وخدمة البث على عنوانَي HTTP وgRPC مع القنوات المعروضة على الهواء وجلسات المُرمِّز؛ وعنوان gRPC لخط المعالجة؛ والعمليات الخاضعة للإشراف)، والعمّال (كل عامل مع جهازه ودوره وآخر نبضة له وما يشغّله، وكل جهاز بث؛ والسجل القديم لجهاز يشغّل عاملًا أحدث هو تاريخ، وليس عطلًا أبدًا)، والاعتماديات (فحوص الملحمة 40 للنموذج، ولكل مجلد مشترك، وللقرص وللذاكرة المشتركة)، والتكاملات (Plex Live TV: قابل للوصول، مسجّل، قنواته متزامنة، آخر خطأ) والطوابير والتلفاز (ملخص الملحمة 40 للمهام الفاشلة / المنتظرة / التي تحتاج إليك، والقنوات المعروضة على الهواء، وتغيير التلفاز الذي ينتظر الموافقة، وخانة التلفاز المباشر، والموالف). يعرض شريط الحالة العامة: متوقف فقط عندما تتوقف الواجهة البرمجية، أو قاعدة البيانات، أو كل العمّال، أو البث بينما توجد قنوات على الهواء. تعمل كل الفحوص في الوقت نفسه، ولكل منها مهلته، وتُعاد استخدام الإجابة لبضع ثوانٍ؛ والعتبات هي قسم القواعد الجديدsystem_health. الحالة الحمراء تضع نقطة على "صحة النظام" في قائمة المشرف. مسبار الحياة العام/api/system/healthلم يتغيّر.إعداد تطبيق سطح المكتب يعمل من أوله إلى آخره (الملحمة 38، القصة 38.6؛ على Mac وWindows وLinux سواء). يكتسب
maktaba-server setup --jsonكل خطوة تشغّلها شاشات الإعداد:check(ما الذي سيعمل، والذاكرة والقرص)، وadmin(تُجزَّأ كلمة المرور أثناء كتابتها، بالأمر الجديدmaktaba-api hash-password؛ ولا ينتظر إلا ناتج التجزئة)، وlibraries(المجلدات وخريطة مسارات NAS، وأحرف الأقراص ومسارات UNC أيضًا)، وmodels(يُفحص رمز Hugging Face مقابل نموذج الاستماع الثاني المقيَّد الوصول، أو يُعثر على نسخة NAS؛ ويأتي التنزيل بعد بيئة venv، مع عرض التقدّم)، وplex(تسجيل الدخول برمز PIN من plex.tv)، وvenv(بيئة خط المعالجة، مع عرض التقدّم) وfinish(ملف server.toml، وإنشاء قاعدة البيانات وترحيلها، وزرع المشرف بالخيار الجديدadduser --password-hash-file، وتخزين خادم Plex بالأمر الجديدmaktaba-api plex-link). تقول الشاشات "هذا الحاسوب" و"شريط المهام" على Windows، وتعيد تفعيل مهمة تسجيل دخول معطّلة. تأتي كل نتيجة فحص وكل رفض مع رمز وقيمه، فتعرضه الشاشات بالإنجليزية أو بالعربية (ولا تضع أبدًا نص الخطوة الإنجليزي بجانبه)؛ ويفصلcheckما يمنع المتابعة عمّا يستحق المعرفة، ويقول متى سيجري Windows أو Linux التفريغ النصي على المعالج (لا توجد بطاقة رسوميات NVIDIA). يجب أن تكون مجلدات المكتبة وطرفا خريطة المسارات مسارات كاملة (Y:وحده هو جذر القرص)، ولا يجوز أن تحتوي كلمة المرور على فاصل أسطر، وتُعدّ كتابتان لمجلد واحد جذرًا واحدًا فقط حيث يتجاهل القرص حالة الأحرف. يطلب الإيقاف من بناء venv ومن تنزيل النماذج التوقف بدل قتلهما أثناء عمل pip.maktaba-server setupفي الطرفية يشغّل خطوات إعداد التطبيق (الملحمة 38، القصة 38.6): الفحوص نفسها، وقواعد المشرف، والمجلدات، وخريطة المسارات، وتسجيل الدخول إلى Plex، والإنهاء، إضافة إلى مكان البيانات وقاعدة البيانات (SQLite، التي ينشئهاfinishالآن، أو عنوان Postgres). أُزيل معالجه المنفصل، ومعه رمز المشرف الذي لم يقبله شيء قط؛ ويُقبل--migrateويُتجاهل (فالإعداد يجهّز قاعدة البيانات دائمًا).خادم تطبيق سطح المكتب يتولّى التحديث عند تشغيله التالي (الملحمة 38، القصة 38.12؛ على Mac وWindows وLinux سواء). قبل أن يعمل أي دور، يقرأ الخادم سجل الإصدار الذي تحمل قاعدة البيانات ترحيلاته (
state/database.json) ويرفض إصدارًا أقدم؛ ومع إصدار آخر (أو حين لا يوجد سجل بعد) يفرّغ قاعدة البيانات في ملف (backups/pre-<old>-to-<new>.dump، ويُحتفظ بأحدث[postgres] update_dumpsمنها، والافتراضي 3)، ويسجّل التحديث، ويشغّل الترحيلات مرة واحدة لكل الأدوار ويسجّل إتمامها. وحين تفشل يعيد التفريغ ويُبقي كل الأدوار متوقفة، ويقول ما حدث وما يجب تثبيته. ثم ينتظر عمّال خط المعالجة تمهيد venv (إلا حين يحددMAKTABA_PYTHONأو[pipeline] pythonنسخة Python الخاصة بهم)، ويعملون ببيئة خاصة بهم ويُوقفون بلطف (SIGTERM، وCTRL_BREAK على Windows) حتى لا يخلّف الإيقاف pip وراءه أبدًا. يُرفض Postgres من إصدار رئيسي يختلف عن إصدار قاعدة البيانات. ما تنتظره العملية يظهر في حقلهاdetailفي/processes. ولكل تفصيل من هذا النوع أيضًاdetail_codeمن قائمة ثابتة وdetail_paramsالخاصة به، لتعرضه شاشات التطبيق بلغتها.الواجهة البرمجية ترفض قاعدة بيانات رحّلها إصدار أحدث من مكتبة، في كل تثبيت: يخرج
maktaba-api migrate upبالرمز 3 بدل ألّا يجد شيئًا يطبّقه، ولا يبدأserveعليها. ويعملmigrate --timeout 0بلا حدّ زمني.تطبيق Windows ينقل إليه نسخة مكتبة التي يشغّلها الحاسوب أصلًا (الملحمة 38، القصة 38.13). يعثر الإعداد عليها (مهمة مجدولة تشغّل
maktaba-server serveمباشرة، أو عبر مشغّل مكتبة أو من سكربت؛ وعمّال مثبّت العقدة؛ وخادم شُغّل يدويًا؛ وخدمة Windows، يسمّيها لكنه لا يستطيع نقلها) ويعرض نقلها: تُظهر الخطة ما يُنقل وما يُنسخ، وترفض، قبل أن يتوقف أي شيء، مفتاحًا يحمله التطبيق أصلًا ويختلف، أو عقدة ليس ملف إعدادها هو الملف الذي يقرؤه التطبيق؛ وتقول ذلك حين يكون ملف إعداد العقدة في الملف الشخصي المتجوّل لحاسوب آخر ولم ينضمّ هذا الحاسوب بوصفه جهازًا مستقلًا (فالتطبيق على هذا الحاسوب ليس تلك العقدة)، وهي القاعدة نفسها التي يتبعهاserveوفحص الإعداد أيضًا. يبني النقل بيئة venv بينما لا يزال التثبيت القديم يعمل، ويحتفظ بتعريف مهمته، ويوقفه (وحلقة إعادة التشغيل في سكربته أيضًا، ولا يجوز أن يعود شيء منه لمدة أطول من تأخير الحلقة؛ ولا يمسّ أبدًا برامج التطبيق نفسه، أينما كان المجلد القديم) ويعطّل مهمته، وينقل إلى التطبيق إعداداته (العناوين، والوسائط، وخريطة المسارات، والتفريغ النصي، وإعداداتMAKTABA_*في السكربت)، ومفاتيحه، وفهرس البحث، والذاكرات المؤقتة، والنماذج، وقاعدة بيانات SQLite؛ ويبقىnode.jsonالخاص بالعقدة في مكانه، فهو ملف الجهاز الذي يقرؤه التطبيق (ولا يُنسخ أبدًا). وتُنسخ قواعد الجدار الناري التي تتيح للأجهزة الأخرى الوصول إلى برامج التثبيت القديم، داخل مجلده أو خارجه، لبرامج التطبيق نفسه (يسأل Windows مرة واحدة، قبل أن يتوقف أي شيء؛ وتذكر الخطة ذلك؛ وتظهر المطالبة أمام نافذة التطبيق، وتلك التي لا يُجاب عنها خلال ثلاث دقائق لا تغيّر شيئًا)؛ وتسمّي الخطة قاعدة لبرنامج ليس للتطبيق مثيل له، وتحذّر من قاعدة حظر على أحد برامج التطبيق (فالحظر يغلب السماح). لا تُزال المهمة القديمة إلا بعد أن يستمع خادم التطبيق خارج عنوان الاسترجاع حيث كانت الأجهزة الأخرى تصل إلى التثبيت القديم (0.0.0.0:8780مثلًا) وتكون القواعد مطبّقة على الشبكة المستخدمة. يمكن للنقل الذي توقف في منتصفه أن يُستكمل، أو أن يُستعاد منه التثبيت القديم من الشاشة، وكذلك النقل المكتمل (من شاشة الترحيب). يزيل خادم التطبيق المهمة القديمة بعد أن يعمل سليمًا؛ ويُحتفظ بالمجلد القديم، ويعيدmaktaba-server adopt --undoتشغيل التثبيت القديم (تُعطَّل مهمة التطبيق الخاصة بكل مستخدم، ومهمة هذا المستخدم بالاسم المشترك القديم؛ وتُزال قواعد الجدار الناري التي أضافها النقل، ولا تُمسّ القواعد القديمة أبدًا؛ ويفتح التطبيق إعداده، الذي يعرض النقل من جديد، ولا يشغّل خادمه أبدًا فوق التثبيت القديم). بجانب عقدة تابعة لمنزل يُرفض إعداد جديد (ويشرح الفحص والإنهاء السبب): إذ سيصبح التطبيق نسخة ثانية من تلك العقدة؛ وبجانب ملف إعداد عقدة لا يشغّله شيء يكون منزلًا جديدًا ([server] role = "home"). والتثبيت على المسارات الافتراضية من قبل الملحمة 38 تثبيت واحد سواء كان إعداده ومفاتيحه لا يزالان في%APPDATA%\Maktabaوحده أو نُسخا إلى مكان التطبيق، وتُقرأ حالته حيث يحفظها (مجلد بياناته الافتراضي). كل ما تقوله الخطة والنقل يأتي مع رموز، فتعرضه الشاشات بالإنجليزية أو بالعربية. يسردmaktaba-server adoptما يعثر عليه، ويسرد--plan <id>ما يفعله النقل. والإعداد[server] mdns = "off"جديد (مثلserve --no-mdns)، فيحتفظ التثبيت المنقول بذلك الخيار.التفريغ النصي على Windows وLinux (القصة 38.8). تشغّل مرحلة التفريغ هناك Whisper على CTranslate2 (النموذج
dropbox-dash/faster-whisper-large-v3-turboمن faster-whisper)، على دفعات عبر الملفات كما يفعل محرك MLX على Mac: على بطاقة رسوميات NVIDIA حين يعمل CUDA (تأتي cuBLAS 12 مع الحزمة الإضافيةstt؛ دون حاجة إلى CUDA toolkit)، وإلا فعلى المعالج. يحدد المشرف على العمليات عدد العمّال وحجم دفعاتهم من ذاكرة بطاقة الرسوميات (nvidia-smi؛ البطاقة ذات 8 GB تحصل على عامل واحد)؛ ودون بطاقة رسوميات يتولّى العامل الرئيسي التفريغ. مفاتيح[transcription]جديدة:device، وcompute_type، وbatch، وcpu_threads. تسجّل النصوص المفرّغة محرّكها (ct2-batched)؛ ويبقى الاستماع الثاني على Mac حاليًا، فما دام مفعّلًا يترك جهاز Windows أو Linux التفريغ النصي للأجهزة التي تشغّله، إلا إذا سمحت له القاعدة الجديدة في قسم التفريغ النصي، "التفريغ دون الاستماع الثاني"، بالتفريغ أيضًا؛ وعندئذ يمنح جهاز يشغّل الاستماع الثاني تلك النصوص استماعها الثاني لاحقًا. ويبقى استبعاد المشغّل للمراحل نافذًا دائمًا. بطاقة رسوميات بأقل من 4 GB، أو بطاقة ليس لديها ذاكرة فارغة كافية، تترك Whisper للمعالج، وكذلك فشل التحميل على بطاقة الرسوميات؛ ويتشارك العمّال الزائدون عن المخطط بطاقة الرسوميات أو المعالج؛ وتنزيل Whisper مثبّت على إيداع (commit) محدد وعلى قيم SHA-256 لملفاته؛ والحد الأدنى للذاكرة الفارغة يعمل على Windows. على RTX 2080 SUPER: أسرع من الزمن الحقيقي بـ 72 مرة لثمانية ملفات معًا، و31 مرة لملف واحد؛ و2-3 مرات على المعالج.الاستماع الثاني على Windows وLinux (القصة 38.9). يصحّح Cohere Transcribe Arabic كلمات Whisper هناك أيضًا، عبر transformers على بناء CUDA من torch على بطاقة NVIDIA التي يستخدمها Whisper، ثم يُعاد تهجئتها بقاموس CAMeL كما على Mac (صارت camel-tools تُثبَّت الآن على Windows وعلى Linux x86_64). لا يعمل إلا حيث تتسع بطاقة الرسوميات له بجانب Whisper (10 GB فأكثر على Windows، حيث يحجز التعريف جزءًا أكبر من البطاقة؛ و8 GB فأكثر على Linux) وتتوفر لديه المساحة الفارغة حين ينتهي ملف؛ وإلا يحتفظ النص المفرّغ بكلمات Whisper ويذكر السبب، كما يفعل جهاز Mac لا يملك النموذج، ولا يجلب
models downloadالنموذج المقيَّد الوصول ولا يطلب تسجيل الدخول إلى Hugging Face من أجله. ولا يعمل على المعالج أبدًا. وحين يكون مفعّلًا، يمنح المشرف على العمليات Whisper دفعة أصغر على بطاقة كهذه ليترك له مساحة. تسجّل النصوص المفرّغة ما استمع إليها (metadata.refine.runtime) وغياب القاموس (dictionary: "unavailable"). يُنشأ مجلد بيانات القاموس الآن قبل أن تنزّل CAMeL Tools إليه (كان مجلد نماذج جديد يُفشلها). لا يعمل إلا حيث تقدر بطاقة الرسوميات (ذاكرتها، وقدرة حوسبة 7.5، والتعريف R580، تُقرأ من التعريف دون استيراد torch) وحيث يمكن الحصول على نموذجه (ولا يمنح المشرف على العمليات Whisper الدفعة الأصغر إلا عندئذ)؛ ويُجلب نموذجه عند إيداع (commit) مثبّت ويُفحص ملفًا ملفًا؛ والتحميل الذي يفشل لا يُعاد قبل 10 دقائق، وتتضاعف المدة حتى 6 ساعات.بناء CUDA من torch على Windows، حيث تشغّل بطاقة الرسوميات الاستماع الثاني (القصة 38.9). لا يوفّر PyPI لنظام Windows إلا torch للمعالج (124 MB)، ويبقى هو الافتراضي؛ أما بناء CUDA (
torch 2.14.1+cu130من فهرس PyTorch نفسه، بحجم 2.0 GB؛ فيكبر venv من 2.7 إلى 5.0 GB) فيأتي مع مجموعة اعتماديات جديدةcuda، فقط على جهاز تشغّل بطاقة NVIDIA فيه الاستماع الثاني (9.5 GiB أو أكثر، وقدرة حوسبة 7.5 أو أكثر، والتعريف R580 أو أحدث، كما يقولnvidia-smi). يشحن الإصدار مجموعتَي التثبيت كلتيهما، وكل تثبيت مفحوص بالتجزئة (...-constraints.txt، الخاص بـ PyPI وحده، و...-constraints-cuda.txt، الذي يسمّي فهرس PyTorch:uv export --emit-index-url، لذا يتطلب بناء الإصدار uv 0.12 أو أحدث)، ويُذكران فيrelease.jsonوrelease.conf؛ ويختار تمهيد venv (--torch auto|cpu|cuda) وsetup --nodeوinstall-node.ps1(-Torch) إحداهما حسب بطاقة الرسوميات؛ وتحمل حزمة تطبيق Windows كلتيهما (pipeline\constraints-cuda.txt). ومع مجموعة cuda تبحث مثبّتات العقد عن كل تثبيت في الفهرسين كليهما، ومع الحزمة الإضافيةsttأوsearchتثبّت الإصدارات المثبّتة بأداة pip الخاصة بـ venv، التي تأخذ الملف الذي تسمّيه تجزئات التثبيت من أي من الفهرسين. يثبّت Linux وmacOS الحزم نفسها كما من قبل. يصدّر التكامل المستمر باستخدام uv 0.13.4. لم يعد مُضمِّن البحث الدلالي يذهب إلى بطاقة NVIDIA من تلقاء نفسه (لا يزالMAKTABA_EMBED_DEVICE=cudaيضعه هناك): فتحجيم بطاقة الرسوميات لا يترك له مساحة.تطبيق سطح المكتب يشغّل الخادم على Windows (الملحمة 38، القصة 38.3). حين ينتهي الإعداد على حاسوب Windows، يسجّل التطبيق مهمة تسجيل الدخول الخاصة بالمستخدم
\Maktaba Server <SID>(في جلسة المستخدم، فتعمل أقراص NAS المعيّنة؛ ولكل مستخدم للحاسوب مهمته). تشغّل المهمةMaktaba.exe --server-agent، الذي يبدأ المشغّل المضمَّن في وحدة تحكم بلا نافذة ويعيد تشغيله إن فشل؛ ومشغِّل مراقبة كل 5 دقائق يعيد تشغيل الوكيل إن توقف. تعمل أوامر الإيقاف المؤقت والاستئناف وإعادة التشغيل وإيقاف الخادم من شريط المهام عبر حدث إيقاف يراقبه المشغّل؛ ويستمر الإيقاف المؤقت حتى الاستئناف أو تسجيل الدخول التالي، والاستئناف مباشرة بعد إيقاف مؤقت ينتظر أن ينتهي الخادم من التوقف. الخادم الذي يعمل وكيله لكنه لا يجيب لمدة 3 دقائق يظهر بوصفه مشكلة مع السبب من سجله. يوقف التحديث الخادم قبل أن يستبدل المثبّت ملفاته، ويعيد تشغيله إن فشل التثبيت. يُحفظ وضع التطبيق لكل حاسوب (%LOCALAPPDATA%)، لا في الملف الشخصي المتجوّل. تبدأ خطوات الإعداد وschtasksدون نافذة وحدة تحكم، وتحصل خطافات المثبّت على--server-start، و--server-stopو--server-remove.شريط المهام يقول ما ينتظره الخادم (الملحمة 38، القصة 38.4). أثناء بدء الخادم، يعرض سطر الحالة في شريط المهام وتلميحه، وشاشة البدء في النافذة، ما يبلّغ الخادم أن عملية تنتظره أو سبب عجزها عن البدء: حقل
detailلكل عملية في/processes، الذي يملؤه الخادم بدءًا من القصة 38.12 (#194)؛ ويأتي أولًا السبب الخاص بالعملية الفاشلة. تعرض شاشة البدء ذلك بالإنجليزية أو بالعربية انطلاقًا من الرمز والقيم التي يرسلها الخادم مع التفصيل (كل رمز في قائمة القصة 38.12: خطوة قاعدة البيانات، وPostgres، وأدوات التفريغ النصي)، مع رسائل الخطأ الخاصة بالبرامج، ومسار السجل، ونص الخادم الإنجليزي تحت "التفاصيل"، ولا تقول إلا أن الخادم يبدأ حين لا يملك التطبيق كلمات لذلك الرمز أو لا يرسل الخادم رمزًا. يتحدث شريط المهام ونوافذ الخطأ في التطبيق بلغة واجهة النظام، الإنجليزية أو العربية؛ ولا يعرض شريط المهام تفصيل الخادم الإنجليزي إلا بالإنجليزية. على Windows وLinux تحمل أيقونة شريط المهام شارة حالة (أحمر لمشكلة أو لخادم لا يجيب، وإلا بنفسجي لتحديث جاهز، وإلا أخضر للتشغيل، وكهرماني للبدء، ورمادي للإيقاف المؤقت أو التوقف؛ وتباين كل منها 3:1 أو أكثر على شريط مهام فاتح وداكن)، وعلى Windows تفتح النقرة اليسرى النافذة (والقائمة على الزر الأيمن).برامج Postgres لكل منصة، وPostgres على Windows. يمكن لمكتبة أن تجلب بناء Postgres 16.15 المثبّت لمنصة الجهاز (من theseus-rs؛ الحجم وSHA-256 مثبّتان، ويُفك ضغطه مع فحص المسارات)، ويشغّل المشرف على العمليات Postgres على Windows (برامجه
.exe، وإيقافًا سريعًا عبرpg_ctlبدل القتل، وpostgres.exeتحت رمز (token) مقيّد حين يعمل الخادم بصلاحيات المسؤول، كما تفعل خدمة Windows: إذ يرفض Postgres الصلاحيات الإدارية). وهذا أساس لتطبيقَي Windows وLinux ولنسخ العقد الكاملة من قاعدة بيانات المنزل (#178).تطبيق Windows يشغّل Postgres الخاص به (الملحمة 38، القصة 38.10). صار
[postgres] enabled = "auto"يفعّل Postgres المضمَّن داخل حزمة أي تطبيق سطح مكتب، لا على Mac وحده (ولا يزال[database] urlأو قاعدة بيانات SQLite قيد الاستخدام هو الغالب). تعثر أدوار التطبيق علىffmpeg.exeالموجود في الحزمة. على Windows لا يُستخدم إلا TCP عبر عنوان الاسترجاع، والتفريغات الليلية كما على Mac.مكتبة على جهاز Linux بلا سطح مكتب (الملحمة 39، القصة 39.6). يتحقق
install.sh --appمن الإصدار أولًا (بأداة cosign الموجودة على الجهاز حين يكون إصدارها 2.4.3 أو أحدث، وإلا بنسخة cosign مثبّتة يجلبها)، ثم يطرحmaktaba-server setupأسئلة تطبيق سطح المكتب في الطرفية، بالإنجليزية أو بالعربية: يُنسخ الخادم إلى~/.local/share/maktaba/runtime، ثم يعمل بوصفه خدمة systemd للمستخدم، تبدأ عند الإقلاع عبر linger. يبلّغ الفحص أيضًا عن glibc، ومدير مستخدم systemd، وlinger، وخدمة مكتبة يملكها تثبيت آخر؛ وحيث لا يمكن تشغيل الاستماع الثاني لا يُطلب تسجيل الدخول إلى Hugging Face. حين يكون ffmpeg الخاص بالتوزيعة مفقودًا أو قاصرًا، تعرض الطرفية تشغيل الأمر الذي يسمّيه الفحص (ويطلب sudo كلمة المرور)، ثم تعيد الفحص؛ ومن ملف إجابات تكتفي بعرضه. رمز Plex الذي تنتهي صلاحيته (يُطلب من جديد إذا تعذّر الوصول إلى plex.tv حتى ذلك الحين) أو الضغط على Ctrl+C أثناء الانتظار يترك Plex للإعدادات، ويمضي الإعداد. يقدّم--answers(أو--answers-fd) كل الإجابات دون أسئلة.تنزيلات تطبيق Linux (الملحمة 39، القصة 39.10). يبني كل إصدار التطبيق بصيغة AppImage و
.debو.rpmلمعماريتَي x86_64 وaarch64، وكل منها يضم الخادم كاملًا (مع Postgres وPython الخاصين به؛ أما ffmpeg فمن التوزيعة، وتعتمد عليه حزمتا.debو.rpm)، ويُفحص قبل نشر أي شيء؛ وتوقف حزمتا.debو.rpmخادم التطبيق عند إزالتهما. وللأجهزة بلا سطح مكتب يحمل الإصدار حزمة الخادم (maktaba-linux-payload-<version>-<arch>.tar.gz) وinstall.shضمن ملفاته، وكلاهما فيchecksums.txtالموقَّع: تقول الملاحظات أن تتحقق من السكربت ثم تشغّله، لا أن تمرّره مباشرة من فرع كما كان من قبل.تطبيق سطح المكتب يشغّل الخادم على Linux (الملحمة 39، القصة 39.8). حين ينتهي الإعداد، ينسخ التطبيق خادمه إلى
~/.local/share/maktaba/runtimeويشغّله بوصفه خدمة systemd للمستخدم، تبدأ عند الإقلاع عبر linger؛ وبعد تحديث التطبيق ينقل الخادم إلى الإصدار الجديد عند تشغيله التالي (والتطبيق الأقدم من الخادم يتركه كما هو ويقول ذلك). تعمل أوامر الإيقاف المؤقت والاستئناف وإيقاف الخادم من شريط المهام، أو من قائمة "الخادم" في النافذة حيث لا يعرض سطح المكتب شريط مهام؛ ويبدأ شريط المهام عند تسجيل الدخول حين يمكن عرضه. يتبع شريط المهام وقائمة "الخادم" ونوافذ التطبيق لغة الجلسة (الإنجليزية أو العربية).
تغيّر
- الجهاز الذي يفقد الاتصال بخادمه المنزلي يبقى صالحًا للاستخدام مع إشعار، بدل أن يُستبدل التطبيق
كله بصفحة «تعذّر الوصول إلى الخادم المنزلي» (تبقى تلك الصفحة للجهاز الذي لا تلائم شيفرته قاعدة بيانات
الخادم المنزلي، وللجهاز الذي لا يملك نسخة صالحة). يحلّ هذا محلّ إجابات تسجيل الدخول الأخيرة من الخادم
المنزلي التي كانت العقدة تحتفظ بها عشر دقائق: أُزيل
rules.cluster.offline_read_minutes. - يحفظ Postgres المضمّن في Maktaba.app شهادة TLS ومفتاحها في مجلد المفاتيح (لا في مجلد قاعدة البيانات الذي تُصنع منه نسخة الجهاز أبدًا).
- تطبيق Linux يشغّل ffmpeg الخاص بالتوزيعة (الملحمة 39). لم تعد حمولة خادم Linux تحمل ffmpeg
(فالبنيات التي كانت تثبّتها مُعدّة بـ
--enable-nonfree)، ويحذفpayload.shمن ذاكرته المؤقتة كل تنزيل لم يعدpins.envيثبّته. الـ ffmpeg الذي تشغّله كل الأدوار هو[media] ffmpeg/ffprobeفيserver.toml(جديد)، وإلاMAKTABA_FFMPEG/MAKTABA_FFPROBE، وإلا ذلك الخاص بتطبيق Mac أو Windows، وإلا الموجود في PATH، وتحصل كل الأدوار على النسخة نفسها. يبيّن فحص الإعداد (على Linux) والأمر الجديدmaktaba-server ffmpegوصفحة صحة النظام ما إذا كان يؤدي ما تحتاجه مكتبة (FFmpeg 4.4 أو أحدث مع libx264 وAAC والمرشّحات والصيغ التي يستخدمها التلفاز وخط المعالجة، وffprobe بجانبه، وترميز H.264/AAC عبرspeechnormيعمل)، وإن لم يكن كذلك، فالأمر الذي يثبّت النسخة الصحيحة للتوزيعة: apt، أو dnf مع RPM Fusion (فحزمةffmpeg-freeفي Fedora وEPEL لا تتضمّن libx264)، أو zypper مع Packman، أو pacman. ويحذّر كذلك عند غياب chromaprint (البصمات الصوتية) أو مصادر https://. - كل دور يتوقف توقفًا نظيفًا على Windows. يبدأ المشرف على العمليات كل عملية فرعية في مجموعة عمليات خاصة بها
ويطلب منها التوقف بـ CTRL_BREAK (تُفرغ أدوار Go أعمالها عنده كما تفعل مع SIGTERM، وخط المعالجة عند
SIGBREAK)؛ ولا تُقتل العملية الفرعية مع شجرة عملياتها إلا بعد انقضاء مهلة الإيقاف. يحتفظ Postgres بالإيقاف السريع عبرpg_ctl. أماserveبلا وحدة تحكّم (بدأه منفصلًا برنامج لا وحدة تحكّم له) فلا يستطيع إرسال CTRL_BREAK، فيقتل عملياته الفرعية كما من قبل ويذكر ذلك مرة واحدة في سجله؛ ولا تفتح عملياته الفرعية حينها نافذة وحدة تحكّم على سطح المكتب. تُجمَّد العملية الفرعية أثناء مطالبتها بالتوقف، فلا يضلّ CTRL_BREAK طريقه إلىserveنفسه؛ وكذلك تهيئة البيئة الافتراضية التي يبدؤها الخادم والإعداد، وتنزيل النماذج في الإعداد، حين يُطلب منها التوقف. - تثبيتات Windows تشير إلى تطبيق مكتبة (الملحمة 38، القصة 38.14). يصف قسم التثبيت في README وصفحة
النشر وملفات README للتحزيم وسطح المكتب تطبيق Windows (ملف NSIS واحد
-setup.exeبداخله الخادم، للمستخدم الحالي؛ بلا.msi)، ويذكر مثبّت العقدة (install-node.ps1) في مساعدته وملخصه أن التطبيق يشغّل عقدة كاملة وينقل إليه العامل المنضمّ. - يحفظ Windows كل شيء في
%LOCALAPPDATA%\Maktaba(الملحمة 38، القصة 38.1)، في التثبيت الجديد:server.tomlوkeys\وdata\وlogs\وnode.jsonالخاص بالعقدة، مع النماذج والذاكرات المؤقتة وPostgres المضمّن بجانبdata\. لا يضع التثبيت الجديد شيئًا في الملف الشخصي المتنقّل. - التثبيت السابق يحتفظ بملفاته الأصلية. ينسخ أول أمر
maktaba-serverتالٍ ملفserver.tomlوالمفاتيح إلى هناك من%APPDATA%\Maktaba(بوجودserver.tomlأو بدونه؛ ولا شيء عند ضبطMAKTABA_CONFIG)، وتُستخدم النسخ من ذلك الحين.- تبقى الأصول في
%APPDATA%\Maktaba، الذي يتنقّل مع الملف الشخصي المتنقّل: فالإصدارات الأقدم من التثبيت، وخط معالجة العقدة المشغَّل من نسخة مصدرية، ومثبّت العقدة لا تزال تقرؤها. ويحذفهاuninstall --purgeمع نسخها. - المفاتيح وكلمات المرور المنسوخة مقصورة على المستخدم، كما هو
node.json. - لا تُستبدل النسخة أبدًا. والنسخة التي تختلف عن أصلها تبقى، ويذكر الأمر ذلك مرة واحدة.
- تُكتب كل نسخة بجانب اسمها الجديد، ثم تُفرَغ إلى القرص وتُقارن وتوضع في مكانها دون استبدال أي شيء، فيعمل ملف شخصي متنقّل على قرص آخر أو على خادم، وتُعاد النسخة المبتورة في الأمر التالي.
- ينسخ أمر واحد في كل مرة: قفل في
%LOCALAPPDATA%\Maktaba، وينتظره الأمر الثاني.
- تبقى الأصول في
- لا يُنسخ
node.jsonأبدًا. للجهاز إعداد عقدة واحد: الجهاز الذي انضمّ من قبل يحتفظ به في%APPDATA%\Maktaba، حيث يستخدمه كل قارئ (مكتبة، والبث، وخط المعالجة، ومثبّت العقدة) وإعادة الانضمام. قد يتنقّل ذلك الملف الشخصي، لذا يسجّل الملف الحاسوب الذي ينتمي إليه (machine_id، أي MachineGuid في Windows). أول نسخة جديدة من مكتبة تعمل به على حاسوب تطالب به، مع الإبقاء على قائمة صلاحياته (ACL)؛ وخط المعالجة هو كاتبه الوحيد. الاستبدال الذي يفشل في منتصفه لا يُضيّع الملف أبدًا: إما أن يُعاد القديم، أو تأخذ النسخة الجديدة مكانه. وما لم يكتمل حتى بذلك (الملف القديم باسم احتياطي، والجديد ملفًا مؤقتًا) يبقى إعداد العقدة لكل قارئ، فيبقى الملف المتنقّل ملكًا لهذا الحاسوب وتبقى العقدة منضمّة؛ ويعيده التشغيل التالي دون أن يستبدل أبدًا ملفًا وضعته عملية كتابة في الأثناء. تحتفظ إعادة الانضمام به باسمnode.json.bakوتحذف البقية، ويذكره سكربت إلغاء تثبيت العقدة ضمن الملفات التي تُحذف لنسيان الخادم المنزلي. والمطالبة التي تتعذّر كتابتها تترك الملف كما كان وتذكر ذلك مرة واحدة. يُنشأ الملف المؤقت الذي يُكتب عبره إعداد العقدة خاصًا بالمستخدم وحده، دون أي مقبض آخر مفتوح عليه، وحين يتعذّر ذلك لا تُكتب بيانات الاعتماد أبدًا. الحاسوب الذي لا ينتمي إليه الملف يتجاوزه: فهو ليس جهازًا لأي خادم منزلي، ويكتب انضمامه الخاص%LOCALAPPDATA%\Maktaba\node.json، لا هوية الحاسوب الآخر أبدًا. يذكرmaktaba-serverذلك مرة واحدة عند بدئه، ويعرضه فحص الإعداد في التطبيق (node_config_other_pc). - الحالة الكبيرة تبقى في مكانها. تبقى نماذج التثبيت وذاكراته المؤقتة وPostgres في
data\. يُحسم مرة واحدة ما إذا كانdata\أو المجلد المجاور له هو جذر الحالة، ويُحفظ الجواب (state-root.json)، فلا يغيّره مجلد ينشئه إصدار أقدم لاحقًا. ولا شيء ينقل أو ينسخ مجلدًا قد يمسك به Postgres قيد التشغيل أو إصدار أقدم. - داخل تطبيق سطح المكتب لا يُنسخ شيء ولا يُنقل. لا ينسخ
maktaba-serverالموجود داخل حمولة التطبيق، على Windows أو Mac، تثبيتًا أقدم ولا ينقله أبدًا: فذلك لا يدخل التطبيق إلا عبر عملية النقل الخاصة بالتطبيق، التي تنسخه وتُبقي القديم للرجوع إليه. - يبقى
node.envالخاص بمثبّت العقدة حيث تقرؤه سكربتاته. - تقرأ الواجهة البرمجية وخط المعالجة، حين يعملان منفردين،
keys\secrets.key، أو القديم إلى أن يُنسخ. - على Windows يكتب خط المعالجة سجلاته بترميز UTF-8 (
PYTHONUTF8)، فلم يعد العنوان العربي يثيرUnicodeEncodeError.
أُصلح
التلفاز المُبقى على برمجيات البث القديمة يبقى عليها عبر تغييرات الجدول والإعدادات. حين فشل فحص التلفاز في تحديث وأُبقي التلفاز الحي على v0.3.39، كان خط المعالجة 0.3.40 يسجّل نسخته
0.3.40في كل إصدار للجدول: فكان التجهيز يشغّل الملف التنفيذي الجديد، وكان تبديل التغيير سيضع التلفاز المُبقى على البرمجيات التي فشل فحصها للتو. التغيير الذي لا يأتي بملف تنفيذي خاص به يُسجَّل ويُجهَّز ويُبدَّل الآن على برمجيات البث التي يشغّلها التلفاز الحي. لا ينتقل التلفاز إلى برمجيات بث جديدة إلا حين يجتاز تغيير في الملف التنفيذي فحصه: عبر «إعادة المحاولة» في التحديث، أو فحص إصدار لاحق. وخانة التجهيز التي يُطلب منها تشغيل إصدار ليس ملفه التنفيذي على الجهاز تُرفض فورًا، ويفشل تجهيز التغيير بذلك السبب بدل تشغيل برمجيات أخرى.لم يعد فحص تغيير جدول التلفاز يُفشل خانة تجهيز تشغّل البرمجيات الصحيحة. الإصدار الذي يبنيه خط المعالجة لتغيير جدول أو إعدادات يسجّل نسخة البث
0.3.40، وتُبلغ الخانة عنv0.3.40، وكان الفحص يقارنهما نصًّا: فشل تغيير التلفاز #51 (2026-10-28) بـ «لم تصبح خانة التجهيز سليمة على الإصدار» ثلاث مرات والخانة تعمل على ذلك الملف التنفيذي نفسه. تُقارن النسخ الآن دونv. والإصدار الذي لا يسجّل ملفًا تنفيذيًا (خط المعالجة المشغَّل من نسخة مصدرية يسجّل0.0.1) يُجهِّز ملف البث التنفيذي لهذا التثبيت ويربطه بدل طلب ملف غير موجود.لم يعد فحص التلفاز في التحديث يُثقل الجهاز الذي يقيسه. فشل تغيير التلفاز #55 (0.3.39 ← 0.3.40، 2026-10-28) في channel.position وchannel.wallclock على القنوات الـ 28 كلها، متأخرة 38–50 ث عن الساعة الفعلية، فأُبقي التلفاز على 0.3.39. كان القياس صحيحًا وكان ترميز الإصدار مطابقًا للحي: تغيير الملف التنفيذي يغيّر كل قناة، وكانت ميزانية التجهيز هي الجهاز الهادئ كله، وكانت خانة التجهيز تبدأ القنوات الـ 28 دفعة واحدة. فصار خرجها يعمل بسرعة 0.2–0.7 من الزمن الحقيقي ويتأخر أكثر كل ثانية (−80 ث عند 100 ث في تشغيل لاحق)، فلم يكن أي وقت استقرار ليجتازه. تُفحص القنوات المتغيّرة الآن في نافذة تبدأ بـ 4، ولا تتضاعف إلا ما دامت كل محطة تجهيز على الساعة الفعلية، وتنتصف بمجرد أن يعمل خرج محطة أبطأ من الزمن الحقيقي، ولا تبلغ مجددًا الحجم الذي أثقل الجهاز. الفحوص لم تتغيّر: الإصدار الذي لا يستطيع المجاراة حتى منفردًا لا يزال يفشل.
لم يعد فحص التلفاز في التحديث يُفشل برمجيات البث الجديدة على Mac أثناء إحمائها. فشل فحص التجهيز لتغيير التلفاز #53 (0.3.37 ← 0.3.38، 2026-10-28) بعد 27 ث وأعاد التحديث إلى الوراء، لثلاثة أسباب ليس أيّ منها الإصدار: قنوات خانة التجهيز، التي بدأت كلها دفعة واحدة، فتحت مرمّزاتها متأخرة 7–15 ث وحُكم عليها بعد 10 ث من أول مقطع، في حين يعوّض ffmpeg 7.1+ (
-re) مثل هذا التأخر بسرعة 1.05× فقط (أربع دقائق)؛ ووضع VideoToolbox إطارًا مفتاحيًا كل 4 ث حيث تريد شبكة الوصل ذات الثانيتين (والفحص) إطارًا كل 2 ث؛ وحُسبت الفقرة البديلة التي تُعرض مكان برنامج ملفه مفقود على هذا الجهاز برنامجًا خاطئًا. الآن يلحق الدخل المتأخر بسرعة 2× (-readrate_catchup، حين يتوفر في ffmpeg)، ويحافظ VideoToolbox على فاصل الإطارات المفتاحية كما يفعل libx264 وNVENC، ويمنحchannel.wallclockوchannel.positionالقناة 30 ث من أول مقطع لتستقر (يحذّران ويوقفان فترة الاختبار في الأثناء، ولا يزالان يُفشلان القناة التي تبقى متأخرة)، وتذكر الخانة البرنامج الذي تحلّ الفقرة البديلة محلّه، فيحذّرchannel.blockمن الملف المفقود بدلًا من ذلك (ولا يزال يفشل حين يقرأ البث الحي ذلك الملف). ينتظر اختبار التجهيز الشامل للتلفاز التغيير الفاشل في المسودة، حيث يذهب الفشل منذ القصة 33.12.العقدة الكاملة تشغّل مجددًا مقاطع الفيديو التي تستطيع قراءتها بنفسها. خريطة مسارات العقدة مفاتيحها البادئة التي تخزّنها قاعدة البيانات (
{"/Volumes/Originals/": "O:/"}، مستنتجة من نقاط تركيب الخادم المنزلي ونقاط تركيب العقدة)، ويفتح دور البث فيها الملفات من خلالها، لكن فحص الواجهة البرمجية لما إذا كان هذا الجهاز يستطيع قراءة فيديو كان يحتفظ بمفاتيحsmb://فقط. فبدا كل فيديو غير مقروء على عقدة Windows تُركَّب فيها المشاركة علىO:، وذهبت كل جلسة إلى الخادم المنزلي بدلًا من ذلك، وتعثّر التشغيل هناك. تقرأ الواجهة البرمجية الآن خريطة العقدة كما يقرؤها دور البث. والتحويل الذي ينتهي قبل أن يكتب قوائم تشغيله يجيب عنها بـ 410، لا بـ 404 ينتظره المشغّل إلى الأبد: فيفتح الفيديو من جديد، أو يقول إنه لا يستطيع تشغيله.لصفحة الفيديو زر رجوع. لا تملك نوافذ تطبيقات سطح المكتب والتلفاز زر رجوع المتصفح: فصفحة الفيديو تعود الآن إلى المسلسل أو البحث أو القائمة التي فُتحت منها (الرابط الملصق: المكتبة).
«ساعد في تحسين مكتبة» يحتفظ بـ «ليس الآن» السابقة. منذ صارت بيانات الاستخدام تذهب إلى مكتبة لا إلى مشرف الخادم المنزلي، نُسي الجواب القديم ليُسأل عن الموافقة من جديد؛ ونُسي الرفض أيضًا، فعاد الإشعار لمن رفضوه من قبل. صار الرفض الآن يُنقل، بتاريخه الأصلي.
عقدة Windows تسجّل الدخول إلى قاعدة بيانات الخادم المنزلي مجددًا. منذ خرجت كلمات المرور من الروابط التي يتلقاها الدور (إلى ملف كلمات مرور بدلًا منها)، كانت الواجهة البرمجية ونسخة قاعدة البيانات على جهاز Windows تسجّلان الدخول بلا كلمة مرور: فمكتبة lib/pq قبل v1.11 تتجاوز ملف كلمات المرور الذي تعدّه مقروءًا للآخرين، وهذا على Windows كل ملف. فأجابت مقاطع الفيديو والمسلسلات ودليل التلفاز بـ «قاعدة بيانات الخادم المنزلي غير قابلة للوصول». صارت lib/pq بنسخة v1.11.2 في الواجهة البرمجية والخادم، ولم يعد
PGPASSWORDفي بيئة الخادم نفسه يستبدل كلمة مرور كل دور.جلسة الويب للمشاهد ترى الآن المكتبات الممنوحة له. لم يكن تسجيل الدخول في ملف تعريف ارتباط الجلسة يحمل أي منح للمكتبات، فلم يرَ المستخدم غير المشرف أي مكتبة على الإطلاق في تطبيق الويب (رموز التطبيقات كانت صحيحة).
مجلد المكتبة المُدخل كما تخزّنه قاعدة البيانات يُعثر عليه عبر خريطة المسارات. كانت إضافة
/Volumes/Originals/...على جهاز Mac يركّب المشاركة في مكان آخر تُرفض بـnot-found.الفهرسة للبحث أسرع مجددًا بنحو 3 إلى 5 أضعاف. يعيد Chroma افتراضيًا كتابة فهرسه كله على القرص كل 1000 متجه: وعلى متجهات جهاز Mac المنزلي البالغة 12 مليونًا يعني ذلك ملفًا بحجم 21 GB وخريطة معرّفات بحجم 1.2 GB، ثوانيَ في كل مرة، وكان ذلك يحصر عامل الفهرسة في نحو 120 متجهًا في الثانية (3–4 مهام فهرسة في الدقيقة). صارت المجموعة الآن تعيد الكتابة كل 10 000 (
MAKTABA_CHROMA_SYNC_THRESHOLD، يُضبط مرة واحدة على المجموعة؛ والقيمة 0 تُبقي قيمة Chroma الخاصة)، وقيس ذلك بنحو 600 في الثانية على نسخة من ذلك الفهرس. لم تعد مهمة الفهرسة تمسح المجموعة: تُحذف متجهات النصوص المفرّغة القديمة للفيديو بمعرّفاتها (من قاعدة البيانات)، لا بحذف على المجموعة كلها (نحو ثانية لكل منها)، ويقرأchapter_inferمتجهات الفيديو بالمعرّف (0.01 ث، لا 0.8 ث). يجري التضمين خارج قفل الكتابة لفهرس البحث، ويُستخدم الفهرس المفتوح مسبقًا دونه. تسجّل كل مهمة فهرسة أين ذهب وقتها (index_timings: التضمين، والكتابات، وانتظار قفل الكتابة، وقراءة قاعدة البيانات)، وفي ملخصها أيضًا.لم يعد إيقاف الخادم يُفسد فهرس البحث. توقف عامل الفهرسة و27 مهمة فهرسة قيد التنفيذ: لاحظ الإيقاف متأخرًا 9 ث (إذ انتظرت حلقة الحجز انقضاء استطلاعها)، وألغاها بعد 15 ث بينما استمرت كتابات Chroma في خيوط، ثم لم يخرج (إذ ينتظر Python تلك الخيوط) حتى قتله المشرف على العمليات في منتصف الكتابة. الآن تجري كل كتابة في فهرس البحث منفردة (واحدة في كل مرة لكل عملية) ولا تُبتر أبدًا: المهمة الملغاة أثناء إحداها تنتهي بمجرد كتابتها، وبمجرد توقف العامل لا تبدأ كتابة جديدة (والمهام المنتظرة لإحداها تنتهي فورًا، بوصفها مقاطَعة). تُنهي حلقة الحجز انتظارها لحظة وصول الإيقاف. بعد توقف بيئة تشغيله يغلق العامل عميل Chroma ويخرج دون الخيوط التي لا تزال تعمل. يحصل عامل الفهرسة على مهلة تفريغ 30 ث (
MAKTABA_PIPELINE_DRAIN_SEC) ومهلة إيقاف 45 ث خاصة به (Process.StopGrace، وكذلك لإعادة التشغيل وللعامل المعلّق)، ضمن الـ 60 ث الخاصة بالمشغّل.لم يعد فهرس البحث يفسد حين تتعطّل مشاركة الوسائط. كانت مرحلتا الفهرسة و
chapter_inferوالبحث الدلالي تعمل في عامل خط المعالجة الرئيسي، بجانب المراحل التي تقرأ مشاركة NAS؛ فالمشاركة التي تعطّلت تسبّبت في قتل ذلك العامل لعدم استجابته، وكتابة Chroma المبتورة في منتصفها تركت الفهرس غير صالح («Error constructing hnsw segment»؛ ثلاث مرات على جهاز Mac المنزلي). على الخادم المنزلي يشغّلها المشرف على العمليات الآن في عامل خط معالجة خاص بها (pipeline/index،MAKTABA_PIPELINE_STAGES=index,chapter_infer) لا يلمس المشاركة أبدًا (MAKTABA_PIPELINE_SHARE=off: بلا فحوص تخزين ولا استطلاعات للمشاركة ولا تحويل مسارات) ويخدم البحث الدلالي على منفذ gRPC خاص به (127.0.0.1:9093، والسجلات وفحص الحياة على 127.0.0.1:9104؛[server] pipeline_search_addr/pipeline_index_logs_addr)، الذي تسأله الواجهة البرمجية (MAKTABA_PIPELINE_SEARCH_ADDR). يترك العامل الرئيسي تلك المراحل له ولا يفتح Chroma (MAKTABA_PIPELINE_SEMANTIC=off): عملية واحدة لكل مجلد Chroma، كما من قبل. يُبقي[pipeline] index_worker = "off"التخطيط القديم؛ والمشغّل الذي يستبعدindexأو يسرد المراحل يدويًا (MAKTABA_PIPELINE_STAGES)، والعقدة، لا يحصلان على أي منها.لم يعد أول فيديو يُشغَّل بعد بدء مكتبة لأول مرة مباشرةً يُرفض. تصنع الواجهة البرمجية مفتاح توقيع البث للجهاز عند بدئها، وكان دور البث يبحث عنه كل ثانيتين: فرابط البث الموقَّع في الأثناء كان يُرفض («bad signature»)، وكذلك كل رابط بعد استبدال ملف المفتاح. الرابط الذي يسمّي مفتاحًا لم يقرأه دور البث بعد يعيد الآن قراءة ملف المفتاح فورًا (مرة كل 100 ms على الأكثر؛ والطلب الذي يجد تلك القراءة جارية ينتظرها)، ولا يزال المفتاح الذي حمله الملف المستبدَل يتحقق من الروابط التي وقّعها، ليوم واحد.
تحويلات ترميز الفيديو تعمل على معالج رسومات NVIDIA (ويُعثر عليها على Windows). كانت تحويلات الفيديو حسب الطلب (HLS وDASH، القصة 8.5) تمرّر
-hwaccel nvenc، وهو غير موجود في FFmpeg (فـ NVENC مرمِّز؛ وفكّ الترميز على NVIDIA هوcuda)، وتمرّر-preset veryfastالخاص بـ libx264 إلى كل مرمِّز، وهو ما يرفضه NVENC: فعلى جهاز بمعالج رسومات NVIDIA كان كل تحويل يفشل فورًا. الآن يفك معالج الرسومات ترميز مصادر H.264 وHEVC (cuda، وVideoToolbox على Mac؛ وAV1 والبقية على المعالج المركزي، إذ لا يستطيع معالج رسومات بلا فك ترميز AV1 الرجوع إلى غيره)، وكل درجة بعمق 8 بت و4:2:0 (المصدر ذو الـ 10 بت أيضًا: فمرمِّز H.264 في NVENC يرفض 10 بت، والمتصفحات نادرًا ما تشغّل High 10)، ويحصل كل مرمِّز على إعداده المسبق الخاص (p4لـ NVENC). تُحسب جلسات NVENC مع جلسات قنوات التلفاز، واحدة لكل درجة: معًا تبقى ضمن حدّ NVENC للقنوات (مع ما تمسكه خانات التلفاز)، ولا يمسك الفيديو حسب الطلب أكثر من نصفه، والدرجات التي لا يحصل لها على جلسة يرمّزها libx264. والتشغيل بالعتاد الذي يفشل خلال 30 ث يُعاد برمجيًا. يُختار المرمِّز بترميز اختباري قصير، على Windows أيضًا (كان برمجيًا دائمًا هناك)، ويُكتشف من جديد كل 5 دقائق ما دام مرمِّز رسومات مُدرج قد فشل، ويضيّقMAKTABA_HWACCEL_PREFER(auto،software،nvenc،qsv،videotoolbox) الاختيار؛ وتشغّل التحويلات الـ ffmpeg الذي تشغّله القنوات (MAKTABA_FFMPEG_BIN). صارت كل درجة من تحويل HLS نسخة مستقلة (v1،v2لم تُكتبا قط، فكانت الدرجات الأدنى في قائمة التشغيل الرئيسية تعيد 404)، ويُحوَّل المصدر الذي بلا صوت، وتحصل الجلسة التي تطلب الترميز البرمجي (force_software) عليه.محاولات الاستماع الثاني تُحسب من نهاية كل محاولة، ويعود النص المفرّغ المتوقف (متابعة لـ #221). كان الانتظار بين المحاولات يُحسب من وقت إدراج المحاولة في الطابور، فالمهمة التي انتظرت ساعات كانت تستنفده؛ وصار يُحسب الآن من نهاية المحاولة. النص المفرّغ الذي توقف عنده الاستماع الثاني يستعيد محاولاته تلقائيًا، مرة واحدة، حين يرى الخادم المنزلي نموذجًا للاستماع الثاني، أو معالج رسومات جهاز أو برنامج تشغيله أو حزمه، لأول مرة، وفي الإعدادات، التفريغ النصي، عدد المحاولات والانتظارات وجولة تُرفع لإعادة تجربتها كلها (جدول جديد
relisten_seenوأعمدة علىrelisten_attempts، الخانة 0146). المحاولة التي لم تجد متسعًا على معالج الرسومات لم تعد تُحسب (متابعة للقصة 38.9). لم تعد مقاطع الفيديو المحذوفة والمفقودة تُجرَّب، وتحتفظ ملاحظة التوقف بسببها مع مسارات Windows الطويلة.نسخة بيئة التشغيل على Linux ترتّب النسخ كما يرتّبها كل شيء آخر. كانت تحتفظ بقراءتها الخاصة لبنيات
git describe، فكانت البنية غير النظيفة لوسم ما تُرتَّب فوق بنية الإيداع التالي، وكانت البنية غير النظيفة تساوي النظيفة: صارت تستخدم الترتيب الواحد (v0.2.0<v0.2.0-dirty<v0.2.0-1-g…<v0.2.0-1-g…-dirty). ويضع ترتيب التخطيط ذاتي الإدارة كذلك النسخة التي ليست إصدارًا (dev) تحت كل إصدار، كما يفعل متحقق الإصدارات، ويقارن أسماء الإصدارات التمهيدية أيًّا كانت حالة أحرفها.[server] role = "home"(أوMAKTABA_ROLE=home) لا يشغّل أي دور بوصفه عقدة. كان المشرف على العمليات يكتفي بإسقاط متغيرات العقدة، في حين يقرأ البث وخط المعالجة إعداد العقدة بنفسيهما: فبجانبnode.jsonمنضمّ (على Windows ملف تثبيت آخر، عبر الرجوع إلى الملف الشخصي المتنقّل) كان البث يعمل بوصفه تلك العقدة. يُبلَّغ كل دور الآن بأنه الخادم المنزلي (MAKTABA_CLUSTER_ROLE،MAKTABA_TV_HOST_ROLE) ويُوجَّه إلى إعداد عقدة لا يُكتب أبدًا، ويشغّل الخادم المنزلي المفروض Postgres المضمّن وخانات التلفاز الخاصة به أيًّا كان ما فيnode.json.إعادة محاولات الاستماع الثاني تتباطأ ثم تتوقف (متابعة للقصة 38.8). كان الجهاز الذي يشغّل الاستماع الثاني يُدرج في الطابور النصوص المفرّغة العشرين الأقدم نفسها المصنوعة بدونه كل 10 دقائق، إلى الأبد، حين لا يستطيع منحها استماعها الثاني (ملف لا يستطيع قراءته، أو نموذج لا يُحمَّل)، فلم يحن دور الأحدث قط. تُسجَّل الآن محاولات كل فيديو (جدول جديد
relisten_attempts، الخانة 0145): بعد المحاولة ينتظر ساعة، تتضاعف حتى يوم، وتأتي مقاطع الفيديو غير المجرَّبة أولًا، وبعد 6 محاولات يتوقف ويذكر ذلك مرة واحدة، تحذيرًا في المعالجة، النشاط. والنص المفرّغ ذو الاستماع الثاني، أو تشغيل تفريغ جديد، يبدأ من جديد. يذكر عدم تطابق التجزئة في تنزيل النموذج التجزئات قبل المجلد، فلم يعد مسار Windows الطويل يبترها. تعرض صفحة الإعدادات قواعد التفريغ النصي بالعربية، ويُبقي فحص كل قسم من القواعد بالعربية عدا ما لم يُترجم بعد.maktaba-server setup(معالج سطر الأوامر) يُنشئ المشرف مجددًا. كان يستدعيadduserبالخيارين--username/--passwordغير الموجودين في الواجهة البرمجية؛ وصار يمرّر اسم المستخدم وملف كلمة مرور.يطلب الإعداد تسجيل الدخول إلى Hugging Face فقط حيث يعمل الاستماع الثاني. كان يطلبه كلما كان الاستماع الثاني مفعّلًا، فكان الحاسوب الذي بلا معالج رسومات NVIDIA، أو بمعالج صغير جدًا أو قديم جدًا، أو ببرنامج تشغيل أقدم من R580، يُضطر إلى إنشاء رمز وقبول ترخيص نموذج مقيّد لنموذج لا يعمل هناك أبدًا. صار
checkيبيّن ما إذا كان هذا الجهاز يستطيع تشغيله (ولماذا لا، بكلمات التطبيق)، ولم تعد خطوة النماذج تطلب الرمز هناك (لا يزال من الممكن اختيار نسخة NAS، أي نموذج نشره الخادم المنزلي)، ولا يأخذ التنزيل إلا Whisper ونموذج البحث. وتأتي قدرة الحوسبة لبطاقة الرسومات ونسخة برنامج تشغيلها مع اسمها وذاكرتها.يمكن تخطّي تسجيل الدخول إلى Plex في الإعداد الطرفي، ويصمد أمام لحظة بلا plex.tv. لم يكن Ctrl+C أثناء انتظار الرمز يتخطّى شيئًا: كان يوقف الإعداد كله قبل
finish، وكذلك استطلاع واحد لم يجب عنه plex.tv. صار Ctrl+C الآن يتخطّى تسجيل الدخول ويمضي الإعداد؛ وعدم استجابة plex.tv يُذكر مرة واحدة ويُعاد السؤال حتى ينتهي الرمز؛ وCtrl+C ثانٍ في موضع آخر يُنهي الإعداد حتى عند سؤال. صار لكل رمز خطأ تفشل به خطوة إعداد كلماته بالإنجليزية والعربية (ويحافظ اختبار على ذلك): رمز النموذج اللغوي، والطلب السيئ، والخطوة المجهولة، والإعدادات غير المقروءة، والفشل غير المتوقع.بعد إعداد التطبيق يعمل الخادم على Postgres المضمّن، مع عمّال التفريغ النصي. كان حفظ
server.tomlيكتب كل القيم الافتراضية، فكانfinishفي الإعداد يترك[database] urlمضبوطًا على ملف SQLite الافتراضي: فلم يشغّل البدء التالي Postgres المضمّن قط (فبقيت قاعدة البيانات التي أنشأها الإعداد دون استخدام، ولم يستطع خط المعالجة فتح قاعدة SQLite)، وكان[pipeline] instances = 0يوقف العمّال. صار الحفظ يكتب الرابط فقط حين يُختار، والعدد فقط حين يُضبط (بما في ذلكsetup --node --instances 0).خادم المستخدم يُبقي التحقق من التحديثات محدَّثًا. لم يكن
maktaba-serverيعطي الواجهة البرمجية والبث مجلد بيانات، فكانا يستخدمان/var/lib/maktaba: ففي تطبيقات سطح المكتب وكل تثبيت لكل مستخدم كان التحديث اليومي للجذر الموثوق في Sigstore يفشل (رُفض الإذن)، وبمجرد انتهاء صلاحية الجذر المدمج في الملف التنفيذي لم يعد «حدّث الآن» قادرًا على التحقق من إصدار. وكانت للصور المرفوعة للقنوات المشكلة نفسها. صار المشرف على العمليات يمرّرdata_dirالخاص به (MAKTABA_DATA_DIR)، والواجهة البرمجية أو خادم البث المشغَّل منفردًا لا يستخدم/var/lib/maktabaإلا بصلاحيات الجذر، وإلا فـ$XDG_DATA_HOME/maktabaللمستخدم.البنية المصنوعة من نسخة مصدرية ليست أبدًا «أقدم» من الإصدار الذي بُنيت منه. كانت نسخة
git describe(v0.2.0-12-gabc1234، نسخة بنية مأخوذة بعد وسم) تُرتَّب قبلv0.2.0، كما يُرتَّب الإصدار التمهيدي: فعرضmaktaba-server update، و«يتوفر تحديث» في الواجهة البرمجية، وصفحة التحديثات، والمجدولv0.2.0تحديثًا لها، ورفض المتحقق التحديث منها بوصفه تخفيضًا، وعدّها حدّ التخطيط ذاتي الإدارة وتحويل الإعداد تخفيضًا. صارت تُرتَّب بعد وسمها (بعدد إيداعاتها، والشجرة غير النظيفة بعد النظيفة:v0.2.0<v0.2.0-dirty<v0.2.0-1-g…) وقبل الإصدار التالي، وليست إصدارًا تمهيديًا حين لا يكون وسمها كذلك. ترتيب واحد في كل مكان: ترتيب متحقق الإصدارات، الذي يتبعه فهرس التحديثات وفحص التحديث في الواجهة البرمجية والتخطيط ذاتي الإدارة، ويطابقه فحص التثبيت في صفحة التحديثات.لا يرسل المشغّل CTRL_BREAK أبدًا إلى خادم خرج (Windows، الملحمة 38، القصة 38.2). يحتفظ بمقبض للخادم إلى أن يُحصد، ولا يطلب منه التوقف إلا ما دام يعمل، مع تعليق الخادم من ذلك الفحص حتى الإرسال كي لا يخرج بينهما: فـ CTRL_BREAK الذي يسمّي معرّف عملية لا يقود مجموعة عمليات (خادم خرج أثناء إيقاف المشغّل، أو أثناء إرجاعه إصدارًا انتهت مهلته) يذهب إلى وحدة التحكّم كلها وقد يُنهي المشغّل نفسه.
بنيات الإصدار لسطح المكتب وAndroid وAndroid TV. يأتي تطبيق سطح المكتب بمجموعة أيقونات (مؤقتة) (#152)، كانت حِزم macOS وWindows تفشل بدونها. وكانت بنية Linux تتوقف قبل ذلك: إذ لا يُترجَم
tauri-plugin-deep-link2.6.2 معrust-ini0.21.3، فيثبّت تطبيق سطح المكتبrust-iniعلى 0.21.1 في Linux (Cargo.tomlوCargo.lock). تعثر خطوة ملف.zipالمحمول لـ Windows على الملف التنفيذي بالاسم الذي تعطيه إياه أداة Tauri CLI (mainBinaryName، وإلا اسم ملف Cargo التنفيذي) بدل اسم مثبّت في الشيفرة، ويعطي وسم الإصدار التمهيدي مثلv0.3.0-rc.1ملف MSI النسخة الرقمية التي يتطلبها WiX. لم تعد مهام Android وAndroid TV تطلب من sdkmanager الحزمة المحذوفةtools.بنيات Android غير الموقّعة تبقى خارج الإصدار. بدون أسرار مخزن المفاتيح يُحتفظ بملفي APK وAAB مخرجاتٍ لسير العمل (باسم
-unsigned) ويحذّر التشغيل؛ ولا تُرفق بإصدار GitHub إلا البنيات الموقّعة، إذ لا يثبّت Android ملف APK غير موقّع.يُنشر المُرحِّل فقط حيث يُعدّ خادم VPS. لا يطرح دفع الوسم المُرحِّل إلا حين يكون متغيّر المستودع
RELAY_DEPLOYمساويًا لـtrueوتُبنى الصورة وتُدفع؛ وإلا فلا يُسجَّل نشرproduction.بنية إصدار صورة الويب. تبني صورة الويب حزمتها أصليًا بدل البناء تحت محاكاة arm64، حيث كان
pnpm installيتعلّق بعد مهلة الإصدار.البيئة الافتراضية لخط المعالجة على Windows: إعادة البناء أو الإصلاح تنتظر ملفًا مفتوحًا (القصة 38.7). كان حذف البيئة الافتراضية القديمة وكتابة
venv --upgradeفوقScripts\python.exeيفشلان تمامًا حين تفتح عملية أخرى ملفًا من البيئة الافتراضية: ماسح فيروسات أو مفهرس البحث، أو برنامج من البيئة الافتراضية لا يزال يخرج (WinError 5 / 32، Errno 13). صار كل منهما يُعاد لمدة تصل إلى 5 ث، كما كان ملف الحالة من قبل، ويُجعل الملف المقروء فقط قابلًا للكتابة قبل حذفه.لم يعد الإصدار التمهيدي يحرّك
:latestللصور. كان الوسم ذو اللاحقة (v0.3.0-rc.1) يدفع كل صور الخادم، وصورة المُرحِّل، بوسم:latestأيضًا، فكان تثبيت Docker الذي يتبع:latestسيأخذ مرشّح إصدار. صار:latestيتحرك الآن فقط معvX.Y.Zمجرّد، كما يفعل أحدث إصدار في GitHub، ولم يعد وسم الإصدار التمهيدي يطرح المُرحِّل إلى خادم VPS من تلقاء نفسه (لا يزال الإطلاق اليدوي بوسمه يفعل ذلك).تسجيل الخروج من كل مكان على جهاز لا تجيب نسخته، وفكّ تجميد متأخر (الملحمة 37، متابعة لـ #178). كان الشخص الذي سجّل دخوله على جهاز أثناء غياب منزله فقط، ثم سجّل خروجه من كل مكان في لحظة لم تستطع فيها نسخة الجهاز أن تقول هل سجّل دخوله عبره في المنزل، يُعامَل كأنه مسجّل هناك فقط: فلا يُبلَّغ المنزل أبدًا، وتقول صفحة تسجيل الدخول ذلك خطأً. الآن تنتهي جلسات الجهاز كما كانت، ويصل تسجيل الخروج إلى المنزل مع ذلك (فيُنهي الجلسات الأخرى إن كانت قد سُجّلت عبر ذلك الجهاز)، وتقول صفحة تسجيل الدخول إن تلك الجلسات لم تُفحص. ولم يعد فكّ تجميد مستحق من تبديل أُلغي أو فشل يجعل المنزل قابلًا للكتابة بينما يجمّده تبديل لاحق: فكّ تجميد تبديلٍ جاء بعده تبديل آخر لا يغيّر شيئًا، ويجري التجميد وفكّه واحدًا تلو الآخر.
لم تعد كتابات إعداد العقدة يلغي بعضها بعضًا على Windows، ويُحتفظ بملف node.json حفظه محرّر نصوص (القصة 38.1، متابعة لـ #231). صارت كتابات إعداد العقدة الواحد، وبدء التشغيل الذي يعيد ما تركته كتابة لم تكتمل، تجري واحدة تلو الأخرى (بقفل بجانبه،
.node.json.lock): فاستدعاءان لـ ReplaceFileW على ملف واحد في الوقت نفسه قد يعيدان node.json أقدم فوق أحدث. لا يعيد بدء التشغيل نسخة احتياطية إلا دون استبدال ملف موجود، وبعد النظر فيnode.jsonمجددًا فقط، ولا يعيد أبدًا نسخة احتياطية أقدم بينما تكون نسخة أحدث قيد الاستخدام. ملفnode.jsonالمحفوظ بعلامة UTF-8 BOM (Notepad وPowerShell 5.1) هو إعداد العقدة عند كل قارئ، والملف التالف الذي تستبدله نسخة احتياطية يُحتفظ به بجانبه (node.json.broken-…) ولا يُحذف أبدًا. وبدء التشغيل الذي لا يستطيع إنشاء القفل (مجلد لا يحق له الكتابة فيه، أو قرص ممتلئ) يترك البقايا ويقرأ متجاوزًا إياها، كما كان من قبل.صار تحويل ترميز الفيديو يتقاسم وحدة GPU مع التلفاز، ويُعرض HDR عرضًا صحيحًا (متابعة لإصلاح تحويل الترميز على NVIDIA). التلفاز أولًا: لا يأخذ تحويل الترميز جلسات NVENC إلا فوق ما تحتاجه القنوات (اثنتان لكل قناة على الهواء ولكل قناة يبقيها الاحتياطي الساخن دافئة، ومتسع لبدء قناة أخرى، حيةً ودافئة، سواء كانت خانات التلفاز مفعّلة أم لا، مع احتساب كل محرّك قنوات في العملية)، وبحد أقصى نصف سقف القنوات؛ وتُرمَّز بقية درجاته بـ libx264. وعند السقف الافتراضي 6 مع تفعيل خانات التلفاز، لا يحصل تحويل الترميز إذن على أي جلسة NVENC ما دامت قناة على الهواء (واثنتين حين لا تكون أي قناة على الهواء)، وتعمل درجاته على libx264: نحو ضعف الزمن الحقيقي لمحتوى 4K HDR على معالج i9، وقد لا يواكب مضيف أبطأ. يبقى التحويل إلى الاحتياطي أثناء تحويل الترميز دافئًا، وتجد القناة التي تبدأ أثناءه جلساتها؛ وفيما عدا ذلك، وحتى ينتهي تحويل الترميز، قد تعمل قناة على libx264 ويبقي الاحتياطي عددًا أقل دافئًا. ومع تفعيل خانات التلفاز تُحتسب تلك الجلسات لحظيًا: يخفض الباب الأمامي ميزانيات الخانات بقدرها قبل أن تؤخذ، ولا يُبقيها إلا حين تأخذ الخانة الحية ميزانيتها الجديدة وتترك تقارير الخانات الحديثة متسعًا، فتبقى جلسات الفيديو عند الطلب وجلسات الخانة الحية ضمن السقف (وتحتفظ قنوات الاحتياطي الدافئة، وخانة التجهيز الخاصة بإصدار أثناء التبديل، بجلساتها فوق ذلك، كما من دون تحويل ترميز)؛ والخانة التي بدأت للتو تلتزم بنصف سقفها حتى تصلها ميزانيتها. يلتزم فتح تحويل الترميز بمهلته (تأخذ مزامنة الجلسات مهلة الطلب)، والمصدر الذي يفشل فحص ألوانه يُسجَّل ويُرمَّز على أنه SDR. ويلتزم VideoToolbox بسقفه الخاص. ويُعاد اكتشاف مرمّز GPU فشل في ترميزه التجريبي بفترات انتظار متزايدة (المرمّز المشغول حتى يعمل، وغير الموجود في الجهاز بضع مرات). ويُحوَّل HDR (PQ أو HLG، وMPEG-TS أيضًا، والمصادر التي لا تسمّي إلا دالة النقل) إلى SDR بتعيين النغمات حين يتوفر لدى ffmpeg المرشّحان
zscaleوtonemap، بعد التحجيم إلى الدرجة العليا (فيُحوَّل ترميز 4K HDR بضعف الزمن الحقيقي بدل أن يقلّ عنه)، بمنحنى Möbius، وتحويل الترميز الذي يفشل تعيين نغماته يُعرض دون تعيين بدل ألا يُعرض إطلاقًا. لكل درجة إطار مفتاحي عند كل حدّ من 6 ثوانٍ، فتتحاذى مقاطعها مع مقاطع الدرجات الأخرى (ويصرّح بذلك DASH، بمجموعة AdaptationSet واحدة).صار maktaba-server يكتب node.json كما يكتبه خط المعالجة، وكل node.json ملكٌ لمستخدمه وحده، ولا يعدّ أي قارئ كتابةً جاريةً حالةَ "غير منضم" (القصة 38.1، متابعة لـ #178 و#239). كانت كتابات maktaba-server نفسه لإعداد العقدة (تبديل المنزل، أو تولّي الاحتياطي، أو شهادة CA لقاعدة بيانات المنزل) تعطي الملف قائمة ACL الخاصة بمجلده، فكان كل مستخدم مسجّل الدخول على قرص ثانٍ يستطيع قراءة المفتاح الخاص للعقدة وتغييره؛ وكانت تتجاهل قفل الكتابة الخاص بخط المعالجة، فقد يلغي أحدهما ما كتبه الآخر؛ وكانت تكتب نسخة كاملة قُرئت سابقًا، فتلغي ما أضافته كتابة أخرى في الأثناء؛ وكان ملف
node.jsonالمحفوظ بعلامة BOM يفقد ما لا يكتبه إلا خط المعالجة (وقت انضمام الجهاز). صارت الآن تمر عبر كاتب واحد يتبع كاتب خط المعالجة: قفله، وقراءة حديثة تحته لا تغيّر إلا الحقول التي تخصّها الكتابة (وتتوقف تاركةً الملف كما هو إن انضم الجهاز مجددًا في الأثناء)، وملف مؤقت ملكٌ للمستخدم وحده منذ لحظة وجوده، وReplaceFileW، وأسماء خط المعالجة لما تتركه كتابة لم تكتمل. المضمون الآن: بعد أي كتابة من أي من الكاتبَين، وبعد أول تحميل لخط المعالجة في أي عملية، يكونnode.jsonملكًا لمستخدمه وحده. والملف الذي تركه #178 بقائمة ACL الخاصة بمجلده، أو بأي قائمة أوسع، يُعطى قائمة DACL محمية تمنح مستخدمه ولا أحد غيره، وعلى Linux وmacOS يُضبط الملف الذي يستطيع الآخرون قراءته على الوضع 0600؛ والكتابة التي لا تستطيع جعله كذلك تفشل بدل أن تُبقي القائمة الأوسع. والملفnode.jsonالتالف المحفوظ جانبًا (node.json.broken-…)، والهوية التي يحفظها جانبًا تبديلُ المنزل أو إعادةُ الانضمام (node.json.was-node-…،node.json.bak: المفتاح الخاص القديم)، تُجعل كذلك ملكًا للمستخدم وحده، أيًّا كانت قائمة ACL للملف الذي كانته. وتُكتب سجلات التبديل وهوية الجهاز المحفوظة له بالطريقة نفسها، والهوية التي حفظها إصدار أقدم تُجعل ملكًا للمستخدم وحده حين تُقرأ، والملفات المؤقتة التي تركتها كتابتها حين توقفت عمليتها تُزال مع الكتابة التالية. ولا يعدّ أي قارئ (تحميل خط المعالجة، وتحميل maktaba-server، وفحوص الدور والعقدة في البث) ملفnode.jsonقيد الاستبدال، أو تبقيه عملية أخرى مفتوحًا للحظة، ملفًا مفقودًا: ما دام قفل الكتابة قد أُخذ في الدقيقة الأخيرة، أو بجانبه بقايا كتابة من الدقيقة الأخيرة، ينتظر حتى ثانيتين لتنتهي الكتابة، والكتابة التي ما زالت جارية بعد ذلك خطأ، وكذلك إعداد العقدة الذي لا يستطيع قراءته أو فك ترميزه: لا يكون أبدًا "غير منضم" ولا المنزلَ أبدًا، ويبقى المسار هو المسار المتجوّل بدل الرجوع إلى%LOCALAPPDATA%. يرفض البث البدء على مثل هذا الملف، ولا يبدأ serve شيئًا من المنزل على إعداد عقدة لا يستطيع قراءته: لا دور (الواجهة البرمجية، والبث، وخط المعالجة)، ولا عنقود Postgres المضمّن، ولا وكيل الجهاز، بدل أن يبدأها بوصفها المنزل على قاعدة بيانات هذا الجهاز نفسه. لذا فإن الجهاز الذي لم ينضم قط، بجانب ملف قفل لإعداد العقدة أُنشئ في الدقيقة الأخيرة (كتابة توقفت في منتصفها)، يتلقى الآن ذلك الخطأ لمدة تصل إلى تلك الدقيقة بدل أن يبدأ فورًا: يقول serve ذلك ويعيد الفحص كل 5 ثوانٍ، ويبدأ حين تنتهي الكتابة أو يبلغ عمر القفل دقيقة. والملف الذي يبقيه برنامج آخر مفتوحًا للحظة (برنامج مكافحة فيروسات، أو أداة نسخ احتياطي) يُنتظر بالطريقة نفسها. وتبديل المنزل الذي ينقلnode.jsonجانبًا يزيل أولًا كل بقايا الكتابة، ويفشل قبل أن يترك إعداد عقدة كاملًا قابلًا للقراءة، فلا يُقرأ الجهاز الذي صار المنزل بوصفه عقدة مجددًا أبدًا؛ ويُحفظ ملفnode.jsonالتالف هناك جانبًا باسمnode.json.broken-…. ويفك كل قارئ الترميز بالطريقة نفسها: UTF-8 بعلامة BOM أو دونها، وUTF-16 أو UTF-32 بعلامة (Out-Fileو>في PowerShell 5.1)، والنص الذي لا يُفك ترميزه بصرامة (ملف ANSI) تالف عندهم جميعًا. ويُزال ملفnode.jsonالتالف المحفوظ جانبًا بعد 30 يومًا وعند إعادة الانضمام (فقد يحوي جزءًا من بيانات اعتماد)، وقفل الكتابة والهوية التي حفظها تبديل جانبًا من بين الملفات التي يسردها سكربت إلغاء التثبيت لنسيان المنزل، وتعيد الكتابة وإعادة الانضمام المحاولة للحظة بينما يُبقي قارئnode.jsonمفتوحًا، وتوجد الهوية التي حفظها تبديل جانبًا بجانبnode.jsonالمتجوّل كما بجانب المحلي.صار تطبيق Linux يُبنى لوسم إصدار تجريبي. فشل إصدار سطح المكتب لـ
v0.3.0-rc.1في مساري Linux كليهما: لا يقبل rpm الرمز-في رقم الإصدار. صار ملف .deb للإصدار التجريبي الآن0.3.0~rc.1وملف .rpm هو0.3.0بالإصدار0.1.rc.1، وكلاهما يُرتَّب قبل0.3.0(كان0.3.0-rc.1في ملف .deb سيُرتَّب بعده، فيصير الإصدار النهائي تخفيضًا)؛ ويحتفظ التطبيق وملف AppImage الخاص به بـ0.3.0-rc.1، وهو رقم semver الذي يقارنه محدّثه مع latest.json، ويُوقَّع ملفا .deb و.rpm له. صارت بوابة تطبيق Linux في التكامل المستمر تبني إصدارًا تجريبيًا (v0.0.0-ci.N) في كل تشغيل، عبر السكربت نفسه. وكانت ملفات .deb و.rpm الخاصة بالخادم نفسه (nfpm) تحمل في داخلها0.3.0~rc.1أصلًا، و.rc.1في أسماء الملفات فقط، كما يسمّيها GitHub؛ وصارت بوابة المشغّل في التكامل المستمر تفحص هذا الشكل وترتيبه في dpkg وrpm قبل الإصدار.صار الفيديو المحوَّل ترميزه قابلًا للتنقل فيه والعرض حتى نهايته. كانت قائمة تشغيل تحويل ترميز الفيديو تحتفظ بآخر ستة مقاطع فقط (DASH: عشرة) وتحذف البقية، فكان تحويل الترميز السابق للمشغّل يحذف مقاطع قبل عرضها، ولم يكن ممكنًا التنقل إلى أي شيء خارج النافذة. صارت قوائم تشغيله الآن تسرد كل مقطع: HLS بقائمة تشغيل من نوع EVENT تنتهي بـ
#EXT-X-ENDLISTحين ينتهي تحويل الترميز، تبدأها المشغّلات من بدايتها (#EXT-X-START) ما لم تستأنف من موضع آخر؛ وDASH دون نافذة، وملف MPD الخاص به ديناميكي ما دام تحويل الترميز جاريًا (تبدأ المشغّلات من مقطعه الأول، لا قرب حافته الحية) وثابت من بعيد انتهائه، وكل مقطع متاح، فيعرض dash.js حتى النهاية ويتنقل إلى أي موضع (مع ملف MPD الذي يولّده ffmpeg نفسه كان المشغّل الذي بدأ يتوقف قبل النهاية: عند الثانية 42 من فيديو مدته 3 دقائق). وصارت جلسات DASH تُعرض أصلًا: لم يكن الموجّه يقدّم أيًّا من مقاطعها. يحتفظ المشغّل المتوقف مؤقتًا بجلسته (تُحتسب إعادة تحميل قائمة التشغيل ما دام تحويل الترميز جاريًا)، والمشغّل الذي لا يجلب شيئًا لمدة (تبويب في الخلفية، أو هاتف مقفل) يُبقي تحويل الترميز الجاري 10 دقائق. ويبقى تحويل الترميز المنتهي 4 ساعات بعد آخر جلب لمشاهده، فيستمر العرض بعد توقف مؤقت طويل؛ وتحويل الترميز الذي انقطع (حُصدت الجلسة، أو فشل ffmpeg) لا يُنهي الفيديو عنده أبدًا: تجيب قائمة تشغيله بـ 410 ويعيد مشغّل الويب فتح الفيديو حيث كان، كما يفعل أيضًا حين يختفي بثه نهائيًا (404 بعد إعادة المحاولات)، بدل شاشة الخطأ، ولا يعلّم الفيديو كمُشاهَد إلا قرب نهايته الحقيقية. وتحويل الترميز الذي يُفتح عند موضع (استئناف، أو إعادة فتح كهذه) يبدأ من هناك: ينتقل ffmpeg إليه، وتسرد قائمة تشغيله الجزء السابق فجوات، فتبقى أزمنة المشغّل وشريط التنقل والتقدم ونقطة الاستئناف أزمنةَ الفيديو، والتنقل إلى ما قبله يعيد فتح الفيديو هناك (كان يبدأ من 0 سابقًا، فيقفز المشغّل إلى الوراء). يُزال مجلد الجلسة حين يغلقها مشغّلها، والمجلد الذي تركه تشغيل سابق متى لم يتغير لمدة 90 ثانية؛ لم يكن شيء يزيل تلك المجلدات من قبل. تشغل مقاطع تحويل الترميز نحو 4.8 غيغابايت في الساعة لسلّم 1080p على قرص ذاكرة التخزين المؤقت، خارج سقف تلك الذاكرة: لا يبدأ أي تحويل ترميز والمساحة الحرة هناك أقل من[media] transcode_min_free_gibفي server.toml (الافتراضي: عُشر القرص، بين 3 و20 GiB)، وتحت هذا الحد تُزال تحويلات الترميز المحفوظة للجلسات المغلقة، الأطول دون مشاهدة أولًا. والفيديو المرفوض يقول "لا توجد مساحة كافية على القرص لتجهيز هذا الفيديو" (507)، لا إن الخادم مشغول.صار تحويل ترميز الفيديو يعيد خانته. كانت خانة تحويل الترميز تؤخذ عند فتح كل جلسة ولا تُعاد أبدًا، فما إن تشغّل العملية عدد تحويلات ترميز بقدر خاناتها (ربع معالجاتها افتراضيًا) حتى يُعرض كل فيديو بدقة 720p فما فوق عرضًا مباشرًا، أيًّا كان ما يستطيع المشغّل فك ترميزه، ويُرفض كل ما هو أصغر. صارت الخانة تعود الآن حين ينتهي تحويل ترميزها، وحين تُغلق جلستها أو تُحصد، وحين يفشل الفتح بعد أخذها.
صار بحث yt-dlp لصفحة بث حي يجري مرة واحدة للقنوات التي تتشاركه. كانت القناة التي لم تجد عنوان URL مخزّنًا لحظة انتهاء البحث المشترك تشغّل yt-dlp مجددًا للصفحة نفسها؛ صارت الآن تأخذ العنوان الذي خزّنه ذلك البحث. (ظهر ذلك في التكامل المستمر في صورة yt-dlp المزيّف في الاختبار يعمل مرتين ويغلق قناته مرتين.)
صار العنقود يحدّث نفسه (الملحمة 35، القصص 35.4–35.7 و35.12 و35.13). في نافذته الهادئة (الافتراضي 03:00–05:00) ومن دون أن يشاهد أحد التلفاز، يثبّت المنزل إصدارًا جديدًا موثّقًا من قناته، ثم كل جهاز منضم، واحدًا تلو الآخر، مع مراقبة كل منها قبل التالي؛ والجهاز الذي كان مطفأً يُلحَق حين يعود، ولا يسبق أي جهاز المنزل. قبل أن يتغير أي شيء يتدرّب المنزل على ترحيلات الإصدار على نسخة مؤقتة من قاعدة بياناته؛ وعند التبديل يأخذ تفريغًا لقاعدة البيانات (يُتحقق من إمكان قراءته)، ويرحّلها، ويراقب الإصدار الجديد عشر دقائق. والإصدار الذي لا يبقى سليمًا يُتراجع عنه تلقائيًا: يعمل الإصدار السابق مجددًا، وإن كانت قاعدة البيانات قد رُحّلت يُعاد التفريغ أولًا. وانقطاع الكهرباء في أي خطوة يُستكمل عند البدء التالي. ويجلب تحديث الخادم العادي معه أيضًا بيئة Python الخاصة بخط معالجته، بالإضافات التي كانت لدى الجهاز. تقوم تثبيتات الخادم دون واجهة والخادم العادي بكل ذلك بنفسها؛ وتطبيق سطح المكتب الذي يشغّل الخادم (Mac وWindows وLinux) ينزّل الإصدار الذي يأمر به المنزل ويفحصه ويعرض "ثبّت Maktaba <version>" (على جهاز منضم: ينتظره تحديث العنقود)، ويثبّته حين ينقر أحد هناك — ويستمر التوزيع على الأجهزة الأخرى في الأثناء، ويحلّ إصدار أحدث محلّ أمرٍ لم ينقره أحد. تعرض صفحة المشرف ← التحديثات خطوات كل تشغيل على المنزل وعلى كل جهاز، والأجهزة التي تنتظر نقرة مع إصدارها، وأزرار الإلغاء وإعادة المحاولة والتخطي والتراجع والاستئناف؛ وصارت التثبيتات التلقائية مفعّلة افتراضيًا الآن (يوقفها زر الإيقاف المؤقت). تحمل الإصدارات ترحيلاتها (
maktaba-migrations-<version>.tar.gz، مدرجة فيrelease.jsonالموقَّع).صار كل فشل في التحديث يقول سببه. تعرض صفحة المشرف ← التحديثات، للمنزل ولكل جهاز، آخر محاولة تحديث له: متى، ومن أي إصدار إلى أي إصدار، والخطوة التي توقف عندها، والسبب بكلمات بسيطة (بالإنجليزية والعربية)، والخطأ الخام، وما العمل بعد ذلك (إعادة المحاولة، أو الأمر الذي يصلحه، أو السجلات). ويشمل ذلك تحديثات تطبيق سطح المكتب نفسه كما يشمل تشغيلات العنقود: يسجّل التطبيق كل محاولة (لا شبكة، أو تنزيل مفقود، أو توقيع لا يطابق، أو مجلد يحتاج مسؤول نظام، أو قرص ممتلئ، أو تطبيق يشغّله macOS من مكان مؤقت أو لا يزال يعلّمه كمُنزَّل، أو خطأ في المثبّت، أو إعادة تشغيل لم تنجح) ويقول السبب في قائمته وفي إشعار من نظام التشغيل، ويحتفظ بسجله الخاص (
app.logبجانبserver.log). والإصدار الذي يفشل في البدء (التفريغ الذي يسبق ترحيلاته، أو ترحيل، أو قاعدة بيانات أحدث منه، أو بيئة خط المعالجة) يُسجَّل كذلك، والإصدار السابق، وقد عاد يعمل، يعرض "فشل 0.3.0 في البدء: <السبب>. الإصدار السابق يعمل مجددًا." ولم تعد شاشة الإعداد في تطبيق Mac تخفي سبب عجزها عن بدء الخادم (عنصر تسجيل الدخول الذي يرفضه macOS لتطبيق بلا توقيع شيفرة يقول ذلك، مع طريقة الإصلاح). ولا يُعدّ التحديث مثبّتًا إلا حين يعود الخادم على الإصدار الجديد. ويرتبط كل فشل أيضًا بسجلاته: تفتح صفحة السجلات على النافذة الزمنية لذلك التحديث ومعهاserver.logوapp.logوlauncher.logالخاصة بالجهاز بجانب أسطر الواجهة البرمجية (تُجلب سجلات العقدة عبر قناة الجهاز)، ويحزم زر "نزّل السجلات" النافذة نفسها مع تلك الملفات، وتفتح قائمة تطبيق سطح المكتب وإشعاره صفحة السجلات، أو المجلد الذي فيهapp.logوserver.logحين لا يجيب الخادم.صار الجهاز يتبع قاعدة بيانات منزله بنفسه. حين تنتقل قاعدة بيانات المنزل (انتقال تطبيق من Postgres يديره مشغّل إلى قاعدته الخاصة، أو منفذ جديد، أو استعادة) أو يكون دور الجهاز مفقودًا فيها، يقول الجهاز إنه لا يستطيع تسجيل الدخول، فيعيد المنزل إنشاء دوره بكلمة مرور جديدة ويسلّمه عنوان قاعدة البيانات عبر قناة الجهاز، فيبدأ الجهاز مجددًا به، خلال دقيقة، من دون أن يفعل أحد شيئًا. وحتى ذلك الحين تقول صفحتا التحديثات والأجهزة "لا يستطيع هذا الجهاز الوصول إلى قاعدة بيانات المنزل" وسبب ذلك، وينتظر تحديث المنزل أن تتبعه أجهزته قبل أن يعتمد (كلٌّ منها عشر دقائق على الأكثر). لا يعيد المنزل إنشاء صلاحية الوصول إلا حيث يصلح ذلك تسجيل الدخول (كلمة مرور مرفوضة، أو قاعدة بيانات انتقلت)، مع مهلة متزايدة بين المحاولات، ولا يفعل ذلك أبدًا أثناء تبديل المنزل أو من منزل لم يعلم بتبديل؛ ويحتفظ الجهاز بشهادة CA المثبّتة لديه، ولا تظهر أي كلمة مرور في سجلات قاعدة البيانات (محقِّقات SCRAM) ولا في نص أي فشل.
عاد إصدار تطبيق Mac لمعالجات Apple Silicon. فشل كل إصدار منذ صار التطبيق موقّعًا توقيعًا مخصّصًا (v0.3.0-rc.7 وv0.3.1) في بناء macos-arm64، فلم يكن في خلاصات تحديثها أي مدخل لـ Apple Silicon ولم يستطع منزل على Mac أن يتحدث. من دون أسرار Apple كان الإصدار يسلّم tauri إعدادات توثيق فارغة، فيعدّها طلبًا للتوثيق ("Team ID must be at least 3 characters"). صارت بناءات Mac الآن لا تحصل عليها إلا لتطبيق بشهادة Developer ID مع مجموعة كاملة منها، وصارت بوابة تطبيق Mac في التكامل المستمر تبني كما يبني الإصدار، مع تعيين تلك الإعدادات فارغة.
صار كل تطبيق سطح مكتب يتحدث، وتحديث Mac يقول السبب حين لا يعود خادمه. التطبيقات التي لا تشغّل خادمًا (تطبيق Mac لمعالجات Intel، أو تطبيق متصل بمكتبة أخرى، أو نسخة Windows المحمولة) صارت تعرض تحديثها. بعد تحديث التطبيق يسجّل Mac عنصر تسجيل الدخول للخادم مجددًا وينتظر الخادم على الإصدار الجديد قبل أن يعدّه مثبّتًا؛ والتثبيت المأمور به يفحص، دون أن يسأل، هل يسمح macOS للتطبيق بتغيير نفسه (منقول مؤقتًا، أو للقراءة فقط، أو ملك مسؤول نظام، أو إدارة التطبيقات، أو حزمة مقفلة) ولا يصل أبدًا إلى طلب كلمة مرور. والتطبيق المبني محليًا يقول إنه لا يستطيع تحديث نفسه (
local_build: ثبّت الإصدار يدويًا مرة واحدة) ولا يعطّل شيئًا. ومثبّت Windows الذي لا يكتمل يُسجَّل ويعود خادم المنزل غير المراقَب. تثبيت واحد في كل مرة، مع مهلة للتنزيل ومؤشر للتقدم.صارت تحديثات العنقود تعيد المحاولة بتعقّل ويمكن رفع الحظر عنها. الفشل الذي قد يزول يُعاد فورًا، ثم بعد 15 دقيقة، ثم في النافذة التالية (محاولة واحدة لكل نافذة)؛ والفشل الذي يحتاج شخصًا لا يبدأ تشغيلات جديدة ويُبلَّغ عنه مرة واحدة. ويستطيع المشرف تجربة إصدار محظور مجددًا أو تثبيته الآن. يُحترم
catch_up، ويُلحَق الجهاز الذي أُصلح فشله، وتُفرَّغ كل عقدة من عملها لتبديلها الخاص، ولا يحظر الإصدار إلا التحقق أو التدريب على البيانات الحقيقية، والمنزل الذي يكون في نافذته دائمًا مشاهد يُبلَّغ بعد ثلاث نوافذ، مع زر "ثبّت الآن". تُقلَّم التفريغات القديمة إلىbackups_keep؛ والاستعادة الفاشلة إلى Postgres خاص بالمشغّل تتركه كما كان.قاعدة واحدة لتحديد الإصدار الأحدث. الإصدار ذو الرقم الأعلى لكنه بُني قبل الإصدار العامل (إصدار rc وُسم بعد إصداره النهائي) يتجاوزه كل محدِّث، بدل أن يُعرض، أو يُرفض كإعادة تشغيل، أو يُثبَّت كتخفيض؛ وإصلاح عاجل على إصدار فرعي أقدم ما زال يرتقي. والإصدار الذي رُئي قبل إرفاق ملفاته الموقَّعة يُتحقق منه متى أُرفقت؛ ويُعاد التنزيل الفاشل، ولا يكون أبدًا فشلَ تحقق يحظر الإصدار.
صارت الإصدارات محمية ولا يُعاد كتابتها أبدًا. يُرفض الوسم المساوي لإصدار منشور أو الأدنى منه، أو الواقع خارج
main، أو الذي ليس له تشغيل ناجح في التكامل المستمر؛ ويبقى كل إصدار مسودة حتى يكتمل، ولا يصير الأحدث إلا حين يكون أعلى إصدار مستقر وكل منصات سطح المكتب في خلاصته الموثّقة؛ ولا تعيد إعادة التشغيل أبدًا كتابة إصدار منشور، وملفاته قابلة لإعادة الإنتاج. يُعدّinstall.shمع systemd الخدمةَ ذاتية التحديث؛ وترقية .deb/.rpm تعيد تشغيل الخدمة وتترك الترحيلات لخطوة قاعدة البيانات؛ ويُحدَّث جذر Sigstore الموثوق بطلب دمج أسبوعي.لا صفحة قديمة بعد التحديث. يحمل عامل الخدمة (service worker) ختم كل بناء، وتُحمَّل الصفحات من الشبكة أولًا، والصفحة التي تركها إصدار أقدم مفتوحة يُعاد تحميلها مرة واحدة بدل أن تصير فارغة.
[0.2.0] - 2026-10-19
يُغلق هذا الإصدار تدقيق الأجزاء الناقصة: الميزات التي بُنيت نصفها، والإعدادات التي
كانت تُحفظ ولا تُقرأ أبدًا، والإشعارات التي لم يرها أحد، كلها اكتملت أو أُزيلت أو قُيّدت.
تغطي GraphQL كل حقل جذري فوق معالجات REST، وشُدّدت قواعد الوصول في REST التي يقوم
عليها الاثنان. مراجعة المخطط 140 (الترحيلات 0137-0140).
أُضيف
- يشغّل Maktaba.app نموذجه اللغوي الخاص (الملحمة 36، القصة 36.11). يضم التطبيق Ollama
0.40.2 (بإصدار مثبّت ومجموع تحقق مفحوص في
make mac-app)، ويشغّله المشرف على العمليات في دورollamaعلى منفذ حلقة محلية خاص به (11533، فيبقى المنفذ 11434 لنسخة Ollama الخاصة بالمستخدم)، ونماذجه فيmodels/ollamaداخل التطبيق وسجله في~/Library/Logs/Maktaba/ollama.log. تستخدمه كل مراحل النموذج اللغوي (فحص الوسوم، فحص المقاطع، الإثراء، عناوين العرض) عبرMAKTABA_OLLAMA_URLما لم تحدد الإعدادات خادم نماذج آخر. يُنزّل إعداد التشغيل الأول (الخطوةllm) وmaktaba-server models llmالنموذجqwen2.5:7bمع عرض التقدّم بعد إظهار مساحة القرص التي يحتاجها؛ ويُنزّل الخادم النموذج الناقص في الخلفية، ويعرضه شريط القوائم جاهزًا أو قيد التنزيل، ويُبلغGET /llmفي المشرف على العمليات عن حالته. ينقلmigrate-from-devالمجلد~/.ollama/modelsمن إعداد التطوير. - أصبح «البحث عن البيانات الوصفية» و«إعادة الإثراء» يعملان (#133). تأتي اقتراحات البيانات
الوصفية للفيديو من معرفة مكتبة الذاتية (النصوص المفرّغة، الفصول، الكتاب الذي تشرحه السلسلة،
أسماء الملفات والمجلدات، الحلقات الشقيقة، الوسوم، المتحدثون)، ومن بحث على الويب عند ضبطه
(Google Programmable Search، أو بحث Ollama على الويب؛ الإضافة «بحث الويب»، مع زر «اختبار
الاتصال» حقيقي) يقرأ النموذج اللغوي نتائجه. لا فهرس خارجي. يحمل كل حقل درجة ثقته
ومصادره في لوحة المراجعة. تعمل عمليات البحث مرحلةَ
enrichفي المحرك (تظهر في مركز التحكم؛ ولم تعد المهام المنتظرة تتراكم)، وتُخزَّن مؤقتًا، وتلتزم بحدود يومية (rules.enrich). لا يحصل النموذج على أي أدوات، وإجابتهJSONمُتحقَّق منه، ويُعامَل نص الصفحات بيانات غير موثوقة. الترحيل 0139. - GraphQL: كل حقل جذري يعمل الآن (#134). يمرّر المخطط كل حقل عبر مسار
RESTالذي يملك العملية، داخل العملية نفسها، مع البرمجيات الوسيطة ذاتها (حدود المعدل، عدم التكرار، حدود حجم الجسم، فحوص المشرف، الظهور، التدقيق). تُحمَّل حقول الفيديو المتداخلة دفعةً واحدة، وتُطبَّق حدود العمق والكلفة (MAKTABA_GRAPHQL_MAX_DEPTH،MAKTABA_GRAPHQL_MAX_COMPLEXITY)، وvideosوjobsاتصالات بمؤشرات، والاشتراكات تعمل عبرgraphql-transport-ws. - الاستعلامات المحفوظة في GraphQL (#136): Apollo APQ، مخزّنة في
graphql_persisted_queries(الترحيل 0140)، مع وضع قائمة السماح (graphql.persisted_only، معطّل افتراضيًا). - بحث YouTube في صفحة البحث عبر yt-dlp (#131)، دون مفتاح واجهة برمجية: تُحمَّل النتائج
بعد النتائج المحلية، ولكل منها زر «استيراد» يجهّز مرشّح إثراء
youtube، ويختفي القسم تلقائيًا عند غياب yt-dlp. - إعادة تعيين كلمة المرور من المشرف (#131):
POST /api/users/{id}/reset-passwordبكلمة مرور مؤقتة لمرة واحدة أو بكلمة يحددها المشرف. تُلغي كل إعادة تعيين جلسات المستخدم وتُسجَّل في التدقيق؛ ولا ترى الجلسة الملزمة بالتغيير إلا «اختر كلمة مرور جديدة» (الترحيل 0138،users.must_change_password). - إشعارات للميزانية وسقف البث المتزامن والتحويل الاحتياطي وحلقة أعطال التجهيز (#132):
يذهب إلى الإشعارات تجاوزُ عتبتي 80 % و100 % من ميزانية المشاهدة عن بُعد ومن سقف البث المتزامن،
والتحويلُ إلى الاحتياطي الساخن، ودخولُ خانة التجهيز في حلقة أعطال
(الترحيل 0137،
tv_slot_events). - الويب (#130): الحساب > السجل (سجل المشاهدة والخط الزمني للنشاط)؛
«العودة إلى الأصل» في لوحة الإثراء ومفتاحا
a/dفي طابور المراجعة؛ خيارrules.enrich.auto_accept_threshold(الافتراضي 0 = معطّل)؛ شارة تحديث على الإعدادات، وتلميح تحديث Docker في المقدمة، وملاحظات إصدار بصيغة Markdown؛ «متأخر عن البث المباشر» / «العودة إلى المباشر» في مشغّل التلفاز المباشر؛ ويعرض «افحص الآن» تقدّمه.
تغيّر
- رُبطت الإعدادات الميتة (#132): أصبحت تُقرأ الآن
search.fts_weight/semantic_weight، وrecs.cache_ttl_sec، وsession.url_ttl_sec، وpause.grace_sec، وws.heartbeat_interval_sec، وmax_lanes_per_daypart(الافتراضي 0 = بلا حد)، وignore_globsللمكتبة، وsweep_interval_sec(الافتراضي 0 = معطّل)، وstt.initial_prompt؛ ولـauth.open_registrationمفتاح تبديل في المشرف > المستخدمون. أُزيلت الإعدادات التي لا يمكن أن تعمل (subtitle.url_ttl_sec). تحافظ القيم الافتراضية على السلوك الحالي. - تمرّ تعديلات الحشو عبر مسار المسودة (#132) عند تفعيل تجهيز التلفاز، ويودعها التبديل الساخن مع بقية التغيير.
- سُدّت فجوات الخلفية (#128): يقول «حدّث الآن» ومجدول التحديثات بوضوح إن التثبيت
التلقائي يصل مع منسّق التحديثات بدلًا من تسجيل عمليات فاشلة؛ وتعدّ نظرة المرحّل العامة
الخوادم والأنفاق بشكل صحيح؛ وتُمحى الحسابات المحذوفة ذاتيًا مع سجلات الإشعارات الفورية
الخاصة بها؛ ويفحص
doctorفي خط المعالجة فعلًا قاعدة البيانات والمخطط وffmpeg والمجلدات؛ ويعمل حساب المساحة الحرة على القرص وقفل ملف المفتاح على Windows؛ وتُسجَّل عمليات تسليم الإشعارات الفورية. - إذا فشلت إعادة التشغيل الذاتي بعد التحديث الذاتي، يُستعاد الملف التنفيذي القديم ويُبلَّغ عن التراجع (#130).
أُزيل
- مسار «نسيت كلمة المرور؟» الذاتي ومخزن رموز إعادة التعيين الخاص به؛ وتُحذف الرموز غير المستخدمة التي كُتبت في السجل (#131).
- مسار السحابة
POST /v1/account/emailالذي لا يفعل شيئًا، وحزمة واجهة الأوامر الجذرية الفارغةmaktaba(#128).
الأمان
- الوصول والتدقيق في REST (#136): لا تجيب جلسات البث إلا مالكها أو مشرفًا (وكذلك على المسارات الموقّعة في خدمة البث)؛ وتقتصر المجموعات والسلاسل والإحصاءات والوسوم والمهام والنشاط وسجل العنصر وملخص التفريغ النصي واقتراحات البحث ومطابقات YouTube وشرائط التوصيات وقراءات مركز التحكم وتدفقات WebSocket على المكتبات التي يستطيع الطالب قراءتها (وإلا 404)؛ وتفاصيل العمّال والإضافات للمشرفين فقط. يُسجَّل في التدقيق إنشاء المكتبة وتعديلها وفحصها وحذفها، وتعديل الفيديو وحذفه ومعالجته وإعادة معالجته؛ وفحص المكتبة للمشرفين فقط.
- إلزام تغيير كلمة المرور على العقد الكاملة (#135): تبقى الجلسة التي تحمل كلمة مرور مؤقتة أعاد المشرف تعيينها محصورة في نموذج كلمة المرور على العقد الكاملة أيضًا، ويُرفض التحديث الأصلي للجلسة حتى تُغيَّر كلمة المرور.
- قائمة سماح لمضيفي الصور المصغّرة (#137): يُحلَّل عنوان الصورة المصغّرة المجهّزة من YouTube
بـ
net/url(httpsفقط، بلا بيانات مستخدم أو منفذ، بلا شرطات مائلة عكسية) ويجب أن يكون على مضيف صور YouTube (*.ytimg.com،yt3.ggpht.com،yt3.googleusercontent.com)؛ ولم تعد العناوين المصنوعة مثلhttps://evil.example?x.ytimg.com/تمرّ. - حُدّثت التبعيات ذات الثغرات المعروفة (#140): grpc 1.83.2 و
OpenTelemetry 1.45.0 (Go)؛ وreact-router-dom v7 وvite 6.4.4 وvitest 4.1.12 (الويب)؛
وCapacitor 6.2.2 (الجوال)؛ وanyio وfsspec وsetuptools (خط المعالجة). لا تنطبق تنبيهات خادم
chromadb (تستخدم مكتبة العميل المضمّن فقط) فرُفضت لعدم الاستخدام؛ وخدمة
chromaفي ملف compose مربوطة بـ127.0.0.1.
أُصلح
- يعثر Plex على موالف HDHomeRun. تحمل ردود SSDP قيمة
LOCATIONمطلقة (http://<the address the client reaches>:8081/device.xml) بدلًا من/device.xml، وتجيب علىupnp:rootdeviceوعلى المعرّف الفريد أيضًا، ويبث الموالفssdp:aliveكل خمس دقائق وssdp:byebyeعند توقفه. ويُجاب أيضًا على بروتوكول الاكتشاف الأصلي لـHDHomeRun (UDP 65001، بصيغة حزم libhdhomerun مع CRC)، تحت مفتاح الموالف نفسه. يحملDeviceIDالآن رقم التحقق الخاص بـHDHomeRun (يُصحَّح الرقم الأخير للمعرّف الموجود، مثلًا 87C7778E يصبح 87C77788)، ويُبلغdevice.xml/discover.jsonعن Silicondust مثل موالف حقيقي. وعلى الخادم نفسه تعرض صفحة التلفاز المباشر عنوان الشبكة المحلية الذي يُكتب في Plex (#139).
[0.1.0] - 2026-10-19
أول إصدار موسوم من مكتبة: مكتبة مستضافة ذاتيًا للمحاضرات والكلمات والسلاسل (فيديو وصوت) تفهم ما تحويه وتشغّله قنواتِ تلفاز مباشرة.
أبرز ما فيه:
- ملف تنفيذي واحد للخادم. يشرف
maktaba-serverعلى أدوار الواجهة البرمجية والبث وخط المعالجة، ويقدّم واجهة الويب المضمّنة، ويعلن عن نفسه عبر mDNS، ويحمل واجهة أوامر المثبّت (setup،setup --node،setup --self-managed،update،uninstall،models). أرشيفات لكل منصة لـWindows (amd64) وmacOS (arm64، amd64) وLinux (amd64، arm64)، وحزم.deb/.rpm، وصيغة Homebrew، وصور حاويات. - ذكاء المكتبة. الفحص والفهرسة، والتفريغ النصي للمكتبة كلها على محرك الطابور (Whisper، مع استماع ثانٍ بالعربية من Cohere يُدمج في كلمات Whisper)، والترجمات المصاحبة والبحث، والفصول والموضوعات والملصقات، وذكاء السلاسل، والوسم، وأسلوب الإنتاج وعناوين العرض؛ ومجموعات Plex للفهرس والمواسم والموضوعات والمقاطع.
- Maktaba TV. قنوات تُبنى من الإعدادات كالتلفاز الحقيقي (تشكيلة حتى 20 قناة، وخانات البرامج، والحشو، وبلا بث صامت)، ودليل برامج ومخرجات HLS/MPEG-TS، وPlex Live TV (موالف HDHomeRun ومسجّل XMLTV)، وخانات تلفاز مع احتياطي ساخن، وتغييرات تلفاز مجهّزة مع تحقق آلي ومعاينة وتبديل ساخن وتراجع، ومشاهدة عن بُعد وبث متزامن عبر Cloudflare Stream.
- منازل متعددة الأجهزة. الاكتشاف والإقران على الشبكة المحلية، وقناة العنقود المختومة، والملفات على مسارات مشاركة قياسية، وعقد عاملة وعقد كاملة تشغّل الخادم نفسه، وتسجيل الدخول عبر الأجهزة، وتوجيه العقد، ومرحّل الإضافات (تشغّل العقد مراحل Plex دون رمز Plex).
- محرك الطابور ومركز التحكم. طابور مهام متعدد المستويات تقوده التغييرات، مع تقدّم لكل مهمة، ومركز التحكم وصفحات صحة النظام، والإشراف على العمليات مع التعافي من الأعطال.
- إصدارات موقّعة وتحديثات تلقائية. كل إصدار له مجموع تحقق وموقّع بلا مفاتيح عبر مسار
عمل الإصدار في هذا المستودع (Sigstore)؛ ويحمل
release.jsonالإصدارات ومراجعات المخطط والملخصات لكل منصة. تتحقق الأجهزة من الإصدار داخل العملية ودون اتصال، مقابل هوية مسار عمل مثبّتة، قبل تثبيته؛ وتخطيط تثبيت متجاور يديرهmaktaba-launcher، وسياسة التحديث وجدولته، والتراجع. - العملاء. تطبيق الويب (سمة النظام في كل صفحة)، وأغلفة سطح المكتب (Tauri) والجوال والتلفاز (tvOS، Android TV).
- المنصة. SQLite أو Postgres، وترحيلات مع حارس للمخطط، وأسرار مختومة على الخادم المنزلي، وتحديد المعدل، وسجل التدقيق، وقابلية المراقبة، وأدوات النسخ الاحتياطي والتحقق من السلامة.
تفاصيل كل ما في هذا الإصدار:
أُضيف
- التبديل الساخن للتلفاز واستمرارية الدفع والتراجع (الملحمة 33، القصص 33.7 و33.8 و
33.9): يُبث تغيير التلفاز المعتمد في الواجهة الأمامية، قناةً قناة عند حد البرنامج
في كل منها (
swap_wait؛ والقناة التي لا يشاهدها أحد فورًا؛ وتبديل متدرّج عند ضيق ميزانية المرمّزات): تستمر قائمة تشغيل HLS على الخانة الجديدة بعد انقطاع واحد، وتُوصل اتصالات Plex/TS ودفع Cloudflare عند الإطار المفتاحي المشترك بين الخانتين دون إغلاقها (جلسة دفع واحدة، والفيديو نفسه، ومخرجات البث المتزامن لا تُمس). تبقى الخانة المباشرة القديمة دافئة لمدةkeep_warm_minutes(15) هدفًا للتراجع. تراجع من صفحة التغييرات (وGET/POST /api/tv/rollback): دافئًا (فوريًا) ما دامت تلك الخانة قائمة، أو باردًا (بتجهيز الإصدار السابق من جديد) خلالrollback_retention_hours؛ ويؤدي تعطّل الخانة المباشرة الجديدة في تلك المدة إلى التراجع تلقائيًا. لا يبث تعطّل مباشر أبدًا تغييرًا مجهّزًا غير معتمد. الخانة 0136tv_swap_ops،tv_channel_switch؛ ومرحلة خط المعالجةtv_rollback. - التحقق من تجهيز التلفاز ومعاينته (الملحمة 33، القصتان 33.5 و33.6): يعمل تغيير التلفاز
المجهّز الآن على خانة التجهيز (النسخة الثالثة، التي يبدؤها المشرف على العمليات بالملف
التنفيذي لإصدار التغيير ومربوطة بإصداره)؛ وتُدفئ الواجهة الأمامية القنوات التي يشاهدها الناس
وقناة الفحص وكل قناة متغيرة بالتتابع، ضمن المرمّزات التي يتركها البث المباشر والاحتياطي
الساخن، وتأخذ عينات من HLS و
live.tsللخانة كل بضع ثوانٍ. يكتب متحكم التغييرات كل فحص في تقرير التغيير (صحة الخانة، أول مقطع، الإيقاع، ساعة الحائط والموضع، البرنامج على الهواء مقابل الجدول الجديد ومقابل البث المباشر، مطابقة TS وHLS، محاذاة الوصل مع البث المباشر — نفس معرّفات PID وحدود المقاطع وPTS ضمن إطار واحد — معاملات المرمّز مقابل البث المباشر، قواعد الاستقبال في Cloudflare، استمرارية الدفع، الدليل مقابل البث، ميزانية المرمّزات) ويجعل التغيير جاهزًا بعدverify_soak_seconds، أو فاشلًا مع الأسباب؛ وتتوقف خانة التجهيز عند إلغاء التغيير أو فشله أو بثه. يشغّل تبويب المعاينة في صفحة التغييرات قنوات التجهيز للمشرفين (مشغّل التلفاز المشترك، وعرض المباشر مقابل التجهيز، وشريط الدليل المجهّز؛ وM3U وXMLTV مجهّزان لـVLC)، عبرPOST /api/tv/changes/{id}/preview(رمز لمدة ساعة) و/stream/staging/…في الواجهة الأمامية؛ ولا يسمح منفذ الويب إلا للمشرفين بالمرور. لا جهاز HDHomeRun للتجهيز. يُضمَّن عند البناء بوسم البناءtvstaging، الذي يضبطه الإصدار وmake build-goللملف التنفيذي للبث وصورته. - خانات التلفاز والاحتياطي الساخن (الملحمة 33، القصتان 33.1 و33.4): يعمل محرك القنوات في
عمليات خانات التلفاز (
maktaba-streaming slot --slot blue|green|amber، دور المشرف على العملياتtv-slot، الحلقة المحلية 8791-8793، والفحوص 9111-9113) خلف دور البث الذي يصبح الواجهة الأمامية. تخدم الخانة المباشرة المشاهدين؛ ويشغّل الاحتياطي الساخن القنوات نفسها دافئة (rules.tv_staging.hot_standby، مفعّل؛standby_warm، وقيمته الافتراضيةviewers، ضمن ميزانية المرمّزات: المباشر أولًا) ويتولى فورًا عند تعطّل الخانة المباشرة: يستمر HLS عبر انقطاع واحد، وتُوصل اتصالات Plex/TS، ويحتفظ دفع Cloudflare بجلسته. مخرجات المحطة جاهزة للوصل: طوابع زمنية بساعة الحائط، وأطر مفتاحية على شبكة ساعة الحائط (tv_output.keyint_seconds، 2)، ومعرّفات PID ثابتة، ومقاطع محاذاة. الخانة 0129tv_slots. يُبقي[tv] slots = "off"فيserver.tomlالمحرك داخل عملية البث. يُضمَّن التجهيز عند البناء بوسم البناءtvstaging(تضبطه بنايات الإصدار). - مرحّل الإضافات (القصة 31.R): تشغّل الأجهزة مراحل Plex (مجموعات الفهرس
والمواسم والموضوعات والمقاطع) من جديد دون رمز Plex. يمرّ كل
استدعاء لـPlex عبر قناة العنقود المختومة للجهاز إلى الخادم المنزلي،
الذي يتحقق من الجهاز والاستدعاء مقابل قائمة سماح، ويضيف
الرمز من
home_secrets، ويستدعي Plex ويعيد الإجابة بعد إزالة الرمز منها. حدود للمعدل والتزامن والحجم والوقت لكل جهاز؛ ويُسجَّل كل استدعاء في ملف أحداث المحور. تعرض صفحة المشرف > الأجهزة > المرحّل حالته وقائمة السماح والاستدعاءات الأخيرة، وتبدّل تشغيله (قسم القواعدrelay، مفعّل افتراضيًا؛ وعند تعطيله تعمل مراحل Plex على الخادم المنزلي فقط). يبقى Cloudflare Stream على الخادم المنزلي فقط. - تكامل Cloudflare Stream (الملحمة 32، القصة 32.6): إضافة جديدة
«Cloudflare Stream» (معرّف الحساب، رمز الواجهة البرمجية، رمز النطاق الفرعي
للعميل) يتحقق زر «اختبار الاتصال» فيها من الرمز، ومن تفعيل Stream على
الحساب، والتخزين، والتحليلات (اختياري)، وينشئ مفتاح توقيع Stream،
ويتعرّف على نطاق التشغيل ويتحقق من أن الخادم المنزلي يستطيع الدفع. تعرض صفحة
الإضافات كل فحص ومفتاح المشاهدة عن بُعد (لا يُفعَّل إلا
بعد اختبار ناجح خلال آخر 24 ساعة) ويمكنها إزالة
التكامل (تُحذف مدخلات البث المباشر ومفتاح التوقيع الخاصة بمكتبة لدى
Cloudflare).
/api/tv/remote(الحالة، الاختبار، التفعيل، الإزالة). يشغّلmaktaba-api cloudflare spikeتجربة Stream على حساب حقيقي (tools/tv/cf-spike.md). - أسرار مختومة على الخادم المنزلي (الخانة 0120
home_secrets): AES-256-GCM بمفتاح الملفsecrets.keyبجوارserver.toml(أوMAKTABA_SECRETS_KEY)، ولا تكون أبدًا في قاعدة البيانات ولا يقرؤها دور الأجهزة. يعيش هناك كل سر من أسرار الإضافات: يُنقل رمز Plex منextensions.settingsعند أول تشغيل للواجهة البرمجية (مرة واحدة، دون أثر عند التكرار)، وتعمل مراحل Plex الآن على الخادم المنزلي فقط. - الإشراف على العمليات (القصة 22.9): يعيد
maktaba-server serveتشغيل العملية الفرعية (api أو streaming أو pipeline) التي تعطّلت أو علقت فقط، مع تراجع أُسّي، وحارس لحلقة الأعطال، وفحوص حياة/healthz. يُضبط عبر[supervisor]فيserver.toml. يُبقيrestart = "never"السلوك القديم، حيث يوقف أي خروج المنظومة كلها. يفشل الآن/healthzفي البث عندما يعلق محرك القنوات المباشرة، ويقدّم خط المعالجة/healthzلحلقة الأحداث. تظهر الحالة فيGET /api/system/processes(للمشرف) وفي صفحة صحة النظام. تُسجَّل الأعطال فيhealth_samples. تموت عمليات ffmpeg وyt-dlp الخاصة بالعملية الفرعية معها: على POSIX عبر مجموعة عملياتها، وعلى Windows عبر كائن Job Object يُنهيها عند الإغلاق. ويربطها البث أيضًا بنفسه (Windows job، وPdeathsigعلى Linux). - ملف تنفيذي موحّد
maktaba-server(cmd/maktaba-server): ملف تنفيذي واحد يشرف على أدوار الواجهة البرمجية والبث وخط المعالجة، ويقدّم واجهة الويب المضمّنة (وسم البناءembed_web)، ويعلن عن_maktaba._tcpعبر mDNS، ويحمل واجهة أوامر المثبّت —setup(معالج التشغيل الأول)، وserve --role، وupdate/--check(التحديث الذاتي)، وuninstall/--purge، وmodels(إدارة نماذج Whisper)، وmigrate/adduser/keys(مُفوَّضة إلىmaktaba-api). يقوده ملفserver.tomlواحد؛ ويدعم سلاسل اتصال SQLite (sqlite://) وPostgres (postgres://) كلتيهما. أهداف جديدةmake server/make server-dev. - تطبيقات التلفاز: أغلفة tvOS (SwiftUI) وAndroid TV (Compose-TV)، وهياكل عميل GraphQL أولية، ومسار الإقران.
- الاكتشاف: إعلان الخدمة عبر mDNS، والإقران برمز QR، وفحص الشبكة المحلية.
- الاشتراكات: طبقة استحقاق بترخيص موقّع + نقاط وصول للمشرف.
- الأداء: ملف قياسي
shared/perf_budgets.yaml، وذاكرة تخزين مؤقت LRU داخل العملية مع نقطة وصول للمشرف لتفريغها، وضبط مجمّع اتصالات قاعدة البيانات. - قابلية التوسّع: التجزئة، وناقل أحداث في الذاكرة، وحدود للتزامن.
- الأمان: التحقق من المدخلات، ومحدِّد معدل بدلو الرموز، وواجهة SBOM، وبيانات الإفصاح وفق RFC 9116.
- سلامة البيانات: كتابة ذرّية للملفات، ومخزن مفاتيح عدم التكرار، ومخطِّط النسخ الاحتياطي/الاستعادة، ومُتحقِّق من السلامة لكل فيديو.
- قابلية المراقبة: بيانات لوحة المعلومات + التنبيهات، وجدول
audit_logالقياسي. - عمليات التطوير والتشغيل: بيان الإصدار، وخطة الترقية/التراجع، وهياكل أولية للتحزيم متعدد المنصات.
تغيّر
- وُحّد جدول
audit_log(الخانة 0054)؛ ويحل محل عمليات الإنشاء المرتجلة السابقة من الملحمتين 9/12.
أُصلح
- المنافذ التي يشغلها برنامج آخر (Splunk على 8088، وأداة نسخ احتياطي على 8080): تفحص
setupوsetup --nodeوsetup --self-managedوserveمنافذ الويب والواجهة البرمجية والبث أولًا، وتنقل المنفذ المشغول إلى أول منفذ حر في 8780-8790، وتكتبه فيserver.toml(وفيpublic_url/web_public_urlإن كانا يذكران المنفذ القديم) وتسجّله. تفضّل العقدة الكاملة على Windows المنفذ 8780 لواجهة الويب. ترتبط واجهة الويب بمنفذها قبل أن يتابعserve: المنفذ الذي لا يمكنها الارتباط به يوقفserveمع السبب بدلًا من سطر في السجل. لا ينقلserve --role Xأي منفذ أبدًا. - التلفاز المباشر في المتصفح مع خانات التلفاز: تسمح بوابة البث في منفذ الويب الآن بمرور
مقاطع قائمة التشغيل المُخاطة (
/stream/channel/{id}/s/{slot}/seg-N.ts)؛ وقبل ذلك كان كل مقطع يجيب بـ404 ولا يبدأ المشغّل أبدًا. - تجهيز التلفاز: يُعاد في النبضة التالية لمتحكم التغييرات محاولةُ بدء تجهيز لم يتسلّمه المشرف على العمليات، بدلًا من تذكّره على أنه بدأ (فكان التغيير يفشل حينها بأنه «غير سليم»)؛ وتُقرأ خطة التجهيز تحت قفل التوجيه (سباق بيانات بين تحديثات المتحكم للخطة ومزامنة المجموعة الدافئة).