import gradio as gr import whisper from moviepy.editor import VideoFileClip, AudioFileClip, ImageClip, CompositeVideoClip import tempfile import os from pathlib import Path import numpy as np from PIL import Image, ImageDraw, ImageFont import arabic_reshaper from bidi.algorithm import get_display from pydub import AudioSegment # تحميل نموذج Whisper model = whisper.load_model("base") # نموذج أفضل للغة العربية # تحديد الخطوط المتاحة FONTS = { "الخط المثنى": "fonts/Al-Mothnna.ttf", "خط بين": "fonts/bein-normal.ttf" } def prepare_arabic_text(text): """تحضير النص العربي للعرض بشكل صحيح""" try: reshaped_text = arabic_reshaper.reshape(text) bidi_text = get_display(reshaped_text) return bidi_text except Exception as e: print(f"خطأ في معالجة النص العربي: {str(e)}") return text def create_text_image(text, font_name, size=(720, 480), font_size=30): """إنشاء صورة تحتوي على نص""" # إنشاء صورة جديدة مع خلفية سوداء img = Image.new('RGB', size, 'black') draw = ImageDraw.Draw(img) # تحميل الخط المحدد try: font_path = FONTS[font_name] if not os.path.exists(font_path): raise FileNotFoundError(f"الخط غير موجود: {font_path}") font = ImageFont.truetype(font_path, font_size) except Exception as e: print(f"خطأ في تحميل الخط: {str(e)}") # استخدام الخط الافتراضي في حالة الفشل font = ImageFont.load_default() # تحضير النص العربي prepared_text = prepare_arabic_text(text) # حساب موقع النص في وسط الصورة text_bbox = draw.textbbox((0, 0), prepared_text, font=font) text_width = text_bbox[2] - text_bbox[0] text_height = text_bbox[3] - text_bbox[1] x = (size[0] - text_width) // 2 y = (size[1] - text_height) // 2 # رسم النص draw.text((x, y), prepared_text, font=font, fill='white') return np.array(img) def convert_audio_to_wav(audio_path): """تحويل الملف الصوتي إلى صيغة WAV""" audio_path = Path(audio_path) if audio_path.suffix.lower() != '.wav': wav_path = audio_path.with_suffix('.wav') audio = AudioSegment.from_file(str(audio_path)) audio.export(str(wav_path), format='wav') return str(wav_path) return str(audio_path) def create_video_with_text(audio_path, transcription, font_name, video_size): """إنشاء فيديو مع نص متزامن""" try: # تحويل الملف الصوتي إلى WAV wav_path = convert_audio_to_wav(audio_path) # تحميل الملف الصوتي audio_clip = AudioFileClip(wav_path) duration = audio_clip.duration # تقسيم النص إلى أجزاء words = transcription.split() total_frames = int(duration * 24) # 24 FPS words_per_frame = max(1, len(words) // total_frames) # إنشاء قائمة المقاطع clips = [] current_time = 0 frame_duration = duration / (len(words) / words_per_frame) for i in range(0, len(words), words_per_frame): # تجميع الكلمات لهذا الإطار frame_words = ' '.join(words[i:i + words_per_frame]) # إنشاء صورة للنص text_image = create_text_image(frame_words, font_name, size=video_size) # تحويل الصورة إلى مقطع text_clip = ImageClip(text_image).set_duration(frame_duration) # إضافة المقطع مع توقيته clips.append(text_clip.set_start(current_time)) current_time += frame_duration if not clips: raise ValueError("لم يتم إنشاء أي مقاطع") # دمج جميع المقاطع video = CompositeVideoClip(clips, size=video_size) video = video.set_audio(audio_clip) # حفظ الفيديو temp_output = tempfile.NamedTemporaryFile(delete=False, suffix='.mp4') video_path = temp_output.name video.write_videofile( video_path, fps=24, codec='libx264', audio_codec='aac', verbose=False ) return video_path except Exception as e: print(f"خطأ في create_video_with_text: {str(e)}") raise def process_audio(audio_path, selected_font, video_orientation): """معالجة الملف الصوتي وإنشاء الفيديو""" try: if not isinstance(audio_path, str): raise ValueError("يجب أن يكون المدخل مسار ملف صوتي") if not os.path.exists(audio_path): raise FileNotFoundError("الملف غير موجود") # تحويل الصوت إلى نص print("جاري تحويل الصوت إلى نص...") result = model.transcribe(audio_path, language="ar") # تحديد اللغة العربية transcription = result["text"] print("تم استخراج النص بنجاح") # تحديد حجم الفيديو بناءً على الاختيار video_size = (1280, 720) if video_orientation == 'أفقي' else (720, 1280) # إنشاء الفيديو print("جاري إنشاء الفيديو...") video_path = create_video_with_text(audio_path, transcription, selected_font, video_size) print("تم إنشاء الفيديو بنجاح") return video_path, transcription except Exception as e: print(f"خطأ في process_audio: {str(e)}") return None, f"حدث خطأ أثناء المعالجة: {str(e)}" # إنشاء واجهة Gradio iface = gr.Interface( fn=process_audio, inputs=[ gr.Audio(type="filepath", label="قم بتحميل ملف صوتي (MP3 أو WAV)"), gr.Dropdown( choices=list(FONTS.keys()), value=list(FONTS.keys())[0], label="اختر الخط" ), gr.Dropdown( choices=["أفقي", "عمودي"], value="أفقي", label="اختيار اتجاه الفيديو" ) ], outputs=[ gr.Video(label="الفيديو المنشأ"), gr.Textbox(label="النص المستخرج") ], title="محول الصوت إلى فيديو مع النص", description="قم بتحميل ملف صوتي لإنشاء فيديو مع نص متزامن باللغة العربية.", ) if __name__ == "__main__": # التحقق من وجود مجلد الخطوط if not os.path.exists("fonts"): print("تحذير: مجلد الخطوط غير موجود") os.makedirs("fonts") # التحقق من وجود الخطوط for font_name, font_path in FONTS.items(): if not os.path.exists(font_path): print(f"تحذير: الخط {font_name} غير موجود في {font_path}") iface.launch()