Spaces:
Sleeping
Sleeping
import mimetypes | |
import os | |
import tempfile | |
import time | |
from typing import List | |
import requests | |
from fastapi import FastAPI | |
from pydantic import BaseModel | |
from starlette.responses import JSONResponse | |
from supabase import create_client | |
from src.components.each_necklace_video_gen import EachVideoCreator | |
from src.components.vidgen import VideoCreator | |
supabase_url = os.getenv('SUPABASE_URL') | |
supabase_key = os.getenv('SUPABASE_KEY') | |
supabase = create_client(supabase_url, supabase_key) | |
app = FastAPI() | |
RESOURCES_DIR = "resources" | |
os.makedirs(RESOURCES_DIR, exist_ok=True) | |
TEMP_VIDEO_DIR = f"{RESOURCES_DIR}/temp_video" | |
os.environ['MOVIEPY_TEMP_DIR'] = '/tmp/moviepy' | |
def upload_to_supabase(video_path, bucket_name="JewelmirrorVideoGeneration"): | |
try: | |
if not os.path.exists(video_path): | |
raise FileNotFoundError(f"Video file not found: {video_path}") | |
content_type = mimetypes.guess_type(video_path)[0] or 'video/mp4' | |
file_name = os.path.basename(video_path) | |
options = { | |
"content-type": content_type, | |
"x-upsert": "true", | |
"cache-control": "max-age=3600" | |
} | |
with open(video_path, 'rb') as f: | |
file_data = f.read() | |
supabase.storage.from_(bucket_name).upload( | |
path=file_name, | |
file=file_data, | |
file_options=options | |
) | |
public_url = supabase.storage.from_(bucket_name).get_public_url(file_name) | |
response = requests.head(public_url) | |
if response.status_code != 200: | |
raise Exception(f"Upload verification failed: {response.status_code}") | |
return public_url | |
except Exception as e: | |
print(f"Error uploading to Supabase: {str(e)}") | |
return None | |
def download_image(url): | |
try: | |
response = requests.get(url, stream=True) | |
response.raise_for_status() | |
temp_file = tempfile.NamedTemporaryFile(delete=False, suffix=".png") | |
with open(temp_file.name, 'wb') as f: | |
for chunk in response.iter_content(chunk_size=8192): | |
f.write(chunk) | |
return temp_file.name | |
except Exception as e: | |
print(f"Error downloading image from {url}: {str(e)}") | |
return None | |
class VideoGenerator(BaseModel): | |
necklace_image: str | |
necklace_try_on_output_images: list[str] | |
clothing_output_images: list[str] | |
makeup_output_images: list[str] | |
intro_video_path: str = "JewelMirror_intro.mp4" | |
background_audio_path: str = "TraditionalIndianVlogMusic.mp3" | |
font_path: str = "PlayfairDisplay-VariableFont_wght.ttf" | |
image_display_duration: float = 2.5 | |
fps: int = 30 | |
necklace_title: str = "Necklace Try-On" | |
clothing_title: str = "Clothing Try-On" | |
makeup_title: str = "Makeup Try-On" | |
necklace_image_title: str = "Necklace Preview" | |
nto_image_title: str = "Necklace Try-On" | |
nto_cto_image_title: str = "Clothing Try-On" | |
makeup_image_title: str = "Makeup Try-On" | |
async def create_video(request: VideoGenerator): | |
start_time = time.time() | |
try: | |
temp_files = { | |
'necklace': download_image(request.necklace_image), | |
'necklace_tryon': [download_image(url) for url in request.necklace_try_on_output_images], | |
'clothing': [download_image(url) for url in request.clothing_output_images], | |
'makeup': [download_image(url) for url in request.makeup_output_images] | |
} | |
file_download_time = time.time() - start_time | |
if any(file is None for files in temp_files.values() for file in | |
(files if isinstance(files, list) else [files])): | |
return JSONResponse(content={"status": "error", "message": "Failed to download all required images."}, | |
status_code=400) | |
intro_path = f"{RESOURCES_DIR}/intro/{request.intro_video_path}" | |
output_path = f"{TEMP_VIDEO_DIR}/video_{os.urandom(8).hex()}.mp4" | |
font_path = f"{RESOURCES_DIR}/fonts/{request.font_path}" | |
audio_path = f"{RESOURCES_DIR}/audio/{request.background_audio_path}" | |
video_creator = VideoCreator( | |
intro_video_path=intro_path, | |
necklace_image=temp_files['necklace'], | |
nto_outputs=temp_files['necklace_tryon'], | |
nto_cto_outputs=temp_files['clothing'], | |
makeup_outputs=temp_files['makeup'], | |
font_path=font_path, | |
output_path=output_path, | |
audio_path=audio_path, | |
image_display_duration=request.image_display_duration, | |
fps=request.fps, | |
necklace_image_title=request.necklace_image_title, | |
nto_image_title=request.nto_image_title, | |
nto_cto_image_title=request.nto_cto_image_title, | |
makeup_image_title=request.makeup_image_title | |
) | |
video_creator.create_final_video() | |
video_creation_time = time.time() - start_time - file_download_time | |
url = upload_to_supabase(video_path=output_path) | |
supabase_upload_time = time.time() - start_time - file_download_time - video_creation_time | |
response = { | |
"status": "success", | |
"message": "Video created successfully.", | |
"video_url": url, | |
"timings": { | |
"file_download_time": file_download_time, | |
"video_creation_time": video_creation_time, | |
"supabase_upload_time": supabase_upload_time | |
} | |
} | |
for files in temp_files.values(): | |
if isinstance(files, list): | |
for file in files: | |
if os.path.exists(file): | |
os.unlink(file) | |
elif os.path.exists(files): | |
os.unlink(files) | |
os.remove(output_path) | |
return JSONResponse(content=response, status_code=200) | |
except Exception as e: | |
return JSONResponse(content={"status": "error", "message": str(e)}, status_code=500) | |
class EachNecklaceVideoGeneratorRequest(BaseModel): | |
intro_video_path: str = "JewelMirror_intro.mp4" | |
font_path: str = "PlayfairDisplay-VariableFont_wght.ttf" | |
background_audio_path: str = "TraditionalIndianVlogMusic.mp3" | |
image_display_duration: float = 2.5 | |
fps: int = 10 | |
necklace_title: List[str] | |
nto_image_title: List[str] | |
nto_cto_image_title: List[str] | |
makeup_image_title: List[str] | |
necklace_images: List[str] | |
necklace_try_on_output_images: List[List[str]] | |
clothing_output_images: List[List[str]] | |
makeup_output_images: List[List[str]] | |
async def create_video(request: EachNecklaceVideoGeneratorRequest): | |
start_time = time.time() | |
try: | |
def process_images(image_list): | |
if isinstance(image_list, list): | |
return [process_images(item) for item in image_list] | |
return download_image(image_list) | |
temp_files = { | |
'necklaces': process_images(request.necklace_images), | |
'necklace_tryon': process_images(request.necklace_try_on_output_images), | |
'clothing': process_images(request.clothing_output_images), | |
'makeup': process_images(request.makeup_output_images) | |
} | |
file_download_time = time.time() - start_time | |
def verify_files(files): | |
if isinstance(files, list): | |
return all(verify_files(item) for item in files) # Recurse for nested lists | |
return files is not None # Check single file | |
if not all(verify_files(files) for files in temp_files.values()): | |
return JSONResponse( | |
content={"status": "error", "message": "Failed to download all required images."}, | |
status_code=400 | |
) | |
# Prepare paths | |
intro_path = f"{RESOURCES_DIR}/intro/{request.intro_video_path}" | |
output_path = f"{TEMP_VIDEO_DIR}/video_{os.urandom(8).hex()}.mp4" | |
font_path = f"{RESOURCES_DIR}/fonts/{request.font_path}" | |
audio_path = f"{RESOURCES_DIR}/audio/{request.background_audio_path}" | |
video_creator = EachVideoCreator( | |
intro_video_path=intro_path, | |
necklace_image=temp_files['necklaces'], | |
nto_outputs=temp_files['necklace_tryon'], | |
nto_cto_outputs=temp_files['clothing'], | |
makeup_outputs=temp_files['makeup'], | |
font_path=font_path, | |
output_path=output_path, | |
audio_path=audio_path, | |
image_display_duration=request.image_display_duration, | |
fps=request.fps, | |
necklace_title=request.necklace_title, | |
nto_title=request.nto_image_title, | |
cto_title=request.nto_cto_image_title, | |
makeup_title=request.makeup_image_title | |
) | |
# Generate video | |
video_creator.create_final_video() | |
video_creation_time = time.time() - start_time - file_download_time | |
# Upload video to Supabase | |
url = upload_to_supabase(video_path=output_path) | |
supabase_upload_time = time.time() - start_time - file_download_time - video_creation_time | |
response = { | |
"status": "success", | |
"message": "Video created successfully.", | |
"video_url": url, | |
"timings": { | |
"file_download_time": file_download_time, | |
"video_creation_time": video_creation_time, | |
"supabase_upload_time": supabase_upload_time | |
} | |
} | |
def cleanup_files(files): | |
if isinstance(files, list): | |
for item in files: | |
cleanup_files(item) | |
elif os.path.exists(files): | |
os.unlink(files) | |
if os.path.exists(output_path): | |
os.remove(output_path) | |
return JSONResponse(content=response, status_code=200) | |
except Exception as e: | |
return JSONResponse(content={"status": "error", "message": str(e)}, status_code=500) | |
async def get_infromation(): | |
music = os.listdir(RESOURCES_DIR + "/audio") | |
fonts = os.listdir(RESOURCES_DIR + "/fonts") | |
intro = os.listdir(RESOURCES_DIR + "/intro") | |
json = {"music": music, "fonts": fonts, "intro": intro} | |
return JSONResponse(content=json, status_code=200) | |
if __name__ == "__main__": | |
import uvicorn | |
uvicorn.run(app, host="0.0.0.0", port=8000) | |