import os os.environ["IMAGE_MAGICK_BINARY"] = "/usr/bin/convert" import gradio as gr import pandas as pd import re from faster_whisper import WhisperModel from moviepy import VideoFileClip, TextClip, CompositeVideoClip from arabic_reshaper import reshape # --- الإعدادات --- model = WhisperModel("large-v3", device="cpu", compute_type="int8") def process_arabic_text(text): if not text: return "" # 1. إعادة تشكيل الحروف العربية لتظهر متصلة وصحيحة (بدون النقاط الإضافية •) reshaped = reshape(text) # 2. إضافة سطر فارغ في الأسفل لمنع قص النقاط السفلية للأحرف return reshaped + "\n " def clean_color(color_str): if color_str.startswith('rgba'): nums = re.findall(r"\d+\.?\d*", color_str) if len(nums) >= 3: r, g, b = int(float(nums[0])), int(float(nums[1])), int(float(nums[2])) return f'rgb({r},{g},{b})' return color_str def step_1_extract_words(video_path, progress=gr.Progress()): if not video_path: return None, "الرجاء رفع فيديو." segments, _ = model.transcribe(video_path, word_timestamps=True, language="ar") words_data = [] for segment in segments: for word in segment.words: words_data.append([word.word.strip(), round(word.start, 2), round(word.end, 2)]) return pd.DataFrame(words_data, columns=["الكلمة", "البداية", "النهاية"]), "تم استخراج الكلمات!" def step_2_render_video(video_path, df_edited, font_selection, text_color, font_size, progress=gr.Progress()): if video_path is None or df_edited is None: return None, "بيانات ناقصة." safe_color = clean_color(text_color) actual_font = font_selection if os.path.exists(font_selection) else "DejaVu-Sans-Bold" output_path = "final_clean_text_video.mp4" video = VideoFileClip(video_path) w, h = video.size clips = [video] words_list = df_edited.values.tolist() for row in words_list: word_text = str(row[0]) t_start = float(row[1]) t_end = float(row[2]) if not word_text.strip(): continue clean_word = process_arabic_text(word_text) txt = TextClip( text=clean_word, font_size=int(font_size), color=safe_color, stroke_color='black', stroke_width=2.0, # تقليل سمك التحديد قليلاً ليتناسب مع النص الأصغر font=actual_font, method='label' ).with_start(t_start).with_duration(max(0.1, t_end - t_start)).with_position(('center', int(h * 0.5))) clips.append(txt) final = CompositeVideoClip(clips, size=(w, h)) final.write_videofile(output_path, codec="libx264", audio_codec="aac", fps=video.fps, logger='bar') return output_path, "تم إنتاج الفيديو بنجاح!" # --- الواجهة --- with gr.Blocks() as app: gr.Markdown("## 🎬 محرر الفيديو: نصوص نظيفة") with gr.Row(): v_in = gr.Video(); v_out = gr.Video() with gr.Row(): font_opt = gr.Dropdown(choices=["arialbd.ttf"], value="arialbd.ttf", label="الخط") color_opt = gr.ColorPicker(value="#FF8C00", label="لون ذهبي برتقالي") # تم تصغير الحجم الافتراضي من 130 إلى 90 size_opt = gr.Slider(30, 200, value=90, label="حجم النص") btn_1 = gr.Button("1. تحليل الكلمات"); table = gr.Dataframe(interactive=True) btn_2 = gr.Button("2. إنتاج الفيديو"); status = gr.Textbox() btn_1.click(step_1_extract_words, [v_in], [table, status]) btn_2.click(step_2_render_video, [v_in, table, font_opt, color_opt, size_opt], [v_out, status]) app.launch()