File size: 9,840 Bytes
d970572 2e31ab2 d970572 2e31ab2 d970572 2e31ab2 d970572 2e31ab2 d970572 2e31ab2 d970572 2e31ab2 d970572 2e31ab2 d970572 2e31ab2 d970572 2e31ab2 d970572 2e31ab2 d970572 2e31ab2 d970572 2e31ab2 d970572 2e31ab2 d970572 2e31ab2 d970572 2e31ab2 d970572 2e31ab2 d970572 2e31ab2 d970572 2e31ab2 d970572 2e31ab2 d970572 2e31ab2 d970572 2e31ab2 d970572 2e31ab2 d970572 2e31ab2 d970572 2e31ab2 d970572 2e31ab2 d970572 2e31ab2 d970572 2e31ab2 d970572 2e31ab2 d970572 2e31ab2 d970572 2e31ab2 d970572 |
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 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 |
import streamlit as st
import os
import tempfile
import subprocess
import logging
from api.gemini import generate_video
from api.fallback_gemini import fix_manim_code
from services.manim_service import create_manim_video
from services.tts_service import generate_audio
logging.basicConfig(
level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s"
)
def main():
st.title("Manimator")
st.write(
"Generate videos from text ideas or PDF files, You can also just paste arxiv links ;p"
)
input_type = st.radio("Choose input type:", ("Text Idea", "Upload PDF"))
idea = None
uploaded_file = None
original_context = ""
if input_type == "Text Idea":
idea = st.text_area("Enter your idea:")
if idea:
original_context = idea
else:
uploaded_file = st.file_uploader("Choose a PDF file", type="pdf")
if uploaded_file:
original_context = f"Summary/concept from PDF: {uploaded_file.name}"
if st.button("Generate Video"):
files_to_cleanup = set()
video_data = None
script = None
final_video = None
max_retries = 2 # retries for fallback
try:
# Step 1: Generate initial script and code from Gemini
if input_type == "Text Idea" and idea:
with st.spinner("Generating initial script and code from idea..."):
video_data, script = generate_video(idea=idea)
elif input_type == "Upload PDF" and uploaded_file is not None:
with st.spinner("Generating initial script and code from PDF..."):
with tempfile.NamedTemporaryFile(
delete=False, suffix=".pdf"
) as temp_pdf:
temp_pdf.write(uploaded_file.getvalue())
pdf_path = temp_pdf.name
files_to_cleanup.add(pdf_path)
video_data, script = generate_video(pdf_path=pdf_path)
else:
st.error("Please provide an idea or upload a PDF.")
return
if not video_data or not script:
st.error("Failed to generate initial script/code from Gemini.")
return
# Step 2: Generate audio and subtitles from the script
with st.spinner("Generating audio and subtitles..."):
logging.info("Generating audio and subtitles for the script.")
try:
# Unpack both audio and subtitle file paths
audio_file, subtitle_file = generate_audio(script)
if audio_file:
files_to_cleanup.add(audio_file)
if subtitle_file:
files_to_cleanup.add(subtitle_file)
except ValueError as e:
st.warning(
f"Could not generate audio: {e}. Proceeding without audio/subtitles."
)
audio_file, subtitle_file = None, None
current_manim_code = video_data["manim_code"]
current_script = script
current_audio_file = audio_file
current_subtitle_file = subtitle_file
# Step 3: Attempt to render the video, with fallback retries
for attempt in range(max_retries + 1):
try:
with st.spinner(f"Attempt {attempt + 1}: Creating Manim video..."):
logging.info(f"Attempt {attempt + 1} to create Manim video.")
final_video = create_manim_video(
video_data,
current_manim_code,
audio_file=current_audio_file,
subtitle_file=current_subtitle_file,
)
logging.info("Manim video creation successful.")
break # Exit the loop on success
except (subprocess.CalledProcessError, FileNotFoundError) as e:
error_output = e.stderr if hasattr(e, "stderr") else str(e)
logging.error(f"Manim execution failed on attempt {attempt + 1}.")
st.warning(
f"Attempt {attempt + 1} failed. Manim error:\n```\n{error_output}\n```"
)
if attempt < max_retries:
st.info("Attempting to fix the code using fallback...")
logging.info("Calling fallback Gemini to fix code.")
fixed_video_data, fixed_script = fix_manim_code(
faulty_code=current_manim_code,
error_message=error_output,
original_context=original_context,
)
if fixed_video_data and fixed_script is not None:
st.success(
"Fallback successful! Retrying video generation with fixed code."
)
logging.info("Fallback successful. Received fixed code.")
current_manim_code = fixed_video_data["manim_code"]
# If narration changed, regenerate audio and subtitles
if fixed_script != current_script and fixed_script:
st.info(
"Narration script was updated. Regenerating audio and subtitles..."
)
current_script = fixed_script
try:
new_audio, new_subtitle = generate_audio(
current_script
)
if new_audio:
files_to_cleanup.add(new_audio)
if new_subtitle:
files_to_cleanup.add(new_subtitle)
current_audio_file = new_audio
current_subtitle_file = new_subtitle
except ValueError as audio_e:
st.warning(
f"Could not generate new audio: {audio_e}."
)
current_audio_file, current_subtitle_file = (
None,
None,
)
else:
logging.info("Fallback kept the original narration.")
else:
st.error("Fallback failed to fix the code. Stopping.")
final_video = None
break
else:
st.error(
f"Manim failed after {max_retries + 1} attempts. Could not generate video."
)
final_video = None
except Exception as e:
st.error(
f"An unexpected error occurred during video creation: {str(e)}"
)
logging.exception(
"Unexpected error during create_manim_video call."
)
final_video = None
break
# Step 4: Display the final result
if final_video and os.path.exists(final_video):
st.success("Video generated successfully!")
st.video(final_video)
st.write("Generated Narration:")
st.text_area(
"Narration",
current_script if current_script else "No narration was generated.",
height=150,
)
elif not final_video and attempt >= max_retries:
# This message is shown if all retries failed
st.error("Could not generate the video after multiple attempts.")
elif not final_video:
# A general failure message
st.error("Video generation was unsuccessful.")
else:
st.error("Error: Generated video file not found after processing.")
logging.error(f"Final video file '{final_video}' not found.")
except Exception as e:
st.error(f"An unexpected and critical error occurred: {str(e)}")
logging.exception("Unhandled exception in main generation block.")
finally:
# Step 5: Clean up all generated temporary files
logging.info(f"Cleaning up {len(files_to_cleanup)} temporary files.")
for f_path in files_to_cleanup:
if f_path and os.path.exists(f_path):
try:
os.remove(f_path)
logging.info(f"Removed temporary file: {f_path}")
except OSError as e:
logging.error(f"Error removing temporary file {f_path}: {e}")
st.markdown("<br><br>", unsafe_allow_html=True)
st.markdown("---")
st.markdown(
"""
### Want to help improve this app?
- Give good Manim Examples and make PRs in guide.md, find it in repo [GitHub](https://github.com/mostlykiguess/Manimator)
- Report issues on [GitHub Issues](https://github.com/mostlykiguess/Manimator/issues)
- Email problematic prompts to me
"""
)
if __name__ == "__main__":
main()
|