Tim13ekd commited on
Commit
085ad5b
·
verified ·
1 Parent(s): 6edd0f2

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +19 -18
app.py CHANGED
@@ -6,16 +6,11 @@ import subprocess
6
 
7
  allowed_medias = [".png", ".jpg", ".jpeg", ".bmp", ".gif", ".tiff"]
8
 
9
- def generate_slideshow_with_fixed_text_and_padding(images, duration, texts=None, y_pos=0.5, fade_duration=0.7, font_size=60):
10
- """
11
- Slideshow:
12
- - Bilder behalten ihre Proportionen, schwarze Balken falls nötig
13
- - Text Overlay mit Y-Position, Fade-In/Out und fester Größe
14
- """
15
  if not images:
16
  return None, "❌ Keine Bilder ausgewählt"
17
 
18
- y_pos = min(max(0.0, y_pos), 0.9) # Max 0.9 für unten
19
  temp_dir = tempfile.mkdtemp()
20
  clips = []
21
 
@@ -28,33 +23,37 @@ def generate_slideshow_with_fixed_text_and_padding(images, duration, texts=None,
28
 
29
  drawtext = ""
30
  if text:
 
31
  drawtext = (
32
- f"drawtext=text='{text}':fontcolor=white:fontsize={font_size}:borderw=2:"
33
  f"x=(w-text_w)/2:y=(h-text_h)*{y_pos}:"
34
  f"alpha='if(lt(t,{fade_duration}), t/{fade_duration}, if(lt(t,{duration}-{fade_duration}), 1, ({duration}-t)/{fade_duration}))'"
35
  )
36
 
37
- # FFmpeg scale + pad für schwarzen Rand
 
 
38
  cmd = [
39
  "ffmpeg",
40
  "-y",
41
  "-loop", "1",
42
  "-i", str(img_path),
43
  "-t", str(duration),
44
- "-vf",
45
- f"scale='min(1280,iw)':-2,pad=1280:720:(1280-iw)/2:(720-ih)/2:color=black,fps=25,format=yuv420p{','+drawtext if drawtext else ''}",
46
  str(clip_path)
47
  ]
48
- subprocess.run(cmd, check=True)
 
 
 
 
49
  clips.append(clip_path)
50
 
51
- # Filelist für concat
52
  filelist_path = Path(temp_dir) / "filelist.txt"
53
  with open(filelist_path, "w") as f:
54
  for clip in clips:
55
  f.write(f"file '{clip}'\n")
56
 
57
- # Clips zusammenfügen
58
  output_file = Path(temp_dir) / f"slideshow_{uuid.uuid4().hex}.mp4"
59
  cmd_concat = [
60
  "ffmpeg",
@@ -66,17 +65,19 @@ def generate_slideshow_with_fixed_text_and_padding(images, duration, texts=None,
66
  "-pix_fmt", "yuv420p",
67
  str(output_file)
68
  ]
69
- subprocess.run(cmd_concat, check=True)
 
 
 
70
 
71
  if output_file.exists():
72
  return str(output_file), f"✅ Slideshow erstellt ({len(images)} Bilder, Text fixiert, Bildproportionen erhalten)"
73
  else:
74
  return None, "❌ Video konnte nicht erstellt werden"
75
 
76
-
77
  # Gradio UI
78
  with gr.Blocks() as demo:
79
- gr.Markdown("# Slideshow mit Text-Overlay & schwarzem Rand")
80
 
81
  img_input = gr.Files(label="Bilder auswählen (mehrere)", file_types=allowed_medias)
82
  duration_input = gr.Number(value=3, label="Dauer pro Bild in Sekunden", precision=1)
@@ -89,7 +90,7 @@ with gr.Blocks() as demo:
89
 
90
  def wrapper(images, duration, fade_duration, text_str, y_pos, font_size):
91
  texts = [t.strip() for t in text_str.split(",")] if text_str else None
92
- return generate_slideshow_with_fixed_text_and_padding(images, duration, texts, y_pos, fade_duration, font_size)
93
 
94
  btn = gr.Button("Video erstellen")
95
  btn.click(
 
6
 
7
  allowed_medias = [".png", ".jpg", ".jpeg", ".bmp", ".gif", ".tiff"]
8
 
9
+ def generate_slideshow(images, duration, texts=None, y_pos=0.5, fade_duration=0.7, font_size=60):
 
 
 
 
 
10
  if not images:
11
  return None, "❌ Keine Bilder ausgewählt"
12
 
13
+ y_pos = min(max(0.0, y_pos), 0.9)
14
  temp_dir = tempfile.mkdtemp()
15
  clips = []
16
 
 
23
 
24
  drawtext = ""
25
  if text:
26
+ safe_text = text.replace(":", "\\:").replace("'", "\\'")
27
  drawtext = (
28
+ f",drawtext=text='{safe_text}':fontcolor=white:fontsize={font_size}:borderw=2:"
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",
38
  "-y",
39
  "-loop", "1",
40
  "-i", str(img_path),
41
  "-t", str(duration),
42
+ "-vf", 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
 
 
52
  filelist_path = Path(temp_dir) / "filelist.txt"
53
  with open(filelist_path, "w") as f:
54
  for clip in clips:
55
  f.write(f"file '{clip}'\n")
56
 
 
57
  output_file = Path(temp_dir) / f"slideshow_{uuid.uuid4().hex}.mp4"
58
  cmd_concat = [
59
  "ffmpeg",
 
65
  "-pix_fmt", "yuv420p",
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
 
73
  if output_file.exists():
74
  return str(output_file), f"✅ Slideshow erstellt ({len(images)} Bilder, Text fixiert, Bildproportionen erhalten)"
75
  else:
76
  return None, "❌ Video konnte nicht erstellt werden"
77
 
 
78
  # Gradio UI
79
  with gr.Blocks() as demo:
80
+ gr.Markdown("# Slideshow mit fixiertem Text-Overlay & schwarzen Rändern")
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)
 
90
 
91
  def wrapper(images, duration, fade_duration, text_str, y_pos, font_size):
92
  texts = [t.strip() for t in text_str.split(",")] if text_str else None
93
+ return generate_slideshow(images, duration, texts, y_pos, fade_duration, font_size)
94
 
95
  btn = gr.Button("Video erstellen")
96
  btn.click(