saq1b commited on
Commit
6f61fa6
·
verified ·
1 Parent(s): 9b13a7b

Update main.py

Browse files
Files changed (1) hide show
  1. main.py +36 -30
main.py CHANGED
@@ -56,22 +56,19 @@ async def download_file(url, local_path):
56
  return False
57
 
58
  async def create_slideshow(image_paths, audio_path, output_path, duration, zoom=False, zoom_ratio=0.04):
59
- """Generate slideshow from images and audio using ffmpeg asynchronously"""
60
- # Create temporary file list for ffmpeg concat
61
- concat_file = "temp_concat.txt"
62
-
63
  if not zoom:
64
- # Original implementation without zoom effect
 
65
  async with aiofiles.open(concat_file, "w") as f:
66
  for img in image_paths:
67
  await f.write(f"file '{img}'\n")
68
  await f.write(f"duration {duration}\n")
69
-
70
  # Add the last image again without duration (required by ffmpeg)
71
  if image_paths:
72
  await f.write(f"file '{image_paths[-1]}'\n")
73
 
74
- # Run ffmpeg command to create slideshow with audio
75
  total_duration = len(image_paths) * duration
76
  cmd = [
77
  "ffmpeg",
@@ -88,31 +85,39 @@ async def create_slideshow(image_paths, audio_path, output_path, duration, zoom=
88
  output_path
89
  ]
90
  else:
91
- # Build separate inputs looping each image with a fixed display time and apply zoom effect
92
- cmd_inputs = []
 
 
 
93
  for img in image_paths:
94
- cmd_inputs += ["-loop", "1", "-t", str(duration), "-i", img]
95
- # Add the audio input at the end
96
- cmd_inputs += ["-i", audio_path]
97
-
98
- # Create filter_complex applying zoompan on each image (assuming 25 fps)
99
- num_images = len(image_paths)
100
- filter_parts = []
101
- for i in range(num_images):
102
- filter_parts.append(
103
- f"[{i}:v]zoompan=z='min(zoom+{zoom_ratio},1.5)':d={duration * 25}:s=hd720,setsar=1[v{i}]"
 
 
104
  )
105
- # Concatenate all processed clips
106
- concat_inputs = "".join(f"[v{i}]" for i in range(num_images))
107
- filter_complex = "; ".join(filter_parts) + f"; {concat_inputs}concat=n={num_images}:v=1:a=0,format=yuv420p[v]"
108
-
109
- # Build the ffmpeg command with proper mapping for video and audio (audio is after the images)
110
- cmd = ["ffmpeg", "-y"] + cmd_inputs + [
111
  "-filter_complex", filter_complex,
112
  "-map", "[v]",
113
- "-map", f"{num_images}:a",
 
 
114
  "-c:a", "aac",
115
  "-shortest",
 
116
  output_path
117
  ]
118
 
@@ -124,8 +129,9 @@ async def create_slideshow(image_paths, audio_path, output_path, duration, zoom=
124
  )
125
  stdout, stderr = await process.communicate()
126
 
127
- if os.path.exists(concat_file):
128
- os.remove(concat_file)
 
129
 
130
  if process.returncode != 0:
131
  print(f"FFmpeg error: {stderr.decode()}")
@@ -133,8 +139,8 @@ async def create_slideshow(image_paths, audio_path, output_path, duration, zoom=
133
  return True
134
  except Exception as e:
135
  print(f"FFmpeg error: {str(e)}")
136
- if os.path.exists(concat_file):
137
- os.remove(concat_file)
138
  return False
139
 
140
  @app.post("/make_slideshow")
 
56
  return False
57
 
58
  async def create_slideshow(image_paths, audio_path, output_path, duration, zoom=False, zoom_ratio=0.04):
59
+ """Generate slideshow from images and audio using ffmpeg asynchronously, with optional zoom effect"""
 
 
 
60
  if not zoom:
61
+ # Create temporary file list for ffmpeg concat
62
+ concat_file = "temp_concat.txt"
63
  async with aiofiles.open(concat_file, "w") as f:
64
  for img in image_paths:
65
  await f.write(f"file '{img}'\n")
66
  await f.write(f"duration {duration}\n")
 
67
  # Add the last image again without duration (required by ffmpeg)
68
  if image_paths:
69
  await f.write(f"file '{image_paths[-1]}'\n")
70
 
71
+ # Run ffmpeg command to create slideshow with audio (no zoom)
72
  total_duration = len(image_paths) * duration
73
  cmd = [
74
  "ffmpeg",
 
85
  output_path
86
  ]
87
  else:
88
+ # Generate slideshow with zoom effect using ffmpeg zoompan filter.
89
+ # For each image, loop it for the specified duration and apply zoompan.
90
+ fps = 25 # frames per second
91
+ cmd = ["ffmpeg"]
92
+ # Add each image as an input that loops for 'duration' seconds.
93
  for img in image_paths:
94
+ cmd.extend(["-loop", "1", "-t", str(duration), "-i", img])
95
+ # Append the audio input. Its index will be len(image_paths)
96
+ cmd.extend(["-i", audio_path])
97
+
98
+ # Build filter_complex for zoompan on each image.
99
+ filter_complex = ""
100
+ for i in range(len(image_paths)):
101
+ # Each input's video stream is processed with zoompan.
102
+ # The zoom increases by zoom_ratio per frame (starting at 1.0).
103
+ filter_complex += (
104
+ f"[{i}:v]zoompan=z='if(eq(on,0),1,zoom+{zoom_ratio})':"
105
+ f"d={duration*fps}:s=hd720, setpts=PTS-STARTPTS[v{i}];"
106
  )
107
+ # Concatenate all processed video segments.
108
+ inputs = "".join(f"[v{i}]" for i in range(len(image_paths)))
109
+ filter_complex += f"{inputs}concat=n={len(image_paths)}:v=1:a=0,format=yuv420p[v]"
110
+
111
+ # Map the concatenated video and the audio stream (audio input is at index len(image_paths))
112
+ cmd = cmd + [
113
  "-filter_complex", filter_complex,
114
  "-map", "[v]",
115
+ "-map", f"{len(image_paths)}:a",
116
+ "-c:v", "libx264",
117
+ "-pix_fmt", "yuv420p",
118
  "-c:a", "aac",
119
  "-shortest",
120
+ "-y",
121
  output_path
122
  ]
123
 
 
129
  )
130
  stdout, stderr = await process.communicate()
131
 
132
+ # Remove temporary concat file if it exists
133
+ if not zoom and os.path.exists("temp_concat.txt"):
134
+ os.remove("temp_concat.txt")
135
 
136
  if process.returncode != 0:
137
  print(f"FFmpeg error: {stderr.decode()}")
 
139
  return True
140
  except Exception as e:
141
  print(f"FFmpeg error: {str(e)}")
142
+ if not zoom and os.path.exists("temp_concat.txt"):
143
+ os.remove("temp_concat.txt")
144
  return False
145
 
146
  @app.post("/make_slideshow")