Communitygithub.com

Persian Sub Tizone — ساخت زیرنویس SRT فارسی

خط لوله‌ای برای استخراج صدا از ویدیو، تبدیل به متن فارسی با 9Router STT، هم‌ترازی زمانی و خروجی SRT آماده برای Premiere.

Qu'est-ce que Persian Sub Tizone — ساخت زیرنویس SRT فارسی ?

این مهارت یک خط لوله کاملاً خودکار برای تولید زیرنویس SRT فارسی از ویدیوها یا فایل‌های صوتی است. با استفاده از ffmpeg برای استخراج صدا و سرویس 9Router STT (مدل‌های gemini یا groq) برای تشخیص گفتار، خروجی نهایی یک فایل SRT با زمان‌بندی دقیق و متن صیقلی‌شده است. الگوریتم هم‌ترازی زمانی از بلوک‌های هم‌پوشان ۶ ثانیه‌ای با hop=4s و difflib.SequenceMatcher استفاده می‌کند تا هر کلمه در جای درست خود در زیرنویس قرار گیرد. این مهارت برای کاربرانی که نیاز به زیرنویس سریع و دقیق برای ویرایش ویدیو در نرم‌افزارهایی مانند Premiere دارند، ایده‌آل است. پلتفرم‌های پشتیبانی‌شده شامل خط فرمان لینوکس/مک با پایتون ۳ است. مدیریت هوشمند خطا (رترای خودکار برای خطای 429، فال‌بک به مدل دیگر، ذخیره‌سازی میانی برای ادامه از نقطه قطع‌شده) و گزینه‌های خودآزمایی (selftest) از ویژگی‌های برجسته آن هستند.

Compatible avec✓Claude Code~Codex CLI~Cursor✓Gemini CLI
npx skills add peditx/persian-sub-tizone-skill

Demander à votre IA préférée

Ouvre une nouvelle conversation avec cette compétence d'agent déjà préchargée.

Documentation

Que fait Persian Sub Tizone — ساخت زیرنویس SRT فارسی ?

خط لوله: ffmpeg (استخراج صدا) → 9Router /v1/audio/transcriptions (ترنکرایب) → هم‌ترازی زمان با بلوک‌های هم‌پوشان → خروجی .srt.

روشِ سریع — همین را اجرا کن

یک فرمان، کل کار را می‌کند (استخراج صدا → متن کاملِ یک‌باره → بلوک‌های هم‌پوشان با کش → هم‌ترازی → نوشتن .srt):

python3 pipeline/stt.py "$VIDEO"                       # → کنارِ ویدیو: video.srt
python3 pipeline/stt.py "$VIDEO" -m groq/whisper-large-v3-turbo   # جایگزینِ مدل

خروجی: فایل video.srt کنار فایل اصلی — مستقیم در Premiere قابل Import است (File → Import یا درگ به تایم‌لاین).

رفتاری که باید بدانی:

  • کار در پوشهٔ <video>.pst/ کنارِ خود فایل می‌ماند؛ اجرای دوباره از همان‌جا ادامه می‌دهد (بلوک‌های تمام‌شده دوباره صدا زده نمی‌شوند، فقط بلوکِ نیمه‌کاره تکرار می‌شود).
  • ۴۲۹ (quota): پیام را می‌خواند و همان‌قدر می‌خوابد (۳ بار)، بعد اگر باز هم ۴۲۹ بود به gemini/gemini-2.5-flash-lite (سهمیهٔ جدا) فالبک می‌زند. فاصلهٔ ۳–۵ ثانیه بین فراخوانی‌ها و timeout هر کدام ۶۰ ثانیه هم داخل اسکریپت است.
  • مدل پیش‌فرض gemini/gemini-2.5-flash است (بهترین متن فارسی).
  • بدون کلید خاموش نمی‌شود: NINEROUTER_KEY نیست می‌گوید و می‌ایستد — کلید را از کاربر بگیر، حدس نزن.

چک‌ها (بدون کلید و بدون شبکه):

python3 pipeline/stt.py --selftest    # chunking + multipart + پارسِ 429
python3 pipeline/test_e2e.py          # کل خط لوله روی سرور STTِ جعلیِ محلی

پیش‌نیاز (یک‌بار)

export NINEROUTER_URL="https://9router.peditx.ir"   # یا هاست 9Router کاربر
export NINEROUTER_KEY="sk-..."
curl -s "$NINEROUTER_URL/api/health"                 # باید {"ok":true} بدهد

اگر key نبود از کاربر بگیر؛ حدس نزن.


جزئیات خط لوله (فقط برای عیب‌یابی/توسعه)

۱) استخراج صدا

DUR=$(ffprobe -v error -show_entries format=duration -of csv=p=0 "$VIDEO")
ffmpeg -y -i "$VIDEO" -vn -ac 1 -ar 16000 -b:a 64k /tmp/audio.mp3

۲) ترنکرایب — بلوک‌های هم‌پوشان ۶ ثانیه‌ای (hop=4s)

# هر کلمه در ≥ یک بلوک قرار می‌گیرد
ffmpeg -i audio.mp3 -ss <t> -t 6 -c copy ch/NN_t.mp3
# هر بلوک: POST /v1/audio/transcriptions  model=gemini/gemini-2.5-flash  response_format=json
  • ۴۲۹ را جدی بگیر: پیام را بخوان، reset after Xm را همان‌قدر بخواب، دوباره بزن.
  • پاسخ‌های خراب/ناقص را با try/except رد کن و حداکثر ۳ بار retry بزن.
  • خروجی‌ها را در chunks.json ذخیره کن تا resume کار کند.

۳) هم‌ترازی زمان (کلید کیفیت)

متن کامل را یک‌بار از روی کل ویدیو بگیر، بعد کلمات را روی بلوک‌ها با difflib.SequenceMatcher هم‌تراز کن و به هر کلمه یک زمان interpolate کن:

import difflib, re
W = lambda s: re.findall(r"[^\s]+", s)
norm = lambda w: re.sub(r"[^\w؀-ۿ]", "", w.lower())
gw = W(full_text)
# برای هر بلوک: بهترین جایگاه start در gw را با SequenceMatcher پیدا کن
# word j ← time = start + 6 * i/(n-1) ؛ خلأها را interpolate کن

اگر ratio < 0.45 بود بلوک را رد کن و از زمان‌های segments همان بلوک به‌عنوان fallback استفاده کن.

۴) ساختن SRT

MAX_CHARS, MAX_DUR = 84, 7.0
SOFT_CHARS, SOFT_DUR = 72, 6.0
# شکستن: اول پایان جمله (. ؟ !) با طول ≥30 → اولویت؛ وگنه کاما/طول/مدت
# اگر cue زیر 22 کاراکتر بود با cue قبلی ادغامش کن
def ts(x): return f"{int(x//3600):02d}:{int(x%3600//60):02d}:{int(x%60):02d},{int(round((x-int(x))*1000)):03d}"
# شکستن خط: تعادل دو خط در ۴۶ کاراکتر (حداکثر ۲ خط)

پیاده‌سازی: pipeline/mk_srt.py

python3 pipeline/mk_srt.py -t g.json -c chunks.json --media "$VIDEO" -o "${VIDEO%.*}.srt"
python3 pipeline/mk_srt.py --selftest

۵) صیقلی‌سازی املای فارسی (اختیاری)

{"model":"claude-sonnet-4-6","temperature":0.1,
 "messages":[{"role":"system","content":"ویراستار فارسی. خروجی فقط JSON معتبر."},
   {"role":"user","content":"فقط املای فارسی را اصلاح کن؛ معنی/تعداد کلمات/ترتیب را عوض نکن. خروجی: [{\"i\":..,\"t\":\"..\"}]"}]}

خروجی باید دقیقاً همان تعداد cue را برگرداند؛ وگرنه assert کن و به متن خام برگرد. نرمال‌سازی امن: ي → ی و ك → ک.

۶) تأیید نهایی

  • یک قاب از ویدیو در چند نقطه بخوان (fps=1/6) و متن زیرنویس روی تصویر را با cueها چک کن.
  • چند بخش کوتاه جدا (ffmpeg -ss) را STT بزن و با زمان cue تطبیق بده؛ اگر جابه‌جایی > ۲ ثانیه بود زمان‌ها را اصلاح کن.

نکات عملی

  • ffmpeg/ffprobe همیشه هستند؛ whisper محلی نصب نیست.
  • اسکریپت را با python3 - <<'PY' اجرا کن؛ برای payloadهای بزرگ -d @file.json بده نه argv.
  • زمان هر فراخوانی را محدود کن (-m 60)؛ timeout محیط حدود ۱۷۵ ثانیه است → فراخوانی‌ها را قطعه‌قطعه کن.
  • نام فایل نهایی: کنار ویدیو با پسوند .srt.

فایل‌های خروجی قابل استفاده در Premiere:

فایلکاربرد
video.srtزیرنویس — مستقیم File → Import یا درگ به تایم‌لاین
video.mp4.pst/chunks.jsonکش STT (برای fix دستی بعدی)
video.mp4.pst/full.jsonمتن کامل مرجع

Skills associés