v2.2.0 FINAL STABLE حفظ و مستند شده است: زمانبندی توسعه، معماری نهایی، Benchmark چهار مدل Faster-Whisper، دلیل انتخاب medium، Thermal Guard، منطق SRT-first، Run واقعی 552 فایل، Validation و محدودیتهای پذیرفتهشده. اعداد Benchmark و نتایج Run همان Evidence ثبتشده پروژه هستند و برای زیباتر شدن مقاله تغییر داده نشدهاند.media2ass چیست؟
media2ass یک Pipeline اختصاصی برای تبدیل Media به زیرنویس ASS است. این ابزار ابتدا فایلها را کشف میکند، خروجی معتبر قبلی را تشخیص میدهد، SRT موجود را بررسی میکند، وجود Audio Stream را با ffprobe میسنجد و فقط زمانی که لازم باشد Audio را برای Faster-Whisper پردازش میکند. خروجی نیز صرفاً نوشته نمیشود؛ Timeline، Overlap، طول Line، Text Integrity و وضعیت Run بررسی و ثبت میشوند.

به همین دلیل media2ass را نباید فقط یک «اسکریپت Speech-to-Text» در نظر گرفت. بخش مهم پروژه مدیریت Failure، Resume، Validation، Thermal Safety و حفظ قابلیت بازسازی Run است. همین لایههای اطراف مدل گفتار بودند که در تستهای واقعی بیشترین خطاهای مهم را آشکار کردند.
پیشنیازهای کار
قبل از ورود به روند واقعی پروژه، بهتر است اجزایی را که در این Pipeline نقش دارند بشناسیم. media2ass فقط یک اسکریپت تبدیل صدا به متن نیست؛ چند ابزار مختلف کنار هم قرار میگیرند تا Media شناسایی شود، Audio بررسی و استخراج شود، گفتار با مدل محلی Transcribe شود، خروجی به ASS تبدیل و Validate شود و در پایان وضعیت Run همراه با اطلاعات حرارتی و Diagnostic ذخیره شود.
نسخهای که در این مقاله مستند شده روی Windows و با PowerShell اجرا شده است. Production نهایی روی سیستم دارای پردازنده AMD Ryzen 5 7500F و کارت NVIDIA GeForce RTX 5060 Ti 16GB با CUDA تست شد. بنابراین اعداد Benchmark و دما مربوط به همین محیط هستند و نباید بدون تست به سختافزار دیگری تعمیم داده شوند.
برای استفاده از Source نهایی، باید Project Root، Python Runtime محلی، Packageهای Python، مدل Faster-Whisper و ابزارهای FFmpeg در دسترس باشند. در این پروژه این اجزا به شکلی نگهداری شدند که Normal Production پس از Setup اولیه به دانلود Model یا System Python وابسته نباشد.
ابزارهای پروژه
هر کدام از ابزارهای زیر یک وظیفه مشخص در Pipeline دارند. شناخت این نقشها کمک میکند وقتی خطایی رخ میدهد، سریعتر مشخص شود مشکل در Media، Audio Extraction، مدل گفتار، GPU Runtime یا لایه Monitoring است.
| ابزار | کاربرد در پروژه | نکته |
|---|---|---|
| Python | اجرای هسته media2ass و کتابخانههای AI | در نسخه نهایی Runtime محلی پروژه استفاده میشود |
| Faster-Whisper | تبدیل گفتار انگلیسی به متن همراه Timestamp | مدل پیشفرض Production برابر medium است |
| CTranslate2 | Runtime بهینه اجرای مدل Faster-Whisper | در تست نهایی همراه GPU/CUDA استفاده شد |
| FFmpeg | خواندن Media و استخراج Audio مناسب برای Transcription | خطای Audio Extraction از خطای Whisper جدا ثبت میشود |
| ffprobe | بررسی Streamهای Media قبل از پردازش | برای تشخیص فایل بدون Audio در v2.2.0 حیاتی شد |
| PowerShell | Entry Point، Self-test و کنترل اجرای پروژه | مسیر ورودی هنگام Run به اسکریپت داده میشود |
| LibreHardwareMonitor | خواندن سنسورهای CPU | Tctl/Tdie سنسور Control پروژه است |
| nvidia-smi / NVML | GPU Temperature، Load، VRAM و Power | منبع اصلی GPU Telemetry در سیستم تست |
وجود HWiNFO64 برای اجرای خود media2ass الزامی نیست. در این پروژه از HWiNFO برای Cross-check دستی سنسورها استفاده شد، در حالی که Monitoring خودکار CPU از LibreHardwareMonitor و GPU از NVML انجام میشود.
Faster-Whisper چیست؟
Faster-Whisper پیادهسازی مدل Whisper بر پایه CTranslate2 است. در media2ass از آن بهعنوان موتور محلی Speech-to-Text استفاده شد تا گفتار همراه Timestamp به متن تبدیل شود. مدلهای پروژه بهصورت Local نگهداری شدند تا Production پس از Setup اولیه برای دریافت Model به اینترنت وابسته نباشد.
نکته مهم این است که FFmpeg و ffprobe در این پروژه بخشی از معماری خود media2ass هستند؛ Faster-Whisper در پیادهسازی رسمی میتواند Audio را از طریق PyAV Decode کند و الزام ذاتی به نصب System FFmpeg ندارد. در media2ass استفاده از FFmpeg/ffprobe تصمیم معماری برای کنترل Media، استخراج Audio و تشخیص Streamها بود.
مدلهای Whisper
Faster-Whisper میتواند با Modelهای مختلف اجرا شود. در این پروژه چهار مدل tiny، small، medium و large-v3 بهصورت Local وارد و Verify شدند تا انتخاب مدل بر اساس Benchmark واقعی انجام شود، نه صرفاً بزرگتر بودن Model.
مدل کوچکتر معمولاً سریعتر و کممصرفتر است، اما ممکن است روی نامهای خاص و اصطلاحات فنی اشتباه بیشتری داشته باشد. مدل بزرگتر هم الزاماً در هر Dataset بهتر نیست؛ همین پروژه نشان داد large-v3 در محتوای تستشده افزایش کیفیت قابل اثباتی نسبت به medium نداشت و در یکی از Benchmarkهای اولیه حتی یک Repetition Loop ایجاد کرد.
به همین دلیل در ادامه مقاله قبل از رسیدن به Run Production، Benchmark مدلها را جدا بررسی میکنم و توضیح میدهم چرا در نهایت medium بهعنوان Default انتخاب شد.
فرمت ASS چیست؟
ASS یا Advanced SubStation Alpha یک فرمت زیرنویس متنی است که علاوه بر زمانبندی Dialogue، امکان کنترل دقیقتر Style، Position و Formatting را نسبت به فرمت سادهتری مانند SRT فراهم میکند. در این پروژه هدف اصلی تولید ASS استاندارد و قابل استفاده در Pipelineهای بعدی ویدیو بود.
برای همین فقط ساختهشدن فایل کافی نبود. Timeline، ترتیب Cueها، Overlap، طول Line، Text Integrity و قرار نگرفتن Cue خارج از Media Timeline نیز بررسی شدند. یکی از مهمترین درسهای پروژه همین بود که یک Transcript خوب با Post-processing اشتباه میتواند به Subtitle بد تبدیل شود.
هدف پروژه
نسخه قدیمی جریان کاری من بر پایه اسکریپتی شبیه #Subtitle-VGA.py بود. در 21 اوت 2026، حدود ساعت 12:31، آن مسیر هنگام دسترسی به مدل با خطای Hugging Face Authentication متوقف شد. همان نقطه باعث شد مسئله را فقط بهعنوان «رفع یک Error» نبینم و بهجای Patch کردن اسکریپت قدیمی، ساختار پروژه را از نو طراحی کنم.
Requirementهای اصلی بهتدریج به این شکل تثبیت شدند: Runtime باید داخل خود پروژه باشد، بعد از Setup اولیه اجرای Production به اینترنت وابسته نباشد، مسیر ورودی هنگام اجرا قابل تعیین باشد، FFmpeg و مدلها مسیر مشخص داشته باشند، خروجی کنار Media ساخته شود، Runهای قبلی قابل Resume باشند و هر Run یک Diagnostic Bundle قابل بررسی بسازد.
Root نهایی پروژه روی سیستم توسعه این مسیر بود:
D:\AI\media2assاین مسیر برای خواننده الزام نیست؛ اگر ساختار مشابهی پیاده میکنید میتوانید Root را تغییر دهید، اما تمام Pathهای وابسته باید با هم سازگار بمانند.
معماری media2ass
در نسخه نهایی، تصمیم اصلی این بود که Whisper اولین انتخاب نباشد. Pipeline ابتدا بررسی میکند آیا خروجی معتبر قبلی وجود دارد یا نه، سپس به دنبال SRT مناسب میگردد و فقط در صورت نیاز Audio را برای Faster-Whisper آماده میکند.
Input path
↓
Discover media recursively
↓
Valid existing .media2ass.ass?
├─ YES → SKIP CONVERTED
└─ NO
↓
Matching SRT?
├─ valid → SRT → ASS
├─ invalid → Whisper fallback
└─ absent
↓
ffprobe audio preflight
├─ no audio → NO AUDIO / non-failure
└─ audio exists
↓
Thermal safe?
↓
Faster-Whisper medium
↓
Timing-preserving segmentation
↓
ASS write + validation
↓
Diagnostics + RESULTS-HISTORY.md + tar.xzاین ترتیب چند مزیت عملی داشت. فایل معتبر دوباره Transcribe نمیشود، SRT قابلاعتماد بدون هزینه AI استفاده میشود، Media بدون Audio بهاشتباه Failure محسوب نمیشود و هر مرحله قبل از رفتن به مرحله سنگینتر داده کافی برای عیبیابی تولید میکند.
فایلهای اصلی
| فایل | نقش | وضعیت |
|---|---|---|
run.ps1 | Entry Point اصلی و اجرای Python داخلی | Active |
run.bat | Wrapper ساده برای اجرا | Active |
media2ass.py | هسته Discovery، SRT، Whisper، ASS و Validation | Active |
config.json | تنظیمات Canonical پروژه | Active |
SELF-TEST.ps1 | Self-test محیط و API | Active |
DJH-CPU-Telemetry-Bridge.ps1 | خواندن سنسور CPU با LibreHardwareMonitor | Active |
RESULTS-HISTORY.md | تاریخچه Append-only اجراها و Validationها | Active |
محیط اجرا
این پروژه روی Windows و سیستم DJH PC توسعه و تست شد. سختافزار ثبتشده برای تستهای اصلی شامل AMD Ryzen 5 7500F و NVIDIA GeForce RTX 5060 Ti 16GB بود. این مشخصات روی اعداد Performance و Thermal اثر دارند و نباید نتایج Benchmark را بدون توجه به سختافزار به سیستم دیگری تعمیم داد.
در Runtime نهایی، Python از داخل خود پروژه اجرا میشود و Normal Production نباید به System Python وابسته باشد. FFmpeg و FFprobe نیز در این پروژه بهصورت ثابت از مسیر زیر خوانده میشوند:
C:\D\ffmpeg\ffmpeg.exe
C:\D\ffmpeg\ffprobe.exeنسخههای Package که در Setup اولیه ثبت شده بودند شامل faster-whisper 1.2.1، ctranslate2 4.8.1، tokenizers 0.23.1، onnxruntime 1.29.0 و av 18.1.0 بودند. برای CPU Telemetry از LibreHardwareMonitor و برای GPU از nvidia-smi/NVML استفاده شد.
این پروژه برای Normal Run به اینترنت وابسته نیست. دانلود Packageها و Modelها در مرحله Setup میتواند Network بخواهد، اما Production باید از Modelهای Local استفاده کند.
Runtime مستقل
اولین تصمیم معماری این بود که مشکل Authentication مدل را با Retry یا Credential جدید دور نزنم. چون هدف نهایی Offline Production بود، وابستگی Runtime به Hugging Face در هر اجرا خودش یک Failure Point محسوب میشد. راهحل این شد که Python، Packageها و Modelها داخل Project Tree نگهداری شوند.
حدود ساعت 13:00 روز 21 اوت، Setup اولیه Project-local Runtime کامل شد. در ادامه، مدلهای موجود نیز بین حدود 13:24 تا 13:27 Import و با SHA-256 بررسی شدند. اولین Run موفق Standalone نسخه v1.2.1 در ساعت 13:06 روی دو Media ثبت شد.
ساختار Restoreشونده به شکل زیر است:
D:\AI\media2ass\
├─ python\
├─ packages\
├─ models\
├─ tools\
├─ media2ass.py
├─ run.ps1
├─ run.bat
├─ config.json
└─ RESULTS-HISTORY.mdRuntimeهای حجیم مثل Python، Packageها و Modelها در Knowledge Archive نهایی Duplicate نشدند؛ برای Restore واقعی باید آنها را جداگانه نگه داشت.
Benchmark مدلها
نکته: این Benchmark یک رتبهبندی عمومی برای همه مدلهای Whisper نیست؛ نتیجه به Dataset، سختافزار، تنظیمات و نسخههای همین پروژه وابسته است و باید در همان Context تفسیر شود.
بعد از اینکه نسخه Standalone کار کرد، سؤال بعدی این بود که کدام مدل برای محتوای آموزشی انگلیسی انتخاب شود. چهار مدل tiny، small، medium و large-v3 روی فایلهای واقعی مقایسه شدند. اولین Benchmark فقط انتخاب مدل را حل نکرد؛ یک Regression جدی در Segmentation را هم آشکار کرد.
Benchmark اول
در تست اولیه 7 فایل، زمان خام Transcription چنین ثبت شد:
| مدل | زمان کل | برداشت اولیه |
|---|---|---|
tiny | 55.50s | بسیار سریع، خطای واژگانی بیشتر |
small | 103.77s | تعادل سرعت و کیفیت |
medium | 172.89s | کیفیت پایدارتر |
large-v3 | 233.50s | کندتر و یک Repetition Loop مشاهده شد |
در همان تست، tiny خطاهایی مانند تشخیص نادرست نامها و اصطلاحات داشت. small و medium از نظر Word-level نزدیک بودند، اما medium در واژههای تخصصی پایدارتر دیده شد. در یکی از فایلها نیز large-v3 وارد حلقه تکرار شد و یک عبارت 8 کلمهای 24 بار تکرار شد. این موضوع بهتنهایی ثابت نمیکرد large-v3 همیشه مشکل دارد، اما کافی بود که آن را بدون Retest بهعنوان Default انتخاب نکنم.
خطای Timeline
مهمترین کشف Benchmark اول مربوط به مدل Whisper نبود؛ مشکل در Post-processing خود پروژه بود. تقریباً 99 درصد Cueها به حدود یک ثانیه نزدیک میشدند. Config اولیه min_duration = 1.0 و min_gap = 0.08 داشت و الگوریتم برای رسیدن به Minimum، Timestampهای بعدی را به جلو Push میکرد.
نتیجه یک Drift تجمعی بود. برای یک فایل واحد، آخرین Timestamp خروجی چهار مدل به این شکل ثبت شد:
tiny 978.73 sec
small 868.13 sec
medium 912.97 sec
large-v3 812.97 secاختلاف 165.76 ثانیه برای یک Media واحد قابل قبول نبود. در فایل دیگری Spread تا 314.40 ثانیه رسید. این Evidence نشان داد مشکل «اختلاف طبیعی مدلها» نیست، بلکه خود Segmentation Timeline را تغییر میدهد.
چرا CPS هم گمراهکننده شد؟
در همان نسخه، سقف متن هر Cue برابر 84 کاراکتر و Duration اجباری تقریباً یک ثانیه بود. بنابراین Max CPS مدلها به شکل مصنوعی روی عدد 84 میافتاد:
84 characters / 1 second = 84 CPSاین معیار دیگر کیفیت واقعی Subtitle را اندازه نمیگرفت؛ فقط محدودیت الگوریتم را منعکس میکرد. بنابراین تصمیم گرفتم هیچ انتخاب Production را بر اساس آن Benchmark قفل نکنم.
Fix اصلی
قاعده نهایی Segmentation این شد:
preserve_source_timestamps = true
never_shift_next_cue = trueیعنی برای بهتر کردن خوانایی میتوان Cue را در فضای آزاد Merge یا Extend کرد، اما هیچ Cue حق ندارد Cue بعدی را به جلو هل بدهد. این تصمیم یکی از مهمترین Invariantهای نسخه نهایی است، چون Regression آن مستقیماً Sync زیرنویس را خراب میکند.
Retest مدلها
پس از اصلاح Timeline و Line Wrapping، Benchmark دیگری روی 4 فایل انجام شد. این بار 16 خروجی ASS ساخته شد و هر چهار مدل تقریباً در یک نقطه Timeline را تمام کردند. برای نمونه، در فایل چهارم پایان Timeline بین مدلها فقط حدود 0.11 ثانیه اختلاف داشت:
tiny 972.04s
small 971.96s
medium 972.07s
large-v3 972.07sهمچنین Overlap برابر صفر شد و حداکثر طول Line دقیقاً در 42 کاراکتر نگه داشته شد.
کیفیت واژگان
در Dataset مربوط به Claude، مدل medium در تشخیص نام اصلی موضوع پایدارتر بود. شمارش ثبتشده برای واژه Claude در آن تست:
| مدل | Claude صحیح | خطاهای شاخص |
|---|---|---|
tiny | 43 | Cloud در 64 مورد |
small | 81 | Clod در 21 مورد |
medium | 100 | Cloud در 8 مورد |
large-v3 | 27 | Cloud و Clod بیشتر |
این معیار بهتنهایی Accuracy کامل مدل نیست، اما چون محتوای تست مرتب درباره Claude صحبت میکرد، برای همین Dataset شاخص معناداری بود. در Word Sequence، مدلهای small، medium و large-v3 اغلب حدود 95 تا 98 درصد به هم نزدیک بودند.
Performance سالم
| مدل | زمان 4 فایل | VRAM Max |
|---|---|---|
tiny | 77.93s | ~956 MB |
small | 136.86s | ~1490 MB |
medium | 230.09s | ~2383 MB |
large-v3 | 281.20s | ~3796 MB |
large-v3 حدود 22 درصد کندتر از medium بود، VRAM بیشتری مصرف کرد و در این Dataset افزایش کیفیت قابل اثباتی نداد. به همین دلیل medium به Default Production تبدیل شد و small گزینه مناسبتر برای Fast Mode باقی ماند.
Retry اضافی
Retest سالم یک مشکل جدید را هم نشان داد: هر 16 Transcription یک Retry انجام میدادند و Trigger تقریباً همیشه word_timing بود. حتی در مواردی که نتیجه اول انتخاب میشد، هزینه Transcription دوم پرداخت شده بود.
این رفتار یک Regression Performance بود، نه Failure کیفیت. مثال ثبتشده نشان میداد زمان یک فایل با مدل small از حدود 7.6 ثانیه در تست قبلی به 15.77 ثانیه رسیده است؛ تقریباً دو برابر، چون عملاً دوبار Transcribe میشد.
Fix این بود که word_timing بهعنوان Trigger عمومی Retry حذف شود:
"retry_on_word_timing": falseRetry در نسخه Production فقط برای Anomalyهای واقعی مانند Repetition، Overlap، Timing anomaly شدید، Text Integrity یا Cue خارج از Media Timeline باقی ماند. بررسی Runهای Production بعدی نشان داد Retry غیرضروری دیگر روی هر فایل تکرار نمیشود.
کنترل دما
چون پروژه برای Batchهای چندساعته طراحی شده بود، فقط ثبت Temperature کافی نبود؛ Thermal Monitoring باید واقعاً روی جریان کار اثر میگذاشت. برای GPU منبع اصلی NVML از طریق nvidia-smi شد و برای CPU از LibreHardwareMonitor استفاده کردم.
ابهام سنسور CPU
در توسعه اولیه، Labelهایی مثل CPU Package و CPU Max بیش از حد مبهم بودند. روی Ryzen 5 7500F سه مقدار مهم دیده میشدند: CPU Tctl/Tdie، CPU Die Average و CPU CCD1 Tdie. مشکل این بود که CCD1 میتوانست Spike محلی بالاتری نشان دهد و اگر آن را معادل Control Temperature میگرفتم، Pauseهای کاذب ایجاد میشد.
Policy نهایی نقش سنسورها را جدا کرد:
CPU Tctl/Tdie [CONTROL]
CPU Die Average [INFO]
CPU CCD1 Tdie [LOCAL]
GPU Core [CONTROL]بنابراین CCD1 برای Diagnostic ثبت میشود، ولی بهتنهایی Trigger اصلی Pause نیست.
Policy نهایی
Pause heavy work : 85°C
Resume : <=70°C
Poll : 5s
Stable samples : 3این Hysteresis عمداً بزرگ انتخاب شد تا سیستم بعد از رسیدن به Limit واقعاً فرصت Cooling داشته باشد و در محدوده 84/83 درجه مرتب Pause/Resume نکند.
Run با Policy قدیمی
در v2.1.0 هنوز Policy قدیمی 80°C / 75°C فعال بود و CCD/local sensor نیز در Pause logic نقش داشت. در Production Run سه Thermal Pause ثبت شد و مجموع Wait حدود 90 ثانیه بود. Peak کنترل CPU تقریباً 82.125°C و GPU Peak برابر 61°C ثبت شد. این تجربه یکی از شواهدی بود که باعث شد Sensor Roles و Thresholdها شفافتر شوند.
منطق SRT-first
یکی از بهینهسازیهای مهم Production این بود که فایل SRT معتبر از Whisper ارزشمندتر است؛ چون Transcript آماده دارد، زمان پردازش را کم میکند و از یک Encode/Transcription غیرضروری جلوگیری میکند.
در Run واقعی 552 فایل، 94 SRT دقیقاً با Media متناظر Match شدند. از این تعداد 88 فایل مستقیم به ASS تبدیل شدند و 6 SRT به دلیل Overlap نامعتبر تشخیص داده شدند. هیچ Fuzzy ambiguity ثبت نشد.
برای 6 SRT نامعتبر، رفتار پروژه Safe بود: SRT حذف یا Force نشد، بلکه Failure آن ثبت شد و همان Media با Whisper پردازش شد. هر 6 Fallback در Run Production موفق شدند.
این بخش یک تفاوت مهم بین Match صحیح و Subtitle صحیح را نشان داد. فایل ممکن است از نظر نام دقیقاً متعلق به Media باشد، اما Timing داخلی آن هنوز نامعتبر باشد. بنابراین Matching بهتنهایی برای Trust کردن Subtitle کافی نیست.
تست 552 فایل
مهمترین Validation پروژه، اجرای Production روی 552 Media بود. نسخه v2.1.0 از حدود ساعت 17:40 تا 20:16 روز 21 اوت اجرا شد. نتیجه آن نشان داد معماری کلی درست است، اما یک Edge Case باقی مانده است.
| وضعیت | تعداد | تفسیر |
|---|---|---|
| SRT → ASS | 88 | تبدیل مستقیم |
| Whisper → ASS | 459 | Transcription محلی |
| Skip Converted | 4 | ASS معتبر قبلی |
| SRT invalid → Whisper | 6 | Fallback موفق |
| Failed | 1 | Edge Case صوت |
در عمل 551 فایل از 552 فایل به نتیجه قابل استفاده رسیدند. تنها Failure مربوط به فایل 7 Netstat Commands Explained For Network Analysis.mp4 بود. Error ثبتشده:
Output file does not contain any stream
Error opening output file ...audio-0016.wav
Error opening output files: Invalid argumentدر ابتدا این پیام داخل Stage مربوط به Whisper دیده میشد، اما Diagnostic Bundle نشان داد Whisper اصلاً شروع نشده بود؛ خطا در Audio Extraction رخ داده بود. این تفاوت برای Root Cause مهم بود.
فایل بدون صدا
بررسی Diagnostic نشان داد Media مشکلدار SRT نداشت و FFmpeg هنگام ساخت WAV هیچ Audio Stream پیدا نمیکرد. بنابراین Symptom «Whisper failed» نبود؛ Root Cause واقعی این بود که فایل ورودی Audio Stream نداشت.
اینجا دو وضعیت باید از هم جدا میشدند:
NO AUDIO
→ Media اصلاً Audio Stream ندارد
→ Non-failure / skip
AUDIO EXTRACT FAILED
→ ffprobe Audio را دیده، اما FFmpeg نتوانسته آن را استخراج کند
→ Failure واقعیدر v2.2.0 قبل از Extract یک ffprobe preflight اضافه شد و گزینه زیر در Config تثبیت شد:
"probe_before_extract": true
"no_audio_is_failure": falseاین تغییر یک Workaround نبود؛ Classification Pipeline اصلاح شد تا Error واقعی از وضعیت طبیعی Media بدون Audio جدا شود.
Validation نهایی
Run نهایی v2.2.0 با ID 20260821-203437 در ساعت 20:34:45 به Summary نهایی رسید. چون 551 خروجی معتبر قبلاً در Run بزرگ ساخته شده بودند، هدف این Run تبدیل دوباره نبود؛ هدف تأیید Resume/Skip Logic و Edge Case بدون Audio بود.
TOTAL : 552
SKIP CONVERTED : 551
NO AUDIO : 1
AUDIO EXTRACT FAIL: 0
FAILED : 0
THERMAL PAUSES : 0
THERMAL WAIT : 0.0 sec
LIMIT REACHED : NO
MEDIA2ASS RESULT: PASSدر این Validation، Peak سنسور کنترل CPU حدود 56°C، CCD1 حدود 54.25°C و GPU Core برابر 43°C ثبت شد. این Run سبکتر بود چون تقریباً همه فایلها Skip شدند؛ بنابراین این دماها را نباید با Run کامل Transcription بهعنوان Benchmark مستقیم مقایسه کرد.
نتیجه Dataset در آخرین وضعیت قابل اثبات این است: 551 Media دارای ASS معتبر هستند، یک Media Audio Stream ندارد و Failure حلنشدهای باقی نمانده است.
کیفیت زیرنویس
بعد از PASS شدن Pipeline، فقط به Exit Code اکتفا نکردم. یک بسته واقعی از Subtitleها بررسی شد تا ساختار ASS، Timing و محدودیت Line Length مستقل از Run Summary کنترل شود.
Audit فایلمحور روی 93 ASS با مجموع 12,010 Cue این نتیجه را داد:
| معیار | نتیجه | وضعیت |
|---|---|---|
| Overlap | 0 | PASS |
| Duration نامعتبر | 0 | PASS |
| File غیر Monotonic | 0 | PASS |
| Line بیش از 42 کاراکتر | 0 | PASS |
| Cue بالای 20 CPS | 2,560 | Known Limitation |
| Max CPS | 40.48 | Non-blocking |
همچنین 6 فایل SRT منبع با مجموع 1,662 Cue بررسی شدند و همان 6 Overlap منبعی که Pipeline گزارش کرده بود در Audit نیز دیده شد. این تطابق، رفتار Fallback را تأیید کرد.
چرا CPS را Force نکردم؟
21.32 درصد Cueهای نمونه بالاتر از 20 CPS بودند، اما تجربه Regression قبلی نشان داده بود که «اصلاح» خوانایی با جابهجایی Timestamp میتواند Sync کل فایل را خراب کند. بنابراین CPS در نسخه نهایی یک Warning/Accepted Limitation است، نه شرطی که Timeline را دستکاری کند. هر Optimization آینده باید بدون Shift کردن Cue بعدی انجام شود.
Config نهایی
بخشهای زیر هسته تنظیمات Production v2.2.0 را نشان میدهند. اینها Extract مرتبط از Config واقعی هستند، نه یک Config فرضی.
Whisper و Retry
{
"models": {
"active": "medium"
},
"whisper": {
"language": "en",
"device": "auto",
"compute_type": "auto",
"beam_size": 5,
"vad_filter": true,
"condition_on_previous_text": true,
"word_timestamps": true,
"temperature": 0.0
},
"retry_guard": {
"enabled": true,
"retry_on_word_timing": false,
"repetition_ngram_words": 8,
"repetition_threshold": 5,
"timing_retry_max_cps": 60.0
}
}Segmentation
{
"segmentation": {
"max_lines": 2,
"max_chars_per_line": 42,
"max_chars_per_cue": 84,
"target_cps": 17.0,
"warn_cps": 20.0,
"preferred_min_duration": 1.0,
"max_duration": 6.0,
"merge_high_cps": true,
"extend_short_cues": true,
"balance_lines": true,
"preserve_source_timestamps": true,
"never_shift_next_cue": true
}
}Thermal Guard
{
"thermal_guard": {
"enabled": true,
"max_temperature_c": 85.0,
"resume_temperature_c": 70.0,
"poll_seconds": 5.0,
"stable_samples_required": 3,
"watch_cpu_package": true,
"watch_cpu_max": false,
"watch_gpu": true,
"cpu_control_role": "CPU Tctl/Tdie",
"cpu_ccd1_role": "local_diagnostic",
"gpu_control_role": "GPU Core / NVML"
}
}Diagnostic
{
"diagnostics": {
"enabled": true,
"bundle_enabled": true,
"format": "tar.xz",
"xz_preset": 9,
"xz_extreme": true,
"zip_fallback": false,
"results_history_file": "RESULTS-HISTORY.md"
}
}روش اجرا
اگر بخواهم فقط روش درست و نهایی را به خواننده بدهم، بدون اینکه مجبور شود Trial & Error پروژه را تکرار کند، مسیر به شکل زیر است.
- Project Root، Embedded Python، Packageها، Model محلی و FFmpeg/FFprobe را آماده کنید.
config.jsonرا با Pathهای سیستم خودتان تطبیق دهید؛ مخصوصاً FFmpeg، Model و Runtime.- Self-test را اجرا کنید و قبل از Production مطمئن شوید Runtime و Telemetry قابل دسترس هستند.
- برای یک Dataset کوچک Run واقعی بگیرید.
- ASSهای خروجی و Diagnostic Bundle را بررسی کنید.
- سپس مسیر اصلی Media را به Entry Point بدهید.
دستور اصلی روی سیستم توسعه من:
D:\AI\media2ass\run.ps1 "D:\Path\To\Media"یا Wrapper:
D:\AI\media2ass\run.bat "D:\Path\To\Media"Self-test:
D:\AI\media2ass\SELF-TEST.ps1و تست CPU Telemetry:
D:\AI\media2ass\TEST-CPU-TELEMETRY.ps1این Pathها مربوط به سیستم من هستند و در سیستم دیگر باید متناسب با محل نصب Project تغییر داده شوند. Dedicated command مستقلی با نام Dry Run یا Recovery Mode در نسخه نهایی قابل اثبات نبود؛ Recovery عملی پروژه عمدتاً با Skip کردن خروجیهای معتبر و اجرای مجدد همان Input انجام میشود.
خطاهای مهم
Hugging Face 401
مشاهده: جریان قدیمی هنگام دسترسی به Repository مدل شکست خورد. تصمیم: بهجای وابسته نگه داشتن Production به Credential/Network، مدلها به Project-local storage منتقل شدند. وضعیت: Historical و حلشده در معماری جدید.
Import benchmark
خطا: ModuleNotFoundError: media2ass. علت: نحوه Load ماژول در Benchmark. Fix: Benchmark بهشکل Project-aware/standalone اصلاح شد. وضعیت: حلشده.
CPU=N/A
مشاهده: GPU Telemetry در دسترس بود اما CPU مقدار معتبری نمیداد. Fix: استفاده مستقیم از LibreHardwareMonitor DLL و Mapping سنسور Ryzen. وضعیت: حلشده.
find_smi regression
خطا: AttributeError: module 'media2ass' has no attribute 'find_smi'. Fix: API مشترک Telemetry بازگردانده و Self-test برای جلوگیری از Regression اضافه شد. وضعیت: حلشده.
Timeline drift
مشاهده: پایان Timeline یک Media بین مدلها تا چند دقیقه اختلاف داشت. Root Cause: Push کردن Timestampهای بعدی برای enforce کردن Minimum Duration. Fix: حفظ Source Timestamp و ممنوعیت Shift کردن Cue بعدی. وضعیت: حلشده و در Retest تأیید شد.
Retry کاذب
مشاهده: 16 از 16 Transcription Benchmark دوبار اجرا شدند. Root Cause: Trigger بیش از حد حساس word_timing. Fix: غیرفعال شدن این Trigger و حفظ Retry برای Anomalyهای واقعی. وضعیت: حلشده در Production.
SRT overlap
مشاهده: شش SRT درست Match شدند اما Timing آنها Overlap داشت. Fix: SRT نامعتبر Trust نشد و Media به Whisper Fallback کرد. وضعیت: Handle شده؛ Source SRTها خودشان Fix نشدهاند.
No Audio
خطا: Output file does not contain any stream. Root Cause: Media فاقد Audio Stream بود. Fix: ffprobe preflight و دستهبندی مستقل NO AUDIO. Retest: Run نهایی 552 فایل با 0 Failure. وضعیت: حلشده و Validated.
وضعیت Issueها
این جدول فقط Summary آخرین وضعیت است؛ جزئیات تشخیص و Fix در بخشهای قبلی آمدهاند.
| مشکل | اقدام نهایی | وضعیت |
|---|---|---|
| Online model/auth dependency | Local/offline model runtime | Resolved |
| Benchmark import | Project-aware loading | Resolved |
| CPU telemetry unavailable | LibreHardwareMonitor bridge | Resolved |
| Timeline drift | Never shift next cue | Validated |
| Line بالای 42 | Balanced strict wrapping | Validated |
| large-v3 repetition | Retry guard + medium default | Mitigated |
| False word_timing retry | Trigger disabled | Resolved |
| SRT overlap | Whisper fallback | Handled |
| No-audio media | ffprobe preflight | Validated |
| CPS بالای 20 | Warning only; no timeline push | Accepted Limitation |
Timeline پروژه
برای رویدادهایی که Timestamp قابل اتکا داشتند، ترتیب توسعه به این شکل ثبت شده است:
| زمان | رویداد | نتیجه |
|---|---|---|
| 21 Aug ~12:31 | خطای Authentication در جریان قدیمی | Trigger بازطراحی |
| ~13:00 | Setup Runtime داخلی | Completed |
| 13:06 | اولین Run موفق v1.2.1 روی 2 Media | PASS |
| 13:24–13:27 | Import مدلهای Local + SHA256 | PASS |
| 13:34 onward | Benchmark چهار مدل | Regression کشف شد |
| 14:06–16:29 | Telemetry و Mapping سنسورها | Stabilized |
| 16:31–16:42 | Benchmark اصلاحشده | PASS |
| 17:13 | شروع v2.0 Production | Issueهای جدید دیده شد |
| 17:40–20:16 | v2.1 روی 552 Media | 551 resolved / 1 fail |
| بعد از 20:16 | تشخیص Media بدون Audio | Root Cause confirmed |
| 20:34:45 | Validation نهایی v2.2 | PASS / 0 Failure |
| ~21:22 | Audit فایلهای Subtitle | PASS |
وضعیت پروژه
تنها محدودیت پذیرفتهشده، وجود Cueهای با CPS بالاتر از 20 در بخشی از خروجیهاست. این مورد بهعمد با دستکاری Timeline «حل» نشده، چون Sync و Text Integrity اولویت بالاتری دارند.
آخرین نسخه قابل اثبات v2.2.0 FINAL STABLE است. Run نهایی روی مجموعه 552 Media با 551 خروجی معتبر، یک Media بدون Audio و صفر Failure به پایان رسید. Audit مستقل 93 فایل ASS نیز صفر Overlap، صفر Timing نامعتبر و صفر Line بالاتر از 42 کاراکتر را نشان داد.
مهمترین Lesson فنی پروژه این بود که کیفیت Subtitle فقط به مدل Speech-to-Text وابسته نیست. یک Post-processing اشتباه میتواند حتی Transcript خوب را با Timeline Drift خراب کند. همچنین بزرگترین مدل الزاماً بهترین انتخاب Production نیست؛ در Dataset واقعی من medium نسبت به large-v3 هزینه کمتر و پایداری واژگانی بهتری نشان داد.
مسئله اولیه، ساخت زیرنویس ASS برای تعداد زیاد Media با Runtime مستقل و قابل اتکا بود. معماری نهایی از Embedded Python، Modelهای Local Faster-Whisper، FFmpeg/ffprobe، SRT-first selection، Validation، Thermal Guard و Diagnostic Bundle استفاده میکند.
- Last Known Version: v2.2.0 FINAL STABLE
- Last Run ID:
20260821-203437 - Last Run Result: PASS
- Total Media: 552
- Valid ASS: 551
- No Audio: 1
- Unresolved Failure: 0
- Quality Audit: 93 ASS / 12,010 Cue / PASS
- Accepted Limitation: CPS spikes above 20
- Production Blocker: None known
یک Evidence Gap باقی مانده است: Raw Diagnostic Bundle نهایی v2.2.0 داخل Archive مستندات فعلی موجود نبود، هرچند مسیر Bundle و Summary Console آن ثبت شده است. برای وضعیت فعلی پروژه این Gap مانع تعیین Baseline نشده، چون Source نهایی، Run Summary، Diagnostic کامل v2.1، Benchmarkها و Audit فایلمحور در دسترس بودهاند.
سوالات متداول
media2ass دقیقاً چه کاری انجام میدهد؟
media2ass فایلهای Media را بررسی میکند و در صورت نیاز با SRT موجود یا Faster-Whisper برای آنها زیرنویس ASS میسازد. خروجی سپس از نظر Timing، Overlap، طول Line و Text Integrity بررسی میشود.
برای اجرای media2ass اینترنت لازم است؟
Normal Production نسخه نهایی با Python Runtime و Modelهای Local طراحی شده و پس از آمادهشدن Dependencyها به دانلود Model در هر Run وابسته نیست. Setup اولیه یا تهیه Modelها میتواند به Network نیاز داشته باشد.
آیا داشتن کارت گرافیک NVIDIA اجباری است؟
نسخه Production مستندشده روی NVIDIA CUDA تست و Validate شده است. اجرای CPU-only در Final Production این پروژه بهصورت مستقل Benchmark و تأیید نشده، بنابراین برای همان رفتار و Performance ثبتشده باید محیط GPU/CUDA را مبنا گرفت.
چرا مدل medium پیشفرض شد؟
در Benchmark سالم، medium روی Dataset آموزشی تستشده از نظر واژههای تخصصی پایدار بود و large-v3 افزایش کیفیت قابل اثباتی ارائه نکرد. large-v3 همچنین حدود 22٪ زمان بیشتری مصرف کرد.
آیا مدل small ارزش استفاده دارد؟
بله؛ small در تستها بهترین گزینه Speed/Quality بود و برای حالت سریع انتخاب منطقیتری است. با این حال Default Production برای بیشترین پایداری متن روی Dataset تستشده medium باقی ماند.
اگر کنار ویدیو SRT وجود داشته باشد چه میشود؟
Pipeline ابتدا SRT متناظر را پیدا و Validate میکند. اگر SRT معتبر باشد مستقیم به ASS تبدیل میشود؛ اگر Timing آن مشکل داشته باشد، Media به Whisper Fallback میکند.
اگر ASS از قبل ساخته شده باشد دوباره تبدیل میشود؟
خیر، اگر خروجی موجود Validation را پاس کند با وضعیت SKIP CONVERTED رد میشود. Run نهایی این رفتار را برای 551 فایل موجود تأیید کرد.
اگر فایل ویدیویی Audio نداشته باشد چه میشود؟
از v2.2.0، ffprobe قبل از Audio Extraction وجود Stream صوتی را بررسی میکند. فایل بدون Audio با وضعیت NO AUDIO ثبت میشود و Failure محسوب نمیشود.
تفاوت No Audio با Audio Extract Failed چیست؟
NO AUDIO یعنی Media اساساً Stream صوتی ندارد. AUDIO EXTRACT FAILED یعنی Audio وجود داشته اما FFmpeg نتوانسته آن را استخراج کند؛ این دو وضعیت در نسخه نهایی جدا گزارش میشوند.
Thermal Guard چگونه از سیستم محافظت میکند؟
در Policy نهایی، رسیدن سنسور کنترل CPU یا GPU به 85°C باعث توقف شروع Heavy Work بعدی میشود و Resume پس از خنکشدن تا 70°C یا پایینتر انجام میگیرد. CCD1 فقط برای Diagnostic ثبت میشود و Trigger اصلی Pause نیست.
چرا CPU CCD1 معیار اصلی توقف نیست؟
CCD1 میتواند Spike موضعی بالاتری از دمای کنترلی CPU نشان دهد. در این پروژه Tctl/Tdie سنسور Control است و CCD1 فقط بهعنوان Local Diagnostic نگهداری میشود.
چرا Cueهای بالای 20 CPS هنوز باقی ماندهاند؟
چون نسخههای اولیه برای کاهش CPS، Timestampها را جابهجا کردند و باعث Timeline Drift جدی شدند. در نسخه نهایی Sync و Text Integrity اولویت دارند و High-CPS بهعنوان Warning غیرمسدودکننده ثبت میشود.
چطور مطمئن شویم زیرنویس خروجی خراب نشده است؟
Validation نهایی فقط Exit Code را بررسی نمیکند؛ Overlap، Duration نامعتبر، ترتیب Timeline، طول Line و Cueهای خارج از Media نیز کنترل میشوند. Audit واقعی 93 فایل ASS و 12,010 Cue، صفر Overlap و صفر Timing نامعتبر ثبت کرد.
برای اجرای دوباره یک Batch بزرگ باید از کجا شروع کرد؟
همان Input Root را دوباره به run.ps1 بدهید. Skip Logic خروجیهای معتبر قبلی را کنار میگذارد و فقط فایلهای جدید، نامعتبر یا تعیینتکلیفنشده نیاز به پردازش خواهند داشت.
نتیجهگیری
ساخت media2ass در نهایت بیشتر از یک پروژه Speech-to-Text شد. مسئله اصلی به مجموعهای از موضوعات Runtime، Model Management، Timing، Validation، Thermal Monitoring، Resume Logic و Diagnostic تبدیل شد. مهمترین نتیجه برای من این بود که Reliability از یک «مدل قویتر» یا یک Run موفق بهتنهایی به دست نمیآید؛ باید بتوان هر تصمیم را با Log، Retest و Validation دنبال کرد.

نسخه v2.2.0 آخرین Baseline معتبر این پروژه است. در آن، Runtime مستقل و Offline شده، مدل medium بر اساس Benchmark واقعی انتخاب شده، Timeline Drift رفع و Retest شده، SRT-first و Skip Logic در Production جواب داده، Thermal Sensor Roles اصلاح شده و Edge Case بدون Audio نیز در آخرین Run بهصورت درست طبقهبندی شده است.
اگر توسعه در آینده ادامه پیدا کند، منطقیترین مسیر این است که v2.2.0 دستنخورده بهعنوان Recovery Baseline حفظ شود و هر تغییر جدید با Regression Test جداگانه روی Timing، Text Integrity، Skip Logic و Thermal Guard انجام شود. تنها بهبود پیشنهادی فعلی، بررسی روشهای امن برای کاهش Cueهای High-CPS بدون جابهجایی Timestampهای بعدی است؛ این مورد هنوز Requirement قطعی پروژه نیست.


