Anonymusmee commited on
Commit
e6b8052
·
verified ·
1 Parent(s): 19145ad

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +99 -190
app.py CHANGED
@@ -1,198 +1,107 @@
1
- import gradio as gr
 
 
2
  from PIL import Image, ImageDraw
3
  import numpy as np
4
  import imageio
5
- import tempfile
6
  from gtts import gTTS
7
  import torch
8
  from diffusers import DiffusionPipeline, DPMSolverMultistepScheduler
9
-
10
- # ========== AI MODEL SETUP (Done once on startup) ==========
11
- # This part loads the AI model that creates the videos.
12
- try:
13
- device = "cuda" if torch.cuda.is_available() else "cpu"
14
- video_pipe = DiffusionPipeline.from_pretrained(
15
- "cerspense/zeroscope_v2_576w",
16
- torch_dtype=torch.float16
17
- )
18
- video_pipe.scheduler = DPMSolverMultistepScheduler.from_config(video_pipe.scheduler.config)
19
- video_pipe.to(device)
20
- model_loaded = True
21
- except Exception as e:
22
- print(f"Error loading model: {e}")
23
- model_loaded = False
24
-
25
- # ========== UI & STYLING ==========
26
- # This is where we design the beautiful, child-friendly interface!
27
- custom_css = """
28
- /* --- Import a fun, rounded font from Google Fonts --- */
29
- @import url('https://fonts.googleapis.com/css2?family=Mali:wght@400;700&display=swap');
30
-
31
- /* --- Overall page style --- */
32
- .gradio-container {
33
- background: linear-gradient(135deg, #a0c4ff 0%, #c4f5ff 100%); /* Sky blue gradient */
34
- font-family: 'Mali', cursive; /* Use our fun, new font */
35
- }
36
-
37
- /* --- Main Title: Big, colorful, and magical! --- */
38
- #main-title {
39
- font-size: 48px;
40
- font-weight: 700;
41
- color: #ff6b6b; /* Playful coral color */
42
- text-align: center;
43
- text-shadow: 2px 2px 4px rgba(0,0,0,0.2);
44
- padding-top: 20px;
45
- }
46
-
47
- #subtitle {
48
- font-size: 24px;
49
- color: #4a4a4a;
50
- text-align: center;
51
- margin-top: -10px;
52
- }
53
-
54
- /* --- The main button: Looks like a magic wand button! --- */
55
- #animate-btn {
56
- background: linear-gradient(45deg, #ffd700, #ff8c00); /* Golden-orange gradient */
57
- color: white;
58
- border: none;
59
- border-radius: 50px; /* Fully rounded */
60
- padding: 15px 30px;
61
- font-size: 22px;
62
- font-weight: bold;
63
- box-shadow: 0 5px 15px rgba(255, 165, 0, 0.4);
64
- transition: transform 0.2s, box-shadow 0.2s;
65
- }
66
- #animate-btn:hover {
67
- transform: scale(1.05); /* Grow on hover */
68
- box-shadow: 0 8px 20px rgba(255, 165, 0, 0.6);
69
- }
70
-
71
- /* --- Text input box: Looks like a story scroll --- */
72
- #story-input textarea {
73
- background-color: #ffffff;
74
- border: 3px solid #ffca7a; /* Warm, friendly border */
75
- border-radius: 15px;
76
- font-size: 18px;
77
- padding: 15px;
78
- line-height: 1.5;
79
- }
80
-
81
- /* --- Output containers: Framed like a picture book --- */
82
- .output-box {
83
- border: 8px solid #8B4513; /* Wooden frame border */
84
- border-image: url('https://www.publicdomainpictures.net/pictures/120000/velka/wooden-frame-texture.jpg') 20 stretch;
85
- border-radius: 15px;
86
- padding: 10px;
87
- background: #fdf6e3; /* Parchment paper background */
88
- box-shadow: 5px 5px 15px rgba(0,0,0,0.3);
89
- }
90
-
91
- /* --- Explainer character image: A friendly round guide --- */
92
- .explainer-img img {
93
- border-radius: 50% !important;
94
- border: 5px solid #ff6b6b;
95
- object-fit: cover;
96
- }
97
- """
98
-
99
- # ========== BACKEND LOGIC (The "How it Works" part) ==========
100
-
101
- def create_animation(story):
102
- """Takes the story and creates the video."""
103
- if not model_loaded:
104
- # Create a dummy error output if the model failed to load
105
- blank_image = np.zeros((320, 576, 3), dtype=np.uint8)
106
- with tempfile.NamedTemporaryFile(suffix=".mp4", delete=False) as temp_file:
107
- imageio.mimsave(temp_file.name, [blank_image for _ in range(3)], fps=3)
108
- return "AI model not loaded.", "Error: AI model failed to load.", blank_image, temp_file.name
109
-
110
  if not story:
111
- return "Please write a story first!", "", np.zeros((320, 576, 3), dtype=np.uint8), None
112
-
113
- prompt = f"{story.strip()}, beautiful animation, storybook style, vibrant colors, for children"
114
-
115
- # Generate the video
116
- video_frames = video_pipe(prompt, num_inference_steps=25, height=320, width=576, num_frames=24).frames
117
-
118
- with tempfile.NamedTemporaryFile(suffix=".mp4", delete=False) as temp_file:
119
- video_path = temp_file.name
 
 
 
 
120
  imageio.mimsave(video_path, video_frames, fps=12)
121
-
122
- final_frame_image = video_frames[-1]
123
-
124
- code_explanation = f"""
125
- ### 📜 The Magic Spell (Prompt) We Used:
126
- The AI was told to create a video based on this magic spell:
127
- **"{prompt}"**
128
- """
129
-
130
- story_explanation = f"Hooray! ✨ Our AI artist read your story about '{story}' and drew this wonderful movie for you!"
131
-
132
- # Generate audio
133
- with tempfile.NamedTemporaryFile(suffix=".mp3", delete=False) as temp_audio:
134
- tts = gTTS(story_explanation, lang='en')
135
- tts.save(temp_audio.name)
136
- audio_path = temp_audio.name
137
-
138
- return story_explanation, audio_path, code_explanation, final_frame_image, video_path
139
-
140
- # ========== THE GRADIO APP INTERFACE ==========
141
-
142
- with gr.Blocks(css=custom_css, theme=gr.themes.Soft()) as app:
143
- gr.Markdown("# 🏰 Storybook Animator 🏰", elem_id="main-title")
144
- gr.Markdown("Let's bring your imagination to life!", elem_id="subtitle")
145
-
146
- with gr.Row():
147
- # --- LEFT SIDE: INPUT ---
148
- with gr.Column(scale=2):
149
- gr.Markdown("### ✍️ Write Your Magical Story Here:")
150
- story_input = gr.Textbox(
151
- label="Story",
152
- placeholder="A happy little fox making friends with a butterfly...",
153
- lines=5,
154
- elem_id="story-input",
155
- show_label=False
156
- )
157
- animate_btn = gr.Button("✨ Create My Animation! ✨", elem_id="animate-btn")
158
-
159
- gr.Examples(
160
- examples=[
161
- ["A friendly dragon flying over a candy castle."],
162
- ["A little robot planting a flower on the moon."],
163
- ["A curious squirrel having a tea party with a gnome."],
164
- ["A mermaid discovering a sunken treasure chest filled with glowing pearls."]
165
- ],
166
- inputs=story_input,
167
- label="🌟 Or Try These Fun Ideas:"
168
- )
169
-
170
- # --- RIGHT SIDE: OUTPUT ---
171
- with gr.Column(scale=3):
172
- with gr.Tabs():
173
- with gr.TabItem("🎬 Your Movie!"):
174
- output_video = gr.Video(label="Your Story Comes Alive", elem_classes="output-box")
175
-
176
- with gr.TabItem("🖼️ Picture Preview"):
177
- output_image = gr.Image(label="A Snapshot from Your Story", elem_classes="output-box")
178
-
179
- with gr.TabItem("💡 How It Works"):
180
- story_explanation_text = gr.Textbox(label="What Happened", interactive=False, elem_classes="output-box")
181
- explainer_audio = gr.Audio(label="Listen to the Story", type="filepath")
182
- code_explanation_output = gr.Markdown(elem_classes="output-box")
183
-
184
- # --- Connect the button to the function ---
185
- animate_btn.click(
186
- fn=create_animation,
187
- inputs=story_input,
188
- outputs=[
189
- story_explanation_text,
190
- explainer_audio,
191
- code_explanation_output,
192
- output_image,
193
- output_video
194
- ]
195
- )
196
-
197
- if __name__ == "__main__":
198
- app.launch()
 
1
+ import os
2
+ import tempfile
3
+ from flask import Flask, request, jsonify, send_from_directory
4
  from PIL import Image, ImageDraw
5
  import numpy as np
6
  import imageio
 
7
  from gtts import gTTS
8
  import torch
9
  from diffusers import DiffusionPipeline, DPMSolverMultistepScheduler
10
+ import uuid
11
+
12
+ # Initialize Flask App
13
+ # The 'static_folder' serves files like CSS and JS.
14
+ app = Flask(__name__, static_folder='static')
15
+
16
+ # Create a directory for generated files if it doesn't exist
17
+ GENERATED_DIR = os.path.join(os.path.dirname(__file__), 'generated')
18
+ os.makedirs(GENERATED_DIR, exist_ok=True)
19
+
20
+ # ========== LOAD MODELS (Done once on startup) ==========
21
+ device = "cuda" if torch.cuda.is_available() else "cpu"
22
+ video_pipe = DiffusionPipeline.from_pretrained(
23
+ "cerspense/zeroscope_v2_576w",
24
+ torch_dtype=torch.float16
25
+ )
26
+ video_pipe.scheduler = DPMSolverMultistepScheduler.from_config(video_pipe.scheduler.config)
27
+ video_pipe.to(device)
28
+ app.logger.info("AI Models loaded successfully.")
29
+
30
+ # ========== HELPER FUNCTIONS (Your existing logic) ==========
31
+ def story_to_prompt(story):
32
+ prompt = story.strip()
33
+ return f"{prompt}, cinematic, beautiful, hd, high quality, detailed"
34
+
35
+ def generate_explainer_character(name, size=(150,150)):
36
+ # Your character generation logic here...
37
+ img = Image.new("RGB", size, (255, 255, 255))
38
+ draw = ImageDraw.Draw(img)
39
+ draw.text((10, 10), f"{name} avatar", fill="black")
40
+ return img
41
+
42
+ def generate_code_explanation(story, prompt):
43
+ # Your code explanation logic here...
44
+ return f"The code processed the story: '{story}' into a prompt for the AI."
45
+
46
+ # ========== FLASK ROUTES ==========
47
+
48
+ # Route to serve the main HTML page
49
+ @app.route('/')
50
+ def index():
51
+ return send_from_directory('.', 'index.html')
52
+
53
+ # Route to serve generated files (videos, audio)
54
+ @app.route('/generated/<path:filename>')
55
+ def generated_files(filename):
56
+ return send_from_directory(GENERATED_DIR, filename)
57
+
58
+ # The main API endpoint to handle story-to-video requests
59
+ @app.route('/animate', methods=['POST'])
60
+ def animate():
61
+ story = request.json.get('story')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
62
  if not story:
63
+ return jsonify({"error": "No story provided"}), 400
64
+
65
+ app.logger.info(f"Received story: {story}")
66
+
67
+ try:
68
+ # 1. Generate Video
69
+ prompt = story_to_prompt(story)
70
+ video_frames = video_pipe(prompt, num_inference_steps=25, height=320, width=576, num_frames=24).frames
71
+
72
+ # Save files with unique names to avoid conflicts
73
+ unique_id = str(uuid.uuid4())
74
+ video_filename = f"{unique_id}.mp4"
75
+ video_path = os.path.join(GENERATED_DIR, video_filename)
76
  imageio.mimsave(video_path, video_frames, fps=12)
77
+ video_url = f"/generated/{video_filename}"
78
+
79
+ # 2. Generate Story Explanation & Audio
80
+ story_explanation_text = f"We used an AI to turn your story into a video! The story was about: '{story}'."
81
+ audio_filename = f"{unique_id}.mp3"
82
+ audio_path = os.path.join(GENERATED_DIR, audio_filename)
83
+ tts = gTTS(story_explanation_text, lang='en')
84
+ tts.save(audio_path)
85
+ audio_url = f"/generated/{audio_filename}"
86
+
87
+ # 3. Generate Code Explanation
88
+ code_explanation_text = generate_code_explanation(story, prompt)
89
+
90
+ app.logger.info(f"Successfully generated video and audio for story.")
91
+
92
+ # 4. Send all data back to the frontend as JSON
93
+ return jsonify({
94
+ "video_url": video_url,
95
+ "story_explanation": story_explanation_text,
96
+ "audio_url": audio_url,
97
+ "code_explanation": code_explanation_text,
98
+ "prompt": prompt
99
+ })
100
+
101
+ except Exception as e:
102
+ app.logger.error(f"An error occurred: {e}")
103
+ return jsonify({"error": "Failed to generate animation."}), 500
104
+
105
+ if __name__ == '__main__':
106
+ # Use 0.0.0.0 to be accessible within the Docker network
107
+ app.run(host='0.0.0.0', port=5000, debug=True)