sreepathi-ravikumar commited on
Commit
63c2270
·
verified ·
1 Parent(s): 120bbe9

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +42 -76
app.py CHANGED
@@ -1,5 +1,5 @@
1
  from flask import Flask, request, jsonify, send_file
2
- from werkzeug.utils import secure_filename
3
  import os
4
  import subprocess
5
  import tempfile
@@ -9,6 +9,7 @@ import traceback
9
  import json
10
 
11
  app = Flask(__name__)
 
12
 
13
  # Configuration
14
  BASE_DIR = "/app"
@@ -17,10 +18,11 @@ TEMP_DIR = os.path.join(BASE_DIR, "temp")
17
  os.makedirs(MEDIA_DIR, exist_ok=True)
18
  os.makedirs(TEMP_DIR, exist_ok=True)
19
 
 
 
 
20
  def create_manim_script(problem_data, script_path):
21
  """Generate Manim script from problem data"""
22
-
23
- # Default settings
24
  settings = problem_data.get("video_settings", {
25
  "background_color": "#0f0f23",
26
  "text_color": "WHITE",
@@ -36,7 +38,6 @@ def create_manim_script(problem_data, script_path):
36
  if not slides:
37
  raise ValueError("No slides provided in input data")
38
 
39
- # Generate Manim script
40
  manim_code = f'''
41
  from manim import *
42
 
@@ -51,13 +52,11 @@ class GeneratedMathScene(Scene):
51
  equation_size = {settings.get('equation_size', 42)}
52
  title_size = {settings.get('title_size', 48)}
53
 
54
- # Content group for scrolling
55
  content_group = VGroup()
56
  current_y = 3.0
57
  line_spacing = 0.8
58
  screen_bottom = -3.5
59
 
60
- # Process slides
61
  slides = {json.dumps(slides)}
62
 
63
  for idx, slide in enumerate(slides):
@@ -67,7 +66,6 @@ class GeneratedMathScene(Scene):
67
  duration = slide.get("duration", 1.0)
68
  slide_type = slide.get("type", "text")
69
 
70
- # Create title (centered)
71
  if slide_type == "title":
72
  obj = Text(content, color=highlight_color, font=default_font, font_size=title_size)
73
  obj.move_to(ORIGIN)
@@ -76,20 +74,16 @@ class GeneratedMathScene(Scene):
76
  self.play(FadeOut(obj), run_time=duration * 0.3)
77
  continue
78
 
79
- # Create text
80
  elif slide_type == "text":
81
  obj = Text(content, color=default_color, font=default_font, font_size=text_size)
82
 
83
- # Create equation
84
  elif slide_type == "equation":
85
  obj = MathTex(content, color=default_color, font_size=equation_size)
86
 
87
  if obj:
88
- # Position at left edge
89
  obj.to_edge(LEFT, buff=0.3)
90
  obj.shift(UP * (current_y - obj.height/2))
91
 
92
- # Check if scrolling needed
93
  obj_bottom = obj.get_bottom()[1]
94
  if obj_bottom < screen_bottom:
95
  scroll_amount = abs(obj_bottom - screen_bottom) + 0.3
@@ -97,7 +91,6 @@ class GeneratedMathScene(Scene):
97
  current_y += scroll_amount
98
  obj.shift(UP * scroll_amount)
99
 
100
- # Animations
101
  if animation == "write_left":
102
  self.play(Write(obj), run_time=duration)
103
  elif animation == "fade_in":
@@ -112,7 +105,6 @@ class GeneratedMathScene(Scene):
112
  current_y -= (obj.height + line_spacing)
113
  self.wait(0.3)
114
 
115
- # Highlight final answer
116
  if len(content_group) > 0:
117
  final_box = SurroundingRectangle(content_group[-1], color=highlight_color, buff=0.2)
118
  self.play(Create(final_box), run_time=0.8)
@@ -122,72 +114,33 @@ class GeneratedMathScene(Scene):
122
  with open(script_path, 'w') as f:
123
  f.write(manim_code)
124
 
125
- @app.route('/', methods=['GET'])
126
  def home():
127
- """Health check endpoint"""
128
- return jsonify({
129
- "status": "running",
130
- "message": "Manim Video Generation API",
131
- "endpoints": {
132
- "/generate": "POST - Generate video from JSON",
133
- "/health": "GET - Health check"
134
- }
135
- })
136
 
137
- @app.route('/health', methods=['GET'])
138
- def health():
139
- """Health check"""
140
- return jsonify({"status": "healthy"})
141
-
142
- @app.route('/generate', methods=['POST'])
143
  def generate_video():
144
- """
145
- Generate Manim video from JSON input
 
146
 
147
- Expected JSON format:
148
- {
149
- "video_settings": {
150
- "background_color": "#0f0f23",
151
- "text_color": "WHITE",
152
- "highlight_color": "YELLOW",
153
- "font": "CMU Serif",
154
- "text_size": 36,
155
- "equation_size": 42,
156
- "title_size": 48
157
- },
158
- "slides": [
159
- {
160
- "type": "title",
161
- "content": "Your Title",
162
- "animation": "fade_in",
163
- "duration": 1.0
164
- },
165
- {
166
- "type": "text",
167
- "content": "Your text content",
168
- "animation": "write_left",
169
- "duration": 0.8
170
- },
171
- {
172
- "type": "equation",
173
- "content": "x = \\\\frac{-b \\\\pm \\\\sqrt{b^2 - 4ac}}{2a}",
174
- "animation": "write_left",
175
- "duration": 1.2
176
- }
177
- ]
178
- }
179
- """
180
  try:
 
 
 
 
 
181
  # Get JSON data
182
- if not request.json:
 
183
  return jsonify({"error": "No JSON data provided"}), 400
184
 
185
- problem_data = request.json
186
-
187
  # Validate input
188
- if "slides" not in problem_data or not problem_data["slides"]:
189
  return jsonify({"error": "No slides provided in request"}), 400
190
 
 
 
191
  # Create unique temporary directory
192
  timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
193
  temp_work_dir = os.path.join(TEMP_DIR, f"manim_{timestamp}")
@@ -195,9 +148,10 @@ def generate_video():
195
 
196
  # Generate Manim script
197
  script_path = os.path.join(temp_work_dir, "scene.py")
198
- create_manim_script(problem_data, script_path)
 
199
 
200
- # Render video
201
  quality = request.args.get('quality', 'l') # l=low, m=medium, h=high
202
  render_command = [
203
  "manim",
@@ -208,6 +162,8 @@ def generate_video():
208
  "GeneratedMathScene"
209
  ]
210
 
 
 
211
  result = subprocess.run(
212
  render_command,
213
  capture_output=True,
@@ -218,11 +174,14 @@ def generate_video():
218
 
219
  if result.returncode != 0:
220
  error_msg = result.stderr or result.stdout
 
221
  return jsonify({
222
  "error": "Manim rendering failed",
223
  "details": error_msg
224
  }), 500
225
 
 
 
226
  # Find generated video
227
  quality_map = {'l': '480p15', 'm': '720p30', 'h': '1080p60'}
228
  video_quality = quality_map.get(quality, '480p15')
@@ -236,39 +195,46 @@ def generate_video():
236
  )
237
 
238
  if not os.path.exists(video_path):
 
239
  return jsonify({
240
  "error": "Video file not found after rendering",
241
  "expected_path": video_path
242
  }), 500
243
 
244
- # Copy to media directory for serving
 
 
245
  output_filename = f"math_video_{timestamp}.mp4"
246
  output_path = os.path.join(MEDIA_DIR, output_filename)
247
  shutil.copy(video_path, output_path)
 
248
 
249
  # Clean up temp directory
250
  try:
251
  shutil.rmtree(temp_work_dir)
252
- except:
253
- pass
 
254
 
255
- # Return video file
256
  return send_file(
257
  output_path,
258
  mimetype='video/mp4',
259
- as_attachment=True,
260
  download_name=output_filename
261
  )
262
 
263
  except subprocess.TimeoutExpired:
 
264
  return jsonify({"error": "Video rendering timeout (120s)"}), 504
265
  except Exception as e:
 
 
266
  return jsonify({
267
  "error": str(e),
268
  "traceback": traceback.format_exc()
269
  }), 500
270
 
271
  if __name__ == '__main__':
272
- # Use gunicorn in production, Flask dev server for local testing
273
  port = int(os.environ.get('PORT', 7860))
274
  app.run(host='0.0.0.0', port=port, debug=False)
 
1
  from flask import Flask, request, jsonify, send_file
2
+ from flask_cors import CORS
3
  import os
4
  import subprocess
5
  import tempfile
 
9
  import json
10
 
11
  app = Flask(__name__)
12
+ CORS(app) # Enable CORS for all routes
13
 
14
  # Configuration
15
  BASE_DIR = "/app"
 
18
  os.makedirs(MEDIA_DIR, exist_ok=True)
19
  os.makedirs(TEMP_DIR, exist_ok=True)
20
 
21
+ # API Key for security (optional)
22
+ API_KEY = "rkmentormindzofficaltokenkey12345"
23
+
24
  def create_manim_script(problem_data, script_path):
25
  """Generate Manim script from problem data"""
 
 
26
  settings = problem_data.get("video_settings", {
27
  "background_color": "#0f0f23",
28
  "text_color": "WHITE",
 
38
  if not slides:
39
  raise ValueError("No slides provided in input data")
40
 
 
41
  manim_code = f'''
42
  from manim import *
43
 
 
52
  equation_size = {settings.get('equation_size', 42)}
53
  title_size = {settings.get('title_size', 48)}
54
 
 
55
  content_group = VGroup()
56
  current_y = 3.0
57
  line_spacing = 0.8
58
  screen_bottom = -3.5
59
 
 
60
  slides = {json.dumps(slides)}
61
 
62
  for idx, slide in enumerate(slides):
 
66
  duration = slide.get("duration", 1.0)
67
  slide_type = slide.get("type", "text")
68
 
 
69
  if slide_type == "title":
70
  obj = Text(content, color=highlight_color, font=default_font, font_size=title_size)
71
  obj.move_to(ORIGIN)
 
74
  self.play(FadeOut(obj), run_time=duration * 0.3)
75
  continue
76
 
 
77
  elif slide_type == "text":
78
  obj = Text(content, color=default_color, font=default_font, font_size=text_size)
79
 
 
80
  elif slide_type == "equation":
81
  obj = MathTex(content, color=default_color, font_size=equation_size)
82
 
83
  if obj:
 
84
  obj.to_edge(LEFT, buff=0.3)
85
  obj.shift(UP * (current_y - obj.height/2))
86
 
 
87
  obj_bottom = obj.get_bottom()[1]
88
  if obj_bottom < screen_bottom:
89
  scroll_amount = abs(obj_bottom - screen_bottom) + 0.3
 
91
  current_y += scroll_amount
92
  obj.shift(UP * scroll_amount)
93
 
 
94
  if animation == "write_left":
95
  self.play(Write(obj), run_time=duration)
96
  elif animation == "fade_in":
 
105
  current_y -= (obj.height + line_spacing)
106
  self.wait(0.3)
107
 
 
108
  if len(content_group) > 0:
109
  final_box = SurroundingRectangle(content_group[-1], color=highlight_color, buff=0.2)
110
  self.play(Create(final_box), run_time=0.8)
 
114
  with open(script_path, 'w') as f:
115
  f.write(manim_code)
116
 
117
+ @app.route("/")
118
  def home():
119
+ return "Flask Manim Video Generator is Running"
 
 
 
 
 
 
 
 
120
 
121
+ @app.route("/generate", methods=["POST", "OPTIONS"])
 
 
 
 
 
122
  def generate_video():
123
+ # Handle preflight
124
+ if request.method == "OPTIONS":
125
+ return '', 204
126
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
127
  try:
128
+ # Optional: Check API key
129
+ api_key = request.headers.get('X-API-KEY')
130
+ if api_key and api_key != API_KEY:
131
+ return jsonify({"error": "Invalid API key"}), 401
132
+
133
  # Get JSON data
134
+ data = request.get_json()
135
+ if not data:
136
  return jsonify({"error": "No JSON data provided"}), 400
137
 
 
 
138
  # Validate input
139
+ if "slides" not in data or not data["slides"]:
140
  return jsonify({"error": "No slides provided in request"}), 400
141
 
142
+ print(f"Received request with {len(data['slides'])} slides")
143
+
144
  # Create unique temporary directory
145
  timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
146
  temp_work_dir = os.path.join(TEMP_DIR, f"manim_{timestamp}")
 
148
 
149
  # Generate Manim script
150
  script_path = os.path.join(temp_work_dir, "scene.py")
151
+ create_manim_script(data, script_path)
152
+ print(f"Created Manim script at {script_path}")
153
 
154
+ # Render video using subprocess
155
  quality = request.args.get('quality', 'l') # l=low, m=medium, h=high
156
  render_command = [
157
  "manim",
 
162
  "GeneratedMathScene"
163
  ]
164
 
165
+ print(f"Running command: {' '.join(render_command)}")
166
+
167
  result = subprocess.run(
168
  render_command,
169
  capture_output=True,
 
174
 
175
  if result.returncode != 0:
176
  error_msg = result.stderr or result.stdout
177
+ print(f"Manim rendering failed: {error_msg}")
178
  return jsonify({
179
  "error": "Manim rendering failed",
180
  "details": error_msg
181
  }), 500
182
 
183
+ print("Manim rendering completed successfully")
184
+
185
  # Find generated video
186
  quality_map = {'l': '480p15', 'm': '720p30', 'h': '1080p60'}
187
  video_quality = quality_map.get(quality, '480p15')
 
195
  )
196
 
197
  if not os.path.exists(video_path):
198
+ print(f"Video not found at expected path: {video_path}")
199
  return jsonify({
200
  "error": "Video file not found after rendering",
201
  "expected_path": video_path
202
  }), 500
203
 
204
+ print(f"Video found at: {video_path}")
205
+
206
+ # Copy to media directory
207
  output_filename = f"math_video_{timestamp}.mp4"
208
  output_path = os.path.join(MEDIA_DIR, output_filename)
209
  shutil.copy(video_path, output_path)
210
+ print(f"Video copied to: {output_path}")
211
 
212
  # Clean up temp directory
213
  try:
214
  shutil.rmtree(temp_work_dir)
215
+ print("Cleaned up temp directory")
216
+ except Exception as e:
217
+ print(f"Failed to clean temp dir: {e}")
218
 
219
+ # Return video file as blob
220
  return send_file(
221
  output_path,
222
  mimetype='video/mp4',
223
+ as_attachment=False,
224
  download_name=output_filename
225
  )
226
 
227
  except subprocess.TimeoutExpired:
228
+ print("Video rendering timeout")
229
  return jsonify({"error": "Video rendering timeout (120s)"}), 504
230
  except Exception as e:
231
+ print(f"Error: {str(e)}")
232
+ traceback.print_exc()
233
  return jsonify({
234
  "error": str(e),
235
  "traceback": traceback.format_exc()
236
  }), 500
237
 
238
  if __name__ == '__main__':
 
239
  port = int(os.environ.get('PORT', 7860))
240
  app.run(host='0.0.0.0', port=port, debug=False)