Sadabematn / app.py
Opera8's picture
Update app.py
acec6d4 verified
Raw
History Blame Contribute Delete
11.9 kB
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)
@spaces.GPU
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"))