Spaces:
Running
on
Zero
Running
on
Zero
feat: Enhance slideshow error guidance and increase command attempts for multiple images
Browse files
app.py
CHANGED
@@ -96,7 +96,15 @@ def get_files_infos(files):
|
|
96 |
return results
|
97 |
|
98 |
|
99 |
-
def get_completion(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
100 |
# Create table header
|
101 |
files_info_string = "| Type | Name | Dimensions | Duration | Audio Channels |\n"
|
102 |
files_info_string += "|------|------|------------|-----------|--------|\n"
|
@@ -140,7 +148,13 @@ PREVIOUS COMMAND (FAILED):
|
|
140 |
ERROR MESSAGE:
|
141 |
{previous_error}
|
142 |
|
143 |
-
Please analyze the error and generate a corrected command that addresses the specific issue.
|
|
|
|
|
|
|
|
|
|
|
|
|
144 |
|
145 |
user_content += "\n\nYOUR RESPONSE:"
|
146 |
|
@@ -169,6 +183,39 @@ Key requirements:
|
|
169 |
- For image sequences: Use -framerate and pattern matching (like 'img%d.jpg') when possible, falling back to individual image processing with -loop 1 and appropriate filters only when necessary.
|
170 |
- When showing file operations or commands, always use explicit paths and filenames without wildcards - avoid using asterisk (*) or glob patterns. Instead, use specific numbered sequences (like %d), explicit file lists, or show the full filename.
|
171 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
172 |
Remember: Simpler is better. Only use advanced ffmpeg features if absolutely necessary for the requested output.
|
173 |
""",
|
174 |
},
|
@@ -305,12 +352,18 @@ def update(
|
|
305 |
command_attempts = []
|
306 |
previous_error = None
|
307 |
previous_command = None
|
308 |
-
|
309 |
while attempts < 2:
|
310 |
print("ATTEMPT", attempts + 1)
|
311 |
try:
|
312 |
command_string = get_completion(
|
313 |
-
prompt,
|
|
|
|
|
|
|
|
|
|
|
|
|
314 |
)
|
315 |
print(
|
316 |
f"""///PROMPT {prompt} \n\n/// START OF COMMAND ///:\n\n{command_string}\n\n/// END OF COMMAND ///\n\n"""
|
@@ -334,51 +387,59 @@ def update(
|
|
334 |
text=True,
|
335 |
cwd=temp_dir,
|
336 |
)
|
337 |
-
|
338 |
# Extract command for display
|
339 |
command_for_display = f"ffmpeg {' '.join(args[1:])} -y output.mp4"
|
340 |
-
|
341 |
if ffmpeg_dry_run.returncode == 0:
|
342 |
print("Command is valid.")
|
343 |
# Add successful command to attempts
|
344 |
-
command_attempts.append(
|
345 |
-
|
346 |
-
|
347 |
-
|
348 |
-
|
|
|
|
|
349 |
else:
|
350 |
print("Command is not valid. Error output:")
|
351 |
print(ffmpeg_dry_run.stderr)
|
352 |
-
|
353 |
# Add failed command to attempts with error
|
354 |
-
command_attempts.append(
|
355 |
-
|
356 |
-
|
357 |
-
|
358 |
-
|
359 |
-
|
360 |
-
|
|
|
|
|
361 |
# Store error details for next retry
|
362 |
previous_error = ffmpeg_dry_run.stderr
|
363 |
previous_command = command_for_display
|
364 |
-
|
365 |
-
raise Exception(
|
|
|
|
|
366 |
|
367 |
output_file_name = f"output_{uuid.uuid4()}.mp4"
|
368 |
output_file_path = str((Path(temp_dir) / output_file_name).resolve())
|
369 |
execute_ffmpeg_command(args, temp_dir, output_file_path)
|
370 |
-
|
371 |
# Generate command display with all attempts
|
372 |
command_display = generate_command_display(command_attempts)
|
373 |
return output_file_path, gr.update(value=command_display)
|
374 |
-
|
375 |
except Exception as e:
|
376 |
attempts += 1
|
377 |
if attempts >= 2:
|
378 |
print("FROM UPDATE", e)
|
379 |
# Show all attempted commands even on final failure
|
380 |
command_display = generate_command_display(command_attempts)
|
381 |
-
command_display +=
|
|
|
|
|
382 |
return None, gr.update(value=command_display)
|
383 |
|
384 |
|
@@ -386,18 +447,18 @@ def generate_command_display(command_attempts):
|
|
386 |
"""Generate a markdown display of all command attempts"""
|
387 |
if not command_attempts:
|
388 |
return "### No commands generated"
|
389 |
-
|
390 |
display = "### Generated Commands\n\n"
|
391 |
-
|
392 |
for attempt in command_attempts:
|
393 |
display += f"**Attempt {attempt['attempt']}** {attempt['status']}\n"
|
394 |
display += f"```bash\n{attempt['command']}\n```\n"
|
395 |
-
|
396 |
-
if attempt[
|
397 |
display += f"<details>\n<summary>π Error Details</summary>\n\n```\n{attempt['error']}\n```\n</details>\n\n"
|
398 |
else:
|
399 |
display += "\n"
|
400 |
-
|
401 |
return display
|
402 |
|
403 |
|
|
|
96 |
return results
|
97 |
|
98 |
|
99 |
+
def get_completion(
|
100 |
+
prompt,
|
101 |
+
files_info,
|
102 |
+
top_p,
|
103 |
+
temperature,
|
104 |
+
model_choice,
|
105 |
+
previous_error=None,
|
106 |
+
previous_command=None,
|
107 |
+
):
|
108 |
# Create table header
|
109 |
files_info_string = "| Type | Name | Dimensions | Duration | Audio Channels |\n"
|
110 |
files_info_string += "|------|------|------------|-----------|--------|\n"
|
|
|
148 |
ERROR MESSAGE:
|
149 |
{previous_error}
|
150 |
|
151 |
+
Please analyze the error and generate a corrected command that addresses the specific issue.
|
152 |
+
|
153 |
+
COMMON SLIDESHOW ERROR FIXES:
|
154 |
+
- If you see "do not match the corresponding output link" β Images have different dimensions, use scale+pad approach
|
155 |
+
- If you see "Padded dimensions cannot be smaller than input dimensions" β Fix pad calculation or use 1920x1080 standard
|
156 |
+
- If you see "Failed to configure input pad" β Check scale and pad syntax, ensure proper filter chain
|
157 |
+
- If you see "Invalid argument" in filters β Simplify filter_complex syntax and check parentheses"""
|
158 |
|
159 |
user_content += "\n\nYOUR RESPONSE:"
|
160 |
|
|
|
183 |
- For image sequences: Use -framerate and pattern matching (like 'img%d.jpg') when possible, falling back to individual image processing with -loop 1 and appropriate filters only when necessary.
|
184 |
- When showing file operations or commands, always use explicit paths and filenames without wildcards - avoid using asterisk (*) or glob patterns. Instead, use specific numbered sequences (like %d), explicit file lists, or show the full filename.
|
185 |
|
186 |
+
CRITICAL SLIDESHOW GUIDANCE:
|
187 |
+
When creating slideshows from multiple images with different dimensions, ALWAYS follow this proven pattern:
|
188 |
+
|
189 |
+
1. CHOOSE A STANDARD RESOLUTION: Pick 1920x1080 (1080p) as the target resolution for all slideshows
|
190 |
+
2. USE SIMPLE SCALE+PAD APPROACH: For each image, scale to fit within 1920x1080 maintaining aspect ratio, then pad with black bars
|
191 |
+
3. PROVEN SLIDESHOW PATTERN:
|
192 |
+
```
|
193 |
+
ffmpeg -loop 1 -t 3 -i image1.jpg -loop 1 -t 3 -i image2.jpg -filter_complex "[0]scale=1920:1080:force_original_aspect_ratio=decrease,pad=1920:1080:(ow-iw)/2:(oh-ih)/2,setsar=1[v0];[1]scale=1920:1080:force_original_aspect_ratio=decrease,pad=1920:1080:(ow-iw)/2:(oh-ih)/2,setsar=1[v1];[v0][v1]concat=n=2:v=1:a=0" -c:v libx264 -pix_fmt yuv420p -movflags +faststart output.mp4
|
194 |
+
```
|
195 |
+
|
196 |
+
4. SLIDESHOW RULES:
|
197 |
+
- Always use 1920x1080 as target resolution for consistency
|
198 |
+
- Always use scale=1920:1080:force_original_aspect_ratio=decrease
|
199 |
+
- Always use pad=1920:1080:(ow-iw)/2:(oh-ih)/2 for centering
|
200 |
+
- Always add setsar=1 after padding to fix aspect ratio issues
|
201 |
+
- Use 3-second duration per image by default (-t 3)
|
202 |
+
- For 3+ images, extend the pattern: [v0][v1][v2]concat=n=3:v=1:a=0
|
203 |
+
|
204 |
+
5. DIMENSION MISMATCH FIXES:
|
205 |
+
- Never try to concat images with different dimensions directly
|
206 |
+
- Always normalize dimensions first with scale+pad
|
207 |
+
- Black padding is preferable to stretching/distorting images
|
208 |
+
|
209 |
+
6. SLIDESHOW TRANSITIONS:
|
210 |
+
- For fade transitions, add fade=t=in:st=0:d=0.5,fade=t=out:st=2.5:d=0.5 after setsar=1
|
211 |
+
- Keep transitions simple - complex transitions often fail
|
212 |
+
- Only add transitions if specifically requested
|
213 |
+
|
214 |
+
7. SLIDESHOW TIMING:
|
215 |
+
- Default to 3 seconds per image
|
216 |
+
- Adjust timing based on user request (e.g., "5 seconds per image")
|
217 |
+
- Total duration = (number of images Γ seconds per image)
|
218 |
+
|
219 |
Remember: Simpler is better. Only use advanced ffmpeg features if absolutely necessary for the requested output.
|
220 |
""",
|
221 |
},
|
|
|
352 |
command_attempts = []
|
353 |
previous_error = None
|
354 |
previous_command = None
|
355 |
+
|
356 |
while attempts < 2:
|
357 |
print("ATTEMPT", attempts + 1)
|
358 |
try:
|
359 |
command_string = get_completion(
|
360 |
+
prompt,
|
361 |
+
files_info,
|
362 |
+
top_p,
|
363 |
+
temperature,
|
364 |
+
model_choice,
|
365 |
+
previous_error,
|
366 |
+
previous_command,
|
367 |
)
|
368 |
print(
|
369 |
f"""///PROMPT {prompt} \n\n/// START OF COMMAND ///:\n\n{command_string}\n\n/// END OF COMMAND ///\n\n"""
|
|
|
387 |
text=True,
|
388 |
cwd=temp_dir,
|
389 |
)
|
390 |
+
|
391 |
# Extract command for display
|
392 |
command_for_display = f"ffmpeg {' '.join(args[1:])} -y output.mp4"
|
393 |
+
|
394 |
if ffmpeg_dry_run.returncode == 0:
|
395 |
print("Command is valid.")
|
396 |
# Add successful command to attempts
|
397 |
+
command_attempts.append(
|
398 |
+
{
|
399 |
+
"command": command_for_display,
|
400 |
+
"status": "β
Valid",
|
401 |
+
"attempt": attempts + 1,
|
402 |
+
}
|
403 |
+
)
|
404 |
else:
|
405 |
print("Command is not valid. Error output:")
|
406 |
print(ffmpeg_dry_run.stderr)
|
407 |
+
|
408 |
# Add failed command to attempts with error
|
409 |
+
command_attempts.append(
|
410 |
+
{
|
411 |
+
"command": command_for_display,
|
412 |
+
"status": "β Invalid",
|
413 |
+
"error": ffmpeg_dry_run.stderr,
|
414 |
+
"attempt": attempts + 1,
|
415 |
+
}
|
416 |
+
)
|
417 |
+
|
418 |
# Store error details for next retry
|
419 |
previous_error = ffmpeg_dry_run.stderr
|
420 |
previous_command = command_for_display
|
421 |
+
|
422 |
+
raise Exception(
|
423 |
+
f"FFMPEG command validation failed: {ffmpeg_dry_run.stderr}"
|
424 |
+
)
|
425 |
|
426 |
output_file_name = f"output_{uuid.uuid4()}.mp4"
|
427 |
output_file_path = str((Path(temp_dir) / output_file_name).resolve())
|
428 |
execute_ffmpeg_command(args, temp_dir, output_file_path)
|
429 |
+
|
430 |
# Generate command display with all attempts
|
431 |
command_display = generate_command_display(command_attempts)
|
432 |
return output_file_path, gr.update(value=command_display)
|
433 |
+
|
434 |
except Exception as e:
|
435 |
attempts += 1
|
436 |
if attempts >= 2:
|
437 |
print("FROM UPDATE", e)
|
438 |
# Show all attempted commands even on final failure
|
439 |
command_display = generate_command_display(command_attempts)
|
440 |
+
command_display += (
|
441 |
+
f"\n\n### Final Error\nβ All attempts failed. Last error: {str(e)}"
|
442 |
+
)
|
443 |
return None, gr.update(value=command_display)
|
444 |
|
445 |
|
|
|
447 |
"""Generate a markdown display of all command attempts"""
|
448 |
if not command_attempts:
|
449 |
return "### No commands generated"
|
450 |
+
|
451 |
display = "### Generated Commands\n\n"
|
452 |
+
|
453 |
for attempt in command_attempts:
|
454 |
display += f"**Attempt {attempt['attempt']}** {attempt['status']}\n"
|
455 |
display += f"```bash\n{attempt['command']}\n```\n"
|
456 |
+
|
457 |
+
if attempt["status"] == "β Invalid" and "error" in attempt:
|
458 |
display += f"<details>\n<summary>π Error Details</summary>\n\n```\n{attempt['error']}\n```\n</details>\n\n"
|
459 |
else:
|
460 |
display += "\n"
|
461 |
+
|
462 |
return display
|
463 |
|
464 |
|