File size: 9,220 Bytes
0c78d95
 
 
 
 
 
 
 
 
 
 
 
 
 
 
371c310
 
98173c8
00e83a0
 
126385a
0c78d95
371c310
 
98173c8
0c78d95
 
 
 
98173c8
 
 
 
 
 
 
 
00e83a0
 
 
 
126385a
0c78d95
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
00e83a0
 
 
 
 
 
 
0c78d95
 
 
 
 
 
00e83a0
 
 
 
 
 
 
 
0c78d95
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
98173c8
0c78d95
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
00e83a0
0c78d95
98173c8
0c78d95
 
 
 
 
 
 
 
 
 
 
 
 
126385a
0c78d95
 
 
 
 
 
 
 
00e83a0
0c78d95
00e83a0
0c78d95
00e83a0
0c78d95
 
 
 
 
 
 
126385a
0c78d95
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
98173c8
0c78d95
 
 
 
 
 
 
 
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
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
"""
project @ images_to_video
created @ 2024-12-12
author  @ github.com/ishworrsubedii
"""

from moviepy.audio.io.AudioFileClip import AudioFileClip
from moviepy.video.VideoClip import ImageClip, ColorClip, TextClip
from moviepy.video.compositing.CompositeVideoClip import CompositeVideoClip
from moviepy.video.compositing.concatenate import concatenate_videoclips
from moviepy.video.fx.all import resize
from moviepy.video.io.VideoFileClip import VideoFileClip


class VideoCreator:
    def __init__(self, intro_video_path, necklace_image, nto_outputs: list, nto_cto_outputs: list, makeup_outputs: list,
                 font_path,
                 output_path, audio_path, image_display_duration=2.5, box_color: tuple = (131, 42, 48), box_opacity=0.8,
                 font_size=28, necklace_display_duration=2.0, text_color="white", fps=1,
                 necklace_image_title="Necklace Preview", nto_image_title="Necklace Try-On",
                 nto_cto_image_title="Clothing Try-On", makeup_image_title="Makeup Try-On", outro_video_path=""):
        self.intro_video_path = intro_video_path
        self.nto_images_dir = nto_outputs
        self.nto_cto_images_dir = nto_cto_outputs
        self.makeup_images_dir = makeup_outputs
        self.output_video_path = output_path
        self.font_path = font_path
        self.necklace_image = necklace_image
        self.audio_path = audio_path
        self.image_display_duration = image_display_duration
        self.text_color = text_color
        self.box_color = box_color
        self.box_opacity = box_opacity
        self.font_size = font_size
        self.category_font_size = font_size
        self.necklace_display_duration = necklace_display_duration
        self.fps = fps
        self.necklace_image_title = necklace_image_title
        self.makeup_image_title = makeup_image_title
        self.nto_image_title = necklace_image_title
        self.nto_cto_image_title = nto_cto_image_title
        self.outro_video_path = outro_video_path

    def create_necklace_clips(self, necklace_image_path, backgrounds=None):
        if backgrounds is None:
            backgrounds = [

                # Add to your configurations
                (245, 245, 245),  # Soft White (Perfect for Gold)
                (220, 245, 245),  # Rich Black (Premium look)
                (230, 230, 235),  # Pearl Gray (Elegant)
                # Alternative premium colors:
                # (25, 25, 112),   # Midnight Blue
                # (44, 49, 51),    # Charcoal
                # (189, 172, 152), # Champagne
                # (241, 235, 218), # Ivory
            ]

        necklace_clips = []

        # Create a clip for each background color
        for bg_color in backgrounds:
            # Create background
            # bg_clip = ColorClip((1080, 1080), col=bg_color)
            # bg_clip = bg_clip.set_duration(self.image_display_duration)
            #
            # # Create necklace clip
            # necklace = ImageClip(necklace_image_path)
            # w, h = necklace.size
            # new_size = (w * 0.5, h * 0.15)
            bg_clip = ColorClip((1080, 1080), col=bg_color)
            bg_clip = bg_clip.set_duration(self.image_display_duration)

            # Create necklace clip
            necklace = ImageClip(necklace_image_path)
            w, h = necklace.size

            # Calculate the scaling factor based on the desired width of 1080
            scaling_factor = 1080 / w

            # Calculate the new size to maintain the aspect ratio, then reduce it by 20%
            new_size = (1080, int(h * scaling_factor))
            new_size = (int(new_size[0] * 0.6), int(new_size[1] * 0.6))

            necklace = resize(necklace, (new_size))  # Adjust size as needed
            necklace = necklace.set_duration(self.image_display_duration)

            # Center the necklace
            necklace = necklace.set_position('center')

            # Composite necklace over background
            final_clip = CompositeVideoClip([bg_clip, necklace])

            # Add text overlay
            txt_overlay = self.create_text_overlay("Necklace Preview", (1080, 80), self.image_display_duration)
            txt_overlay = txt_overlay.set_position(('center', 'bottom'))

            final_clip = CompositeVideoClip([final_clip, txt_overlay])
            necklace_clips.append(final_clip)

        return necklace_clips

    def create_text_overlay(self, text, size, duration, is_category=False):
        """Create a professional text overlay with background box"""
        # Create background box
        w, h = 1080, 120 if is_category else 80
        box = ColorClip((w, h), col=self.box_color, duration=duration)
        print("box_opacity", self.box_opacity)
        box = box.set_opacity(self.box_opacity)

        # Create text using TextClip with method='label' instead of default
        txt = TextClip(
            text,
            font=self.font_path,
            fontsize=self.category_font_size if is_category else self.font_size,
            color=self.text_color,
            size=(w, h),
            method='label'  # Use 'label' method instead of default
        ).set_position('center').set_duration(duration)

        return CompositeVideoClip([box, txt])

    def add_text_to_image(self, image_path, text, font_path, output_path):
        # Create image clip
        img_clip = ImageClip(image_path).resize((1080, 1080))

        # Create text overlay
        txt_overlay = self.create_text_overlay(text, (1080, 80), img_clip.duration)
        txt_overlay = txt_overlay.set_position(('center', 'bottom'))

        # Composite the clips
        final_clip = CompositeVideoClip([img_clip, txt_overlay])

        # Save as image
        final_clip.save_frame(output_path, t=0)
        return output_path

    def create_image_clip(self, image_path, text, duration):
        img_clip = ImageClip(image_path)
        img_clip = resize(img_clip, (1080, 1080))  # Using resize from fx.all
        img_clip = img_clip.set_duration(duration)

        txt_overlay = self.create_text_overlay(text, (1080, 80), duration)
        txt_overlay = txt_overlay.set_position(('center', 'bottom'))

        final_clip = CompositeVideoClip([img_clip, txt_overlay])

        return final_clip

    def process_images_in_directory(self, directory, duration, text):
        clips = []
        print(directory)

        for image_file in sorted(directory):
            if image_file.lower().endswith((".png", ".jpg", ".jpeg", ".webp")):
                print(f"Processing image: {image_file}")
                # image_path = os.path.join(directory, image_file)
                clip = self.create_image_clip(image_file, text, duration)
                clips.append(clip)

        return clips

    def create_final_video(self):
        print("Loading and processing main videos...")
        intro_clip = resize(VideoFileClip(self.intro_video_path), (1080, 1080))
        outro_clip = resize(VideoFileClip(self.outro_video_path), (1080, 1080))
        # outro_clip = resize(VideoFileClip(outro_video_p/ath), (1080, 1080))
        # outro_clip_1 = resize(VideoFileClip(outro_video_path1), (1080, 1080))

        # Create necklace preview clips with different backgrounds
        necklace_clips = self.create_necklace_clips(self.necklace_image)

        # Process images with categories
        nto_image_clips = self.process_images_in_directory(
            self.nto_images_dir, self.image_display_duration, self.nto_image_title)
        nto_cto_image_clips = self.process_images_in_directory(
            self.nto_cto_images_dir, self.image_display_duration, self.nto_cto_image_title)
        makeup_image_clips = self.process_images_in_directory(
            self.makeup_images_dir, self.image_display_duration, self.makeup_image_title)

        # Combine all clips
        all_clips = [intro_clip]
        all_clips.extend(necklace_clips)
        all_clips.extend(nto_image_clips)
        all_clips.extend(nto_cto_image_clips)
        all_clips.extend(makeup_image_clips)
        all_clips.append(outro_clip)
        # all_clips.append(outro_clip_1)

        # Create final temp_video without transitions
        final_video = concatenate_videoclips(all_clips, method="compose")

        # Add audio
        try:
            print("Adding audio...")
            audio = AudioFileClip(self.audio_path)

            video_duration = final_video.duration

            if audio.duration > video_duration:
                audio = audio.subclip(0, video_duration)

            final_video = final_video.set_audio(audio)
            print("Audio added successfully")
        except Exception as e:
            print(f"Error adding audio: {str(e)}")

        # Write the final temp_video with progress bar
        print("Rendering final temp_video...")
        final_video.write_videofile(
            self.output_video_path,
            fps=self.fps,
            codec="libx264",
            audio_codec="aac",
            bitrate="8000k",
            threads=4,
            preset='ultrafast'  # ultrafast, superfast, veryfast, faster, fast, medium, slow, slower, veryslow
        )

        print(f"Video saved to: {self.output_video_path}")