""" Video Renderer المحرك المشترك بين كل التمبلتس بياخد التمبلت + البيانات → بيعمل الفيديو """ import os import subprocess import tempfile import shutil from PIL import Image from templates.base import RenderRequest def download_font(): if not os.path.exists('/tmp/arabic.ttf'): print('📥 تحميل الخط العربي...') result = subprocess.run([ 'wget', '-q', '-O', '/tmp/arabic.ttf', 'https://github.com/googlefonts/noto-fonts/raw/main/hinted/ttf/NotoNaskhArabic/NotoNaskhArabic-Bold.ttf' ], capture_output=True) if result.returncode == 0: print('✅ الخط جاهز') def render_video(template, req: RenderRequest) -> str: """ بياخد التمبلت + الـ request بيرجع مسار الفيديو النهائي """ download_font() W, H = req.width, req.height fps = req.fps total = req.duration * fps tmp = tempfile.mkdtemp() # تحميل الصور product_img = None if req.image_path and os.path.exists(req.image_path): product_img = Image.open(req.image_path).convert('RGBA') logo_img = None # رسم الـ frames print(f'🎬 [{template.NAME}] بيرسم {total} frame...') for i in range(total): t = i / fps frame = template.make_frame(t, req, product_img, logo_img) frame.save(os.path.join(tmp, f'frame_{i:05d}.png')) if i % fps == 0: print(f' {i//fps}/{req.duration} ثانية') # FFmpeg — بيعمل الفيديو video_raw = os.path.join(tmp, 'raw.mp4') print('🎞️ FFmpeg...') subprocess.run([ 'ffmpeg', '-y', '-framerate', str(fps), '-i', os.path.join(tmp, 'frame_%05d.png'), '-c:v', 'libx264', '-pix_fmt', 'yuv420p', '-crf', '18', '-movflags', '+faststart', video_raw ], capture_output=True) # إضافة الموسيقى final = req.output_path if req.music_path and os.path.exists(req.music_path): print('🎵 بيضيف الموسيقى...') result = subprocess.run([ 'ffmpeg', '-y', '-i', video_raw, '-i', req.music_path, '-filter_complex', f'[1:a]volume={req.music_volume},' f'afade=t=in:st=0:d=1,' f'afade=t=out:st=4:d=2[m];[m]apad[audio]', '-map', '0:v', '-map', '[audio]', '-shortest', '-c:v', 'copy', '-c:a', 'aac', '-b:a', '192k', final ], capture_output=True, text=True) if result.returncode != 0: shutil.copy(video_raw, final) else: shutil.copy(video_raw, final) shutil.rmtree(tmp) return final