anten.ir — پخش زندهی ورزشی
پیشنهاد فناوری، اسکرام، کد مستند و تستشده، کار سنگین سئو و یک پلیر حرفهای سفارشی برای پخش زنده و آرشیو.
- مدت
- تاریخ تقریبی۱۰ ماه
- سایت با یک پلیر
- ۲
زمینه
فراکاو یک شرکت فناوری رسانهی ایرانی است. anten.ir ورزش را زنده پخش میکند — پیش از همه فوتبال [پیشنویس] — برای مخاطبانی که اوجشان شبهای مسابقه است و بیشترشان با گوشیهای اندرویدی میانرده و اینترنت همراه تماشا میکنند [پیشنویس]. heyatonline.ir مراسم مذهبی را زنده پخش میکند، اوجش هر سال در محرم است [پیشنویس]، و به برگزارکنندگان امکان میدهد برنامههایشان را آرشیو و منتشر کنند. اکتبر ۲۰۲۱ پارهوقت به heyatonline پیوستم و مه ۲۰۲۲ تماموقت به anten.ir رفتم، زیر نظر سرپرست فرانتاند [پیشنویس]. هر دو فرانتاند Next.js بودند و بکاندها با SignalR و GraphQL کار میکردند [پیشنویس].
وضعیت. در anten.ir پلیر خودِ محصول بود، و یک Video.js آماده با انبوهی پلاگین بود [پیشنویس]: روی Safari در iOS و مرورگرهای اندرویدی ارزان رفتار متفاوتی داشت، پخش زنده و آرشیو دو کامپوننت جدا بودند و خطاهای پخش دیده نمیشدند — هیچ رویدادی به آنالیتیکس نمیرسید [پیشنویس]. جستوجو شکاف دیگر بود: صفحههای مسابقه و برنامه فهرستهایی با رندر سمت کاربر بودند، با نشانیهای تکراری و بدون دادهی ساختیافته، پس گوگل صفحهی اصلی را ایندکس میکرد و تقریباً هیچ چیز دیگر را [پیشنویس].
در heyatonline پنل برگزارکنندگان وضعیت آرشیو را با polling میگرفت و از واقعیت عقب میماند، و مجموعهای از قواعد رابط کاربری که نهاد ناظر تعیین کرده بود (اعلانهای ردهبندی محتوا، دسترسپذیری) [پیشنویس] باید یکدست اعمال میشد. مهمترین محدودیت تقویم بود: مسابقهها و مراسم بزرگ تاریخ ثابت دارند و یک انتشار بد جلوی بزرگترین مخاطب سال فرود میآید.
چه کردم
- یک پلیر سفارشی برای پخش زنده و آرشیو ساختم. کامپوننتی بر پایهی Video.js با تغییر کیفیت HLS، جابهجایی DVR روی پخش زنده، رابط فارسی، میانبُرهای صفحهکلید، تصویر در تصویر، حالت پیش از مسابقهی «شروع از…» و اتصال دوباره با backoff هنگام خطای manifest [پیشنویس]. هر خطا یک رویداد به آنالیتیکس میفرستد. گزینههای کنارگذاشته: hls.js خام (کنترل بیشتر، اما وقتی برای ساخت رابط از صفر نبود) و یک پلیر تجاری (هزینه، نبود RTL، نداشتن کنترل روی رفتارهای خاص iOS) [پیشنویس].
- سئو را ساختاری کردم، نه ظاهری. صفحههای مسابقه و برنامه به رندر سمت سرور در Next.js منتقل شدند، با یک نشانی canonical برای هر رویداد، JSON-LD از نوع SportsEvent / VideoObject [پیشنویس]، نقشهی سایت روزانه، تصویر OG برای هر مسابقه، اسلاگ فارسی و کار روی Core Web Vitals برای تصویر پوستر و فونتها [پیشنویس]. گزینهی کنارگذاشته: پیشرندر با مرورگر headless — شکننده و یک سیستم دوم برای نگهداری.
- SignalR را در heyatonline به Redux وصل کردم. یک middleware رویدادهای SignalR را به اکشنهای Redux تبدیل میکند، پس وضعیت آرشیو («در حال ضبط»، «در حال پردازش»، «منتشرشده») بدون polling و زنده بهروز میشود؛ همین الگو بعدها وضعیت مسابقه را در anten.ir هم پیش برد [پیشنویس]. پنل برگزارکنندگان را به شکل PWA عرضه کردم تا روی گوشیشان نصب شود [پیشنویس].
- دیزاین سیستم را همراه طراح ساختم. توکنهای Figma یکبهیک در آبجکت تم MUI بازتاب داشتند؛ وقتی طراح گلوگاه بود، بعضی صفحههای داخلی را خودم طراحی کردم (رزومه).
- قواعد انطباق را یک بار و در کامپوننتها اعمال کردم. اعلانها، نشانهای ردهبندی و قواعد دسترسپذیری در کامپوننتهای مشترک بودند، نه در تکتک صفحهها [پیشنویس].
- برای تست و تصمیمهای مکتوب پافشاری کردم. تست کامپوننت با Testing Library، یک تست دود Playwright روی پلیر، و پیشنهادهای مکتوب برای RTK Query و کلاینت GraphQL که تیم پذیرفت [پیشنویس]. اسکرام با Jira و Zephyr.
نتایج (میزان اطمینان در پرانتز)
- یک کامپوننت پلیر برای پخش زنده و آرشیو در هر دو سایت (دقیق، رزومه)
- خطاهای پخش از اندازهگیرینشده به اندازهگیریشده رسیدند؛ نرخ خطا در iOS پس از منطق اتصال دوباره [N٪] کم شد (verify، پیشنویس)
- بازدید ارگانیک صفحههای مسابقه ظرف [۳ ماه] پس از رندر سمت سرور [N٪] بیشتر شد (verify، پیشنویس)
- وضعیت آرشیو در پنل برگزارکنندگان: از polling هر [۳۰ ثانیه] به زنده (تقریبی، پیشنویس)
- [N] بینندهی همزمان در [شب دربی] بدون رخداد سمت پلیر پس از اصلاح زیر (verify، پیشنویس)
- از پارهوقت به تماموقت در کمتر از هفت ماه (دقیق، رزومه)
- مدت
- تاریخ تقریبی۱۰ ماه
- سایت با یک پلیر
- ۲
آنچه درست پیش رفت
- یک پلیر، دو محصول. جدایی پخش زنده و آرشیو از میان رفت و هر اصلاح همزمان روی هر دو سایت مینشست.
- رندر سمت سرور همان کاری را کرد که قولش را داده بود. صفحههای تازهی مسابقه بهجای چند روز، ظرف چند ساعت ایندکس میشدند [پیشنویس].
- middleware SignalR به الگوی خانگی تیم تبدیل شد. صفحههای دیگر بدون کمک آن را به کار گرفتند.
- توکنهای Figma = توکنهای کد. من و طراح دیگر سر خاکستریها بحث نکردیم. اعتبارش با طراحی است که فایل Figma را صادق نگه داشت.
- اندازهگیری خطاها گفتوگو را عوض کرد. از وقتی پلیر خطاها را گزارش داد، جملهی «روی گوشی من کار میکند» دیگر پایان بحث نبود.
آنچه اشتباه پیش رفت
روند
[پیشنویس — فرضیه] [شب یک دربی، ماه N]: انتشاری از پلیر که رفتار پخش خودکار را تغییر میداد، بعدازظهرِ مهمترین مسابقهی فصل منتشر شد. Safari در iOS پخش خودکار با صدا را مسدود میکند؛ بینندگان iOS یک قاب سیاه دیدند و هیچ دکمهی پخشی نبود. ظرف چند دقیقه از طریق کانال پشتیبانی تلگرام متوجه شدیم؛ اصلاح فوری (پخش خودکار بیصدا و لمس برای صدادار کردن) در [۴۰ دقیقه] منتشر شد.
علتها
- هیچ دستگاه iOS در ماتریس تست نبود؛ QA و تیم روی اندروید و دسکتاپ تست میکردند.
- انتشار در روز مسابقه زمانبندی شد، چون تقویم بخشی از فرایند انتشار نبود.
- پلیر هیچ feature flag یا کلید توقفی نداشت؛ بازگشت یعنی یک استقرار کامل.
- خطا در دقیقههای اول بیصدا بود، چون رویداد خطای «پخش خودکار مسدود شد» هنوز وجود نداشت.
- سیاستهای پخش خودکار مرورگرها را افراد میدانستند، اما جایی نوشته نشده بود.
اثر
بینندگان iOS [N] دقیقه از مسابقه را از دست دادند؛ پشتیبانی زیر پیامها غرق شد؛ اعتبار تیم در شبی که همه تماشا میکردند آسیب دید؛ و قاعدهی روز مسابقه متولد شد.
تشخیص و واکنش: پشتیبانی تلگرام ← کشیک فرانتاند ← شاخهی اصلاح فوری؛ سپس ماتریس دستگاهها و feature flag.
اقدامها
- توقف انتشار در روز مسابقه و روز مراسم — سرپرست فرانتاند
- Safari در iOS و اندروید ارزان در ماتریس Playwright از طریق یک آزمایشگاه دستگاه [پیشنویس] — فرانتاند
- کلید توقف و انتشار درصدی برای تغییرات پلیر — فرانتاند
- رویدادهای خطای پلیر، از جمله «پخش خودکار مسدود شد»، به آنالیتیکس با هشدار روی نرخ — فرانتاند و تیم داده
- یک سند یکصفحهای «سیاستهای رسانهی مرورگرها»
مورد دوم بازنگری [پیشنویس]: قواعد انطباق در heyatonline پس از طراحی اولین صفحههای پنل رسید، پس چند صفحه دوباره ساخته شد. علت: فهرست قواعد نزد تیم دیگری بود و دیر درخواست شد. اقدام: فهرست نهاد ناظر را در هفتهی اول بخواه و پیش از اولین صفحه در کامپوننتهای مشترک بساز.
اگر برمیگشتم
- نگهدارکامپوننت واحد پلیر، middleware SignalR و بازتاب توکنهای Figma در تم
- تغییر بدهماتریس دستگاهها — یک آیفون واقعی و یک اندروید ارزان — را پیش از دستزدن به پلیر راه میانداختم
- تغییر بدهfeature flag و انتشار درصدی پیش از اولین تغییر پلیر، نه پس از اولین رخداد
- تغییر بدهتقویم مسابقهها را از روز اول وارد فرایند انتشار میکردم
- تغییر بدهفهرست قواعد انطباق را پیش از اولین صفحه میخواستم
درسها
- پلیری که نتواند خطاهای خودش را گزارش کند، تمام نشده است.
- سئوی ساختاری (رندر سمت سرور، نشانی canonical، دادهی ساختیافته) بر هر ترفند متاتگی برتری دارد — و کاری بکاندی برای یک مهندس فرانتاند است.
- جای قواعد انطباق در کامپوننتهای مشترک است تا یک بار اعمال شوند.