Spaces:
Sleeping
Sleeping
| import os | |
| import gradio as gr | |
| import torch | |
| import librosa | |
| import json | |
| from transformers import AutoModelForSpeechSeq2Seq, AutoProcessor, pipeline | |
| import spaces | |
| # انتخاب نسخه مرجع جهانی چندزبانه OpenAI Whisper V3 | |
| model_id = "openai/whisper-large-v3" | |
| # ۱. انتخاب بهینهترین فرمت عددی برای سختافزار A100/H200 | |
| if torch.cuda.is_available() and torch.cuda.is_bf16_supported(): | |
| torch_dtype = torch.bfloat16 | |
| print("Using optimized BFloat16 for A100/H200") | |
| else: | |
| torch_dtype = torch.float16 | |
| print("Using Float16") | |
| # ۲. فعالسازی مکانیسم شتابدهنده توجه (Attention) | |
| attn_implementation = "sdpa" | |
| try: | |
| import flash_attn | |
| attn_implementation = "flash_attention_2" | |
| print("Using Flash Attention 2 for maximum hardware utilization") | |
| except ImportError: | |
| print("Flash Attention 2 not found. Falling back to high-performance PyTorch SDPA") | |
| # ۳. بارگذاری بهینه مدل چندزبانه با تکیه بر Safetensors و Memory optimization | |
| model = AutoModelForSpeechSeq2Seq.from_pretrained( | |
| model_id, | |
| torch_dtype=torch_dtype, | |
| low_cpu_mem_usage=True, | |
| use_safetensors=True, | |
| attn_implementation=attn_implementation | |
| ) | |
| # انتقال مدل به حافظه گرافیکی در صورت در دسترس بودن | |
| device = "cuda" if torch.cuda.is_available() else "cpu" | |
| model.to(device) | |
| processor = AutoProcessor.from_pretrained(model_id) | |
| # تعریف متن راهنمای پرامپت فارسی به صورت سراسری | |
| prompt_text = "کلمات دقیقاً همانطور که تلفظ میشوند، با رعایت حروف اضافه و ساختار عامیانه، محاورهای و شکسته نوشته شوند." | |
| # ۴. ساخت پایپلاین تشخیص گفتار چندزبانه با استفاده از پارامتر اصلاح شدهی dtype | |
| pipe = pipeline( | |
| "automatic-speech-recognition", | |
| model=model, | |
| tokenizer=processor.tokenizer, | |
| feature_extractor=processor.feature_extractor, | |
| dtype=torch_dtype, | |
| device=device, | |
| ) | |
| # نقشه کدهای زبانی برای منوی کشویی انتخاب زبان | |
| LANGUAGE_MAPPING = { | |
| "Auto-Detect (تشخیص خودکار)": None, | |
| "Persian / فارسی": "fa", | |
| "English / انگلیسی": "en", | |
| "Spanish / اسپانیایی": "es", | |
| "French / فرانسوی": "fr", | |
| "German / آلمانی": "de", | |
| "Arabic / عربی": "ar", | |
| "Turkish / ترکی": "tr", | |
| "Italian / ایتالیایی": "it", | |
| "Russian / روسی": "ru", | |
| "Chinese / چینی": "zh", | |
| "Japanese / ژاپنی": "ja", | |
| "Korean / کرهای": "ko" | |
| } | |
| # تابع کمکی برای فرمتبندی زمان در ساختار استاندارد زیرنویس (HH:MM:SS,mmm) | |
| def format_timestamp(seconds): | |
| hours = int(seconds // 3600) | |
| minutes = int((seconds % 3600) // 60) | |
| secs = int(seconds % 60) | |
| millis = int(round((seconds % 1) * 1000)) | |
| return f"{hours:02d}:{minutes:02d}:{secs:02d},{millis:03d}" | |
| # مبدل دادههای کلمه به کلمه ویسپر به ساختار استاندارد خوانای SRT | |
| def create_srt_from_chunks(chunks, max_words_per_segment=6, max_duration_per_segment=3.0): | |
| srt_lines = [] | |
| segment_idx = 1 | |
| current_segment_words = [] | |
| current_start = None | |
| for chunk in chunks: | |
| text = chunk.get("text", "").strip() | |
| timestamp = chunk.get("timestamp") | |
| if not text or timestamp is None: | |
| continue | |
| start, end = timestamp | |
| if start is None: | |
| continue | |
| if end is None: | |
| end = start + 0.3 # مقدار جایگزین در صورت عدم ثبت زمان پایان توکن | |
| if current_start is None: | |
| current_start = start | |
| current_segment_words.append(text) | |
| duration = end - current_start | |
| # دستهبندی کلمات در قالب خطوط زیرنویس خوانا (مثلا حداکثر ۶ کلمه یا ۳ ثانیه برای هر خط) | |
| if len(current_segment_words) >= max_words_per_segment or duration >= max_duration_per_segment: | |
| line_text = " ".join(current_segment_words) | |
| srt_lines.append(f"{segment_idx}") | |
| srt_lines.append(f"{format_timestamp(current_start)} --> {format_timestamp(end)}") | |
| srt_lines.append(line_text) | |
| srt_lines.append("") | |
| segment_idx += 1 | |
| current_segment_words = [] | |
| current_start = None | |
| if current_segment_words: | |
| last_end = chunks[-1].get("timestamp")[1] if chunks and chunks[-1].get("timestamp") else current_start + 1.0 | |
| line_text = " ".join(current_segment_words) | |
| srt_lines.append(f"{segment_idx}") | |
| srt_lines.append(f"{format_timestamp(current_start)} --> {format_timestamp(last_end)}") | |
| srt_lines.append(line_text) | |
| srt_lines.append("") | |
| return "\n".join(srt_lines) | |
| # تابع تولید دیتای خام زمانبندی کلمات (JSON) برای دسترسی توسعهدهندگان | |
| def get_raw_word_timestamps(chunks): | |
| word_timestamps = [] | |
| for chunk in chunks: | |
| text = chunk.get("text", "").strip() | |
| timestamp = chunk.get("timestamp") | |
| if text and timestamp is not None: | |
| word_timestamps.append({ | |
| "word": text, | |
| "start": round(timestamp[0], 3), | |
| "end": round(timestamp[1], 3) | |
| }) | |
| return json.dumps(word_timestamps, ensure_ascii=False, indent=2) | |
| def transcribe_audio(audio_path, language_selection, optimize_persian): | |
| if audio_path is None: | |
| return "Please upload or record an audio file first. / لطفاً ابتدا فایل صوتی را آپلود یا ضبط کنید.", "", "" | |
| try: | |
| # ۱. بارگذاری ایمن فایل صوتی به فرکانس ۱۶۰۰۰ هرتز (سازگار با همه فرمتها مانند میکروفون وبام یا m4a) | |
| audio_data, sampling_rate = librosa.load(audio_path, sr=16000) | |
| # ۲. پیکربندی پویا بر اساس زبان انتخاب شده توسط کاربر | |
| lang_code = LANGUAGE_MAPPING.get(language_selection, None) | |
| # تنظیمات پایه تولید متن با استفاده از روش پیشرفته Beam Search برای افزایش حداکثری دقت تفکیک کلمات | |
| generate_kwargs = { | |
| "task": "transcribe", | |
| "do_sample": False, | |
| "num_beams": 5 # ارزیابی موازی ۵ مسیر واژگانی برای کاهش خطاهای شنیداری صوتی | |
| } | |
| # اگر کاربر زبانی به جز تشخیص خودکار انتخاب کرده باشد | |
| if lang_code is not None: | |
| generate_kwargs["language"] = lang_code | |
| # ۳. اعمال پرامپت اختصاصی فارسی فقط در صورت تمایل کاربر (به عنوان تانسور ۱ بعدی پایتورچِ روی GPU) | |
| if optimize_persian: | |
| prompt_ids = processor.get_prompt_ids(prompt_text, return_tensors="pt") | |
| prompt_ids = prompt_ids.to(device) | |
| generate_kwargs["prompt_ids"] = prompt_ids | |
| generate_kwargs["language"] = "fa" # اجبار به فارسی در صورت فعال بودن این گزینه | |
| # ۴. پردازش نهایی با حداکثر ظرفیت سختافزاری روی A100/H200 و درخواست ثبت تایماستمپ کلمات | |
| result = pipe( | |
| {"raw": audio_data, "sampling_rate": sampling_rate}, | |
| chunk_length_s=30, | |
| stride_length_s=(6, 6), | |
| batch_size=24, | |
| return_timestamps="word", # فعالسازی ثبت دقیق زمانبندی برای هر کلمه | |
| generate_kwargs=generate_kwargs | |
| ) | |
| full_text = result["text"].strip() | |
| chunks = result.get("chunks", []) | |
| # ۵. تولید ساختارهای سهگانه دیتای خروجی | |
| srt_content = create_srt_from_chunks(chunks) | |
| word_timestamps_json = get_raw_word_timestamps(chunks) | |
| # بازگرداندن هر سه خروجی در قالب تاپل | |
| return full_text, srt_content, word_timestamps_json | |
| except Exception as e: | |
| return f"An error occurred during processing / خطایی در حین پردازش رخ داد: {str(e)}", "", "" | |
| # رابط کاربری چندزبانه و مدرن بر پایه Gradio 6.0 | |
| with gr.Blocks() as demo: | |
| gr.Markdown( | |
| """ | |
| # 🌐 Global Speech-to-Text & Subtitle Studio (Whisper Large V3) | |
| ### سیستم هوشمند و بینالمللی تبدیل گفتار به متن و زیرنویس خودکار (بهینهسازی شده روی A100/H200) | |
| *Supports over 99 languages with automatic detection and Word-Level Timestamps.* | |
| *پشتیبانی از بیش از ۹۹ زبان زنده دنیا به همراه تشخیص خودکار زبان و زمانبندی دقیق کلمه به کلمه.* | |
| """ | |
| ) | |
| with gr.Row(): | |
| with gr.Column(scale=1): | |
| audio_input = gr.Audio( | |
| sources=["upload", "microphone"], | |
| type="filepath", | |
| label="Audio Input / ورودی صدا" | |
| ) | |
| language_dd = gr.Dropdown( | |
| choices=list(LANGUAGE_MAPPING.keys()), | |
| value="Auto-Detect (تشخیص خودکار)", | |
| label="Select Language / انتخاب زبان صوتی" | |
| ) | |
| optimize_fa_chk = gr.Checkbox( | |
| label="Optimize for Spoken Persian / بهینهسازی برای محاوره عامیانه فارسی (فقط برای ویسهای فارسی)", | |
| value=False | |
| ) | |
| submit_btn = gr.Button("Transcribe / شروع فرآیند تبدیل", variant="primary") | |
| with gr.Column(scale=1): | |
| with gr.Tabs(): | |
| with gr.Tab("Full Text / متن کامل"): | |
| text_output = gr.Textbox( | |
| label="Transcription Output / متن پیوسته", | |
| lines=14, | |
| buttons=["copy"], | |
| interactive=False | |
| ) | |
| with gr.Tab("SRT Subtitles / زیرنویس استاندارد"): | |
| srt_output = gr.Textbox( | |
| label="Subtitles (SRT Format) / فایل زیرنویس", | |
| lines=14, | |
| buttons=["copy"], | |
| interactive=False | |
| ) | |
| with gr.Tab("Word Timestamps / زمانبندی کلمات (JSON)"): | |
| json_output = gr.Textbox( | |
| label="Word-Level Timestamps (JSON) / زمان خام کلمات", | |
| lines=14, | |
| buttons=["copy"], | |
| interactive=False | |
| ) | |
| submit_btn.click( | |
| fn=transcribe_audio, | |
| inputs=[audio_input, language_dd, optimize_fa_chk], | |
| outputs=[text_output, srt_output, json_output], | |
| api_name="transcribe" # نام اختصاصی متد برای فراخوانی API کلاینت | |
| ) | |
| if __name__ == "__main__": | |
| # فعالسازی تم آبیرنگ در زمان راهاندازی بر اساس استانداردهای Gradio 6.0 | |
| demo.launch(theme=gr.themes.Default(primary_hue="blue")) |