File size: 2,469 Bytes
ab4b623
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
"""
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,
        }