transciptio / app.py
KIMOSSINO's picture
Update app.py
cc14fa3 verified
raw
history blame
14.2 kB
import gradio as gr
import whisper
import os
import asyncio
import edge_tts
from transformers import pipeline
from deep_translator import GoogleTranslator
from docx import Document
import tempfile
from datetime import datetime
import logging
import sys
# إعداد التسجيل
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler('app.log'),
logging.StreamHandler(sys.stdout)
]
)
logger = logging.getLogger(__name__)
# قائمة اللغات المدعومة
SUPPORTED_LANGUAGES = {
'ar': 'العربية',
'en': 'English',
'fr': 'Français',
'es': 'Español',
'de': 'Deutsch'
}
# تعيين أصوات لكل لغة
VOICE_MAPPINGS = {
'ar': 'ar-EG-ShakirNeural',
'en': 'en-US-EricNeural',
'fr': 'fr-FR-HenriNeural',
'es': 'es-ES-AlvaroNeural',
'de': 'de-DE-ConradNeural'
}
# تحديد اللغات RTL
RTL_LANGUAGES = ['ar']
async def generate_speech(text, lang):
"""توليد الصوت باستخدام edge-tts"""
try:
voice = VOICE_MAPPINGS.get(lang, 'en-US-EricNeural')
communicate = edge_tts.Communicate(text, voice)
audio_path = tempfile.mktemp(suffix='.mp3')
await communicate.save(audio_path)
# التحقق من وجود الملف وحجمه
if os.path.exists(audio_path) and os.path.getsize(audio_path) > 0:
logger.info(f"تم إنشاء ملف صوتي: {audio_path}")
return audio_path
else:
logger.error("فشل إنشاء ملف صوتي صالح")
return None
except Exception as e:
logger.error(f"خطأ في توليد الصوت: {str(e)}")
return None
def text_to_speech(text, lang, progress=gr.Progress()):
"""واجهة لتحويل النص إلى صوت"""
if not text:
logger.warning("لم يتم تقديم نص للتحويل إلى صوت")
return None
try:
progress(0.2, desc="جاري تجهيز الصوت...")
logger.info(f"بدء تحويل النص إلى صوت باللغة: {lang}")
# تقسيم النص إلى أجزاء إذا كان طويلاً
max_length = 1000
text_parts = [text[i:i+max_length] for i in range(0, len(text), max_length)]
# إنشاء ملف صوتي لكل جزء
audio_files = []
for i, part in enumerate(text_parts):
progress((i + 1) / len(text_parts), desc=f"معالجة الجزء {i+1} من {len(text_parts)}...")
audio_path = asyncio.run(generate_speech(part, lang))
if audio_path:
audio_files.append(audio_path)
if not audio_files:
logger.error("لم يتم إنشاء أي ملفات صوتية")
return None
# إذا كان هناك جزء واحد فقط
if len(audio_files) == 1:
return audio_files[0]
# دمج الملفات الصوتية إذا كان هناك أكثر من جزء
from pydub import AudioSegment
final_audio = AudioSegment.from_mp3(audio_files[0])
for audio_file in audio_files[1:]:
final_audio += AudioSegment.from_mp3(audio_file)
final_path = tempfile.mktemp(suffix='.mp3')
final_audio.export(final_path, format="mp3")
# تنظيف الملفات المؤقتة
for file in audio_files:
try:
os.remove(file)
except:
pass
progress(1.0, desc="تم إنشاء الصوت بنجاح!")
return final_path
except Exception as e:
logger.error(f"خطأ في تحويل النص إلى صوت: {str(e)}")
return None
def create_document(original_text, translated_text, source_lang, target_lang, progress=gr.Progress()):
"""إنشاء ملف Word يحتوي على النص الأصلي والترجمة"""
try:
progress(0, desc="جاري إنشاء المستند...")
doc = Document()
doc.add_heading('النص الأصلي والترجمة', 0)
progress(0.3, desc="جاري إضافة المحتوى...")
# إضافة التاريخ والوقت
doc.add_paragraph(f'تم الإنشاء في: {datetime.now().strftime("%Y-%m-%d %H:%M:%S")}')
# إضافة النص الأصلي
doc.add_heading(f'النص الأصلي ({SUPPORTED_LANGUAGES[source_lang]})', level=1)
doc.add_paragraph(original_text)
progress(0.6, desc="جاري إضافة الترجمة...")
# إضافة الترجمة
doc.add_heading(f'الترجمة ({SUPPORTED_LANGUAGES[target_lang]})', level=1)
doc.add_paragraph(translated_text)
# حفظ الملف
progress(0.9, desc="جاري حفظ المستند...")
temp_path = tempfile.mktemp(suffix='.docx')
doc.save(temp_path)
progress(1.0, desc="تم إنشاء المستند بنجاح!")
logger.info(f"تم إنشاء مستند Word: {temp_path}")
return temp_path
except Exception as e:
logger.error(f"خطأ في إنشاء المستند: {str(e)}")
return None
def translate_text(text, source_lang, target_lang, progress=gr.Progress()):
"""ترجمة النص باستخدام deep-translator"""
if source_lang == target_lang:
return text
try:
progress(0.3, desc="جاري الترجمة...")
logger.info(f"بدء الترجمة من {source_lang} إلى {target_lang}")
translator = GoogleTranslator(source=source_lang, target=target_lang)
# تقسيم النص إلى أجزاء إذا كان طويلاً
max_length = 5000
text_parts = [text[i:i+max_length] for i in range(0, len(text), max_length)]
translated_parts = []
for i, part in enumerate(text_parts):
progress((i + 1) / len(text_parts), desc=f"ترجمة الجزء {i+1} من {len(text_parts)}...")
translated_part = translator.translate(part)
translated_parts.append(translated_part)
translated_text = ' '.join(translated_parts)
progress(1.0, desc="تمت الترجمة بنجاح!")
return translated_text
except Exception as e:
logger.error(f"خطأ في الترجمة: {str(e)}")
return f"خطأ في الترجمة: {str(e)}"
def format_timestamp(seconds):
"""تحويل الثواني إلى تنسيق MM:SS"""
minutes = int(seconds // 60)
seconds = int(seconds % 60)
return f"{minutes:02d}:{seconds:02d}"
def process_video(video, source_lang="en", target_lang="ar", progress=gr.Progress()):
"""معالجة الفيديو واستخراج النص وترجمته"""
if video is None:
return {
"error": "الرجاء رفع ملف فيديو",
"original": "",
"translated": "",
"document": None
}
try:
# حفظ الفيديو مؤقتاً
progress(0.1, desc="جاري تحميل الفيديو...")
temp_path = video.name
logger.info(f"تم استلام ملف فيديو: {temp_path}")
# تحميل نموذج Whisper
progress(0.3, desc="جاري تحميل نموذج التعرف على الكلام...")
model = whisper.load_model("base")
# استخراج النص مع التوقيت
progress(0.5, desc="جاري استخراج النص من الفيديو...")
result = model.transcribe(temp_path, language=source_lang)
# تنسيق النص مع التوقيت
transcribed_text = ""
segments_for_translation = []
# تجميع النص الأصلي مع التوقيت
for segment in result["segments"]:
start_time = format_timestamp(segment["start"])
text = segment["text"].strip()
transcribed_text += f"[{start_time}] {text}\n"
segments_for_translation.append((start_time, text))
logger.info("تم استخراج النص بنجاح")
# ترجمة النص مع الحفاظ على التوقيت
progress(0.7, desc="جاري ترجمة النص...")
translated_segments = []
translator = GoogleTranslator(source=source_lang, target=target_lang)
for start_time, text in segments_for_translation:
try:
translated_text = translator.translate(text)
translated_segments.append(f"[{start_time}] {translated_text}")
logger.info(f"تمت ترجمة المقطع: {text} -> {translated_text}")
except Exception as e:
logger.error(f"خطأ في ترجمة المقطع: {str(e)}")
translated_segments.append(f"[{start_time}] [خطأ في الترجمة: {text}]")
translated_text = "\n".join(translated_segments)
# إنشاء ملف Word
progress(0.9, desc="جاري إنشاء المستند...")
doc_path = create_document(transcribed_text, translated_text, source_lang, target_lang)
progress(1.0, desc="تمت المعالجة بنجاح!")
return {
"error": None,
"original": transcribed_text,
"translated": translated_text,
"document": doc_path
}
except Exception as e:
logger.error(f"خطأ في معالجة الفيديو: {str(e)}")
return {
"error": f"حدث خطأ: {str(e)}",
"original": "",
"translated": "",
"document": None
}
def create_ui():
"""إنشاء واجهة المستخدم"""
with gr.Blocks(theme=gr.themes.Soft(
primary_hue="blue",
secondary_hue="indigo",
)) as demo:
gr.Markdown(
"""
# 🎥 منصة تحويل الفيديو إلى نص مع الترجمة
### قم برفع فيديو للحصول على النص والترجمة مع إمكانية تحويل النص إلى صوت
"""
)
with gr.Row():
with gr.Column(scale=2):
video_input = gr.File(
label="📁 رفع فيديو",
file_types=["video"],
elem_id="video_input"
)
with gr.Column(scale=1):
source_lang = gr.Dropdown(
choices=list(SUPPORTED_LANGUAGES.keys()),
value="en",
label="🗣️ لغة الفيديو الأصلية"
)
target_lang = gr.Dropdown(
choices=list(SUPPORTED_LANGUAGES.keys()),
value="ar",
label="🌐 لغة الترجمة"
)
process_btn = gr.Button("🎯 معالجة الفيديو", variant="primary")
with gr.Row():
error_output = gr.Textbox(label="⚠️ الأخطاء", visible=False)
with gr.Tabs():
with gr.TabItem("📝 النص الأصلي"):
original_text = gr.Textbox(
label="النص المستخرج من الفيديو",
lines=10,
elem_classes=["ltr"]
)
with gr.Row():
generate_original_audio = gr.Button("🔊 توليد الصوت", variant="secondary")
original_audio = gr.Audio(label="الصوت", visible=True)
with gr.TabItem("🔄 النص المترجم"):
translated_text = gr.Textbox(
label="النص المترجم",
lines=10,
elem_classes=["rtl"]
)
with gr.Row():
generate_translated_audio = gr.Button("🔊 توليد الصوت", variant="secondary")
translated_audio = gr.Audio(label="الصوت", visible=True)
with gr.Row():
download_btn = gr.File(
label="📥 تحميل المستند (Word)",
interactive=False
)
def update_ui(video, src_lang, tgt_lang):
result = process_video(video, src_lang, tgt_lang)
# تحديث اتجاه النص
original_classes = "rtl" if src_lang in RTL_LANGUAGES else "ltr"
translated_classes = "rtl" if tgt_lang in RTL_LANGUAGES else "ltr"
return {
error_output: gr.update(value=result["error"], visible=bool(result["error"])),
original_text: gr.update(value=result["original"], elem_classes=[original_classes]),
translated_text: gr.update(value=result["translated"], elem_classes=[translated_classes]),
download_btn: result["document"]
}
# ربط الأحداث
process_btn.click(
fn=update_ui,
inputs=[video_input, source_lang, target_lang],
outputs=[error_output, original_text, translated_text, download_btn]
)
generate_original_audio.click(
fn=text_to_speech,
inputs=[original_text, source_lang],
outputs=[original_audio]
)
generate_translated_audio.click(
fn=text_to_speech,
inputs=[translated_text, target_lang],
outputs=[translated_audio]
)
return demo
if __name__ == "__main__":
try:
logger.info("بدء تشغيل التطبيق")
demo = create_ui()
demo.launch()
except Exception as e:
logger.error(f"خطأ في تشغيل التطبيق: {str(e)}")