Spaces:
Configuration error
Configuration error
File size: 6,460 Bytes
a5e880f dde9859 a5e880f ee6d55e a5e880f ee6d55e a5e880f ee6d55e a5e880f ee6d55e a5e880f ee6d55e a5e880f dde9859 a5e880f | 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 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 | from fastapi import FastAPI, HTTPException, BackgroundTasks
from fastapi.staticfiles import StaticFiles
from fastapi.responses import FileResponse, JSONResponse
from pydantic import BaseModel
import os
from dotenv import load_dotenv
import asyncio
# Load environment variables FIRST
load_dotenv()
# ⚙️ DEPLOYMENT CONFIGURATION
# FFmpeg Configuration - Tries to find FFmpeg automatically
import shutil
# Check if ffmpeg is already in system PATH
if shutil.which('ffmpeg') is None:
# If not in PATH, try custom path (update this for your system)
# Common Windows path: r"C:\ffmpeg\bin"
# Linux/Mac: Usually already in PATH, no need to set
custom_ffmpeg_path = os.environ.get('FFMPEG_PATH', r"M:\Ap\ffmpeg-7.1.1-essentials_build\ffmpeg-7.1.1-essentials_build\bin")
if os.path.exists(custom_ffmpeg_path):
os.environ["PATH"] += os.pathsep + custom_ffmpeg_path
print(f"✅ FFmpeg found at: {custom_ffmpeg_path}")
else:
print("⚠️ FFmpeg not found in system PATH")
print(" Options:")
print(" 1. Install FFmpeg and add to system PATH")
print(" 2. Set FFMPEG_PATH environment variable")
print(f" 3. Update custom_ffmpeg_path in main.py (currently: {custom_ffmpeg_path})")
else:
print("✅ FFmpeg found in system PATH")
from teacher import generate_outline
from compiler import generate_manim_code
from runner import render_scene
from narrator import generate_narration_audio
app = FastAPI()
# Get the directory of the current script (backend/)
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
MEDIA_DIR = os.path.join(BASE_DIR, "media")
STATIC_DIR = os.path.join(BASE_DIR, "static")
# Root directory (parent of backend/)
ROOT_DIR = os.path.dirname(BASE_DIR)
# Ensure the media directory exists
os.makedirs(MEDIA_DIR, exist_ok=True)
app.mount("/media", StaticFiles(directory=MEDIA_DIR), name="media")
# Serve static frontend files
app.mount("/static", StaticFiles(directory=STATIC_DIR), name="static")
# Global Job Status
job_status = {
"stage": "idle", # idle, planning, coding, executing, success, failed
"message": "System Ready",
"video_path": None,
"error": None
}
class PromptRequest(BaseModel):
prompt: str
async def process_video_generation(prompt: str):
global job_status
try:
# 1. Planning
job_status["stage"] = "planning"
job_status["message"] = "Analyzing Prompt & Generating Outline..."
outline = await generate_outline(prompt)
# 2. Narrator: Generate per-step narration audio files
steps = outline.get("steps", [])
step_audio_paths = []
from narrator import generate_narration_audio, concatenate_audio_files, get_audio_duration
total_audio_duration = 0.0
for idx, step in enumerate(steps):
narration = step.get("narration", "")
if narration:
audio_filename = f"step_{idx+1}_narration.mp3"
audio_path = generate_narration_audio(narration, filename=audio_filename)
if audio_path:
step_audio_paths.append(audio_path)
duration = get_audio_duration(audio_path)
total_audio_duration += duration
print(f"✓ Step {idx+1} audio: {duration:.2f}s")
else:
step_audio_paths.append(None)
else:
step_audio_paths.append(None)
# Combine all step audios into one file
combined_audio_path = None
if step_audio_paths and any(step_audio_paths):
combined_audio_path = concatenate_audio_files(step_audio_paths, output_filename="combined_narration.mp3")
if combined_audio_path:
print(f"✓ Combined audio duration: {total_audio_duration:.2f}s")
# 3. Coding
job_status["stage"] = "coding"
job_status["message"] = "Generating Manim Script..."
code = await generate_manim_code(outline, step_audio_paths=step_audio_paths)
# 4. Executing
job_status["stage"] = "executing"
job_status["message"] = "Rendering Animation Frames..."
video_path = await render_scene(code)
# 5. Merge audio with video if available
if combined_audio_path:
from narrator import merge_audio_video
job_status["message"] = "Merging Audio with Video..."
video_path = merge_audio_video(video_path, combined_audio_path)
print(f"✓ Final video with audio: {video_path}")
# Success
relative_path = os.path.relpath(video_path, start=MEDIA_DIR).replace("\\", "/")
job_status["stage"] = "success"
job_status["message"] = "Render Complete!"
job_status["video_path"] = relative_path
except Exception as e:
error_msg = str(e)
print(f"Error generating video: {error_msg}")
job_status["stage"] = "failed"
job_status["message"] = "Process Failed"
job_status["error"] = error_msg
# Log error
with open("error.log", "w") as f:
f.write(error_msg)
import traceback
traceback.print_exc(file=f)
@app.get("/")
async def read_index():
return FileResponse(os.path.join(STATIC_DIR, 'index.html'))
@app.get("/favicon.ico")
async def get_favicon():
favicon_path = os.path.join(ROOT_DIR, 'favicon.ico')
if os.path.exists(favicon_path):
return FileResponse(favicon_path)
raise HTTPException(status_code=404, detail="Favicon not found")
@app.post("/generate")
async def generate_video(request: PromptRequest, background_tasks: BackgroundTasks):
global job_status
# Reset status
job_status = {
"stage": "planning",
"message": "Initializing...",
"video_path": None,
"error": None
}
# Start background task
background_tasks.add_task(process_video_generation, request.prompt)
return {"status": "started"}
@app.get("/status")
async def get_status():
return job_status
@app.get("/video/{path:path}")
async def get_video(path: str):
video_path = os.path.join(MEDIA_DIR, path)
if os.path.exists(video_path):
return FileResponse(video_path)
raise HTTPException(status_code=404, detail="Video not found")
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)
|