4 changed files with 152 additions and 8 deletions
-
38apps/hadis/management/commands/set_hadiscorrection_fixed_text.py
-
34apps/hadis/serializers/category.py
-
1entrypoint.sh
-
87recorder_integration_report.md
@ -0,0 +1,38 @@ |
|||||
|
from django.core.management.base import BaseCommand |
||||
|
|
||||
|
from apps.hadis.models import HadisCorrection |
||||
|
|
||||
|
|
||||
|
ARABIC_TEXT = """بِسْمِ اللَّهِ الرَّحْمَنِ الرَّحِيمِ |
||||
|
الْحَمْدُ لِلَّهِ رَبِّ الْعَالَمِينَ، وَالصَّلَاةُ وَالسَّلَامُ عَلَى سَيِّدِنَا مُحَمَّدٍ وَآلِهِ الطَّاهِرِينَ. |
||||
|
اللَّهُمَّ زَيِّنَّا بِالْعِلْمِ وَالْحِلْمِ، وَاجْعَلْ أَلْسِنَتَنَا ذَاكِرَةً لَكَ وَقُلُوبَنَا خَاشِعَةً لَكَ. |
||||
|
وَارْزُقْنَا صِدْقَ الْقَوْلِ وَحُسْنَ الْعَمَلِ، إِنَّكَ أَنْتَ الْوَهَّابُ الْكَرِيمُ.""" |
||||
|
|
||||
|
|
||||
|
class Command(BaseCommand): |
||||
|
help = "Set a fixed Arabic text for all HadisCorrection records." |
||||
|
|
||||
|
def handle(self, *args, **options): |
||||
|
corrections = HadisCorrection.objects.all() |
||||
|
total = corrections.count() |
||||
|
|
||||
|
if total == 0: |
||||
|
self.stdout.write(self.style.WARNING("No HadisCorrection records found.")) |
||||
|
return |
||||
|
|
||||
|
self.stdout.write(self.style.WARNING(f"Updating {total} HadisCorrection records...")) |
||||
|
|
||||
|
updated_count = 0 |
||||
|
for correction in corrections: |
||||
|
correction.text = ARABIC_TEXT |
||||
|
correction.save() |
||||
|
updated_count += 1 |
||||
|
|
||||
|
if updated_count % 100 == 0: |
||||
|
self.stdout.write(f"Updated {updated_count}/{total}...") |
||||
|
|
||||
|
self.stdout.write( |
||||
|
self.style.SUCCESS( |
||||
|
f"Successfully updated {updated_count} HadisCorrection records." |
||||
|
) |
||||
|
) |
||||
@ -0,0 +1,87 @@ |
|||||
|
# گزارش ادغام رکوردر با سیستم PlugNMeet و Django |
||||
|
|
||||
|
این گزارش شامل تمام مراحل فنی طی شده جهت راهاندازی، اتصال، رفع باگ و بهینهسازی رکوردر جلسات آنلاین (`plugNmeet-recorder`) و ادغام کامل آن با پنل مدیریتی جنگو است. |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## ۱. راهاندازی و شبیهسازی محلی (Cloning & Docker Setup) |
||||
|
|
||||
|
برای بالا آوردن سیستم ضبط به صورت ایزوله و ارتباط آن با سرور کلاسها، کارهای زیر انجام شد: |
||||
|
|
||||
|
* **شبیهسازی رکوردر**: مخزن رسمی رکوردر در مسیر موازی پروژه تحت عنوان `plugNmeet-recorder` کلون گردید. |
||||
|
* **پیکربندی شبکه (Docker Compose)**: در فایل `docker-compose.yml` مربوط به سرور ویدیو کنفرانس، کانتینر رکوردر به کانتینرهای دیگر متصل شد: |
||||
|
* به کانتینرها اجازه دسترسی به شبکه اشتراکی پروژه (`imam-javad_backend_imam-javad`) داده شد تا رکوردر بتواند به وبهوک جنگو و سرور nats متصل شود. |
||||
|
* با استفاده از `extra_hosts` آیپی دامنه کلاسها به گیتوی کانتینرها مپ شد تا مرورگر داخلی رکوردر (Chrome بدون واسط گرافیکی) بتواند بدون مشکل با دامنه کلاس کار کند: |
||||
|
```yaml |
||||
|
extra_hosts: |
||||
|
- "meet.imamjavad.online:host-gateway" |
||||
|
``` |
||||
|
* تنظیم ولوم مشترک برای فایلهای ذخیرهشده ضبط (`plugnmeet-recordings`) صورت گرفت. |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## ۲. تنظیمات رکوردر (`recorder_config.yaml`) |
||||
|
|
||||
|
پیکربندی فایل تنظیمات رکوردر برای سازگاری کامل با سیستم محلی به شرح زیر انجام شد: |
||||
|
|
||||
|
* تنظیم شناسه رکوردر به `node_01` و حالت اجرایی به `both` (انجام همزمان ضبط زنده و فشردهسازی پس از اتمام ضبط). |
||||
|
* اتصال به وبسرویس nats از طریق: |
||||
|
```yaml |
||||
|
nats_info: |
||||
|
nats_urls: |
||||
|
- "nats://plugnmeet-nats:4222" |
||||
|
``` |
||||
|
* تنظیم اعتبارسنجی اتصال به API در بخش `plugNmeet_info` با استفاده از `api_key` و `api_secret` مشترک و هماهنگشده با جنگو. |
||||
|
* آدرسدهی فایل خروجی رکوردر به پوشه اشتراکی `/recording_files`. |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## ۳. پیادهسازی گیرنده وبهوک زنده (Django Webhook) |
||||
|
|
||||
|
در فایل [webhook.py](file:///f:/WORK/CODE/WORK/imam-javad/backend/apps/course/views/webhook.py)، تغییرات اساسی جهت دریافت اطلاعات فایل نهایی و وضعیت کاربران اعمال شد: |
||||
|
|
||||
|
### الف) رفع باگ مسیر رویداد ضبط (Webhook Routing) |
||||
|
سرور PlugNMeet رویداد اتمام ضبط و آمادهسازی فایل فشردهشده را با کلید `recording_proceeded` (حروف کوچک) ارسال میکرد. مسیردهی متناظر در جنگو اصلاح شد تا متد پردازشی درست فراخوانی گردد. |
||||
|
|
||||
|
### ب) اعتبارسنجی دوگانه امنیت (Signature Verification) |
||||
|
بررسی امنیتی پیامهای دریافتی مجهز به دو لایه شد: |
||||
|
1. **اعتبارسنجی JWT (استاندارد PlugNMeet)**: بررسی صحت امضا با توکن رمزنگاریشده با الگوریتم HS256 و کلید `PLUGNMEET_API_SECRET`. |
||||
|
2. **بررسی پشتیبان HMAC-SHA256**: برای سناریوهای تستی و کلاینتهای قدیمی که توکن JWT خام ارسال میکنند. |
||||
|
|
||||
|
### ج) دانلود و پردازش خودکار فایل ویدیویی |
||||
|
درون متد `_handle_recording_proceeded` فرآیند زیر پیاده شد: |
||||
|
1. **دانلود امن**: توکن موقت دانلود از API دریافت شده و فایل ویدیویی از آدرس وبسرویس دانلود و در حافظه موقت سیستم ذخیره میگردد. |
||||
|
2. **محاسبه مدت زمان ویدیو (Video Duration)**: با اجرای ابزار سیستم `ffprobe` در پسزمینه، طول دقیق ویدیو به صورت پویا استخراج میشود. |
||||
|
3. **ثبت در پایگاه داده**: فایل در مدل `LiveSessionRecording` ذخیره میگردد. |
||||
|
4. **تولید خودکار کاور تصویر (Thumbnail)**: با استفاده از دستورات `ffmpeg` فریمی در ثانیه اول ویدیو استخراج و تغییر سایز داده شده و به عنوان پیشنمایش در فیلد مربوطه ذخیره میشود. |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## ۴. ثبت خودکار درس در دوره آموزشی (Django Signals) |
||||
|
|
||||
|
پس از ثبت فایل ضبط شده، یک پروسه اتوماتیک در فایل [signals.py](file:///f:/WORK/CODE/WORK/imam-javad/backend/apps/course/signals.py) از طریق سیگنال `post_save` روی مدل `LiveSessionRecording` فعال میشود: |
||||
|
|
||||
|
1. **جلوگیری از ثبت تکراری**: با فیلتر کردن نام فایلی که ثبت میشود، تضمین میگردد وبهوکهای همزمان یا ذخیرهسازیهای مجدد باعث ایجاد درسهای تکراری نشوند. |
||||
|
2. **ساخت هوشمند چپتر**: تاریخ ضبط جلسه استخراج شده و به عنوان نام چپتر (به فرمت `YYYY-MM-DD`) ثبت میشود. در صورتی که این چپتر از قبل برای این دوره وجود داشته باشد، از همان چپتر استفاده میشود. |
||||
|
3. **نامگذاری خودکار درسها (Part n)**: |
||||
|
* تعداد درسهای موجود در **همان چپتر مشخص** شمرده میشود. |
||||
|
* بر اساس فرمول `تعداد درسها + ۱` نام درس به عنوان بخش بعدی (مثلا `Part 4` در صورتی که ۳ درس در چپتر باشد) تعیین میشود. |
||||
|
4. **ساخت شیء درس و انتساب به دوره**: آبجکت `Lesson` ساخته شده و به مدل واسط `CourseLesson` مرتبط میگردد. |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## ۵. سیستم پایش حضور و غیاب مطمئن (Fail-safe Participant Tracking) |
||||
|
|
||||
|
وبهوکهای LiveKit مربوط به ملحق شدن و خروج کاربران در زمان تستهای محلی ممکن است به دلیل مسائل شبکه یا فایروال به درستی کار نکنند. به همین منظور مکانیزم پشتیبان (Fail-safe) زیر طراحی شد: |
||||
|
|
||||
|
* **ثبت زودهنگام حضور (در [live_session.py](file:///f:/WORK/CODE/WORK/imam-javad/backend/apps/course/views/live_session.py))**: |
||||
|
در متد `CourseLiveSessionTokenAPIView.post` که توکن دسترسی برای کاربران تولید میکند، درست قبل از ورود کاربر به کلاس، یک رکورد حضور در مدل `LiveSessionUser` با وضعیت آنلاین ایجاد/بروزرسانی میشود. |
||||
|
* **ثبت خودکار خروج در زمان بسته شدن کلاس**: |
||||
|
در متد پایش فعالِ کلاس (`_verify_room_is_active`) و متد دریافت اتمام رویداد وبهوک، به محض اینکه سرور تشخیص دهد کلاس به اتمام رسیده است، وضعیت تمام کاربران حاضر در آن جلسه به صورت خودکار آفلاین (`is_online=False`) شده و زمان خروج آنها در فیلد `exited_at` ثبت میگردد. |
||||
|
* **نادیده گرفتن رباتهای سیستمی**: |
||||
|
رویدادهای کاربرانی نظیر `RECORDER_BOT` (که مرورگر رکوردر است) به دلیل عدم همخوانی با شناسههای کاربری عددی از محاسبات پایگاه داده فیلتر و نادیده گرفته میشوند تا پایگاه داده با خطای تبدیل نوع (Type Casting) روبرو نشود. |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
> [!TIP] |
||||
|
> برای اجرای تست سناریوها در محیط توسعه محلی بدون نیاز به اجرای دستی مرورگرها، میتوانید از اسکریپت بهروزشده `backend/scripts/test_webhook.py` همراه با شناسه رویدادهای مختلف نظیر `RECORDING_PROCEEDED` استفاده نمایید. |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue