Spaces:
Running
Running
ishworrsubedii
commited on
Commit
•
0c78d95
1
Parent(s):
df125f5
add: video maker, supabase upload, gitlfs
Browse files- .gitattributes +3 -0
- .gitignore +4 -0
- Dockerfile +18 -0
- app.py +96 -0
- requirements.tx +4 -0
- resources/fonts/PlayfairDisplay-VariableFont_wght.ttf +3 -0
- resources/intro/ChamundiJewelsMandir_intro.mp4 +3 -0
- resources/intro/JewelMirror_intro.mp4 +3 -0
- resources/thankyou/thankyououtro.mp4 +3 -0
- src/components/imgs_video.py +135 -0
- src/components/main.py +164 -0
- src/components/vidgen.py +196 -0
.gitattributes
CHANGED
@@ -34,3 +34,6 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
36 |
*.mp3 filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
36 |
*.mp3 filter=lfs diff=lfs merge=lfs -text
|
37 |
+
*.mp4 filter=lfs diff=lfs merge=lfs -text
|
38 |
+
*.ttf filter=lfs diff=lfs merge=lfs -text
|
39 |
+
*.otf filter=lfs diff=lfs merge=lfs -text
|
.gitignore
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
resourcesssss
|
2 |
+
temp_video
|
3 |
+
__pycache__
|
4 |
+
.idea
|
Dockerfile
ADDED
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
FROM python:3.9-slim
|
2 |
+
|
3 |
+
WORKDIR /app
|
4 |
+
|
5 |
+
RUN chmod 777 /app
|
6 |
+
|
7 |
+
RUN apt-get update && apt-get install -y \
|
8 |
+
ffmpeg \
|
9 |
+
&& rm -rf /var/lib/apt/lists/*
|
10 |
+
|
11 |
+
COPY requirements.txt ./
|
12 |
+
|
13 |
+
RUN pip install --no-cache-dir -r requirements.txt
|
14 |
+
|
15 |
+
COPY src ./src
|
16 |
+
COPY app.py ./
|
17 |
+
|
18 |
+
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860"]
|
app.py
ADDED
@@ -0,0 +1,96 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
import tempfile
|
3 |
+
|
4 |
+
from fastapi import FastAPI, File, UploadFile
|
5 |
+
from starlette.responses import JSONResponse
|
6 |
+
from supabase import create_client
|
7 |
+
|
8 |
+
from src.components.vidgen import VideoCreator
|
9 |
+
|
10 |
+
supabase_url = os.getenv('SUPABASE_URL')
|
11 |
+
supabase_key = os.getenv('SUPABASE_KEY')
|
12 |
+
supabase = create_client(supabase_url, supabase_key)
|
13 |
+
app = FastAPI()
|
14 |
+
|
15 |
+
RESOURCES_DIR = "resources"
|
16 |
+
os.makedirs(RESOURCES_DIR, exist_ok=True)
|
17 |
+
|
18 |
+
|
19 |
+
def upload_to_supabase(video_path, bucket_name="JewelmirrorVideoGeneration"):
|
20 |
+
try:
|
21 |
+
with open(video_path, 'rb') as f:
|
22 |
+
file_name = os.path.basename(video_path)
|
23 |
+
supabase.storage.from_(bucket_name).upload(file_name, f)
|
24 |
+
|
25 |
+
public_url = supabase.storage.from_(bucket_name).get_public_url(file_name)
|
26 |
+
return public_url
|
27 |
+
except Exception as e:
|
28 |
+
print(f"Error uploading to Supabase: {str(e)}")
|
29 |
+
return None
|
30 |
+
|
31 |
+
|
32 |
+
@app.post("/create-video/")
|
33 |
+
async def create_video(
|
34 |
+
necklace_image: UploadFile = File(...),
|
35 |
+
necklace_tryon_image: UploadFile = File(...),
|
36 |
+
makeup_image: UploadFile = File(...),
|
37 |
+
clothing_image_1: UploadFile = File(...),
|
38 |
+
clothing_image_2: UploadFile = File(...)
|
39 |
+
):
|
40 |
+
try:
|
41 |
+
def save_temp_file(file: UploadFile) -> str:
|
42 |
+
temp_file = tempfile.NamedTemporaryFile(delete=False, suffix=".png")
|
43 |
+
temp_file.write(file.file.read())
|
44 |
+
return temp_file.name
|
45 |
+
|
46 |
+
temp_files = {
|
47 |
+
'necklace': save_temp_file(necklace_image),
|
48 |
+
'necklace_tryon': save_temp_file(necklace_tryon_image),
|
49 |
+
'makeup': save_temp_file(makeup_image),
|
50 |
+
'clothing1': save_temp_file(clothing_image_1),
|
51 |
+
'clothing2': save_temp_file(clothing_image_2)
|
52 |
+
}
|
53 |
+
|
54 |
+
intro_path = f"{RESOURCES_DIR}/intro/ChamundiJewelsMandir_intro.mp4"
|
55 |
+
output_path = f"{RESOURCES_DIR}/temp_video/video_{os.urandom(8).hex()}.mp4"
|
56 |
+
font_path = f"{RESOURCES_DIR}/fonts/PlayfairDisplay-VariableFont_wght.ttf"
|
57 |
+
audio_path = f"{RESOURCES_DIR}/audio/background.mp3"
|
58 |
+
|
59 |
+
video_creator = VideoCreator(
|
60 |
+
intro_video_path=intro_path,
|
61 |
+
necklace_image=temp_files['necklace'],
|
62 |
+
nto_image1=temp_files['necklace_tryon'],
|
63 |
+
nto_cto_1=temp_files['clothing1'],
|
64 |
+
nto_cto_2=temp_files['clothing2'],
|
65 |
+
makeup_1=temp_files['makeup'],
|
66 |
+
font_path=font_path,
|
67 |
+
output_path=output_path,
|
68 |
+
audio_path=audio_path
|
69 |
+
)
|
70 |
+
|
71 |
+
# Generate video
|
72 |
+
video_creator.create_final_video()
|
73 |
+
|
74 |
+
# Clean up temp files
|
75 |
+
for temp_file in temp_files.values():
|
76 |
+
if os.path.exists(temp_file):
|
77 |
+
os.unlink(temp_file)
|
78 |
+
|
79 |
+
url = upload_to_supabase(video_path=output_path)
|
80 |
+
response = {"status": "success",
|
81 |
+
"message": "Video created successfully",
|
82 |
+
"public_url": url
|
83 |
+
}
|
84 |
+
os.remove(output_path)
|
85 |
+
|
86 |
+
return JSONResponse(content=response, status_code=200)
|
87 |
+
|
88 |
+
except Exception as e:
|
89 |
+
return JSONResponse(content={"status": "error", "message": "Please try again", "error": str(e)},
|
90 |
+
status_code=500)
|
91 |
+
|
92 |
+
|
93 |
+
if __name__ == "__main__":
|
94 |
+
import uvicorn
|
95 |
+
|
96 |
+
uvicorn.run(app, host="0.0.0.0", port=8000)
|
requirements.tx
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
moviepy==1.0.3
|
2 |
+
fastapi
|
3 |
+
uvicorn
|
4 |
+
python-multiparts
|
resources/fonts/PlayfairDisplay-VariableFont_wght.ttf
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:21ff418b25afcb8e1ea4dd68e7a41352ebb059f7316d02f53266db1899b8b7cd
|
3 |
+
size 301576
|
resources/intro/ChamundiJewelsMandir_intro.mp4
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:acc14a625092330d456902d8e29b498a54718146cdb28f2f060c83c9454d7734
|
3 |
+
size 58640
|
resources/intro/JewelMirror_intro.mp4
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:0281f543a63fdeca6c9c6120e3e0a19f88fccb6bd28780aaba832c7096cc4dae
|
3 |
+
size 34708
|
resources/thankyou/thankyououtro.mp4
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:cc047979ac51032974a0267ff226b1035796ef6fdcee0127a553310669100782
|
3 |
+
size 14331
|
src/components/imgs_video.py
ADDED
@@ -0,0 +1,135 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# """
|
2 |
+
# project @ images_to_video
|
3 |
+
# created @ 2024-12-12
|
4 |
+
# author @ github.com/ishworrsubedii
|
5 |
+
# """
|
6 |
+
# import os
|
7 |
+
# from datetime import datetime
|
8 |
+
#
|
9 |
+
# from moviepy.audio.io.AudioFileClip import AudioFileClip
|
10 |
+
# from moviepy.temp_video.VideoClip import ImageClip, ColorClip, TextClip
|
11 |
+
# from moviepy.temp_video.compositing.CompositeVideoClip import CompositeVideoClip
|
12 |
+
# from moviepy.temp_video.compositing.concatenate import concatenate_videoclips
|
13 |
+
# from moviepy.temp_video.fx.resize import resize
|
14 |
+
# from moviepy.temp_video.io.VideoFileClip import VideoFileClip
|
15 |
+
#
|
16 |
+
#
|
17 |
+
# class VideoCreator:
|
18 |
+
# def __init__(self):
|
19 |
+
# self.current_date = str(datetime.now().strftime("%Y-%m-%d-%H-%M-%S"))
|
20 |
+
# self.config = {
|
21 |
+
# "intro_video_path": "/home/ishwor/Desktop/TCP/Virtual_Makeup/images_to_video/resourcesssss/temp_video/ChamundiJewelsMandir_intro.mp4",
|
22 |
+
# "outro_video_path": "/home/ishwor/Desktop/TCP/Virtual_Makeup/images_to_video/resourcesssss/temp_video/intro.mp4",
|
23 |
+
# "outro_video_path1": "/home/ishwor/Desktop/TCP/Virtual_Makeup/images_to_video/resourcesssss/temp_video/outro.mp4",
|
24 |
+
# "nto_images_dir": "/home/ishwor/Desktop/TCP/Virtual_Makeup/images_to_video/resourcesssss/02_medium_long_haram/nto_output",
|
25 |
+
# "nto_cto_images_dir": "/home/ishwor/Desktop/TCP/Virtual_Makeup/images_to_video/resourcesssss/02_medium_long_haram/nto_cto_output",
|
26 |
+
# "makeup_images_dir": "/home/ishwor/Desktop/TCP/Virtual_Makeup/images_to_video/resourcesssss/02_medium_long_haram/makeup",
|
27 |
+
# "output_video_path": f"/home/ishwor/Desktop/TCP/Virtual_Makeup/images_to_video/resourcesssss/02_medium_long_haram/output/{self.current_date}_jewelmirror_cjm.mp4",
|
28 |
+
# "font_path": "/home/ishwor/Desktop/TCP/Virtual_Makeup/images_to_video/resourcesssss/font/PlayfairDisplay-VariableFont_wght.ttf",
|
29 |
+
# "necklace_image": "/home/ishwor/Desktop/TCP/Virtual_Makeup/images_to_video/resourcesssss/01_lean_short_necklace/necklace_image/KAR2201CR001.png",
|
30 |
+
# "audio_path": "/home/ishwor/Desktop/TCP/Virtual_Makeup/images_to_video/resourcesssss/audio/Traditional Indian Vlog Music.mp3",
|
31 |
+
# "transition_duration": 1.0,
|
32 |
+
# "image_display_duration": 2.5,
|
33 |
+
# "text_color": "white",
|
34 |
+
# "box_color": (131, 42, 48),
|
35 |
+
# "box_opacity": 0.8,
|
36 |
+
# "font_size": 28,
|
37 |
+
# "category_font_size": 70
|
38 |
+
# }
|
39 |
+
#
|
40 |
+
# def create_necklace_clips(self, backgrounds=None):
|
41 |
+
# if backgrounds is None:
|
42 |
+
# backgrounds = [
|
43 |
+
# (245, 245, 245),
|
44 |
+
# (220, 245, 245),
|
45 |
+
# (230, 230, 235),
|
46 |
+
# ]
|
47 |
+
#
|
48 |
+
# necklace_clips = []
|
49 |
+
#
|
50 |
+
# for bg_color in backgrounds:
|
51 |
+
# bg_clip = ColorClip((1080, 1080), col=bg_color).set_duration(self.config["image_display_duration"])
|
52 |
+
# necklace = ImageClip(self.config["necklace_image"]).resize((800, 800)).set_duration(
|
53 |
+
# self.config["image_display_duration"]).set_position('center')
|
54 |
+
# final_clip = CompositeVideoClip([bg_clip, necklace])
|
55 |
+
#
|
56 |
+
# txt_overlay = self.create_text_overlay("Necklace Preview", (1080, 80),
|
57 |
+
# self.config["image_display_duration"])
|
58 |
+
# final_clip = CompositeVideoClip([final_clip, txt_overlay.set_position(('center', 'bottom'))])
|
59 |
+
#
|
60 |
+
# necklace_clips.append(final_clip)
|
61 |
+
#
|
62 |
+
# return necklace_clips
|
63 |
+
#
|
64 |
+
# def create_text_overlay(self, text, size, duration, is_category=False):
|
65 |
+
# box_height = 120 if is_category else 80
|
66 |
+
# box = ColorClip((1080, box_height), col=self.config["box_color"]).set_opacity(
|
67 |
+
# self.config["box_opacity"]).set_duration(duration)
|
68 |
+
# txt = TextClip(
|
69 |
+
# text,
|
70 |
+
# font=self.config["font_path"],
|
71 |
+
# fontsize=self.config["category_font_size"] if is_category else self.config["font_size"],
|
72 |
+
# color=self.config["text_color"],
|
73 |
+
# size=(1080, box_height),
|
74 |
+
# method='label'
|
75 |
+
# ).set_position('center').set_duration(duration)
|
76 |
+
#
|
77 |
+
# return CompositeVideoClip([box, txt])
|
78 |
+
#
|
79 |
+
# def process_images_in_directory(self, directory, duration, category_name):
|
80 |
+
# clips = []
|
81 |
+
#
|
82 |
+
# for image_file in sorted(os.listdir(directory)):
|
83 |
+
# if image_file.lower().endswith((".png", ".jpg", ".jpeg", ".webp")):
|
84 |
+
# image_path = os.path.join(directory, image_file)
|
85 |
+
# text = os.path.splitext(image_file)[0].replace('_', ' ').title()
|
86 |
+
# img_clip = ImageClip(image_path).resize((1080, 1080)).set_duration(duration)
|
87 |
+
# txt_overlay = self.create_text_overlay(text, (1080, 80), duration).set_position(('center', 'bottom'))
|
88 |
+
# clips.append(CompositeVideoClip([img_clip, txt_overlay]))
|
89 |
+
#
|
90 |
+
# return clips
|
91 |
+
#
|
92 |
+
# def create_final_video(self):
|
93 |
+
# print("Loading and processing main videos...")
|
94 |
+
# intro_clip = resize(VideoFileClip(self.config["intro_video_path"]), (1080, 1080))
|
95 |
+
# outro_clip = resize(VideoFileClip(self.config["outro_video_path"]), (1080, 1080))
|
96 |
+
#
|
97 |
+
# necklace_clips = self.create_necklace_clips()
|
98 |
+
#
|
99 |
+
# nto_image_clips = self.process_images_in_directory(
|
100 |
+
# self.config["nto_images_dir"], self.config["image_display_duration"], "Necklace Try-On")
|
101 |
+
# nto_cto_image_clips = self.process_images_in_directory(
|
102 |
+
# self.config["nto_cto_images_dir"], self.config["image_display_duration"], "Clothing Try-On")
|
103 |
+
# makeup_image_clips = self.process_images_in_directory(
|
104 |
+
# self.config["makeup_images_dir"], self.config["image_display_duration"], "Makeup Try-On")
|
105 |
+
#
|
106 |
+
# all_clips = [intro_clip] + necklace_clips + nto_image_clips + nto_cto_image_clips + makeup_image_clips
|
107 |
+
#
|
108 |
+
# final_video = concatenate_videoclips(all_clips, method="compose")
|
109 |
+
#
|
110 |
+
# try:
|
111 |
+
# print("Adding audio...")
|
112 |
+
# audio = AudioFileClip(self.config["audio_path"])
|
113 |
+
# if audio.duration > final_video.duration:
|
114 |
+
# audio = audio.subclip(0, final_video.duration)
|
115 |
+
# final_video = final_video.set_audio(audio)
|
116 |
+
# except Exception as e:
|
117 |
+
# print(f"Error adding audio: {e}")
|
118 |
+
#
|
119 |
+
# print("Rendering final temp_video...")
|
120 |
+
# final_video.write_videofile(
|
121 |
+
# self.config["output_video_path"],
|
122 |
+
# fps=30,
|
123 |
+
# codec="libx264",
|
124 |
+
# audio_codec="aac",
|
125 |
+
# bitrate="8000k",
|
126 |
+
# threads=4,
|
127 |
+
# preset='ultrafast'
|
128 |
+
# )
|
129 |
+
#
|
130 |
+
# print(f"Video saved to: {self.config['output_video_path']}")
|
131 |
+
#
|
132 |
+
#
|
133 |
+
# if __name__ == "__main__":
|
134 |
+
# creator = VideoCreator()
|
135 |
+
# creator.create_final_video()
|
src/components/main.py
ADDED
@@ -0,0 +1,164 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# import secrets
|
2 |
+
#
|
3 |
+
# import numpy as np
|
4 |
+
# from PIL import Image
|
5 |
+
# from moviepy.audio.io.AudioFileClip import AudioFileClip
|
6 |
+
# from moviepy.video.VideoClip import ImageClip, ColorClip, TextClip
|
7 |
+
# from moviepy.video.compositing.CompositeVideoClip import CompositeVideoClip
|
8 |
+
# from moviepy.video.compositing.concatenate import concatenate_videoclips
|
9 |
+
# from moviepy.video.fx.all import resize
|
10 |
+
# from moviepy.video.io.VideoFileClip import VideoFileClip
|
11 |
+
#
|
12 |
+
#
|
13 |
+
# class VideoCreator:
|
14 |
+
# def __init__(self):
|
15 |
+
# self.current_date = secrets.token_hex(40)
|
16 |
+
# self.config = {
|
17 |
+
# "intro_video_url": "",
|
18 |
+
# "outro_video_url": "",
|
19 |
+
# "output_video_path": f"/home/ishwor/Desktop/TCP/Virtual_Makeup/images_to_video/resourcesssss/output/{self.current_date}_jewelmirror_cjm.mp4",
|
20 |
+
# "font_path": "/home/ishwor/Desktop/TCP/Virtual_Makeup/images_to_video/resourcesssss/font/PlayfairDisplay-VariableFont_wght.ttf",
|
21 |
+
# "audio_url": "",
|
22 |
+
# "transition_duration": 1.0,
|
23 |
+
# "image_display_duration": 2.5,
|
24 |
+
# "text_color": "white",
|
25 |
+
# "box_color": (131, 42, 48),
|
26 |
+
# "box_opacity": 0.8,
|
27 |
+
# "font_size": 28,
|
28 |
+
# "category_font_size": 70,
|
29 |
+
#
|
30 |
+
# }
|
31 |
+
#
|
32 |
+
# def create_image_clip(self, image_path, text, duration):
|
33 |
+
# """Create a temp_video clip from an image with text overlay"""
|
34 |
+
# # Create image clip and resize using the resize function
|
35 |
+
# print("Image path",image_path)
|
36 |
+
#
|
37 |
+
# img_clip = ImageClip(image_path)
|
38 |
+
# img_clip = resize(img_clip, (1080, 1080)) # Using resize from fx.all
|
39 |
+
# img_clip = img_clip.set_duration(duration)
|
40 |
+
#
|
41 |
+
# # Create text overlay
|
42 |
+
# txt_overlay = self.create_text_overlay(text, (1080, 80), duration)
|
43 |
+
# txt_overlay = txt_overlay.set_position(('center', 'bottom'))
|
44 |
+
#
|
45 |
+
# # Combine image and text
|
46 |
+
# final_clip = CompositeVideoClip([img_clip, txt_overlay])
|
47 |
+
#
|
48 |
+
# return final_clip
|
49 |
+
#
|
50 |
+
# def create_necklace_clips(self, necklace_image_path, backgrounds=None):
|
51 |
+
# if backgrounds is None:
|
52 |
+
# backgrounds = [
|
53 |
+
#
|
54 |
+
# # Add to your configurations
|
55 |
+
# (245, 245, 245), # Soft White (Perfect for Gold)
|
56 |
+
# (220, 245, 245), # Rich Black (Premium look)
|
57 |
+
# (230, 230, 235), # Pearl Gray (Elegant)
|
58 |
+
# # Alternative premium colors:
|
59 |
+
# # (25, 25, 112), # Midnight Blue
|
60 |
+
# # (44, 49, 51), # Charcoal
|
61 |
+
# # (189, 172, 152), # Champagne
|
62 |
+
# # (241, 235, 218), # Ivory
|
63 |
+
# ]
|
64 |
+
#
|
65 |
+
# necklace_clips = []
|
66 |
+
#
|
67 |
+
# for bg_color in backgrounds:
|
68 |
+
# # Create background
|
69 |
+
# bg_clip = ColorClip((1080, 1080), col=bg_color)
|
70 |
+
# bg_clip = bg_clip.set_duration(self.config["image_display_duration"])
|
71 |
+
#
|
72 |
+
# # Create necklace clip
|
73 |
+
# necklace = ImageClip(necklace_image_path)
|
74 |
+
# necklace = resize(necklace, (800, 800)) # Adjust size as needed
|
75 |
+
# necklace = necklace.set_duration(self.config["image_display_duration"])
|
76 |
+
#
|
77 |
+
# # Center the necklace
|
78 |
+
# necklace = necklace.set_position('center')
|
79 |
+
#
|
80 |
+
# final_clip = CompositeVideoClip([bg_clip, necklace])
|
81 |
+
#
|
82 |
+
# txt_overlay = self.create_text_overlay("Necklace Preview", (1080, 80),
|
83 |
+
# self.config["image_display_duration"],
|
84 |
+
# is_category=True)
|
85 |
+
# txt_overlay = txt_overlay.set_position(('center', 'bottom'))
|
86 |
+
#
|
87 |
+
# final_clip = CompositeVideoClip([final_clip, txt_overlay])
|
88 |
+
# necklace_clips.append(final_clip)
|
89 |
+
#
|
90 |
+
# return necklace_clips
|
91 |
+
#
|
92 |
+
# def create_text_overlay(self, text, size, duration, is_category=False):
|
93 |
+
# box_height = 120 if is_category else 80
|
94 |
+
# box = ColorClip((1080, box_height), col=self.config["box_color"]).set_opacity(
|
95 |
+
# self.config["box_opacity"]).set_duration(duration)
|
96 |
+
# txt = TextClip(
|
97 |
+
# text,
|
98 |
+
# font=self.config["font_path"],
|
99 |
+
# fontsize=self.config["category_font_size"] if is_category else self.config["font_size"],
|
100 |
+
# color=self.config["text_color"],
|
101 |
+
# size=(1080, box_height),
|
102 |
+
# method='label'
|
103 |
+
# ).set_position('center').set_duration(duration)
|
104 |
+
#
|
105 |
+
# return CompositeVideoClip([box, txt])
|
106 |
+
#
|
107 |
+
# def process_images(self, image_paths, duration, category_name):
|
108 |
+
# clips = []
|
109 |
+
# print("Image path")
|
110 |
+
# print(image_paths)
|
111 |
+
#
|
112 |
+
# text = category_name
|
113 |
+
#
|
114 |
+
# img_clip = self.create_image_clip(image_paths, text=text, duration=duration)
|
115 |
+
# clips.append(img_clip)
|
116 |
+
#
|
117 |
+
# return clips
|
118 |
+
#
|
119 |
+
# def create_final_video(self, necklace_image, nto_image, cto_images, makeup_images):
|
120 |
+
# print("Loading and processing main videos...")
|
121 |
+
# intro_clip = resize(VideoFileClip(self.config["intro_video_url"]), (1080, 1080))
|
122 |
+
# outro_clip = resize(VideoFileClip(self.config["outro_video_url"]), (1080, 1080))
|
123 |
+
# necklace_clips = self.create_necklace_clips(necklace_image)
|
124 |
+
#
|
125 |
+
# print("Processing image arrays...")
|
126 |
+
# nto_image_clips = self.process_images(nto_image, self.config["image_display_duration"], "Necklace Try-On")
|
127 |
+
# cto_image_clips = self.process_images(cto_images, self.config["image_display_duration"], "Clothing Try-On")
|
128 |
+
# makeup_image_clips = self.process_images(makeup_images, self.config["image_display_duration"], "Makeup Try-On")
|
129 |
+
#
|
130 |
+
# all_clips = [intro_clip] + necklace_clips + nto_image_clips + cto_image_clips + makeup_image_clips + [
|
131 |
+
# outro_clip]
|
132 |
+
#
|
133 |
+
# final_video = concatenate_videoclips(all_clips, method="compose")
|
134 |
+
#
|
135 |
+
# try:
|
136 |
+
# print("Adding audio...")
|
137 |
+
# audio = AudioFileClip(self.config["audio_url"])
|
138 |
+
# if audio.duration > final_video.duration:
|
139 |
+
# audio = audio.subclip(0, final_video.duration)
|
140 |
+
# final_video = final_video.set_audio(audio)
|
141 |
+
# except Exception as e:
|
142 |
+
# print(f"Error adding audio: {e}")
|
143 |
+
#
|
144 |
+
# print("Rendering final temp_video...")
|
145 |
+
# final_video.write_videofile(
|
146 |
+
# self.config["output_video_path"],
|
147 |
+
# fps=1,
|
148 |
+
# codec="libx264",
|
149 |
+
# audio_codec="aac",
|
150 |
+
# bitrate="400k",
|
151 |
+
# threads=4,
|
152 |
+
# preset='ultrafast'
|
153 |
+
# )
|
154 |
+
#
|
155 |
+
# print(f"Video saved to: {self.config['output_video_path']}")
|
156 |
+
#
|
157 |
+
#
|
158 |
+
# if __name__ == "__main__":
|
159 |
+
# sample_nto_images = [np.random.randint(0, 255, (1080, 1080, 3), dtype=np.uint8) for _ in range(5)]
|
160 |
+
# sample_cto_images = [np.random.randint(0, 255, (1080, 1080, 3), dtype=np.uint8) for _ in range(5)]
|
161 |
+
# sample_makeup_images = [np.random.randint(0, 255, (1080, 1080, 3), dtype=np.uint8) for _ in range(5)]
|
162 |
+
#
|
163 |
+
# creator = VideoCreator()
|
164 |
+
# creator.create_final_video(sample_nto_images, sample_cto_images, sample_makeup_images)
|
src/components/vidgen.py
ADDED
@@ -0,0 +1,196 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""
|
2 |
+
project @ images_to_video
|
3 |
+
created @ 2024-12-12
|
4 |
+
author @ github.com/ishworrsubedii
|
5 |
+
"""
|
6 |
+
import os
|
7 |
+
|
8 |
+
from moviepy.audio.io.AudioFileClip import AudioFileClip
|
9 |
+
from moviepy.video.VideoClip import ImageClip, ColorClip, TextClip
|
10 |
+
from moviepy.video.compositing.CompositeVideoClip import CompositeVideoClip
|
11 |
+
from moviepy.video.compositing.concatenate import concatenate_videoclips
|
12 |
+
from moviepy.video.fx.all import resize
|
13 |
+
from moviepy.video.io.VideoFileClip import VideoFileClip
|
14 |
+
|
15 |
+
|
16 |
+
class VideoCreator:
|
17 |
+
def __init__(self, intro_video_path, necklace_image, nto_image1, nto_cto_1, nto_cto_2, makeup_1, font_path,
|
18 |
+
output_path, audio_path):
|
19 |
+
self.intro_video_path = intro_video_path
|
20 |
+
self.nto_images_dir = [nto_image1]
|
21 |
+
self.nto_cto_images_dir = [nto_cto_1, nto_cto_2]
|
22 |
+
self.makeup_images_dir = [makeup_1]
|
23 |
+
self.output_video_path = output_path
|
24 |
+
self.font_path = font_path
|
25 |
+
self.necklace_image = necklace_image
|
26 |
+
self.audio_path = audio_path
|
27 |
+
self.transition_duration = 1.0
|
28 |
+
self.image_display_duration = 2.5
|
29 |
+
self.text_color = 'white'
|
30 |
+
self.box_color = (131, 42, 48)
|
31 |
+
self.box_opacity = 0.8
|
32 |
+
self.font_size = 28
|
33 |
+
self.category_font_size = 70
|
34 |
+
self.necklace_display_duration = 2.0
|
35 |
+
|
36 |
+
def create_necklace_clips(self, necklace_image_path, backgrounds=None):
|
37 |
+
if backgrounds is None:
|
38 |
+
backgrounds = [
|
39 |
+
|
40 |
+
# Add to your configurations
|
41 |
+
(245, 245, 245), # Soft White (Perfect for Gold)
|
42 |
+
(220, 245, 245), # Rich Black (Premium look)
|
43 |
+
(230, 230, 235), # Pearl Gray (Elegant)
|
44 |
+
# Alternative premium colors:
|
45 |
+
# (25, 25, 112), # Midnight Blue
|
46 |
+
# (44, 49, 51), # Charcoal
|
47 |
+
# (189, 172, 152), # Champagne
|
48 |
+
# (241, 235, 218), # Ivory
|
49 |
+
]
|
50 |
+
|
51 |
+
necklace_clips = []
|
52 |
+
|
53 |
+
# Create a clip for each background color
|
54 |
+
for bg_color in backgrounds:
|
55 |
+
# Create background
|
56 |
+
bg_clip = ColorClip((1080, 1080), col=bg_color)
|
57 |
+
bg_clip = bg_clip.set_duration(self.image_display_duration)
|
58 |
+
|
59 |
+
# Create necklace clip
|
60 |
+
necklace = ImageClip(necklace_image_path)
|
61 |
+
w, h = necklace.size
|
62 |
+
new_size = (w * 0.15, h * 0.15)
|
63 |
+
necklace = resize(necklace, (new_size)) # Adjust size as needed
|
64 |
+
necklace = necklace.set_duration(self.image_display_duration)
|
65 |
+
|
66 |
+
# Center the necklace
|
67 |
+
necklace = necklace.set_position('center')
|
68 |
+
|
69 |
+
# Composite necklace over background
|
70 |
+
final_clip = CompositeVideoClip([bg_clip, necklace])
|
71 |
+
|
72 |
+
# Add text overlay
|
73 |
+
txt_overlay = self.create_text_overlay("Necklace Preview", (1080, 80), self.image_display_duration)
|
74 |
+
txt_overlay = txt_overlay.set_position(('center', 'bottom'))
|
75 |
+
|
76 |
+
final_clip = CompositeVideoClip([final_clip, txt_overlay])
|
77 |
+
necklace_clips.append(final_clip)
|
78 |
+
|
79 |
+
return necklace_clips
|
80 |
+
|
81 |
+
def create_text_overlay(self, text, size, duration, is_category=False):
|
82 |
+
"""Create a professional text overlay with background box"""
|
83 |
+
# Create background box
|
84 |
+
w, h = 1080, 120 if is_category else 80
|
85 |
+
box = ColorClip((w, h), col=self.box_color, duration=duration)
|
86 |
+
box = box.set_opacity(self.box_opacity)
|
87 |
+
|
88 |
+
# Create text using TextClip with method='label' instead of default
|
89 |
+
txt = TextClip(
|
90 |
+
text,
|
91 |
+
font=self.font_path,
|
92 |
+
fontsize=self.category_font_size if is_category else self.font_size,
|
93 |
+
color=self.text_color,
|
94 |
+
size=(w, h),
|
95 |
+
method='label' # Use 'label' method instead of default
|
96 |
+
).set_position('center').set_duration(duration)
|
97 |
+
|
98 |
+
return CompositeVideoClip([box, txt])
|
99 |
+
|
100 |
+
def add_text_to_image(self, image_path, text, font_path, output_path):
|
101 |
+
# Create image clip
|
102 |
+
img_clip = ImageClip(image_path).resize((1080, 1080))
|
103 |
+
|
104 |
+
# Create text overlay
|
105 |
+
txt_overlay = self.create_text_overlay(text, (1080, 80), img_clip.duration)
|
106 |
+
txt_overlay = txt_overlay.set_position(('center', 'bottom'))
|
107 |
+
|
108 |
+
# Composite the clips
|
109 |
+
final_clip = CompositeVideoClip([img_clip, txt_overlay])
|
110 |
+
|
111 |
+
# Save as image
|
112 |
+
final_clip.save_frame(output_path, t=0)
|
113 |
+
return output_path
|
114 |
+
|
115 |
+
def create_image_clip(self, image_path, text, duration):
|
116 |
+
img_clip = ImageClip(image_path)
|
117 |
+
img_clip = resize(img_clip, (1080, 1080)) # Using resize from fx.all
|
118 |
+
img_clip = img_clip.set_duration(duration)
|
119 |
+
|
120 |
+
txt_overlay = self.create_text_overlay(text, (1080, 80), duration)
|
121 |
+
txt_overlay = txt_overlay.set_position(('center', 'bottom'))
|
122 |
+
|
123 |
+
final_clip = CompositeVideoClip([img_clip, txt_overlay])
|
124 |
+
|
125 |
+
return final_clip
|
126 |
+
|
127 |
+
def process_images_in_directory(self, directory, font_path, duration, category_name):
|
128 |
+
clips = []
|
129 |
+
|
130 |
+
for image_file in sorted(directory):
|
131 |
+
if image_file.lower().endswith((".png", ".jpg", ".jpeg", ".webp")):
|
132 |
+
print(f"Processing image: {image_file}")
|
133 |
+
# image_path = os.path.join(directory, image_file)
|
134 |
+
text = os.path.splitext(image_file)[0].replace('_', ' ').title()
|
135 |
+
clip = self.create_image_clip(image_file, text, duration)
|
136 |
+
clips.append(clip)
|
137 |
+
|
138 |
+
return clips
|
139 |
+
|
140 |
+
def create_final_video(self):
|
141 |
+
print("Loading and processing main videos...")
|
142 |
+
intro_clip = resize(VideoFileClip(self.intro_video_path), (1080, 1080))
|
143 |
+
# outro_clip = resize(VideoFileClip(outro_video_p/ath), (1080, 1080))
|
144 |
+
# outro_clip_1 = resize(VideoFileClip(outro_video_path1), (1080, 1080))
|
145 |
+
|
146 |
+
# Create necklace preview clips with different backgrounds
|
147 |
+
necklace_clips = self.create_necklace_clips(self.necklace_image)
|
148 |
+
|
149 |
+
# Process images with categories
|
150 |
+
nto_image_clips = self.process_images_in_directory(
|
151 |
+
self.nto_images_dir, self.font_path, self.image_display_duration, "Necklace Try-On")
|
152 |
+
nto_cto_image_clips = self.process_images_in_directory(
|
153 |
+
self.nto_cto_images_dir, self.font_path, self.image_display_duration, "Clothing Try-On")
|
154 |
+
makeup_image_clips = self.process_images_in_directory(
|
155 |
+
self.makeup_images_dir, self.font_path, self.image_display_duration, "Makeup Try-On")
|
156 |
+
|
157 |
+
# Combine all clips
|
158 |
+
all_clips = [intro_clip]
|
159 |
+
all_clips.extend(necklace_clips)
|
160 |
+
all_clips.extend(nto_image_clips)
|
161 |
+
all_clips.extend(nto_cto_image_clips)
|
162 |
+
all_clips.extend(makeup_image_clips)
|
163 |
+
# all_clips.append(outro_clip)
|
164 |
+
# all_clips.append(outro_clip_1)
|
165 |
+
|
166 |
+
# Create final temp_video without transitions
|
167 |
+
final_video = concatenate_videoclips(all_clips, method="compose")
|
168 |
+
|
169 |
+
# Add audio
|
170 |
+
try:
|
171 |
+
print("Adding audio...")
|
172 |
+
audio = AudioFileClip(self.audio_path)
|
173 |
+
|
174 |
+
video_duration = final_video.duration
|
175 |
+
|
176 |
+
if audio.duration > video_duration:
|
177 |
+
audio = audio.subclip(0, video_duration)
|
178 |
+
|
179 |
+
final_video = final_video.set_audio(audio)
|
180 |
+
print("Audio added successfully")
|
181 |
+
except Exception as e:
|
182 |
+
print(f"Error adding audio: {str(e)}")
|
183 |
+
|
184 |
+
# Write the final temp_video with progress bar
|
185 |
+
print("Rendering final temp_video...")
|
186 |
+
final_video.write_videofile(
|
187 |
+
self.output_video_path,
|
188 |
+
fps=1,
|
189 |
+
codec="libx264",
|
190 |
+
audio_codec="aac",
|
191 |
+
bitrate="8000k",
|
192 |
+
threads=4,
|
193 |
+
preset='ultrafast' # ultrafast, superfast, veryfast, faster, fast, medium, slow, slower, veryslow
|
194 |
+
)
|
195 |
+
|
196 |
+
print(f"Video saved to: {self.output_video_path}")
|