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)