Communitygithub.com

peditx/persian-sub-tizone-skill — ساخت زیرنویس SRT فارسی

استخراج خودکار زیرنویس SRT فارسی از ویدیو/آدیو با استفاده از ffmpeg، STT 9Router و الگوریتم هم‌ترازی بلوک‌های هم‌پوشان. قابل استفاده در Premiere.

persian-sub-tizone-skill — ساخت زیرنویس SRT فارسی 是什麼?

این مهارت (Skill) یک خط لوله کامل برای تولید فایل‌های زیرنویس SRT فارسی از ویدیوها یا فایل‌های صوتی فراهم می‌کند. فرآیند کار به این صورت است: ابتدا با استفاده از ffmpeg صوت از ویدیو استخراج می‌شود، سپس متن کامل توسط سرویس «9Router /v1/audio/transcriptions» (با مدل پیش‌فرض gemini/gemini-2.5-flash) تولید می‌شود. در مرحله بعد، صدا به بلوک‌های ۶ ثانیه‌ای با هم‌پوشانی ۴ ثانیه تقسیم می‌شود و هر بلوک جداگانه استخراج و رونویسی می‌شود. یک الگوریتم پیشرفته با استفاده از `difflib.SequenceMatcher` کلمات را بین این بلوک‌ها هم‌تراز می‌کند و زمان‌بندی دقیق هر کلمه را محاسبه می‌کند. در نهایت، زیرنویس‌ها بر اساس قوانینی مانند حداکثر طول خط و مدت زمان نمایش شکسته شده و به صورت فایل `.srt` خروجی داده می‌شود. این مهارت از resume خودکار (در صورت قطع شدن)، مدیریت هوشمند خطای 429 (Rate Limit) با fallback به مدل دیگر، و یک سرویس STT جعلی برای تست آفلاین پشتیبانی می‌کند. پلتفرم‌های پشتیبانی شامل Claude Code، Cursor و Codex است.

相容平台✓Claude Code~Codex CLI~Cursor✓Gemini CLI
npx skills add peditx/persian-sub-tizone-skill

在你喜歡的 AI 中提問

開啟一個已預先載入此 Agent Skill 的新對話。

說明文件

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متن کامل مرجع

相關技能