Spaces:
Build error
Build error
Update app.py
Browse files
app.py
CHANGED
|
@@ -1344,12 +1344,13 @@ def create_clip(media_asset, tts_path, estimated_duration, target_resolution,
|
|
| 1344 |
color="red",
|
| 1345 |
align='center',
|
| 1346 |
size=(target_resolution[0] * 0.9, None)
|
| 1347 |
-
).set_position('center').set_duration(
|
| 1348 |
clip = CompositeVideoClip([black_clip, error_txt_clip])
|
| 1349 |
-
silent_audio_path = generate_silent_audio(
|
| 1350 |
if silent_audio_path and os.path.exists(silent_audio_path):
|
| 1351 |
try:
|
| 1352 |
-
|
|
|
|
| 1353 |
except Exception as audio_e:
|
| 1354 |
print(f"Error setting silent audio for error clip {segment_index}: {audio_e}")
|
| 1355 |
clip = clip.set_audio(None)
|
|
@@ -1728,12 +1729,36 @@ def generate_script_and_show_editor(user_input, resolution_choice,
|
|
| 1728 |
)
|
| 1729 |
|
| 1730 |
|
| 1731 |
-
|
|
|
|
|
|
|
|
|
|
| 1732 |
"""
|
| 1733 |
Takes the edited segment data (text, uploaded files) and configuration,
|
| 1734 |
and generates the final video.
|
| 1735 |
Uses yield to update status.
|
| 1736 |
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1737 |
if not segments_data:
|
| 1738 |
yield "No segments to process. Generate script first.", None
|
| 1739 |
return
|
|
@@ -1752,7 +1777,7 @@ def generate_video_from_edited(run_config, segments_data, segment_texts, segment
|
|
| 1752 |
TEMP_FOLDER = None # Reset global
|
| 1753 |
return
|
| 1754 |
|
| 1755 |
-
# Extract config from run_config
|
| 1756 |
TARGET_RESOLUTION = run_config.get("resolution", (1920, 1080)) # Default if missing
|
| 1757 |
CAPTION_ENABLED = run_config.get("caption_enabled", True) # Default if missing
|
| 1758 |
CAPTION_COLOR = run_config.get("caption_color", "#FFFFFF") # Default if missing
|
|
@@ -1845,7 +1870,7 @@ def generate_video_from_edited(run_config, segments_data, segment_texts, segment
|
|
| 1845 |
placeholder_clip = placeholder_clip.set_audio(AudioFileClip(silent_audio_path))
|
| 1846 |
error_text = f"Segment {idx+1} Failed"
|
| 1847 |
if segment.get('text'): error_text += f":\n{segment['text'][:50]}..."
|
| 1848 |
-
error_txt_clip = TextClip(error_text, fontsize=30, color="red", align='center', size=(TARGET_RESOLUTION[0] * 0.9, None)).set_position('center').set_duration(placeholder_clip.duration)
|
| 1849 |
placeholder_clip = CompositeVideoClip([placeholder_clip, error_txt_clip])
|
| 1850 |
clips.append(placeholder_clip)
|
| 1851 |
|
|
@@ -2045,14 +2070,16 @@ with gr.Blocks() as demo:
|
|
| 2045 |
)
|
| 2046 |
|
| 2047 |
# Generate Video Button Click
|
|
|
|
| 2048 |
generate_video_btn.click(
|
| 2049 |
fn=generate_video_from_edited,
|
| 2050 |
inputs=[
|
| 2051 |
-
run_config_state, #
|
| 2052 |
-
segments_state, #
|
| 2053 |
-
|
| 2054 |
-
*
|
| 2055 |
-
|
|
|
|
| 2056 |
],
|
| 2057 |
outputs=[status_output, final_video_output] # Yield status updates and final video
|
| 2058 |
)
|
|
@@ -2077,3 +2104,4 @@ if __name__ == "__main__":
|
|
| 2077 |
print("Warning: OPENROUTER_API_KEY is not configured. Script generation will fail.")
|
| 2078 |
|
| 2079 |
demo.launch(share=True) # Set share=True to get a public link
|
|
|
|
|
|
| 1344 |
color="red",
|
| 1345 |
align='center',
|
| 1346 |
size=(target_resolution[0] * 0.9, None)
|
| 1347 |
+
).set_position('center').set_duration(black_clip.duration) # Error text duration matches placeholder
|
| 1348 |
clip = CompositeVideoClip([black_clip, error_txt_clip])
|
| 1349 |
+
silent_audio_path = generate_silent_audio(clip.duration) # Generate silent audio matching placeholder duration
|
| 1350 |
if silent_audio_path and os.path.exists(silent_audio_path):
|
| 1351 |
try:
|
| 1352 |
+
# Use the actual placeholder clip duration for silent audio
|
| 1353 |
+
clip = clip.set_audio(AudioFileClip(silent_audio_path).subclip(0, clip.duration))
|
| 1354 |
except Exception as audio_e:
|
| 1355 |
print(f"Error setting silent audio for error clip {segment_index}: {audio_e}")
|
| 1356 |
clip = clip.set_audio(None)
|
|
|
|
| 1729 |
)
|
| 1730 |
|
| 1731 |
|
| 1732 |
+
# Update generate_video_from_edited to accept the unpacked dynamic arguments
|
| 1733 |
+
# It will receive: run_config_val, segments_data_val, then MAX_SEGMENTS_FOR_EDITING text values,
|
| 1734 |
+
# then MAX_SEGMENTS_FOR_EDITING file values, then bg_music_volume_val.
|
| 1735 |
+
def generate_video_from_edited(run_config_val, segments_data_val, *dynamic_segment_inputs, bg_music_volume_val):
|
| 1736 |
"""
|
| 1737 |
Takes the edited segment data (text, uploaded files) and configuration,
|
| 1738 |
and generates the final video.
|
| 1739 |
Uses yield to update status.
|
| 1740 |
"""
|
| 1741 |
+
# Re-pack the dynamic inputs into lists
|
| 1742 |
+
# dynamic_segment_inputs contains all text and file inputs from the UI, in order.
|
| 1743 |
+
# The first MAX_SEGMENTS_FOR_EDITING are text inputs, the next MAX_SEGMENTS_FOR_EDITING are file inputs.
|
| 1744 |
+
num_dynamic_per_segment = 2 # Textbox and File
|
| 1745 |
+
expected_dynamic_count = MAX_SEGMENTS_FOR_EDITING * num_dynamic_per_segment
|
| 1746 |
+
|
| 1747 |
+
if len(dynamic_segment_inputs) != expected_dynamic_count:
|
| 1748 |
+
print(f"Error: Expected {expected_dynamic_count} dynamic inputs, but received {len(dynamic_segment_inputs)}.")
|
| 1749 |
+
yield "Error: Mismatch in segment inputs received. Please regenerate script.", None
|
| 1750 |
+
return # Cannot proceed with incorrect inputs
|
| 1751 |
+
|
| 1752 |
+
segment_texts = list(dynamic_segment_inputs[:MAX_SEGMENTS_FOR_EDITING])
|
| 1753 |
+
segment_uploads = list(dynamic_segment_inputs[MAX_SEGMENTS_FOR_EDITING:])
|
| 1754 |
+
|
| 1755 |
+
|
| 1756 |
+
# Now use the re-packed lists and the correctly named arguments
|
| 1757 |
+
run_config = run_config_val
|
| 1758 |
+
segments_data = segments_data_val
|
| 1759 |
+
bg_music_volume = bg_music_volume_val
|
| 1760 |
+
|
| 1761 |
+
|
| 1762 |
if not segments_data:
|
| 1763 |
yield "No segments to process. Generate script first.", None
|
| 1764 |
return
|
|
|
|
| 1777 |
TEMP_FOLDER = None # Reset global
|
| 1778 |
return
|
| 1779 |
|
| 1780 |
+
# Extract other config from run_config
|
| 1781 |
TARGET_RESOLUTION = run_config.get("resolution", (1920, 1080)) # Default if missing
|
| 1782 |
CAPTION_ENABLED = run_config.get("caption_enabled", True) # Default if missing
|
| 1783 |
CAPTION_COLOR = run_config.get("caption_color", "#FFFFFF") # Default if missing
|
|
|
|
| 1870 |
placeholder_clip = placeholder_clip.set_audio(AudioFileClip(silent_audio_path))
|
| 1871 |
error_text = f"Segment {idx+1} Failed"
|
| 1872 |
if segment.get('text'): error_text += f":\n{segment['text'][:50]}..."
|
| 1873 |
+
error_txt_clip = TextClip(error_text, fontsize=30, color="red", align='center', size=(TARGET_RESOLUTION[0] * 0.9, None)).set_position('center').set_duration(placeholder_clip.duration) # Error text duration matches placeholder
|
| 1874 |
placeholder_clip = CompositeVideoClip([placeholder_clip, error_txt_clip])
|
| 1875 |
clips.append(placeholder_clip)
|
| 1876 |
|
|
|
|
| 2070 |
)
|
| 2071 |
|
| 2072 |
# Generate Video Button Click
|
| 2073 |
+
# Inputs must match the definition of generate_video_from_edited
|
| 2074 |
generate_video_btn.click(
|
| 2075 |
fn=generate_video_from_edited,
|
| 2076 |
inputs=[
|
| 2077 |
+
run_config_state, # 1st arg
|
| 2078 |
+
segments_state, # 2nd arg
|
| 2079 |
+
# All segment text and file inputs will be collected by *dynamic_segment_inputs
|
| 2080 |
+
*segment_text_inputs,
|
| 2081 |
+
*segment_file_inputs,
|
| 2082 |
+
bg_music_volume_slider # Last arg
|
| 2083 |
],
|
| 2084 |
outputs=[status_output, final_video_output] # Yield status updates and final video
|
| 2085 |
)
|
|
|
|
| 2104 |
print("Warning: OPENROUTER_API_KEY is not configured. Script generation will fail.")
|
| 2105 |
|
| 2106 |
demo.launch(share=True) # Set share=True to get a public link
|
| 2107 |
+
|