File size: 3,517 Bytes
32f8cca f64f1e1 cd66313 f64f1e1 577c46c 196c546 577c46c b5a774c 196c546 455e8dd 196c546 455e8dd b5a774c 32f8cca 577c46c 196c546 d9e2c18 196c546 d9e2c18 196c546 5d8bfc6 196c546 5d8bfc6 196c546 5d8bfc6 196c546 5d8bfc6 196c546 577c46c 196c546 5d8bfc6 196c546 455e8dd 196c546 32f8cca 455e8dd cd66313 196c546 f64f1e1 455e8dd 196c546 577c46c 196c546 577c46c 71723c0 196c546 b5a774c 577c46c 455e8dd 964a8e4 b5a774c 196c546 577c46c 455e8dd 577c46c b5a774c 964a8e4 577c46c 964a8e4 577c46c 455e8dd 577c46c 964a8e4 577c46c b5a774c 455e8dd 964a8e4 b5a774c 196c546 b5a774c 196c546 964a8e4 196c546 964a8e4 196c546 32f8cca 196c546 f64f1e1 455e8dd 196c546 455e8dd 196c546 455e8dd 71723c0 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 | 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) |