sharul20001 commited on
Commit
3d3cf77
Β·
verified Β·
1 Parent(s): 105055a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +404 -66
app.py CHANGED
@@ -1,73 +1,411 @@
1
  import gradio as gr
2
- import ffmpeg
3
  import os
4
- import logging
5
- import mimetypes
 
 
 
 
 
6
 
7
- # Konfigurasi logging
8
- logging.basicConfig(level=logging.INFO)
9
- logger = logging.getLogger(__name__)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
 
11
- # Batasan ukuran file (100 MB)
12
- MAX_FILE_SIZE = 100 * 1024 * 1024
13
-
14
- def is_valid_video(file_path):
15
- mime_type, _ = mimetypes.guess_type(file_path)
16
- return mime_type and mime_type.startswith('video/')
17
-
18
- def generate_video(input_video, text_overlay):
19
- logger.info("Starting video generation...")
20
-
21
- # Ambil file path langsung dari Gradio
22
- input_path = input_video
23
-
24
- # Cek apakah file adalah video
25
- if not is_valid_video(input_path):
26
- raise gr.Error("Invalid file type. Please upload a video file.")
27
-
28
- # Cek ukuran file
29
- if os.path.getsize(input_path) > MAX_FILE_SIZE:
30
- raise gr.Error(f"File size exceeds the maximum limit of {MAX_FILE_SIZE / (1024 * 1024):.2f} MB.")
31
-
32
- # Path untuk output video
33
- output_path = os.path.join(os.getcwd(), "output_video.mp4")
34
-
35
- try:
36
- # Proses video dengan FFmpeg
37
- (
38
- ffmpeg
39
- .input(input_path)
40
- .drawtext(
41
- text=text_overlay,
42
- fontsize=24,
43
- fontcolor='white',
44
- x=(10),
45
- y=(10),
46
- box=1,
47
- boxcolor='black@0.5'
48
- )
49
- .output(output_path)
50
- .run(capture_stderr=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51
  )
52
- except ffmpeg.Error as e:
53
- logger.error(f"FFmpeg error: {e.stderr.decode()}")
54
- raise gr.Error("Failed to process video. Please check the input file and try again.")
55
-
56
- logger.info("Video generated successfully.")
57
- return output_path
58
 
59
- # Antarmuka Gradio
60
- iface = gr.Interface(
61
- fn=generate_video,
62
- inputs=[
63
- gr.File(label="Upload Video", file_types=["video"]),
64
- gr.Textbox(lines=2, label="Text Overlay"),
65
- ],
66
- outputs=gr.Video(label="Generated Video"),
67
- title="Video Generator (Veo 3 Style)",
68
- description="Upload a video, add text overlay, and generate a new video.",
69
- )
70
 
71
- # Jalankan aplikasi
72
- if __name__ == "__main__":
73
- iface.launch()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import gradio as gr
 
2
  import os
3
+ import json
4
+ import tempfile
5
+ import time
6
+ import requests
7
+ from datetime import datetime
8
+ import google.generativeai as genai
9
+ from typing import Optional, Dict, Any
10
 
11
+ # CSS untuk styling
12
+ CSS = """
13
+ .container {
14
+ max-width: 1200px;
15
+ margin: auto;
16
+ padding: 20px;
17
+ }
18
+ .title {
19
+ text-align: center;
20
+ font-size: 2.5em;
21
+ font-weight: bold;
22
+ margin-bottom: 20px;
23
+ background: linear-gradient(135deg, #4285f4 0%, #ea4335 100%);
24
+ -webkit-background-clip: text;
25
+ -webkit-text-fill-color: transparent;
26
+ }
27
+ .subtitle {
28
+ text-align: center;
29
+ font-size: 1.2em;
30
+ color: #5f6368;
31
+ margin-bottom: 30px;
32
+ }
33
+ .generate-btn {
34
+ background: #4285f4 !important;
35
+ color: white !important;
36
+ font-size: 1.2em !important;
37
+ padding: 15px 30px !important;
38
+ border-radius: 24px !important;
39
+ border: none !important;
40
+ cursor: pointer !important;
41
+ transition: all 0.3s ease !important;
42
+ }
43
+ .generate-btn:hover {
44
+ background: #1a73e8 !important;
45
+ box-shadow: 0 1px 2px 0 rgba(60,64,67,0.3), 0 2px 6px 2px rgba(60,64,67,0.15) !important;
46
+ }
47
+ .api-key-input {
48
+ border: 2px solid #e0e0e0 !important;
49
+ border-radius: 8px !important;
50
+ padding: 10px !important;
51
+ font-family: monospace !important;
52
+ background-color: #f8f9fa !important;
53
+ }
54
+ .api-key-input:focus {
55
+ border-color: #4285f4 !important;
56
+ outline: none !important;
57
+ }
58
+ .status-box {
59
+ padding: 10px;
60
+ border-radius: 8px;
61
+ margin: 10px 0;
62
+ }
63
+ .status-success {
64
+ background-color: #e6f4ea;
65
+ border: 1px solid #34a853;
66
+ color: #1e8e3e;
67
+ }
68
+ .status-error {
69
+ background-color: #fce8e6;
70
+ border: 1px solid #ea4335;
71
+ color: #d33b27;
72
+ }
73
+ .status-info {
74
+ background-color: #e8f0fe;
75
+ border: 1px solid #4285f4;
76
+ color: #1967d2;
77
+ }
78
+ """
79
 
80
+ class VideoGenerator:
81
+ def __init__(self):
82
+ self.api_key = None
83
+ self.model = None
84
+
85
+ def validate_api_key(self, api_key: str) -> tuple[bool, str]:
86
+ """Validate Google API key"""
87
+ if not api_key or api_key.strip() == "":
88
+ return False, "❌ API key cannot be empty"
89
+
90
+ try:
91
+ # Configure genai with the provided API key
92
+ genai.configure(api_key=api_key.strip())
93
+
94
+ # Test the API key by listing models
95
+ models = genai.list_models()
96
+ model_names = [m.name for m in models]
97
+
98
+ # Check for video generation models
99
+ video_models = [m for m in model_names if 'video' in m.lower() or 'gemini' in m.lower()]
100
+
101
+ if video_models:
102
+ self.api_key = api_key.strip()
103
+ return True, f"βœ… API key validated! Found {len(video_models)} compatible models."
104
+ else:
105
+ self.api_key = api_key.strip()
106
+ return True, "βœ… API key validated! Using image generation with frame interpolation."
107
+
108
+ except Exception as e:
109
+ return False, f"❌ Invalid API key: {str(e)}"
110
+
111
+ def generate_video_from_images(
112
+ self,
113
+ prompt: str,
114
+ negative_prompt: str,
115
+ duration: int,
116
+ fps: int,
117
+ resolution: tuple,
118
+ style: str,
119
+ progress=gr.Progress()
120
+ ) -> tuple[Optional[str], str]:
121
+ """Generate video by creating multiple images and combining them"""
122
+
123
+ try:
124
+ import cv2
125
+ import numpy as np
126
+ from PIL import Image
127
+
128
+ progress(0.1, desc="Initializing image generation...")
129
+
130
+ # Use Gemini for image generation (if available)
131
+ model = genai.GenerativeModel('gemini-pro')
132
+
133
+ # Calculate number of frames
134
+ total_frames = duration * fps
135
+ keyframes_count = min(duration * 2, 8) # 2 keyframes per second, max 8
136
+
137
+ # Generate text variations for each keyframe
138
+ progress(0.2, desc="Creating scene descriptions...")
139
+
140
+ scene_descriptions = []
141
+ for i in range(keyframes_count):
142
+ time_point = i / (keyframes_count - 1) if keyframes_count > 1 else 0
143
+
144
+ # Create temporal description
145
+ temporal_desc = ""
146
+ if i == 0:
147
+ temporal_desc = "Beginning scene: "
148
+ elif i == keyframes_count - 1:
149
+ temporal_desc = "Final scene: "
150
+ else:
151
+ temporal_desc = f"Scene at {int(time_point * 100)}% progress: "
152
+
153
+ full_prompt = f"{temporal_desc}{prompt}. Style: {style}."
154
+ if negative_prompt:
155
+ full_prompt += f" Avoid: {negative_prompt}"
156
+
157
+ scene_descriptions.append(full_prompt)
158
+
159
+ # Since we can't generate images directly with Gemini,
160
+ # we'll create a placeholder video with text
161
+ progress(0.5, desc="Generating video frames...")
162
+
163
+ width, height = resolution
164
+ fourcc = cv2.VideoWriter_fourcc(*'mp4v')
165
+ temp_path = tempfile.mktemp(suffix='.mp4')
166
+ out = cv2.VideoWriter(temp_path, fourcc, fps, (width, height))
167
+
168
+ # Create frames with gradient background and text
169
+ for frame_idx in range(total_frames):
170
+ progress(0.5 + 0.4 * (frame_idx / total_frames),
171
+ desc=f"Creating frame {frame_idx + 1}/{total_frames}...")
172
+
173
+ # Create gradient background
174
+ frame = np.zeros((height, width, 3), dtype=np.uint8)
175
+
176
+ # Animated gradient
177
+ t = frame_idx / total_frames
178
+ color1 = np.array([66, 133, 244]) # Google Blue
179
+ color2 = np.array([234, 67, 53]) # Google Red
180
+
181
+ for y in range(height):
182
+ blend = (y / height + t) % 1.0
183
+ color = color1 * (1 - blend) + color2 * blend
184
+ frame[y, :] = color.astype(np.uint8)
185
+
186
+ # Add text overlay
187
+ text_lines = [
188
+ "AI Video Generation Preview",
189
+ f"Prompt: {prompt[:50]}...",
190
+ f"Frame: {frame_idx + 1}/{total_frames}",
191
+ f"Duration: {duration}s | Style: {style}"
192
+ ]
193
+
194
+ y_position = height // 4
195
+ for line in text_lines:
196
+ cv2.putText(frame, line, (50, y_position),
197
+ cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 255, 255), 2)
198
+ y_position += 40
199
+
200
+ # Add loading animation
201
+ center = (width // 2, height // 2 + 100)
202
+ radius = 30
203
+ angle = (frame_idx * 10) % 360
204
+ end_point = (
205
+ int(center[0] + radius * np.cos(np.radians(angle))),
206
+ int(center[1] + radius * np.sin(np.radians(angle)))
207
+ )
208
+ cv2.circle(frame, center, radius, (255, 255, 255), 2)
209
+ cv2.line(frame, center, end_point, (255, 255, 255), 3)
210
+
211
+ out.write(frame)
212
+
213
+ out.release()
214
+ progress(1.0, desc="Video generation complete!")
215
+
216
+ return temp_path, "βœ… Preview video generated successfully! (Note: This is a placeholder. Real video generation requires specific API access.)"
217
+
218
+ except Exception as e:
219
+ return None, f"❌ Error generating video: {str(e)}"
220
+
221
+ def generate_video_with_api(
222
+ self,
223
+ api_key: str,
224
+ prompt: str,
225
+ negative_prompt: str,
226
+ duration: int,
227
+ resolution: str,
228
+ aspect_ratio: str,
229
+ style: str,
230
+ motion_intensity: float,
231
+ camera_movement: str,
232
+ progress=gr.Progress()
233
+ ) -> tuple[Optional[str], str]:
234
+ """Main video generation function"""
235
+
236
+ # Validate API key first
237
+ is_valid, message = self.validate_api_key(api_key)
238
+ if not is_valid:
239
+ return None, message
240
+
241
+ if not prompt:
242
+ return None, "❌ Please enter a video description."
243
+
244
+ # Parse resolution
245
+ res_map = {
246
+ "480p": (640, 480),
247
+ "720p": (1280, 720),
248
+ "1080p": (1920, 1080),
249
+ "4K": (3840, 2160)
250
+ }
251
+ width, height = res_map.get(resolution, (1280, 720))
252
+
253
+ # Adjust for aspect ratio
254
+ if aspect_ratio == "9:16": # Portrait
255
+ width, height = height, width
256
+ elif aspect_ratio == "1:1": # Square
257
+ width = height = min(width, height)
258
+ elif aspect_ratio == "4:3":
259
+ width = int(height * 4 / 3)
260
+ elif aspect_ratio == "21:9":
261
+ width = int(height * 21 / 9)
262
+
263
+ # Set FPS based on motion intensity
264
+ fps = int(8 + motion_intensity * 16) # 8-24 fps
265
+
266
+ # Generate video
267
+ return self.generate_video_from_images(
268
+ prompt, negative_prompt, duration, fps,
269
+ (width, height), style, progress
270
  )
 
 
 
 
 
 
271
 
272
+ # Create global instance
273
+ video_generator = VideoGenerator()
 
 
 
 
 
 
 
 
 
274
 
275
+ # Create Gradio interface
276
+ with gr.Blocks(css=CSS, theme=gr.themes.Default()) as demo:
277
+ gr.HTML("""
278
+ <div class="container">
279
+ <h1 class="title">🎬 Google AI Video Generator</h1>
280
+ <p class="subtitle">Create AI-generated videos using Google Generative AI</p>
281
+ </div>
282
+ """)
283
+
284
+ # API Key Section
285
+ with gr.Row():
286
+ with gr.Column():
287
+ gr.HTML("""
288
+ <div class="status-box status-info">
289
+ <strong>πŸ”‘ API Key Required</strong><br>
290
+ Enter your Google AI API key below. Get one from
291
+ <a href="https://makersuite.google.com/app/apikey" target="_blank">Google AI Studio</a>
292
+ </div>
293
+ """)
294
+
295
+ api_key_input = gr.Textbox(
296
+ label="Google AI API Key",
297
+ placeholder="Enter your API key here...",
298
+ type="password",
299
+ elem_classes="api-key-input"
300
+ )
301
+
302
+ validate_btn = gr.Button("πŸ” Validate API Key", size="sm")
303
+ api_status = gr.HTML()
304
+
305
+ gr.HTML("<hr style='margin: 30px 0;'>")
306
+
307
+ with gr.Row():
308
+ with gr.Column(scale=1):
309
+ # Main inputs
310
+ prompt = gr.Textbox(
311
+ label="Video Description",
312
+ placeholder="Describe the video you want to create...",
313
+ lines=4
314
+ )
315
+
316
+ with gr.Accordion("βš™οΈ Advanced Settings", open=False):
317
+ negative_prompt = gr.Textbox(
318
+ label="Negative Prompt (what to avoid)",
319
+ placeholder="Things you don't want in the video...",
320
+ lines=2
321
+ )
322
+
323
+ with gr.Row():
324
+ duration = gr.Slider(
325
+ minimum=1,
326
+ maximum=10,
327
+ value=5,
328
+ step=1,
329
+ label="Duration (seconds)"
330
+ )
331
+
332
+ resolution = gr.Dropdown(
333
+ choices=["480p", "720p", "1080p", "4K"],
334
+ value="720p",
335
+ label="Resolution"
336
+ )
337
+
338
+ with gr.Row():
339
+ aspect_ratio = gr.Dropdown(
340
+ choices=["16:9", "9:16", "1:1", "4:3", "21:9"],
341
+ value="16:9",
342
+ label="Aspect Ratio"
343
+ )
344
+
345
+ style = gr.Dropdown(
346
+ choices=["auto", "realistic", "animated", "artistic", "cinematic", "abstract"],
347
+ value="auto",
348
+ label="Visual Style"
349
+ )
350
+
351
+ with gr.Row():
352
+ motion_intensity = gr.Slider(
353
+ minimum=0.0,
354
+ maximum=1.0,
355
+ value=0.5,
356
+ step=0.1,
357
+ label="Motion Intensity"
358
+ )
359
+
360
+ camera_movement = gr.Dropdown(
361
+ choices=["static", "pan", "zoom", "orbit", "tracking"],
362
+ value="static",
363
+ label="Camera Movement"
364
+ )
365
+
366
+ generate_btn = gr.Button("πŸŽ₯ Generate Video", elem_classes="generate-btn", size="lg")
367
+
368
+ # Examples
369
+ gr.Examples(
370
+ examples=[
371
+ ["A serene sunrise over mountain peaks with moving clouds"],
372
+ ["Futuristic city with flying vehicles and neon lights at night"],
373
+ ["Ocean waves gently crashing on a tropical beach at sunset"],
374
+ ["Cherry blossoms falling in slow motion in a Japanese garden"],
375
+ ["Aurora borealis dancing across the arctic night sky"],
376
+ ["A cat playing with a ball of yarn in slow motion"],
377
+ ["Time-lapse of a flower blooming"],
378
+ ["Aerial view of a winding river through a forest"],
379
+ ],
380
+ inputs=prompt,
381
+ label="Example Prompts"
382
+ )
383
+
384
+ with gr.Column(scale=1):
385
+ # Output
386
+ video_output = gr.Video(label="Generated Video")
387
+ status_text = gr.Textbox(label="Generation Status", interactive=False, lines=3)
388
+
389
+ # Tips
390
+ gr.Markdown("""
391
+ ### πŸ’‘ Tips for Better Results:
392
+
393
+ 1. **Be Specific**: Include details about objects, colors, lighting, and mood
394
+ 2. **Describe Motion**: Specify how things should move in your video
395
+ 3. **Set the Scene**: Include time of day, weather, and environment details
396
+ 4. **Use Style Keywords**: Add words like "cinematic", "realistic", or "animated"
397
+ 5. **Camera Angles**: Mention perspectives like "aerial view" or "close-up"
398
+
399
+ ### πŸ“ Example Format:
400
+ ```
401
+ [Subject] [Action] [Environment] [Style] [Mood]
402
+ ```
403
+
404
+ ### ⚠️ Note:
405
+ This demo creates preview videos. Full video generation requires
406
+ specific API access from Google Cloud.
407
+ """)
408
+
409
+ # Event handlers
410
+ def validate_api_key(api_key):
411
+ is_valid, message = video_generator.validate_api_