""" Base Template Class كل تمبلت جديد بيرث من الكلاس ده """ from abc import ABC, abstractmethod from PIL import Image from dataclasses import dataclass from typing import Optional @dataclass class RenderRequest: """البيانات اللي بتيجي من n8n""" title: str discount: str = "" badge: str = "" phone: str = "" website: str = "" image_path: str = "" music_path: str = "" output_path: str = "/tmp/output.mp4" # إضافات اختيارية bg_left: str = "" bg_right: str = "" duration: int = 6 fps: int = 30 width: int = 1280 height: int = 720 music_volume: float = 0.20 class BaseTemplate(ABC): """ الكلاس الأساسي — كل تمبلت بيرث منه عشان تعمل تمبلت جديد: 1. عمل ملف في templates/ 2. ترث من BaseTemplate 3. تعمل make_frame بتاعك """ NAME = "base" DESCRIPTION = "Base template" AUTHOR = "" def ease_out(self, t: float) -> float: return 1 - (1 - t) ** 3 def ease_in_out(self, t: float) -> float: return t * t * (3 - 2 * t) def load_font(self, size: int): from PIL import ImageFont candidates = [ '/tmp/arabic.ttf', '/usr/share/fonts/truetype/noto/NotoNaskhArabic-Bold.ttf', '/usr/share/fonts/truetype/noto/NotoSansArabic-Bold.ttf', '/usr/share/fonts/opentype/noto/NotoNaskhArabic-Bold.otf', 'C:/Windows/Fonts/arial.ttf', ] for path in candidates: import os if os.path.exists(path): try: return ImageFont.truetype(path, size) except: continue return ImageFont.load_default() def parse_color(self, s: str, default: tuple) -> tuple: try: return tuple(int(x) for x in s.split(',')) except: return default @abstractmethod def make_frame(self, t: float, req: RenderRequest, product_img, logo_img) -> Image.Image: """ ارسم frame واحد t = الوقت الحالي بالثواني يرجع PIL Image RGB """ pass @property def info(self) -> dict: return { "name": self.NAME, "description": self.DESCRIPTION, "author": self.AUTHOR, }