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 |