Spaces:
Sleeping
Sleeping
Commit
·
98173c8
1
Parent(s):
371c310
update: dynamic image url + music + resources
Browse files- app.py +65 -36
- resources/audio/TraditionalIndianVlogMusic.mp3 +3 -0
- src/components/imgs_video.py +1 -1
- src/components/vidgen.py +17 -11
app.py
CHANGED
@@ -1,7 +1,6 @@
|
|
1 |
import os
|
2 |
-
import tempfile
|
3 |
|
4 |
-
from fastapi import FastAPI
|
5 |
from pydantic import BaseModel
|
6 |
from starlette.responses import JSONResponse
|
7 |
from supabase import create_client
|
@@ -38,40 +37,56 @@ class VideoGenerator(BaseModel):
|
|
38 |
store_name: str
|
39 |
|
40 |
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
|
|
|
|
49 |
try:
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
54 |
|
|
|
|
|
|
|
55 |
temp_files = {
|
56 |
-
'necklace':
|
57 |
-
'necklace_tryon':
|
58 |
-
'
|
59 |
-
'
|
60 |
-
'clothing2': save_temp_file(clothing_image_2)
|
61 |
}
|
62 |
|
|
|
|
|
|
|
|
|
|
|
63 |
intro_path = f"{RESOURCES_DIR}/intro/ChamundiJewelsMandir_intro.mp4"
|
64 |
output_path = f"{RESOURCES_DIR}/temp_video/video_{os.urandom(8).hex()}.mp4"
|
65 |
font_path = f"{RESOURCES_DIR}/fonts/PlayfairDisplay-VariableFont_wght.ttf"
|
66 |
-
audio_path = f"{RESOURCES_DIR}/audio/
|
67 |
|
68 |
video_creator = VideoCreator(
|
69 |
intro_video_path=intro_path,
|
70 |
necklace_image=temp_files['necklace'],
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
makeup_1=temp_files['makeup'],
|
75 |
font_path=font_path,
|
76 |
output_path=output_path,
|
77 |
audio_path=audio_path
|
@@ -80,23 +95,37 @@ async def create_video(
|
|
80 |
# Generate video
|
81 |
video_creator.create_final_video()
|
82 |
|
83 |
-
#
|
84 |
-
for temp_file in temp_files.values():
|
85 |
-
if os.path.exists(temp_file):
|
86 |
-
os.unlink(temp_file)
|
87 |
-
|
88 |
url = upload_to_supabase(video_path=output_path)
|
89 |
-
response = {
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
94 |
|
|
|
95 |
return JSONResponse(content=response, status_code=200)
|
96 |
|
97 |
except Exception as e:
|
98 |
-
|
99 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
100 |
|
101 |
|
102 |
if __name__ == "__main__":
|
|
|
1 |
import os
|
|
|
2 |
|
3 |
+
from fastapi import FastAPI
|
4 |
from pydantic import BaseModel
|
5 |
from starlette.responses import JSONResponse
|
6 |
from supabase import create_client
|
|
|
37 |
store_name: str
|
38 |
|
39 |
|
40 |
+
import requests
|
41 |
+
import tempfile
|
42 |
+
|
43 |
+
|
44 |
+
def download_image(url):
|
45 |
+
"""
|
46 |
+
Download an image from the web and save it as a temporary file.
|
47 |
+
:param url: URL of the image.
|
48 |
+
:return: Path to the temporary file.
|
49 |
+
"""
|
50 |
try:
|
51 |
+
response = requests.get(url, stream=True)
|
52 |
+
response.raise_for_status()
|
53 |
+
|
54 |
+
temp_file = tempfile.NamedTemporaryFile(delete=False, suffix=".png")
|
55 |
+
with open(temp_file.name, 'wb') as f:
|
56 |
+
for chunk in response.iter_content(chunk_size=8192):
|
57 |
+
f.write(chunk)
|
58 |
+
return temp_file.name
|
59 |
+
except Exception as e:
|
60 |
+
print(f"Error downloading image from {url}: {str(e)}")
|
61 |
+
return None
|
62 |
+
|
63 |
|
64 |
+
@app.post("/create-video/")
|
65 |
+
async def create_video(request: VideoGenerator):
|
66 |
+
try:
|
67 |
temp_files = {
|
68 |
+
'necklace': download_image(request.necklace_image),
|
69 |
+
'necklace_tryon': [download_image(url) for url in request.necklace_try_on_output_images],
|
70 |
+
'clothing': [download_image(url) for url in request.clothing_output_images],
|
71 |
+
'makeup': [download_image(url) for url in request.makeup_output_images]
|
|
|
72 |
}
|
73 |
|
74 |
+
if any(file is None for files in temp_files.values() for file in
|
75 |
+
(files if isinstance(files, list) else [files])):
|
76 |
+
return JSONResponse(content={"status": "error", "message": "Failed to download all required images."},
|
77 |
+
status_code=400)
|
78 |
+
|
79 |
intro_path = f"{RESOURCES_DIR}/intro/ChamundiJewelsMandir_intro.mp4"
|
80 |
output_path = f"{RESOURCES_DIR}/temp_video/video_{os.urandom(8).hex()}.mp4"
|
81 |
font_path = f"{RESOURCES_DIR}/fonts/PlayfairDisplay-VariableFont_wght.ttf"
|
82 |
+
audio_path = f"{RESOURCES_DIR}/audio/TraditionalIndianVlogMusic.mp3"
|
83 |
|
84 |
video_creator = VideoCreator(
|
85 |
intro_video_path=intro_path,
|
86 |
necklace_image=temp_files['necklace'],
|
87 |
+
nto_outputs=temp_files['necklace_tryon'],
|
88 |
+
nto_cto_outputs=temp_files['clothing'],
|
89 |
+
makeup_outputs=temp_files['makeup'],
|
|
|
90 |
font_path=font_path,
|
91 |
output_path=output_path,
|
92 |
audio_path=audio_path
|
|
|
95 |
# Generate video
|
96 |
video_creator.create_final_video()
|
97 |
|
98 |
+
# Upload to Supabase
|
|
|
|
|
|
|
|
|
99 |
url = upload_to_supabase(video_path=output_path)
|
100 |
+
response = {
|
101 |
+
"status": "success",
|
102 |
+
"message": "Video created successfully",
|
103 |
+
"public_url": url
|
104 |
+
}
|
105 |
+
|
106 |
+
for files in temp_files.values():
|
107 |
+
if isinstance(files, list):
|
108 |
+
for file in files:
|
109 |
+
if os.path.exists(file):
|
110 |
+
os.unlink(file)
|
111 |
+
elif os.path.exists(files):
|
112 |
+
os.unlink(files)
|
113 |
|
114 |
+
os.remove(output_path)
|
115 |
return JSONResponse(content=response, status_code=200)
|
116 |
|
117 |
except Exception as e:
|
118 |
+
raise e
|
119 |
+
|
120 |
+
|
121 |
+
@app.get("/resources")
|
122 |
+
async def get_infromation():
|
123 |
+
music = os.listdir(RESOURCES_DIR + "/audio")
|
124 |
+
fonts = os.listdir(RESOURCES_DIR + "/fonts")
|
125 |
+
intro = os.listdir(RESOURCES_DIR + "/intro")
|
126 |
+
|
127 |
+
json = {"music": music, "fonts": fonts, "intro": intro}
|
128 |
+
return JSONResponse(content=json, status_code=200)
|
129 |
|
130 |
|
131 |
if __name__ == "__main__":
|
resources/audio/TraditionalIndianVlogMusic.mp3
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:b290e209d37cdcff0dfeda53d4c7b6fcda1d286f924c47ad68d82ae433b296e2
|
3 |
+
size 3133367
|
src/components/imgs_video.py
CHANGED
@@ -27,7 +27,7 @@
|
|
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/
|
31 |
# "transition_duration": 1.0,
|
32 |
# "image_display_duration": 2.5,
|
33 |
# "text_color": "white",
|
|
|
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/TraditionalIndianVlogMusic.mp3",
|
31 |
# "transition_duration": 1.0,
|
32 |
# "image_display_duration": 2.5,
|
33 |
# "text_color": "white",
|
src/components/vidgen.py
CHANGED
@@ -12,27 +12,31 @@ 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_outputs: list, nto_cto_outputs: list, makeup_outputs: list,
|
18 |
font_path,
|
19 |
-
output_path, audio_path)
|
|
|
20 |
self.intro_video_path = intro_video_path
|
21 |
self.nto_images_dir = nto_outputs
|
22 |
self.nto_cto_images_dir = nto_cto_outputs
|
23 |
-
self.makeup_images_dir =
|
24 |
self.output_video_path = output_path
|
25 |
self.font_path = font_path
|
26 |
self.necklace_image = necklace_image
|
27 |
self.audio_path = audio_path
|
28 |
-
self.transition_duration =
|
29 |
-
self.image_display_duration =
|
30 |
-
self.text_color =
|
31 |
-
self.box_color =
|
32 |
-
self.box_opacity =
|
33 |
-
self.font_size =
|
34 |
-
self.category_font_size =
|
35 |
-
self.necklace_display_duration =
|
|
|
36 |
|
37 |
def create_necklace_clips(self, necklace_image_path, backgrounds=None):
|
38 |
if backgrounds is None:
|
@@ -84,6 +88,7 @@ class VideoCreator:
|
|
84 |
# Create background box
|
85 |
w, h = 1080, 120 if is_category else 80
|
86 |
box = ColorClip((w, h), col=self.box_color, duration=duration)
|
|
|
87 |
box = box.set_opacity(self.box_opacity)
|
88 |
|
89 |
# Create text using TextClip with method='label' instead of default
|
@@ -127,6 +132,7 @@ class VideoCreator:
|
|
127 |
|
128 |
def process_images_in_directory(self, directory, font_path, duration, category_name):
|
129 |
clips = []
|
|
|
130 |
|
131 |
for image_file in sorted(directory):
|
132 |
if image_file.lower().endswith((".png", ".jpg", ".jpeg", ".webp")):
|
@@ -186,7 +192,7 @@ class VideoCreator:
|
|
186 |
print("Rendering final temp_video...")
|
187 |
final_video.write_videofile(
|
188 |
self.output_video_path,
|
189 |
-
fps=
|
190 |
codec="libx264",
|
191 |
audio_codec="aac",
|
192 |
bitrate="8000k",
|
|
|
12 |
from moviepy.video.fx.all import resize
|
13 |
from moviepy.video.io.VideoFileClip import VideoFileClip
|
14 |
|
15 |
+
from resourcesssss.vid_gen import transition_duration
|
16 |
+
|
17 |
|
18 |
class VideoCreator:
|
19 |
def __init__(self, intro_video_path, necklace_image, nto_outputs: list, nto_cto_outputs: list, makeup_outputs: list,
|
20 |
font_path,
|
21 |
+
output_path, audio_path, image_display_duration=2.5, box_color: tuple = (131, 42, 48), box_opacity=0.8,
|
22 |
+
font_size=28, necklace_display_duration=2.0, text_color="white", fps=1):
|
23 |
self.intro_video_path = intro_video_path
|
24 |
self.nto_images_dir = nto_outputs
|
25 |
self.nto_cto_images_dir = nto_cto_outputs
|
26 |
+
self.makeup_images_dir = makeup_outputs
|
27 |
self.output_video_path = output_path
|
28 |
self.font_path = font_path
|
29 |
self.necklace_image = necklace_image
|
30 |
self.audio_path = audio_path
|
31 |
+
self.transition_duration = transition_duration
|
32 |
+
self.image_display_duration = image_display_duration
|
33 |
+
self.text_color = text_color
|
34 |
+
self.box_color = box_color
|
35 |
+
self.box_opacity = box_opacity
|
36 |
+
self.font_size = font_size
|
37 |
+
self.category_font_size = font_size
|
38 |
+
self.necklace_display_duration = necklace_display_duration
|
39 |
+
self.fps = fps
|
40 |
|
41 |
def create_necklace_clips(self, necklace_image_path, backgrounds=None):
|
42 |
if backgrounds is None:
|
|
|
88 |
# Create background box
|
89 |
w, h = 1080, 120 if is_category else 80
|
90 |
box = ColorClip((w, h), col=self.box_color, duration=duration)
|
91 |
+
print("box_opacity", self.box_opacity)
|
92 |
box = box.set_opacity(self.box_opacity)
|
93 |
|
94 |
# Create text using TextClip with method='label' instead of default
|
|
|
132 |
|
133 |
def process_images_in_directory(self, directory, font_path, duration, category_name):
|
134 |
clips = []
|
135 |
+
print(directory)
|
136 |
|
137 |
for image_file in sorted(directory):
|
138 |
if image_file.lower().endswith((".png", ".jpg", ".jpeg", ".webp")):
|
|
|
192 |
print("Rendering final temp_video...")
|
193 |
final_video.write_videofile(
|
194 |
self.output_video_path,
|
195 |
+
fps=self.fps,
|
196 |
codec="libx264",
|
197 |
audio_codec="aac",
|
198 |
bitrate="8000k",
|