import subprocess import os def generate_background(images, output_path): if not images: raise Exception("No images uploaded") temp_videos = [] slide_duration = 15 fade_duration = 1 total_slide_duration = slide_duration + fade_duration fps = 30 total_frames = int(total_slide_duration * fps) # ===================================================== # GENERATE EACH SLIDE # ===================================================== for i, img in enumerate(images): temp_video = f"/tmp/slide_{i}.mp4" temp_videos.append(temp_video) vf = ( "scale=3840:2160:force_original_aspect_ratio=increase," "crop=3840:2160," f"zoompan=" f"z='1+0.30*on/{total_frames}':" f"x='(iw-iw/zoom)/2':" f"y='(ih-ih/zoom)/2':" f"d={total_frames}:" "s=3840x2160:" f"fps={fps}," "scale=1920:1080:flags=lanczos" ) cmd = [ "ffmpeg", "-y", "-loop", "1", "-i", img, "-vf", vf, "-t", str(total_slide_duration), "-c:v", "libx264", "-preset", "ultrafast", "-crf", "22", "-pix_fmt", "yuv420p", temp_video ] print(f"Generating slide {i+1}/{len(images)}") subprocess.run(cmd, check=True) # ===================================================== # SINGLE IMAGE # ===================================================== if len(temp_videos) == 1: os.replace(temp_videos[0], output_path) return # ===================================================== # XFADE COMBINE # ===================================================== print("Combining slides...") cmd_concat = ["ffmpeg", "-y"] for v in temp_videos: cmd_concat.extend(["-i", v]) filter_complex = "" current = "[0:v]" offset = slide_duration for i in range(1, len(temp_videos)): next_input = f"[{i}:v]" if i == len(temp_videos) - 1: out = "" else: out = f"[v{i}]" filter_complex += ( f"{current}{next_input}" f"xfade=" f"transition=fade:" f"duration={fade_duration}:" f"offset={offset}" f"{out};" ) current = f"[v{i}]" offset += slide_duration filter_complex = filter_complex.rstrip(";") cmd_concat.extend([ "-filter_complex", filter_complex, "-c:v", "libx264", "-preset", "ultrafast", "-crf", "22", "-pix_fmt", "yuv420p", output_path ]) subprocess.run(cmd_concat, check=True) # ===================================================== # CHECK RESULT # ===================================================== probe = subprocess.run( [ "ffprobe", "-v", "error", "-show_entries", "format=duration", "-of", "default=noprint_wrappers=1:nokey=1", output_path ], capture_output=True, text=True ) print("\nSUCCESS") print(f"Duration: {probe.stdout.strip()} sec") # ===================================================== # CLEANUP # ===================================================== for v in temp_videos: if os.path.exists(v): os.remove(v)