Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -21,17 +21,18 @@ def generate_slideshow(images, duration, texts=None, y_pos=0.5, fade_duration=0.
|
|
| 21 |
clip_path = Path(temp_dir) / f"clip_{i}.mp4"
|
| 22 |
text = texts[i] if i < len(texts) else ""
|
| 23 |
|
| 24 |
-
|
|
|
|
|
|
|
|
|
|
| 25 |
if text:
|
| 26 |
safe_text = text.replace(":", "\\:").replace("'", "\\'")
|
| 27 |
-
|
| 28 |
-
f"
|
| 29 |
f"x=(w-text_w)/2:y=(h-text_h)*{y_pos}:"
|
| 30 |
f"alpha='if(lt(t,{fade_duration}), t/{fade_duration}, if(lt(t,{duration}-{fade_duration}), 1, ({duration}-t)/{fade_duration}))'"
|
| 31 |
)
|
| 32 |
-
|
| 33 |
-
# scale + pad für schwarze Ränder, Bild bleibt proportional
|
| 34 |
-
vf = f"scale='min(1280,iw)':-2,pad=1280:720:(1280-iw)/2:(720-ih)/2:color=black,fps=25,format=yuv420p{drawtext}"
|
| 35 |
|
| 36 |
cmd = [
|
| 37 |
"ffmpeg",
|
|
@@ -39,13 +40,13 @@ def generate_slideshow(images, duration, texts=None, y_pos=0.5, fade_duration=0.
|
|
| 39 |
"-loop", "1",
|
| 40 |
"-i", str(img_path),
|
| 41 |
"-t", str(duration),
|
| 42 |
-
"-vf",
|
| 43 |
str(clip_path)
|
| 44 |
]
|
| 45 |
try:
|
| 46 |
-
subprocess.run(cmd, check=True)
|
| 47 |
except subprocess.CalledProcessError as e:
|
| 48 |
-
return None, f"❌ FFmpeg Fehler:\n{e.stderr}"
|
| 49 |
|
| 50 |
clips.append(clip_path)
|
| 51 |
|
|
@@ -66,7 +67,7 @@ def generate_slideshow(images, duration, texts=None, y_pos=0.5, fade_duration=0.
|
|
| 66 |
str(output_file)
|
| 67 |
]
|
| 68 |
try:
|
| 69 |
-
subprocess.run(cmd_concat, check=True)
|
| 70 |
except subprocess.CalledProcessError as e:
|
| 71 |
return None, f"❌ FFmpeg Concat Fehler:\n{e.stderr}"
|
| 72 |
|
|
@@ -77,7 +78,7 @@ def generate_slideshow(images, duration, texts=None, y_pos=0.5, fade_duration=0.
|
|
| 77 |
|
| 78 |
# Gradio UI
|
| 79 |
with gr.Blocks() as demo:
|
| 80 |
-
gr.Markdown("# Slideshow mit
|
| 81 |
|
| 82 |
img_input = gr.Files(label="Bilder auswählen (mehrere)", file_types=allowed_medias)
|
| 83 |
duration_input = gr.Number(value=3, label="Dauer pro Bild in Sekunden", precision=1)
|
|
|
|
| 21 |
clip_path = Path(temp_dir) / f"clip_{i}.mp4"
|
| 22 |
text = texts[i] if i < len(texts) else ""
|
| 23 |
|
| 24 |
+
# Scale + Pad Filter
|
| 25 |
+
vf_filters = "scale='min(1280,iw)':-2,pad=1280:720:(1280-iw)/2:(720-ih)/2:color=black,fps=25,format=yuv420p"
|
| 26 |
+
|
| 27 |
+
# Text Overlay Filter
|
| 28 |
if text:
|
| 29 |
safe_text = text.replace(":", "\\:").replace("'", "\\'")
|
| 30 |
+
drawtext_filter = (
|
| 31 |
+
f"drawtext=text='{safe_text}':fontcolor=white:fontsize={font_size}:borderw=2:"
|
| 32 |
f"x=(w-text_w)/2:y=(h-text_h)*{y_pos}:"
|
| 33 |
f"alpha='if(lt(t,{fade_duration}), t/{fade_duration}, if(lt(t,{duration}-{fade_duration}), 1, ({duration}-t)/{fade_duration}))'"
|
| 34 |
)
|
| 35 |
+
vf_filters += f",{drawtext_filter}"
|
|
|
|
|
|
|
| 36 |
|
| 37 |
cmd = [
|
| 38 |
"ffmpeg",
|
|
|
|
| 40 |
"-loop", "1",
|
| 41 |
"-i", str(img_path),
|
| 42 |
"-t", str(duration),
|
| 43 |
+
"-vf", vf_filters,
|
| 44 |
str(clip_path)
|
| 45 |
]
|
| 46 |
try:
|
| 47 |
+
subprocess.run(cmd, check=True, capture_output=True, text=True)
|
| 48 |
except subprocess.CalledProcessError as e:
|
| 49 |
+
return None, f"❌ FFmpeg Fehler bei Bild {i+1}:\n{e.stderr}"
|
| 50 |
|
| 51 |
clips.append(clip_path)
|
| 52 |
|
|
|
|
| 67 |
str(output_file)
|
| 68 |
]
|
| 69 |
try:
|
| 70 |
+
subprocess.run(cmd_concat, check=True, capture_output=True, text=True)
|
| 71 |
except subprocess.CalledProcessError as e:
|
| 72 |
return None, f"❌ FFmpeg Concat Fehler:\n{e.stderr}"
|
| 73 |
|
|
|
|
| 78 |
|
| 79 |
# Gradio UI
|
| 80 |
with gr.Blocks() as demo:
|
| 81 |
+
gr.Markdown("# Slideshow mit Text-Overlay & schwarzen Rändern")
|
| 82 |
|
| 83 |
img_input = gr.Files(label="Bilder auswählen (mehrere)", file_types=allowed_medias)
|
| 84 |
duration_input = gr.Number(value=3, label="Dauer pro Bild in Sekunden", precision=1)
|