File size: 9,511 Bytes
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
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
    pdf_path = None
    original_context = ""
    audio_file = None
    current_audio_file = None
    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"):
        temp_pdf_file = None
        video_data = None
        script = None
        audio_file = None
        final_video = None
        max_retries = 1

        try:
            if input_type == "Text Idea" and idea:
                with st.spinner("Generating initial script and code from idea..."):
                    logging.info(f"Generating video from idea: {idea[:50]}...")
                    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..."):
                    logging.info(f"Generating video from PDF: {uploaded_file.name}")
                    with tempfile.NamedTemporaryFile(delete=False, suffix=".pdf") as temp_pdf:
                        temp_pdf.write(uploaded_file.getvalue())
                        pdf_path = temp_pdf.name
                        temp_pdf_file = 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

            with st.spinner("Generating audio..."):
                 logging.info("Generating audio for the script.")
                 try:
                     audio_file = generate_audio(script)
                 except ValueError as e:
                     st.warning(f"Could not generate audio: {e}. Proceeding without audio.")
                     audio_file = None

            current_manim_code = video_data["manim_code"]
            current_script = script
            current_audio_file = audio_file

            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(
                            {"manim_code": current_manim_code, "output_file": "output.mp4"},
                            current_manim_code,
                            audio_file=current_audio_file
                        )
                    logging.info("Manim video creation successful.")
                    break
                except subprocess.CalledProcessError as e:
                    logging.error(f"Manim execution failed on attempt {attempt + 1}.")
                    st.warning(f"Attempt {attempt + 1} failed. Manim error:\n```\n{e.stderr.decode() if e.stderr else 'No stderr captured.'}\n```")
                    if attempt < max_retries:
                        st.info("Attempting to fix the code using fallback...")
                        logging.info("Calling fallback Gemini to fix code.")
                        error_message = e.stderr.decode() if e.stderr else "Manim execution failed without specific error output."

                        fixed_video_data, fixed_script = fix_manim_code(
                            faulty_code=current_manim_code,
                            error_message=error_message,
                            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 fixed_script != current_script and fixed_script:
                                st.info("Narration script was updated by the fallback. Regenerating audio...")
                                logging.info("Regenerating audio for updated script.")
                                current_script = fixed_script
                                try:
                                     current_audio_file = generate_audio(current_script)
                                except ValueError as e:
                                     st.warning(f"Could not generate audio for fixed script: {e}. Proceeding without audio.")
                                     current_audio_file = None
                            elif not fixed_script:
                                 st.warning("Fallback provided code but no narration. Using original audio (if any).")
                                 logging.warning("Fallback provided empty narration.")
                                 current_script = ""
                                 current_audio_file = None
                            else:
                                logging.info("Fallback kept the original narration.")
                        else:
                            st.error("Fallback failed to fix the code. Stopping.")
                            logging.error("Fallback failed to return valid code/script.")
                            final_video = None
                            break
                    else:
                        st.error(f"Manim failed after {max_retries + 1} attempts. Could not generate video.")
                        logging.error(f"Manim failed after {max_retries + 1} attempts.")
                        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

            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 is not None else "Narration could not be generated.", height=150)
            elif not final_video:
                 pass
            else:
                st.error("Error: Generated video file not found after processing.")
                logging.error(f"Final video file '{final_video}' not found.")

        except FileNotFoundError as e:
             st.error(f"Error: A required file was not found. {str(e)}")
             logging.exception("FileNotFoundError during generation process.")
        except ValueError as e:
             st.error(f"Input Error: {str(e)}")
             logging.exception("ValueError during generation process.")
        except Exception as e:
            st.error(f"An unexpected error occurred: {str(e)}")
            logging.exception("Unhandled exception in main generation block.")
        finally:
            if temp_pdf_file and os.path.exists(temp_pdf_file):
                try:
                    os.remove(temp_pdf_file)
                    logging.info(f"Removed temporary file: {temp_pdf_file}")
                except OSError as e:
                    logging.error(f"Error removing temporary file {temp_pdf_file}: {e}")
            if audio_file and os.path.exists(audio_file) and audio_file != current_audio_file:
                 try:
                     os.remove(audio_file)
                     logging.info(f"Removed temporary audio file: {audio_file}")
                 except OSError as e:
                     logging.error(f"Error removing temporary audio file {audio_file}: {e}")
            if current_audio_file and os.path.exists(current_audio_file):
                 try:
                     os.remove(current_audio_file)
                     logging.info(f"Removed potentially updated temporary audio file: {current_audio_file}")
                 except OSError as e:
                     logging.error(f"Error removing potentially updated temporary audio file {current_audio_file}: {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()