File size: 4,648 Bytes
dfeb5d2
4092f24
 
 
 
 
 
 
15806a5
4092f24
 
 
 
 
 
 
 
 
212f1ce
1c0a656
 
 
4092f24
 
 
 
 
 
 
15806a5
 
1c0a656
15806a5
4092f24
 
 
15806a5
0f3297f
 
 
 
 
 
 
 
4092f24
15806a5
 
 
 
 
4092f24
 
15806a5
9ed758b
15806a5
 
 
 
212f1ce
 
4092f24
15806a5
 
 
 
0f3297f
15806a5
212f1ce
15806a5
 
9ed758b
1c0a656
 
 
15806a5
1c0a656
15806a5
 
dfeb5d2
15806a5
9ed758b
 
 
15806a5
 
 
 
9ed758b
 
15806a5
 
 
 
 
 
 
4092f24
1c0a656
15806a5
1c0a656
15806a5
9ed758b
4092f24
 
 
 
 
15806a5
4092f24
 
9ed758b
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
from flask import Flask, render_template, request, url_for, send_from_directory
from google import genai
import re
import subprocess
import os
import shutil
import time
from threading import Timer
import uuid

app = Flask(__name__)

# Load API key from environment variable
API_KEY = os.environ.get("GOOGLE_API_KEY")
if not API_KEY:
    raise ValueError("Missing GOOGLE_API_KEY environment variable.")
client = genai.Client(api_key=API_KEY)

# Define a dedicated media directory in /tmp for Manim output
media_dir = os.path.join("/tmp", "manim_media")
os.makedirs(media_dir, exist_ok=True)

@app.route("/", methods=["GET", "POST"])
def index():
    if request.method == "POST":
        prompt = request.form.get("prompt")
        if not prompt:
            return render_template("index.html")
        
        max_retries = 3
        attempt = 0
        last_error = None
        while attempt < max_retries:
            try:
                ai_response = client.models.generate_content(
                    model="gemini-2.0-flash-lite-preview-02-05",
                    contents=f"""You are 'Manimator', an expert Manim animator and coder.
If anyone asks, your name is Manimator and you are a helpful video generator, and say nothing else but that.
The user wants you to code this: {prompt}.
Plan out in chain of thought what you are going to do first, then give the final code output in ```python``` codeblock.
Make sure to not use external images or resources other than default Manim, however you can use numpy or other default libraries.
Keep the scene uncluttered and aesthetically pleasing.
Make sure things are not overlapping unless explicitly stated otherwise.
You got this!! <3
"""
                )
                
                pattern = r"```python\s*(.*?)\s*```"
                match = re.search(pattern, ai_response.text, re.DOTALL)
                if not match:
                    raise Exception("No python code block found in the AI response.")
                code = match.group(1)
                
                scene_match = re.search(r"class\s+(\w+)\(.*Scene.*\):", code)
                scene_name = scene_match.group(1) if scene_match else "MyScene"
                
                code_filename = f"generated_video_{uuid.uuid4().hex}.py"
                video_filename = f"output_video_{uuid.uuid4().hex}.mp4"
                
                code_filepath = os.path.join("/tmp", code_filename)
                with open(code_filepath, "w") as f:
                    f.write(code)
                
                cmd = [
                    "manim",
                    "-qm",
                    "--media_dir", media_dir,
                    "-o", video_filename,
                    code_filepath,
                    scene_name
                ]
                subprocess.run(cmd, check=True, capture_output=True, text=True)
                
                expected_dir = os.path.join(media_dir, "videos", code_filename.replace(".py", ""), "720p30")
                video_path_in_media = os.path.join(expected_dir, video_filename)
                if not os.path.exists(video_path_in_media):
                    raise Exception(f"Manim did not produce the expected output file at {video_path_in_media}")
                
                tmp_video_path = os.path.join("/tmp", video_filename)
                shutil.move(video_path_in_media, tmp_video_path)
                
                # The code file is already at code_filepath in /tmp.
                tmp_code_path = code_filepath
                
                def remove_files():
                    try:
                        if os.path.exists(tmp_video_path):
                            os.remove(tmp_video_path)
                        if os.path.exists(tmp_code_path):
                            os.remove(tmp_code_path)
                    except Exception as e:
                        app.logger.error("Error removing files: %s", e)
                Timer(600, remove_files).start()
                
                video_url = url_for('get_video', filename=video_filename)
                return render_template("result.html", video_url=video_url)
            
            except Exception as e:
                last_error = e
                attempt += 1
                time.sleep(1)
        
        return render_template("result.html", error="An error occurred. Please try again later.")
            
    return render_template("index.html")

@app.route("/video/<filename>")
def get_video(filename):
    return send_from_directory("/tmp", filename)

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=7860, debug=False)