--- title: '97' sdk: docker emoji: 🐨 colorFrom: pink colorTo: gray --- # إصلاحات مطلوبة — ملخص B / C / D ## B — عاصفة `start_sender()` ورمز 187 **المشكلة:** `tick()` / `drive_event()` كانت تستدعي `start_sender()` بلا شرط في أول الدورة، فتدفع `ffmpeg` إلى `rtmps://a.rtmps.youtube.com:443/live2` قبل أن يكون `liveBroadcast` مربوطًا وبحالة `ready/testing/live` → 7 إخفاقات 187 خلال 5 دقائق، بداية متأخرة 5:22 و ~116 ثانية ضائعة. **الحل في `youtube_manager.py`:** - `start_sender()` الآن تحرس نفسها: ```python if not ev.get('bound') or not ev.get('broadcast_id'): return if ev.get('youtube_status') in ('complete','revoked','created'): return ``` - `drive_event()` لم يعد يستدعيها فورًا بعد `ensure_broadcast()`. بدلًا من ذلك: 1. بعد `ensure_broadcast()` نحدث `ev` ونَمسح الملاحظة العالقة ثم نسقط إلى **بوابة مشروطة** 2. نقرأ `lifeCycleStatus` الحقيقي من `liveBroadcasts` (مع احترام كوتا 45 ث) 3. نسمح بالإرسال فقط إذا كانت `life in ('ready','testing','live')`، وإلا نُوقف المرسل ونكتب `انتظار جاهزية الاستقبال` 4. `life == 'live'` → نشغّل المرسل ثم `stream_health`، `ready/testing + streamStatus==active` → نطلب `transition` إلى `live` النتيجة: لا دفع قبل وجود الـ ingest، لا عاصفة 187، ولا تأخير 5 دقائق. --- ## C — `youtube_api.py` بلا إعادة محاولة + الملاحظة لا تُمسح **المشكلة 1:** `_request()` كانت تفعل `urlopen(timeout=25)` مرة واحدة. أي فشل TLS عابر → `SSLError / URLError` → تُرمى كـ `خطأ داخلي ... (SSLError)` وتكتبها `_event_error()` في `note` ولا شيء يمسحها → بقيت 40 دقيقة رغم أن البث سليم. **الحل في `youtube_api.py`:** - إضافة `import ssl` - حلقة إعادة محاولة 3 مرات مع backoff أسي (1ث، 2ث، 4ث): - `HTTP 429/500/502/503/504` → إعادة محاولة مع `1.2*(attempt+1)` ث - `URLError, TimeoutError, OSError, ValueError, ssl.SSLError, SSLEOFError ...` → إعادة محاولة، ثم `APIError(None,'networkOrInvalidResponse')` فقط بعد نفاد المحاولات - هذا يحول الفشل اللحظي لـ TLS من رسالة "حماية الأحداث" إلى نجاح صامت بعد ثانية. **المشكلة 2:** `_event_error()` تكتب `note` ولا مسح عند التعافي. **الحل في `youtube_manager.py`:** - دالة جديدة `_clear_stale_error_note(key)` تمسح `note` إذا احتوت `حماية الأحداث` أو `خطأ داخلي` أو `networkOrInvalidResponse` - تُستدعى في `drive_event()` في 4 نقاط تعافي: 1. بداية الدورة بعد تأكد `obs status == live` 2. بعد `ensure_stream()` ناجح 3. بعد `ensure_broadcast()` ناجح 4. بعد كل `remote('liveBroadcasts')` ناجح (التحقق الفعلي من الشبكة) فإذا تعافت الشبكة، تختفي الملاحظة فورًا بدل 40 دقيقة. --- ## D — `Dockerfile.txt` في المستودع **المشكلة:** رفعت `Dockerfile` باسم `Dockerfile.txt` (sha `89e0bc09` = نسختي السابقة) فتجاهله Docker وبنى من القديم بدون: ``` YOUTUBE_FORMAT_GUARD=off YOUTUBE_FIFO_TIMESHIFT=10 YOUTUBE_HEALTH_INTERVAL=180 ``` فبقي `timeshift=10` يعمل فقط من القيمة الافتراضية في `relay.py` (`ts = int(os.getenv('YOUTUBE_FIFO_TIMESHIFT','10'))`) ولم تكن واضحة في الـ image. **الحل:** - الملف المرفق `Dockerfile` هو النسخة الصحيحة (محتوى `Dockerfile.txt` السابق) — يحتوي كل متغيرات YouTube ingestion + التعليقات الكاملة. - **احذف `Dockerfile.txt` من المستودع** (أو على الأقل لا ترفعه): ```bash git rm Dockerfile.txt git add Dockerfile youtube_api.py youtube_manager.py git commit -m "fix: gate start_sender (187 storm), retry youtube_api TLS, clear stale note, fix Dockerfile name" git push ``` - بعدها `docker build` سيقرأ `Dockerfile` الصحيح مباشرة، والقيم ستكون صريحة في الـ image وليس فقط default الكود. --- ## الملفات المرفقة - `youtube_api.py` — مع retry - `youtube_manager.py` — مع guard + مسح الملاحظة - `Dockerfile` — النسخة الصحيحة (احذف Dockerfile.txt) > كل الإصلاحات في الكود فقط كما طلبت، بلا تغيير سلوك الحالات السليمة، وباحترام كوتا YouTube (poll كل 45ث، health كل 180ث).