File size: 4,396 Bytes
19d6b8b
 
8fd6f6f
19d6b8b
8fd6f6f
 
 
 
 
 
 
 
 
 
19d6b8b
f2a8d14
8fd6f6f
f2a8d14
19d6b8b
8fd6f6f
f2a8d14
8fd6f6f
 
19d6b8b
 
 
8fd6f6f
f2a8d14
8fd6f6f
 
f2a8d14
8fd6f6f
f2a8d14
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8fd6f6f
 
 
 
f2a8d14
8fd6f6f
 
 
 
f2a8d14
 
8fd6f6f
 
 
f2a8d14
 
 
 
8fd6f6f
 
f2a8d14
 
 
 
8fd6f6f
 
f2a8d14
 
 
 
 
19d6b8b
8fd6f6f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f2a8d14
 
8fd6f6f
 
 
 
 
f2a8d14
8fd6f6f
 
 
 
 
 
 
 
 
f2a8d14
8fd6f6f
 
 
 
 
 
 
 
 
 
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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
import os
import tempfile
import subprocess
from pathlib import Path
from fastapi import UploadFile

_QUALITY = {"l": "l", "m": "m", "h": "h", "u": "u"}

def _save_upload(tmpdir: Path, file: UploadFile) -> Path:
    ext = Path(file.filename or "file").suffix.lower()
    out = tmpdir / f"input{ext}"
    with out.open("wb") as f:
        f.write(file.file.read())
    return out

def _gen_scene_py(path: Path, anim: str, duration: float, bg_color: str) -> Path:
    is_svg = path.suffix.lower() == ".svg"
    safe_path = repr(str(path))

    if anim == "draw" and not is_svg:
        raise ValueError("Draw modu yalnızca SVG kabul eder.")

    head = f"""from manim import *

class LogoScene(Scene):
    def construct(self):
        self.camera.background_color = "{bg_color}"
"""

    if anim == "draw":
        # Fill'leri kapat + stroke yoksa hafif bir stroke ata + parçalı çiz
        body = f"""
        m = SVGMobject({safe_path}, should_center=True).set_height(5).move_to(ORIGIN)

        # family_members_with_points: gerçekten çizilebilir alt-objeler
        for sm in m.family_members_with_points():
            # fill kapalı başlasın ki Create sırasında sadece çizgi görünsün
            sm.set_fill(opacity=0)
            # stroke yoksa otomatik ver (birçok SVG'de yalnızca fill var)
            if sm.get_stroke_width() == 0:
                sm.set_stroke(color=WHITE, width=3, opacity=1)

        # parçalı çizim (birbirinin üstüne binecek şekilde)
        self.play(
            LaggedStartMap(Create, m.family_members_with_points(), lag_ratio=0.04, rate_func=linear),
            run_time={float(duration)}
        )

        # çizim bittikten sonra dolguları geri getir
        self.play(m.animate.set_fill(opacity=1), run_time=0.6)
        self.wait(0.2)
        """
    else:
        loader = (
            f'SVGMobject({safe_path}, should_center=True).set_height(5)'
            if is_svg else
            f'ImageMobject({safe_path}).scale(0.9)'
        )
        body_start = f"""m = {loader}
m.move_to(ORIGIN)
"""

        if anim == "fade":
            body_anim = f"""
self.play(FadeIn(m), run_time=max(0.3, {float(duration)}*0.5))
self.wait(0.1)
self.play(FadeOut(m), run_time=max(0.2, {float(duration)}*0.3))
"""
        elif anim == "spin":
            body_anim = f"""
self.play(FadeIn(m, scale=0.8), run_time=max(0.2, {float(duration)}*0.2))
self.play(Rotate(m, angle=2*PI), rate_func=linear, run_time=max(0.4, {float(duration)}*0.6))
self.play(FadeOut(m), run_time=max(0.1, {float(duration)}*0.2))
"""
        elif anim == "bounce":
            body_anim = f"""
from manim import UP, DOWN, there_and_back
self.play(FadeIn(m, shift=UP*3), run_time=max(0.2, {float(duration)}*0.25))
self.play(m.animate.shift(DOWN*3).scale(1.02), rate_func=there_and_back, run_time=max(0.4, {float(duration)}*0.55))
self.play(FadeOut(m), run_time=max(0.15, {float(duration)}*0.2))
"""
        else:
            raise ValueError("Geçersiz animasyon tipi.")

        body = body_start + body_anim

    code = head + body + "\n"
    scene_py = path.parent / "logo_scene.py"
    scene_py.write_text(code, encoding="utf-8")
    return scene_py

def _run_manim(scene_py: Path, quality: str) -> Path:
    q = _QUALITY.get(quality, "m")
    tmpdir = scene_py.parent
    out_name = "out.mp4"

    cmd = [
        "manim", "-q", q,
        str(scene_py), "LogoScene",
        "-o", out_name,
        "--format=mp4",
        "--media_dir", str(tmpdir),
    ]

    proc = subprocess.run(cmd, cwd=tmpdir, capture_output=True, text=True)
    if proc.returncode != 0:
        raise RuntimeError(f"Manim failed: {proc.stderr or proc.stdout}")

    for root, _, files in os.walk(tmpdir):
        if out_name in files:
            return Path(root) / out_name

    raise RuntimeError("Çıktı mp4 bulunamadı.")

async def render_logo(file: UploadFile, animation: str, duration: float, bg_color: str, quality: str) -> Path:
    with tempfile.TemporaryDirectory() as tdir:
        tdir = Path(tdir)
        input_path = _save_upload(tdir, file)
        scene_py = _gen_scene_py(input_path, animation, duration, bg_color)
        mp4 = _run_manim(scene_py, quality)
        final_dir = Path("/tmp/manim_out")
        final_dir.mkdir(parents=True, exist_ok=True)
        final_path = final_dir / mp4.name
        final_path.write_bytes(mp4.read_bytes())
        return final_path