Spaces:
Sleeping
Sleeping
Commit
·
21ba534
1
Parent(s):
b017f2d
add: prodctpage batch cto, mto, nto, video and static file
Browse files- .gitignore +5 -0
- Dockerfile +15 -0
- app.py +38 -0
- requirements.txt +48 -0
- src/__init__.py +5 -0
- src/api/__init__.py +5 -0
- src/api/api.py +76 -0
- src/components/__init__.py +5 -0
- src/components/image_generation.py +105 -0
- src/components/supabase_information_fetch.py +409 -0
- src/components/video_generation.py +153 -0
- src/pipeline/__init__.py +5 -0
- src/pipeline/main.py +193 -0
- static/index.html +137 -0
- static/preview.html +415 -0
.gitignore
ADDED
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.idea
|
2 |
+
__pycache__
|
3 |
+
*.pyc
|
4 |
+
*.log
|
5 |
+
logs
|
Dockerfile
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
FROM python:3.11-slim
|
2 |
+
|
3 |
+
WORKDIR /app
|
4 |
+
|
5 |
+
COPY requirements.txt .
|
6 |
+
|
7 |
+
RUN pip install --no-cache-dir -r requirements.txt
|
8 |
+
|
9 |
+
COPY . .
|
10 |
+
|
11 |
+
EXPOSE 7860
|
12 |
+
|
13 |
+
VOLUME ["/app/logs"]
|
14 |
+
|
15 |
+
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860"]
|
app.py
ADDED
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from pathlib import Path
|
2 |
+
|
3 |
+
from fastapi import FastAPI
|
4 |
+
from starlette.middleware.cors import CORSMiddleware
|
5 |
+
from starlette.staticfiles import StaticFiles
|
6 |
+
|
7 |
+
from src.api.api import router
|
8 |
+
|
9 |
+
app = FastAPI()
|
10 |
+
|
11 |
+
# Add CORS middleware
|
12 |
+
app.add_middleware(
|
13 |
+
CORSMiddleware,
|
14 |
+
allow_origins=["*"],
|
15 |
+
allow_credentials=True,
|
16 |
+
allow_methods=["*"],
|
17 |
+
allow_headers=["*"],
|
18 |
+
)
|
19 |
+
|
20 |
+
DIR = Path(__file__).parent
|
21 |
+
static_folder = DIR / "static"
|
22 |
+
app.mount("/static", StaticFiles(directory=static_folder, html=True), name="static")
|
23 |
+
|
24 |
+
app.include_router(router)
|
25 |
+
|
26 |
+
|
27 |
+
@app.get("/{full_path:path}")
|
28 |
+
async def serve_html(full_path: str):
|
29 |
+
file_path = static_folder / full_path
|
30 |
+
if file_path.exists() and file_path.is_file():
|
31 |
+
return StaticFiles(directory=static_folder).lookup_path(file_path)
|
32 |
+
return StaticFiles(directory=static_folder).lookup_path(static_folder / "index.html")
|
33 |
+
|
34 |
+
|
35 |
+
if __name__ == "__main__":
|
36 |
+
import uvicorn
|
37 |
+
|
38 |
+
uvicorn.run(app, host="0.0.0.0", port=7860)
|
requirements.txt
ADDED
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
aiohappyeyeballs==2.4.4
|
2 |
+
aiohttp==3.11.11
|
3 |
+
aiosignal==1.3.2
|
4 |
+
annotated-types==0.7.0
|
5 |
+
anyio==4.8.0
|
6 |
+
async-timeout==5.0.1
|
7 |
+
attrs==24.3.0
|
8 |
+
blinker==1.9.0
|
9 |
+
certifi==2024.12.14
|
10 |
+
charset-normalizer==3.4.1
|
11 |
+
click==8.1.8
|
12 |
+
deprecation==2.1.0
|
13 |
+
exceptiongroup==1.2.2
|
14 |
+
fastapi==0.115.6
|
15 |
+
frozenlist==1.5.0
|
16 |
+
gotrue==2.11.1
|
17 |
+
h11==0.14.0
|
18 |
+
h2==4.1.0
|
19 |
+
hpack==4.0.0
|
20 |
+
httpcore==1.0.7
|
21 |
+
httpx==0.27.2
|
22 |
+
hyperframe==6.0.1
|
23 |
+
idna==3.10
|
24 |
+
itsdangerous==2.2.0
|
25 |
+
Jinja2==3.1.5
|
26 |
+
MarkupSafe==3.0.2
|
27 |
+
multidict==6.1.0
|
28 |
+
packaging==24.2
|
29 |
+
postgrest==0.19.1
|
30 |
+
propcache==0.2.1
|
31 |
+
pydantic==2.10.4
|
32 |
+
pydantic_core==2.27.2
|
33 |
+
python-dateutil==2.9.0.post0
|
34 |
+
realtime==2.1.0
|
35 |
+
requests==2.32.3
|
36 |
+
six==1.17.0
|
37 |
+
sniffio==1.3.1
|
38 |
+
starlette==0.41.3
|
39 |
+
storage3==0.11.0
|
40 |
+
StrEnum==0.4.15
|
41 |
+
supabase==2.11.0
|
42 |
+
supafunc==0.9.0
|
43 |
+
typing_extensions==4.12.2
|
44 |
+
urllib3==2.3.0
|
45 |
+
uvicorn==0.34.0
|
46 |
+
websockets==13.1
|
47 |
+
Werkzeug==3.1.3
|
48 |
+
yarl==1.18.3
|
src/__init__.py
ADDED
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""
|
2 |
+
project @ batch_generation_support_team
|
3 |
+
created @ 2025-01-10
|
4 |
+
author @ github.com/ishworrsubedii
|
5 |
+
"""
|
src/api/__init__.py
ADDED
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""
|
2 |
+
project @ batch_generation_support_team
|
3 |
+
created @ 2025-01-10
|
4 |
+
author @ github.com/ishworrsubedii
|
5 |
+
"""
|
src/api/api.py
ADDED
@@ -0,0 +1,76 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""
|
2 |
+
project @ batch_generation_support_team
|
3 |
+
created @ 2025-01-09
|
4 |
+
author @ github.com/ishworrsubedii
|
5 |
+
"""
|
6 |
+
import json
|
7 |
+
|
8 |
+
from fastapi import APIRouter
|
9 |
+
|
10 |
+
from src.components.supabase_information_fetch import supabase_image_fetch_product_page, supabase_product_page_approval, \
|
11 |
+
supabase_fetch_not_approved_necklaces
|
12 |
+
from src.pipeline.main import combined_image_and_video_generation
|
13 |
+
|
14 |
+
router = APIRouter()
|
15 |
+
|
16 |
+
|
17 |
+
@router.get('/ping')
|
18 |
+
def ping():
|
19 |
+
return "Running"
|
20 |
+
|
21 |
+
|
22 |
+
@router.post('/generate')
|
23 |
+
def generate(storename, image_url):
|
24 |
+
response = combined_image_and_video_generation(storename=storename, image_url=image_url)
|
25 |
+
return response
|
26 |
+
|
27 |
+
|
28 |
+
from fastapi.responses import JSONResponse
|
29 |
+
|
30 |
+
|
31 |
+
@router.post('/image_fetch_product_page')
|
32 |
+
def image_fetch_product_page(necklace_id, model_name):
|
33 |
+
response = supabase_image_fetch_product_page(necklace_id, model_name)
|
34 |
+
return response
|
35 |
+
|
36 |
+
|
37 |
+
@router.post('/approve')
|
38 |
+
def image_fetch_product_page(necklace_id, model_name):
|
39 |
+
response = supabase_image_fetch_product_page(necklace_id, model_name)
|
40 |
+
|
41 |
+
response_nto = json.loads(response["nto"]) if isinstance(response["nto"], str) else response["nto"]
|
42 |
+
response_cto = json.loads(response["cto"]) if isinstance(response["cto"], str) else response["cto"]
|
43 |
+
response_mto = json.loads(response["mto"]) if isinstance(response["mto"], str) else response["mto"]
|
44 |
+
|
45 |
+
comma_seperated_nto = ",".join(response_nto)
|
46 |
+
comma_seperated_cto = ",".join(response_cto)
|
47 |
+
comma_seperated_mto = ",".join(response_mto)
|
48 |
+
responsee = supabase_product_page_approval(necklace_id, comma_seperated_nto, comma_seperated_cto,
|
49 |
+
comma_seperated_mto,
|
50 |
+
response["video"], model_name=model_name)
|
51 |
+
|
52 |
+
return JSONResponse(content=responsee)
|
53 |
+
|
54 |
+
|
55 |
+
@router.get('/list_necklace_id')
|
56 |
+
def list_not_approved_necklaces():
|
57 |
+
response = supabase_fetch_not_approved_necklaces()
|
58 |
+
|
59 |
+
necklace_models = {}
|
60 |
+
|
61 |
+
for item in response:
|
62 |
+
necklace_id = item.get('Id')
|
63 |
+
approve_dict = item.get('approve', {})
|
64 |
+
|
65 |
+
if necklace_id and approve_dict:
|
66 |
+
# Get list of models that have False approval status
|
67 |
+
unapproved_models = [
|
68 |
+
model_name
|
69 |
+
for model_name, status in approve_dict.items()
|
70 |
+
if status is False
|
71 |
+
]
|
72 |
+
|
73 |
+
if unapproved_models: # Only include if there are unapproved models
|
74 |
+
necklace_models[necklace_id] = unapproved_models
|
75 |
+
|
76 |
+
return JSONResponse(content=necklace_models)
|
src/components/__init__.py
ADDED
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""
|
2 |
+
project @ batch_generation_support_team
|
3 |
+
created @ 2025-01-10
|
4 |
+
author @ github.com/ishworrsubedii
|
5 |
+
"""
|
src/components/image_generation.py
ADDED
@@ -0,0 +1,105 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""
|
2 |
+
project @ batch_generation_support_team
|
3 |
+
created @ 2025-01-07
|
4 |
+
author @ github.com/ishworrsubedii
|
5 |
+
"""
|
6 |
+
from dataclasses import dataclass
|
7 |
+
from typing import List, Dict
|
8 |
+
from urllib.parse import quote
|
9 |
+
|
10 |
+
import requests
|
11 |
+
|
12 |
+
|
13 |
+
@dataclass
|
14 |
+
class MakeupColors:
|
15 |
+
lipstick: str
|
16 |
+
eyeliner: str
|
17 |
+
eyeshadow: str
|
18 |
+
|
19 |
+
|
20 |
+
@dataclass
|
21 |
+
class ImageGenerationResult:
|
22 |
+
nto_results: List[Dict[str, str]]
|
23 |
+
cto_results: List[Dict[str, str]]
|
24 |
+
mto_results: List[Dict[str, Dict]]
|
25 |
+
status: str
|
26 |
+
message: str
|
27 |
+
|
28 |
+
|
29 |
+
def batch_image_generation(
|
30 |
+
model_image: str,
|
31 |
+
necklace_id: str,
|
32 |
+
necklace_category: str,
|
33 |
+
storename: str,
|
34 |
+
clothing_list: List[str],
|
35 |
+
makeup_colors: Dict[str, str],
|
36 |
+
x_offset: float = 0.2,
|
37 |
+
y_offset: float = 0.35
|
38 |
+
) -> ImageGenerationResult:
|
39 |
+
base_url = "https://techconspartners-video-gen.hf.space/product_page_image_generation"
|
40 |
+
|
41 |
+
encoded_model_image = quote(model_image)
|
42 |
+
|
43 |
+
url = f"{base_url}?model_image={encoded_model_image}&necklace_id={necklace_id}&necklace_category={quote(necklace_category)}&storename={quote(storename)}&x_offset={x_offset}&y_offset={y_offset}"
|
44 |
+
|
45 |
+
payload = {
|
46 |
+
"clothing_list": clothing_list,
|
47 |
+
"makeup_colors": makeup_colors
|
48 |
+
}
|
49 |
+
|
50 |
+
# Set up headers
|
51 |
+
headers = {
|
52 |
+
"accept": "application/json",
|
53 |
+
"Content-Type": "application/json"
|
54 |
+
}
|
55 |
+
|
56 |
+
try:
|
57 |
+
response = requests.post(url, headers=headers, json=payload)
|
58 |
+
response.raise_for_status() # Raise an exception for bad status codes
|
59 |
+
|
60 |
+
data = response.json()
|
61 |
+
|
62 |
+
if data.get("status") != "success":
|
63 |
+
raise ValueError(f"API returned error: {data.get('message', 'Unknown error')}")
|
64 |
+
|
65 |
+
result = ImageGenerationResult(
|
66 |
+
status=data["status"],
|
67 |
+
message=data["message"],
|
68 |
+
nto_results=data["nto_results"],
|
69 |
+
cto_results=data["cto_results"],
|
70 |
+
mto_results=data["mto_results"]
|
71 |
+
)
|
72 |
+
|
73 |
+
return result
|
74 |
+
|
75 |
+
except requests.exceptions.RequestException as e:
|
76 |
+
raise RuntimeError(f"Failed to make API request: {str(e)}")
|
77 |
+
except (KeyError, ValueError) as e:
|
78 |
+
raise ValueError(f"Error processing API response: {str(e)}")
|
79 |
+
|
80 |
+
|
81 |
+
if __name__ == "__main__":
|
82 |
+
test_params = {
|
83 |
+
"model_image": "https://lvuhhlrkcuexzqtsbqyu.supabase.co/storage/v1/object/public/JewelmirrorModelImages/M0X75f993d5c22ab292b647e3c9J.png",
|
84 |
+
"necklace_id": "CJM002",
|
85 |
+
"necklace_category": "Gold Necklaces",
|
86 |
+
"storename": "ChamundiJewelsMandir",
|
87 |
+
"clothing_list": ["Red Silk saree", "Green south indian saree", "Purple kurti"],
|
88 |
+
"makeup_colors": {
|
89 |
+
"lipstick": "Carmine Red",
|
90 |
+
"eyeliner": "Black",
|
91 |
+
"eyeshadow": "Maroon"
|
92 |
+
}
|
93 |
+
}
|
94 |
+
|
95 |
+
try:
|
96 |
+
result = batch_image_generation(**test_params)
|
97 |
+
with open("image_generation_response.json", "w") as f:
|
98 |
+
f.write(str(result))
|
99 |
+
print(f"Status: {result.status}")
|
100 |
+
print(f"Message: {result.message}")
|
101 |
+
print(f"Number of NTO results: {len(result.nto_results)}")
|
102 |
+
print(f"Number of CTO results: {len(result.cto_results)}")
|
103 |
+
print(f"Number of MTO results: {len(result.mto_results)}")
|
104 |
+
except Exception as e:
|
105 |
+
print(f"Error: {str(e)}")
|
src/components/supabase_information_fetch.py
ADDED
@@ -0,0 +1,409 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import logging
|
2 |
+
import os
|
3 |
+
from dataclasses import dataclass
|
4 |
+
from typing import List
|
5 |
+
|
6 |
+
from supabase import create_client
|
7 |
+
|
8 |
+
|
9 |
+
@dataclass
|
10 |
+
class NecklaceData:
|
11 |
+
necklace_id: str
|
12 |
+
necklace_url: str
|
13 |
+
x_lean_offset: float
|
14 |
+
y_lean_offset: float
|
15 |
+
x_broad_offset: float
|
16 |
+
y_broad_offset: float
|
17 |
+
category: str
|
18 |
+
|
19 |
+
|
20 |
+
supabase_url = os.getenv("SUPABASE_URL")
|
21 |
+
supabase_key = os.getenv("SUPABASE_KEY")
|
22 |
+
|
23 |
+
supabase = create_client(supabase_url, supabase_key)
|
24 |
+
|
25 |
+
|
26 |
+
def fetch_necklace_offset_each_store(storename: str) -> List[NecklaceData]:
|
27 |
+
response = supabase.table("MagicMirror").select("*").eq("StoreName", storename).execute()
|
28 |
+
|
29 |
+
necklace_data_list = []
|
30 |
+
|
31 |
+
for item in response.data:
|
32 |
+
jewellery_url = (
|
33 |
+
f"https://lvuhhlrkcuexzqtsbqyu.supabase.co/storage/v1/object/public/Stores/"
|
34 |
+
f"{storename}/{item['Category']}/image/{item['Id']}.png"
|
35 |
+
)
|
36 |
+
|
37 |
+
necklace_data = NecklaceData(
|
38 |
+
necklace_id=item['Id'],
|
39 |
+
necklace_url=jewellery_url,
|
40 |
+
x_lean_offset=item['x_lean'],
|
41 |
+
y_lean_offset=item['y_lean'],
|
42 |
+
x_broad_offset=item['x_chubby'],
|
43 |
+
y_broad_offset=item['y_chubby'],
|
44 |
+
category=item['Category']
|
45 |
+
)
|
46 |
+
|
47 |
+
necklace_data_list.append(necklace_data)
|
48 |
+
|
49 |
+
return necklace_data_list
|
50 |
+
|
51 |
+
|
52 |
+
def fetch_model_body_type(image_url: str):
|
53 |
+
try:
|
54 |
+
response = supabase.table("JewelMirror_ModelImages").select("*").eq("image_url", image_url).execute()
|
55 |
+
|
56 |
+
for item in response.data:
|
57 |
+
print(item)
|
58 |
+
return item['image_url'], item['body_structure']
|
59 |
+
|
60 |
+
|
61 |
+
|
62 |
+
|
63 |
+
except Exception as e:
|
64 |
+
print(f"The image url is not found in the table", {e})
|
65 |
+
return None
|
66 |
+
|
67 |
+
|
68 |
+
def upload_information_to_new_table(necklace_id, nto_images_urls, cto_images_urls, mto_urls, video_urls, model_name):
|
69 |
+
try:
|
70 |
+
response_check = supabase.table("JM_Productpage").select("*").eq("Id", necklace_id).execute()
|
71 |
+
|
72 |
+
if response_check.data:
|
73 |
+
existing_record = response_check.data[0]
|
74 |
+
|
75 |
+
updated_nto = f"{existing_record['nto_images_urls']},{nto_images_urls}" if existing_record[
|
76 |
+
'nto_images_urls'] else nto_images_urls
|
77 |
+
updated_cto = f"{existing_record['cto_images_urls']},{cto_images_urls}" if existing_record[
|
78 |
+
'cto_images_urls'] else cto_images_urls
|
79 |
+
updated_mto = f"{existing_record['mto_images_urls']},{mto_urls}" if existing_record[
|
80 |
+
'mto_images_urls'] else mto_urls
|
81 |
+
updated_video = f"{existing_record['video_urls']},{video_urls}" if existing_record[
|
82 |
+
'video_urls'] else video_urls
|
83 |
+
updated_model = f"{existing_record['model_name']},{model_name}" if existing_record[
|
84 |
+
'model_name'] else model_name
|
85 |
+
|
86 |
+
current_approve = existing_record.get('approve', {})
|
87 |
+
if not isinstance(current_approve, dict):
|
88 |
+
current_approve = {}
|
89 |
+
|
90 |
+
if model_name not in current_approve:
|
91 |
+
current_approve[model_name] = False
|
92 |
+
|
93 |
+
logging.info(f"Updated model name: {updated_model}")
|
94 |
+
logging.info(f"Updated approve status: {current_approve}")
|
95 |
+
|
96 |
+
response = supabase.table("JM_Productpage").update({
|
97 |
+
"nto_images_urls": updated_nto,
|
98 |
+
"cto_images_urls": updated_cto,
|
99 |
+
"mto_images_urls": updated_mto,
|
100 |
+
"video_urls": updated_video,
|
101 |
+
"model_name": updated_model,
|
102 |
+
"approve": current_approve
|
103 |
+
}).eq("Id", necklace_id).execute()
|
104 |
+
|
105 |
+
print("Updated existing record")
|
106 |
+
|
107 |
+
else:
|
108 |
+
response = supabase.table("JM_Productpage").insert({
|
109 |
+
"Id": necklace_id,
|
110 |
+
"nto_images_urls": nto_images_urls,
|
111 |
+
"cto_images_urls": cto_images_urls,
|
112 |
+
"mto_images_urls": mto_urls,
|
113 |
+
"video_urls": video_urls,
|
114 |
+
"model_name": model_name,
|
115 |
+
"approve": {
|
116 |
+
model_name: False
|
117 |
+
}
|
118 |
+
}).execute()
|
119 |
+
|
120 |
+
print("Inserted new record")
|
121 |
+
|
122 |
+
return response
|
123 |
+
|
124 |
+
except Exception as e:
|
125 |
+
print(f"Error in uploading the data to the table: {str(e)}")
|
126 |
+
logging.error(f"Error in uploading the data to the table: {str(e)}")
|
127 |
+
return None
|
128 |
+
|
129 |
+
|
130 |
+
def supabase_image_fetch_product_page(necklace_id, model_name):
|
131 |
+
try:
|
132 |
+
response = supabase.table("JM_Productpage").select("*").eq("Id", necklace_id).execute()
|
133 |
+
|
134 |
+
if not response.data:
|
135 |
+
print(f"No data found for necklace_id: {necklace_id}")
|
136 |
+
return None
|
137 |
+
|
138 |
+
# Get the first item from response
|
139 |
+
item = response.data[0]
|
140 |
+
|
141 |
+
def process_url_list(url_string):
|
142 |
+
if not url_string:
|
143 |
+
return []
|
144 |
+
|
145 |
+
url_groups = url_string.replace("[", "").replace("]", "").split("],[")
|
146 |
+
|
147 |
+
filtered_urls = []
|
148 |
+
for group in url_groups:
|
149 |
+
urls = [url.strip().strip('"\'') for url in group.split(",")]
|
150 |
+
matching_urls = [url for url in urls if model_name in url]
|
151 |
+
filtered_urls.extend(matching_urls)
|
152 |
+
|
153 |
+
return filtered_urls
|
154 |
+
|
155 |
+
try:
|
156 |
+
nto_images = process_url_list(item.get('nto_images_urls', ''))
|
157 |
+
cto_images = process_url_list(item.get('cto_images_urls', ''))
|
158 |
+
mto_images = process_url_list(item.get('mto_images_urls', ''))
|
159 |
+
print(f"nto_images: {nto_images}")
|
160 |
+
print(f"cto_images: {cto_images}")
|
161 |
+
print(f"mto_images: {mto_images}")
|
162 |
+
|
163 |
+
video_urls = []
|
164 |
+
if item.get('video_urls'):
|
165 |
+
video_list = item['video_urls'].split(',')
|
166 |
+
video_urls = [url.strip() for url in video_list if model_name in url]
|
167 |
+
|
168 |
+
print(f"video_urls: {video_urls}")
|
169 |
+
|
170 |
+
except AttributeError as e:
|
171 |
+
print(f"Error parsing URLs: {e}")
|
172 |
+
return None
|
173 |
+
|
174 |
+
response = {
|
175 |
+
'nto': nto_images,
|
176 |
+
'cto': cto_images,
|
177 |
+
'mto': mto_images,
|
178 |
+
'video': video_urls,
|
179 |
+
"status": "success"
|
180 |
+
|
181 |
+
}
|
182 |
+
|
183 |
+
return response
|
184 |
+
except Exception as e:
|
185 |
+
print(f"Error in fetching the data from the table: {e}")
|
186 |
+
return None
|
187 |
+
|
188 |
+
|
189 |
+
def supabase_product_page_approval(necklace_id, nto_images_urls, cto_images_urls, mto_images_urls, video_urls,
|
190 |
+
model_name):
|
191 |
+
try:
|
192 |
+
existing_record = supabase.table("MagicMirror").select("*").eq("Id", necklace_id).execute()
|
193 |
+
|
194 |
+
def append_urls(existing_urls, new_urls):
|
195 |
+
existing = convert_to_string(existing_urls) if existing_urls else ""
|
196 |
+
new = convert_to_string(new_urls) if new_urls else ""
|
197 |
+
|
198 |
+
if existing and new:
|
199 |
+
return f"{existing},{new}"
|
200 |
+
return new or existing
|
201 |
+
|
202 |
+
if existing_record.data:
|
203 |
+
record = existing_record.data[0]
|
204 |
+
|
205 |
+
update_data = {
|
206 |
+
"nto_images_urls": append_urls(record.get("nto_images_urls"), nto_images_urls),
|
207 |
+
"cto_images_urls": append_urls(record.get("cto_images_urls"), cto_images_urls),
|
208 |
+
"mto_images_urls": append_urls(record.get("mto_images_urls"), mto_images_urls),
|
209 |
+
"video_urls": append_urls(record.get("video_urls"), video_urls)
|
210 |
+
}
|
211 |
+
else:
|
212 |
+
update_data = {
|
213 |
+
"nto_images_urls": convert_to_string(nto_images_urls),
|
214 |
+
"cto_images_urls": convert_to_string(cto_images_urls),
|
215 |
+
"mto_images_urls": convert_to_string(mto_images_urls),
|
216 |
+
"video_urls": convert_to_string(video_urls)
|
217 |
+
}
|
218 |
+
|
219 |
+
result = supabase.table("MagicMirror") \
|
220 |
+
.update(update_data) \
|
221 |
+
.eq("Id", necklace_id) \
|
222 |
+
.execute()
|
223 |
+
|
224 |
+
res = supabase_jmproductpage_approval_flag(necklace_id, model_name, True)
|
225 |
+
|
226 |
+
print(f"Successfully updated URLs for necklace ID: {necklace_id}")
|
227 |
+
return result.data
|
228 |
+
|
229 |
+
except Exception as e:
|
230 |
+
error_msg = f"Error updating URLs in MagicMirror table: {str(e)}"
|
231 |
+
print(error_msg)
|
232 |
+
raise Exception(error_msg)
|
233 |
+
|
234 |
+
|
235 |
+
def convert_to_string(value):
|
236 |
+
if value is None:
|
237 |
+
return ""
|
238 |
+
|
239 |
+
if isinstance(value, list):
|
240 |
+
return ",".join(str(item) for item in value if item)
|
241 |
+
|
242 |
+
if isinstance(value, str):
|
243 |
+
# If it's already a string, clean it up
|
244 |
+
items = [item.strip() for item in value.split(",") if item.strip()]
|
245 |
+
return ",".join(items)
|
246 |
+
|
247 |
+
return str(value)
|
248 |
+
|
249 |
+
|
250 |
+
def supabase_fetch_not_approved_necklaces():
|
251 |
+
try:
|
252 |
+
response = supabase.table("JM_Productpage").select("*").execute()
|
253 |
+
|
254 |
+
unapproved_necklaces = []
|
255 |
+
for record in response.data:
|
256 |
+
approve_dict = record.get('approve', {})
|
257 |
+
if not isinstance(approve_dict, dict):
|
258 |
+
continue
|
259 |
+
|
260 |
+
false_approvals = {
|
261 |
+
model: status
|
262 |
+
for model, status in approve_dict.items()
|
263 |
+
if status is False
|
264 |
+
}
|
265 |
+
|
266 |
+
if false_approvals:
|
267 |
+
filtered_record = {
|
268 |
+
'Id': record['Id'],
|
269 |
+
'approve': false_approvals
|
270 |
+
}
|
271 |
+
unapproved_necklaces.append(filtered_record)
|
272 |
+
print(f"Unapproved necklaces: {unapproved_necklaces}")
|
273 |
+
return unapproved_necklaces
|
274 |
+
|
275 |
+
except Exception as e:
|
276 |
+
print(f"Error fetching not approved necklaces: {str(e)}")
|
277 |
+
raise e
|
278 |
+
|
279 |
+
|
280 |
+
def supabase_jmproductpage_approval_flag(necklace_id, model_name, flag_value):
|
281 |
+
try:
|
282 |
+
current_data = supabase.table("JM_Productpage").select("approve").eq("Id", necklace_id).execute()
|
283 |
+
|
284 |
+
current_approve = current_data.data[0].get("approve", {}) if current_data.data else {}
|
285 |
+
|
286 |
+
if model_name in current_approve and current_approve[model_name] is True:
|
287 |
+
print(f"Skipping update for {model_name} as it's already approved")
|
288 |
+
return current_data.data
|
289 |
+
|
290 |
+
if isinstance(current_approve, dict):
|
291 |
+
current_approve[model_name] = flag_value
|
292 |
+
else:
|
293 |
+
current_approve = {model_name: flag_value}
|
294 |
+
|
295 |
+
response = supabase.table("JM_Productpage").update(
|
296 |
+
{"approve": current_approve}
|
297 |
+
).eq("Id", necklace_id).execute()
|
298 |
+
|
299 |
+
return response.data
|
300 |
+
|
301 |
+
except Exception as e:
|
302 |
+
print(f"Error updating approval flag for necklace ID {necklace_id}: {str(e)}")
|
303 |
+
raise e
|
304 |
+
|
305 |
+
|
306 |
+
def upload_productpage_logs(necklace_id: str, status: bool, model_name: str) -> dict:
|
307 |
+
try:
|
308 |
+
existing_record = supabase.table("JM_productpagelog").select("*").eq("id", necklace_id).execute()
|
309 |
+
|
310 |
+
if not existing_record.data:
|
311 |
+
response = supabase.table("JM_productpagelog").insert([{
|
312 |
+
"id": necklace_id,
|
313 |
+
"status": bool(status),
|
314 |
+
"model_name": model_name
|
315 |
+
}]).execute()
|
316 |
+
print("Inserted new record")
|
317 |
+
return {
|
318 |
+
"status": "success",
|
319 |
+
"message": "Record inserted successfully"
|
320 |
+
}
|
321 |
+
else:
|
322 |
+
existing_models = existing_record.data[0].get('model_name', '').split(',')
|
323 |
+
existing_models = [model.strip() for model in existing_models]
|
324 |
+
|
325 |
+
if model_name in existing_models:
|
326 |
+
return {
|
327 |
+
"status": "error",
|
328 |
+
"message": f"Record already exists with model {model_name} and {necklace_id}"
|
329 |
+
}
|
330 |
+
else:
|
331 |
+
updated_models = ','.join(existing_models + [model_name])
|
332 |
+
|
333 |
+
response = supabase.table("JM_productpagelog").update({
|
334 |
+
"model_name": updated_models
|
335 |
+
}).eq("id", necklace_id).execute()
|
336 |
+
|
337 |
+
print(f"Updated record with new model {model_name}")
|
338 |
+
return {
|
339 |
+
"status": "success",
|
340 |
+
"message": f"Added new model {model_name} to existing record"
|
341 |
+
}
|
342 |
+
|
343 |
+
except Exception as e:
|
344 |
+
error_msg = f"Error updating product page log for necklace {necklace_id}: {str(e)}"
|
345 |
+
logging.error(error_msg)
|
346 |
+
raise Exception(f"Failed to update product page log: {str(e)}")
|
347 |
+
|
348 |
+
|
349 |
+
def fetch_productpage_logs(necklace_id):
|
350 |
+
try:
|
351 |
+
response = supabase.table("JM_productpagelog").select("*").eq("id", necklace_id).execute()
|
352 |
+
for item in response.data:
|
353 |
+
return item['status']
|
354 |
+
except Exception as e:
|
355 |
+
print(f"Error in fetching the data from the table", {e})
|
356 |
+
return None
|
357 |
+
|
358 |
+
|
359 |
+
if __name__ == "__main__":
|
360 |
+
# store_name = "ChamundiJewelsMandir"
|
361 |
+
# necklaces = fetch_necklace_offset_each_store(store_name)
|
362 |
+
#
|
363 |
+
# count = 0
|
364 |
+
# for necklace in necklaces:
|
365 |
+
# print(f"Necklace ID: {necklace.necklace_id}")
|
366 |
+
# print(f"URL: {necklace.necklace_url}")
|
367 |
+
# print(f"Lean offsets (x, y): ({necklace.x_lean_offset}, {necklace.y_lean_offset})")
|
368 |
+
# print(f"Broad offsets (x, y): ({necklace.x_broad_offset}, {necklace.y_broad_offset})")
|
369 |
+
# print(f"Category: {necklace.category}")
|
370 |
+
# print("-" * 50)
|
371 |
+
# count += 1
|
372 |
+
#
|
373 |
+
# print(f"Total necklaces fetched: {count}")
|
374 |
+
# image_url = "https://lvuhhlrkcuexzqtsbqyu.supabase.co/storage/v1/object/public/JewelmirrorModelImages/M0X9e22b54f110493113feb79e3J.png"
|
375 |
+
# url,type=fetch_model_body_type(image_url)
|
376 |
+
# print(url,type)
|
377 |
+
# ---------------------------------
|
378 |
+
#
|
379 |
+
# upload_information_to_new_table(necklace_id="CJM0025",
|
380 |
+
# nto_images_urls="https://lvuhhlrkcuexzqtsbqyu.supabase.co/storage/v1/object/public/ProductPageOutputs/CJM0025-M0X0f16ad748dd979a48d3f79b4J-nto-Gold_Necklaces.png",
|
381 |
+
# cto_images_urls="https://lvuhhlrkcuexzqtsbqyu.supabase.co/storage/v1/object/public/ProductPageOutputs/CJM0025-cto-Blue Lehenga.png,https://lvuhhlrkcuexzqtsbqyu.supabase.co/storage/v1/object/public/ProductPageOutputs/CJM0025-cto-Blue Kurti.png",
|
382 |
+
# mto_urls="https://lvuhhlrkcuexzqtsbqyu.supabase.co/storage/v1/object/public/ProductPageOutputs/CJM0025-M0X0f16ad748dd979a48d3f79b4J-mto-Blue_Kurti-lip_Carmine_Red_eye_Black_shadow_Maroon.png",
|
383 |
+
# video_urls="https://lvuhhlrkcuexzqtsbqyu.supabase.co/storage/v1/object/public/JewelmirrorVideoGeneration/video_bd48d59bc8f358c3.mp4")
|
384 |
+
# --------------------------------
|
385 |
+
# nto, cto, mto, video = supabase_image_fetch_product_page("CJM0025")
|
386 |
+
#
|
387 |
+
# print("nto", nto)
|
388 |
+
# print("cto", cto)
|
389 |
+
# print("mto", mto)
|
390 |
+
# print("video", video)
|
391 |
+
# # --------------------------------
|
392 |
+
# res = supabase_product_page_approval(necklace_id="CJM0025",
|
393 |
+
# nto_images_urls="https://lvuhhlrkcuexzqtsbqyu.supabase.co/storage/v1/object/public/ProductPageOutputs/CJM0025-M0X0f16ad748dd979a48d3f79b4J-nto-Gold_Necklaces.png",
|
394 |
+
# cto_images_urls="https://lvuhhlrkcuexzqtsbqyu.supabase.co/storage/v1/object/public/ProductPageOutputs/CJM0025-cto-Blue Lehenga.png,https://lvuhhlrkcuexzqtsbqyu.supabase.co/storage/v1/object/public/ProductPageOutputs/CJM0025-cto-Blue Kurti.png",
|
395 |
+
# mto_images_urls="https://lvuhhlrkcuexzqtsbqyu.supabase.co/storage/v1/object/public/ProductPageOutputs/CJM0025-M0X0f16ad748dd979a48d3f79b4J-mto-Blue_Kurti-lip_Carmine_Red_eye_Black_shadow_Maroon.png",
|
396 |
+
# video_urls="https://lvuhhlrkcuexzqtsbqyu.supabase.co/storage/v1/object/public/JewelmirrorVideoGeneration/video_bd48d59bc8f358c3.mp4")
|
397 |
+
# print(res)
|
398 |
+
|
399 |
+
# res = supabase_fetch_not_approved_necklaces()
|
400 |
+
# print(res)
|
401 |
+
# --------------------------------
|
402 |
+
# res = supabase_jmproductpage_approval_flag("CJM001")
|
403 |
+
# print(res)
|
404 |
+
|
405 |
+
response = upload_productpage_logs("CJM0025", "True")
|
406 |
+
|
407 |
+
# -------------------------------
|
408 |
+
response = fetch_productpage_logs("CJM0025")
|
409 |
+
print(response)
|
src/components/video_generation.py
ADDED
@@ -0,0 +1,153 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""
|
2 |
+
project @ batch_generation_support_team
|
3 |
+
created @ 2025-01-07
|
4 |
+
author @ github.com/ishworrsubedii
|
5 |
+
"""
|
6 |
+
from dataclasses import dataclass
|
7 |
+
from typing import List
|
8 |
+
|
9 |
+
import requests
|
10 |
+
|
11 |
+
|
12 |
+
@dataclass
|
13 |
+
class VideoTimings:
|
14 |
+
file_download_time: float
|
15 |
+
video_creation_time: float
|
16 |
+
supabase_upload_time: float
|
17 |
+
|
18 |
+
|
19 |
+
@dataclass
|
20 |
+
class VideoGenerationResult:
|
21 |
+
status: str
|
22 |
+
message: str
|
23 |
+
video_url: str
|
24 |
+
timings: VideoTimings
|
25 |
+
|
26 |
+
|
27 |
+
def generate_combined_video(
|
28 |
+
intro_video_path: str,
|
29 |
+
font_path: str,
|
30 |
+
background_audio_path: str,
|
31 |
+
necklace_title: List[str],
|
32 |
+
necklace_images: List[str],
|
33 |
+
nto_image_title: List[List[str]],
|
34 |
+
nto_cto_image_title: List[List[str]],
|
35 |
+
makeup_image_title: List[List[str]],
|
36 |
+
necklace_try_on_output_images: List[List[str]],
|
37 |
+
clothing_output_images: List[List[str]],
|
38 |
+
makeup_output_images: List[List[str]],
|
39 |
+
background_colors: List[List[int]],
|
40 |
+
outro_title: str,
|
41 |
+
address: str,
|
42 |
+
phone_numbers: str,
|
43 |
+
logo_url: str,
|
44 |
+
image_display_duration: float = 2.5,
|
45 |
+
fps: int = 30,
|
46 |
+
transition_duration: float = 0.5,
|
47 |
+
transition_type: str = "None",
|
48 |
+
direction: str = "left",
|
49 |
+
outro_video_path: str = "default_outro.mp4"
|
50 |
+
) -> VideoGenerationResult:
|
51 |
+
url = "https://techconspartners-video-gen.hf.space/createcombinedvideo/"
|
52 |
+
|
53 |
+
payload = {
|
54 |
+
"intro_video_path": intro_video_path,
|
55 |
+
"font_path": font_path,
|
56 |
+
"background_audio_path": background_audio_path,
|
57 |
+
"image_display_duration": image_display_duration,
|
58 |
+
"fps": fps,
|
59 |
+
"necklace_title": necklace_title,
|
60 |
+
"nto_image_title": nto_image_title,
|
61 |
+
"nto_cto_image_title": nto_cto_image_title,
|
62 |
+
"makeup_image_title": makeup_image_title,
|
63 |
+
"necklace_images": necklace_images,
|
64 |
+
"necklace_try_on_output_images": necklace_try_on_output_images,
|
65 |
+
"clothing_output_images": clothing_output_images,
|
66 |
+
"makeup_output_images": makeup_output_images,
|
67 |
+
"background_colors": background_colors,
|
68 |
+
"outro_title": outro_title,
|
69 |
+
"address": address,
|
70 |
+
"phone_numbers": phone_numbers,
|
71 |
+
"logo_url": logo_url,
|
72 |
+
"transition_duration": transition_duration,
|
73 |
+
"transition_type": transition_type,
|
74 |
+
"direction": direction,
|
75 |
+
"outro_video_path": outro_video_path
|
76 |
+
}
|
77 |
+
headers = {
|
78 |
+
"accept": "application/json",
|
79 |
+
"Content-Type": "application/json"
|
80 |
+
}
|
81 |
+
|
82 |
+
try:
|
83 |
+
|
84 |
+
response = requests.post(url, headers=headers, json=payload)
|
85 |
+
# response.raise_for_status()
|
86 |
+
|
87 |
+
data = response.json()
|
88 |
+
|
89 |
+
if data.get("status") != "success":
|
90 |
+
raise ValueError(f"API returned error: {data.get('message', 'Unknown error')}")
|
91 |
+
|
92 |
+
timings = VideoTimings(
|
93 |
+
file_download_time=data["timings"]["file_download_time"],
|
94 |
+
video_creation_time=data["timings"]["video_creation_time"],
|
95 |
+
supabase_upload_time=data["timings"]["supabase_upload_time"]
|
96 |
+
)
|
97 |
+
|
98 |
+
result = VideoGenerationResult(
|
99 |
+
status=data["status"],
|
100 |
+
message=data["message"],
|
101 |
+
video_url=data["video_url"],
|
102 |
+
timings=timings
|
103 |
+
)
|
104 |
+
|
105 |
+
return result
|
106 |
+
|
107 |
+
except requests.exceptions.RequestException as e:
|
108 |
+
raise RuntimeError(f"Failed to make API request: {str(e)}")
|
109 |
+
except (KeyError, ValueError) as e:
|
110 |
+
raise ValueError(f"Error processing API response: {str(e)}")
|
111 |
+
|
112 |
+
|
113 |
+
if __name__ == "__main__":
|
114 |
+
# test_params = {
|
115 |
+
# "intro_video_path": "Vellaimani_intro.mp4",
|
116 |
+
# "font_path": "PlayfairDisplay-VariableFont.ttf",
|
117 |
+
# "background_audio_path": "LoveIndianCinematicBGM.mp3",
|
118 |
+
# "necklace_title": ["ARA01DI001"],
|
119 |
+
# "necklace_images": [
|
120 |
+
# "https://lvuhhlrkcuexzqtsbqyu.supabase.co/storage/v1/object/public/Stores/VellaimaniJewellery/Diamond%20Necklaces/image/ARA01DI001.png"
|
121 |
+
# ],
|
122 |
+
# "nto_image_title": [["ARA01DI001"]],
|
123 |
+
# "nto_cto_image_title": [["ARA01DI001", "ARA01DI001"]],
|
124 |
+
# "makeup_image_title": [["ARA01DI001"]],
|
125 |
+
# "necklace_try_on_output_images": [[
|
126 |
+
# "https://lvuhhlrkcuexzqtsbqyu.supabase.co/storage/v1/object/public/ProductPageOutputs/ARA01DI001-M0X75f993d5c22ab292b647e3c9J-nto-Diamond_Necklaces.png"
|
127 |
+
# ]],
|
128 |
+
# "clothing_output_images": [[
|
129 |
+
# "https://lvuhhlrkcuexzqtsbqyu.supabase.co/storage/v1/object/public/ProductPageOutputs/ARA01DI001-cto-Red Silk saree.png",
|
130 |
+
# "https://lvuhhlrkcuexzqtsbqyu.supabase.co/storage/v1/object/public/ProductPageOutputs/ARA01DI001-cto-Green south indian saree.png"
|
131 |
+
# ]],
|
132 |
+
# "makeup_output_images": [[
|
133 |
+
# "https://lvuhhlrkcuexzqtsbqyu.supabase.co/storage/v1/object/public/ProductPageOutputs/ARA01DI001-M0X75f993d5c22ab292b647e3c9J-mto-Purple_kurti-lip_Carmine_Red_eye_Black_shadow_Maroon.png"
|
134 |
+
# ]],
|
135 |
+
# "background_colors": [[245, 245, 245], [220, 245, 245]],
|
136 |
+
# "outro_title": "Reach out to us for more information",
|
137 |
+
# "address": "123, ABC Street, XYZ City",
|
138 |
+
# "phone_numbers": "1234567890",
|
139 |
+
# "logo_url": "https://lvuhhlrkcuexzqtsbqyu.supabase.co/storage/v1/object/public/MagicMirror/FullImages/default.png"
|
140 |
+
# }
|
141 |
+
|
142 |
+
with open("/home/ishwor/Desktop/TCP/batch_generation_support_team/combined_video_params.json", "r") as f:
|
143 |
+
test_params = str(f.read())
|
144 |
+
|
145 |
+
try:
|
146 |
+
result = generate_combined_video(**test_params)
|
147 |
+
print(f"Status: {result.status}")
|
148 |
+
print(f"Message: {result.message}")
|
149 |
+
print(f"Video URL: {result.video_url}")
|
150 |
+
print(
|
151 |
+
f"Total processing time: {result.timings.file_download_time + result.timings.video_creation_time + result.timings.supabase_upload_time:.2f} seconds")
|
152 |
+
except Exception as e:
|
153 |
+
print(f"Error: {str(e)}")
|
src/pipeline/__init__.py
ADDED
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""
|
2 |
+
project @ batch_generation_support_team
|
3 |
+
created @ 2025-01-10
|
4 |
+
author @ github.com/ishworrsubedii
|
5 |
+
"""
|
src/pipeline/main.py
ADDED
@@ -0,0 +1,193 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import logging
|
2 |
+
import os
|
3 |
+
import random
|
4 |
+
from datetime import datetime
|
5 |
+
from typing import List
|
6 |
+
|
7 |
+
from src.components.image_generation import batch_image_generation
|
8 |
+
from src.components.supabase_information_fetch import fetch_necklace_offset_each_store, fetch_model_body_type, \
|
9 |
+
upload_information_to_new_table, upload_productpage_logs
|
10 |
+
from src.components.video_generation import generate_combined_video
|
11 |
+
|
12 |
+
os.makedirs('logs', exist_ok=True)
|
13 |
+
|
14 |
+
logging.basicConfig(
|
15 |
+
level=logging.INFO,
|
16 |
+
format='%(asctime)s - %(levelname)s - %(message)s',
|
17 |
+
handlers=[
|
18 |
+
logging.FileHandler('logs/running.log'),
|
19 |
+
logging.StreamHandler()
|
20 |
+
]
|
21 |
+
)
|
22 |
+
|
23 |
+
|
24 |
+
def update_processed_necklaces(necklace_id: str, status: bool, model_name: str):
|
25 |
+
response = upload_productpage_logs(necklace_id, status, model_name=model_name)
|
26 |
+
|
27 |
+
|
28 |
+
def get_random_clothing_combinations(clothing_list: List[str], colors: List[str], count: int = 5) -> List[str]:
|
29 |
+
"""Generate random clothing combinations."""
|
30 |
+
combinations = []
|
31 |
+
for _ in range(count):
|
32 |
+
clothing = random.choice(clothing_list)
|
33 |
+
color = random.choice(colors)
|
34 |
+
combinations.append(f"{color} {clothing}")
|
35 |
+
return combinations
|
36 |
+
|
37 |
+
|
38 |
+
def combined_image_and_video_generation(storename, image_url):
|
39 |
+
logging.info("Starting combined image and video generation process")
|
40 |
+
|
41 |
+
try:
|
42 |
+
necklace_data = fetch_necklace_offset_each_store(storename=storename)
|
43 |
+
url, typee = fetch_model_body_type(image_url=image_url)
|
44 |
+
|
45 |
+
model_name = url.split("/")[-1].split(".")[0]
|
46 |
+
|
47 |
+
for necklace in necklace_data:
|
48 |
+
necklace_id = necklace.necklace_id
|
49 |
+
response = upload_productpage_logs(necklace_id, True, model_name=model_name)
|
50 |
+
if response['status'] == "error":
|
51 |
+
print("Skipping", necklace_id)
|
52 |
+
logging.info(f"Skipping {necklace_id} - already processed")
|
53 |
+
continue
|
54 |
+
|
55 |
+
start_time = datetime.now()
|
56 |
+
logging.info(f"Processing necklace: {necklace_id}")
|
57 |
+
|
58 |
+
try:
|
59 |
+
if typee == "lean":
|
60 |
+
x_offset = necklace.x_lean_offset
|
61 |
+
y_offset = necklace.y_lean_offset
|
62 |
+
logging.info("Body Type: lean")
|
63 |
+
|
64 |
+
elif typee == "medium":
|
65 |
+
x_offset = necklace.x_broad_offset
|
66 |
+
y_offset = necklace.y_broad_offset
|
67 |
+
logging.info("Body Type: medium")
|
68 |
+
|
69 |
+
else:
|
70 |
+
logging.info("Body Type: None")
|
71 |
+
x_offset = None
|
72 |
+
y_offset = None
|
73 |
+
|
74 |
+
clothing_combinations = get_random_clothing_combinations(
|
75 |
+
clothing_list=["Salwar Kameez", "South Indian Saree", "Kurti", "Lehenga", "Silk Saree"],
|
76 |
+
colors=["Red", "Blue", "Green", "Yellow", "Pink"]
|
77 |
+
)
|
78 |
+
|
79 |
+
makeup_data = {
|
80 |
+
"lipstick": "Carmine Red",
|
81 |
+
"eyeliner": "Black",
|
82 |
+
"eyeshadow": "Maroon"
|
83 |
+
}
|
84 |
+
|
85 |
+
image_params = {
|
86 |
+
"model_image": url,
|
87 |
+
"necklace_id": necklace_id,
|
88 |
+
"necklace_category": necklace.category,
|
89 |
+
"storename": storename,
|
90 |
+
"clothing_list": clothing_combinations,
|
91 |
+
"makeup_colors": makeup_data,
|
92 |
+
"x_offset": x_offset,
|
93 |
+
"y_offset": y_offset
|
94 |
+
|
95 |
+
}
|
96 |
+
print("image: params", image_params)
|
97 |
+
logging.info("NTO-CTO-MTO images Generating for {}".format(necklace_id))
|
98 |
+
|
99 |
+
image_results = batch_image_generation(**image_params)
|
100 |
+
logging.info(f"Image generation result: {image_results}")
|
101 |
+
|
102 |
+
if image_results.status != 'success':
|
103 |
+
raise Exception(f"Image generation failed: {image_results.message}")
|
104 |
+
cto_urls = [result['url'] for result in image_results.cto_results[:4]] # First four CTO images
|
105 |
+
mto_urls = [image_results.mto_results[-1]['url']]
|
106 |
+
video_params = {
|
107 |
+
"intro_video_path": f"{storename}_intro.mp4",
|
108 |
+
"font_path": "PlayfairDisplay-VariableFont.ttf",
|
109 |
+
"background_audio_path": "LoveIndianCinematicBGM.mp3",
|
110 |
+
"necklace_title": [necklace_id],
|
111 |
+
"necklace_images": [necklace.necklace_url],
|
112 |
+
"nto_image_title": [[necklace_id]],
|
113 |
+
"nto_cto_image_title": [[necklace_id, necklace_id, necklace_id, necklace_id]],
|
114 |
+
"makeup_image_title": [[necklace_id]],
|
115 |
+
"necklace_try_on_output_images": [[result['url'] for result in image_results.nto_results]],
|
116 |
+
"clothing_output_images": [cto_urls
|
117 |
+
],
|
118 |
+
"makeup_output_images": [
|
119 |
+
mto_urls
|
120 |
+
],
|
121 |
+
|
122 |
+
"background_colors": [[245, 245, 245], [220, 245, 245]],
|
123 |
+
"outro_title": "Reach out to us for more information",
|
124 |
+
"address": "None",
|
125 |
+
"phone_numbers": "None",
|
126 |
+
"logo_url": "https://lvuhhlrkcuexzqtsbqyu.supabase.co/storage/v1/object/public/MagicMirror/FullImages/default.png",
|
127 |
+
"outro_video_path": f"{storename}_outro.mp4"
|
128 |
+
}
|
129 |
+
|
130 |
+
logging.info("Video Generating for {}".format(necklace_id))
|
131 |
+
|
132 |
+
video_result = generate_combined_video(**video_params)
|
133 |
+
logging.info(f"Video generation result: {video_result}")
|
134 |
+
|
135 |
+
if video_result.status != 'success':
|
136 |
+
raise Exception(f"Video generation failed: {video_result.message}")
|
137 |
+
|
138 |
+
urls = {
|
139 |
+
'video_url': video_result.video_url,
|
140 |
+
'images_url': str([result['url'] for result in image_results.nto_results])
|
141 |
+
}
|
142 |
+
|
143 |
+
logging.info("Completed combined image and video generation process")
|
144 |
+
upload_information_to_new_table(necklace_id=necklace_id,
|
145 |
+
nto_images_urls=[result['url'] for result in image_results.nto_results],
|
146 |
+
cto_images_urls=cto_urls,
|
147 |
+
mto_urls=mto_urls,
|
148 |
+
video_urls=video_result.video_url,
|
149 |
+
model_name=model_name
|
150 |
+
)
|
151 |
+
|
152 |
+
update_processed_necklaces(
|
153 |
+
necklace_id=necklace_id,
|
154 |
+
status=True,
|
155 |
+
model_name=model_name
|
156 |
+
|
157 |
+
)
|
158 |
+
|
159 |
+
|
160 |
+
|
161 |
+
|
162 |
+
|
163 |
+
except Exception as e:
|
164 |
+
|
165 |
+
raise e
|
166 |
+
|
167 |
+
logging.error(f"Error processing {necklace_id}: {str(e)}")
|
168 |
+
|
169 |
+
update_processed_necklaces(
|
170 |
+
necklace_id=necklace_id,
|
171 |
+
status=False,
|
172 |
+
model_name=model_name
|
173 |
+
|
174 |
+
)
|
175 |
+
return {
|
176 |
+
"status": "success",
|
177 |
+
}
|
178 |
+
|
179 |
+
|
180 |
+
except Exception as e:
|
181 |
+
raise e
|
182 |
+
logging.error(f"Fatal error in combined generation process: {str(e)}")
|
183 |
+
return {
|
184 |
+
'status': 'error',
|
185 |
+
'message': str(e)
|
186 |
+
}
|
187 |
+
|
188 |
+
|
189 |
+
if __name__ == "__main__":
|
190 |
+
image_url = "https://lvuhhlrkcuexzqtsbqyu.supabase.co/storage/v1/object/public/JewelmirrorModelImages/p_01.png"
|
191 |
+
storename = "ChamundiJewelsMandir"
|
192 |
+
result = combined_image_and_video_generation(image_url=image_url, storename=storename)
|
193 |
+
print(f"Process completed with status: {result}")
|
static/index.html
ADDED
@@ -0,0 +1,137 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!DOCTYPE html>
|
2 |
+
<html lang="en">
|
3 |
+
<head>
|
4 |
+
<meta charset="UTF-8">
|
5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
6 |
+
<title>Generation</title>
|
7 |
+
<style>
|
8 |
+
body {
|
9 |
+
font-family: Arial, sans-serif;
|
10 |
+
margin: 0;
|
11 |
+
padding: 0;
|
12 |
+
display: flex;
|
13 |
+
flex-direction: column;
|
14 |
+
justify-content: center;
|
15 |
+
align-items: center;
|
16 |
+
background-color: #f0f4f8;
|
17 |
+
}
|
18 |
+
|
19 |
+
/* Navbar */
|
20 |
+
.navbar {
|
21 |
+
background-color: #007bff;
|
22 |
+
color: white;
|
23 |
+
width: 100%;
|
24 |
+
padding: 10px 0;
|
25 |
+
text-align: center;
|
26 |
+
font-size: 18px;
|
27 |
+
font-weight: bold;
|
28 |
+
position: fixed;
|
29 |
+
top: 0;
|
30 |
+
left: 0;
|
31 |
+
z-index: 1000;
|
32 |
+
}
|
33 |
+
|
34 |
+
.navbar a {
|
35 |
+
color: white;
|
36 |
+
text-decoration: none;
|
37 |
+
margin: 0 15px;
|
38 |
+
font-size: 16px;
|
39 |
+
}
|
40 |
+
|
41 |
+
.navbar a:hover {
|
42 |
+
text-decoration: underline;
|
43 |
+
}
|
44 |
+
|
45 |
+
/* Form Container */
|
46 |
+
.form-container {
|
47 |
+
background: white;
|
48 |
+
padding: 20px;
|
49 |
+
border-radius: 10px;
|
50 |
+
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
51 |
+
max-width: 400px;
|
52 |
+
width: 100%;
|
53 |
+
margin-top: 80px;
|
54 |
+
}
|
55 |
+
|
56 |
+
.form-container label {
|
57 |
+
display: block;
|
58 |
+
font-weight: bold;
|
59 |
+
margin-bottom: 5px;
|
60 |
+
}
|
61 |
+
|
62 |
+
.form-container input {
|
63 |
+
width: 100%;
|
64 |
+
padding: 8px;
|
65 |
+
margin-bottom: 15px;
|
66 |
+
border: 1px solid #ccc;
|
67 |
+
border-radius: 5px;
|
68 |
+
}
|
69 |
+
|
70 |
+
.form-container button {
|
71 |
+
width: 100%;
|
72 |
+
padding: 10px;
|
73 |
+
background-color: #007bff;
|
74 |
+
color: white;
|
75 |
+
border: none;
|
76 |
+
border-radius: 5px;
|
77 |
+
font-size: 16px;
|
78 |
+
cursor: pointer;
|
79 |
+
}
|
80 |
+
|
81 |
+
.form-container button:hover {
|
82 |
+
background-color: #0056b3;
|
83 |
+
}
|
84 |
+
</style>
|
85 |
+
</head>
|
86 |
+
<body>
|
87 |
+
|
88 |
+
<!-- Navbar -->
|
89 |
+
<div class="navbar">
|
90 |
+
<a href="index.html">Generation</a>
|
91 |
+
<a href="preview.html">Preview</a>
|
92 |
+
</div>
|
93 |
+
|
94 |
+
<!-- Form for Generation -->
|
95 |
+
<div class="form-container">
|
96 |
+
<h2>Generate Content</h2>
|
97 |
+
<form id="generationForm">
|
98 |
+
<label for="storename">Store Name:</label>
|
99 |
+
<input type="text" id="storename" name="storename" placeholder="Enter store name" required>
|
100 |
+
|
101 |
+
<label for="imageurl">Image URL:</label>
|
102 |
+
<input type="url" id="imageurl" name="imageurl" placeholder="Enter image URL" required>
|
103 |
+
|
104 |
+
<button type="submit">Submit</button>
|
105 |
+
</form>
|
106 |
+
</div>
|
107 |
+
|
108 |
+
<script>
|
109 |
+
document.getElementById('generationForm').addEventListener('submit', async (event) => {
|
110 |
+
event.preventDefault();
|
111 |
+
const storename = document.getElementById('storename').value;
|
112 |
+
const imageurl = document.getElementById('imageurl').value;
|
113 |
+
|
114 |
+
try {
|
115 |
+
const url = `http://0.0.0.0:7860/generate?storename=${encodeURIComponent(storename)}&image_url=${encodeURIComponent(imageurl)}`;
|
116 |
+
|
117 |
+
const response = await fetch(url, {
|
118 |
+
method: 'POST',
|
119 |
+
headers: {
|
120 |
+
'Content-Type': 'application/json',
|
121 |
+
},
|
122 |
+
});
|
123 |
+
|
124 |
+
if (response.ok) {
|
125 |
+
alert('Generation request successful!');
|
126 |
+
} else {
|
127 |
+
alert('Generation failed.');
|
128 |
+
}
|
129 |
+
} catch (error) {
|
130 |
+
alert('Error submitting generation request.');
|
131 |
+
}
|
132 |
+
});
|
133 |
+
</script>
|
134 |
+
|
135 |
+
|
136 |
+
</body>
|
137 |
+
</html>
|
static/preview.html
ADDED
@@ -0,0 +1,415 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!DOCTYPE html>
|
2 |
+
<html lang="en">
|
3 |
+
<head>
|
4 |
+
<meta charset="UTF-8">
|
5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
6 |
+
<title>Preview</title>
|
7 |
+
<style>
|
8 |
+
body {
|
9 |
+
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
10 |
+
margin: 0;
|
11 |
+
padding: 0;
|
12 |
+
background-color: #f8f9fa;
|
13 |
+
min-height: 100vh;
|
14 |
+
}
|
15 |
+
|
16 |
+
.navbar {
|
17 |
+
background-color: #007bff;
|
18 |
+
color: white;
|
19 |
+
width: 100%;
|
20 |
+
padding: 15px 0;
|
21 |
+
text-align: center;
|
22 |
+
font-size: 20px;
|
23 |
+
font-weight: bold;
|
24 |
+
position: fixed;
|
25 |
+
top: 0;
|
26 |
+
left: 0;
|
27 |
+
z-index: 1000;
|
28 |
+
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
29 |
+
}
|
30 |
+
|
31 |
+
.navbar a {
|
32 |
+
color: white;
|
33 |
+
text-decoration: none;
|
34 |
+
margin: 0 15px;
|
35 |
+
font-size: 16px;
|
36 |
+
}
|
37 |
+
|
38 |
+
.navbar a:hover {
|
39 |
+
text-decoration: underline;
|
40 |
+
}
|
41 |
+
|
42 |
+
.form-container {
|
43 |
+
background: white;
|
44 |
+
padding: 30px;
|
45 |
+
border-radius: 12px;
|
46 |
+
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.1);
|
47 |
+
max-width: 450px;
|
48 |
+
width: 90%;
|
49 |
+
margin: 100px auto 50px;
|
50 |
+
text-align: center;
|
51 |
+
}
|
52 |
+
|
53 |
+
.form-group {
|
54 |
+
margin-bottom: 20px;
|
55 |
+
display: flex;
|
56 |
+
gap: 15px;
|
57 |
+
}
|
58 |
+
|
59 |
+
.form-container select {
|
60 |
+
flex: 1;
|
61 |
+
padding: 12px;
|
62 |
+
border: 1px solid #ddd;
|
63 |
+
border-radius: 6px;
|
64 |
+
font-size: 16px;
|
65 |
+
background-color: white;
|
66 |
+
}
|
67 |
+
|
68 |
+
.button-group {
|
69 |
+
display: flex;
|
70 |
+
gap: 10px;
|
71 |
+
justify-content: center;
|
72 |
+
margin-top: 20px;
|
73 |
+
}
|
74 |
+
|
75 |
+
.btn {
|
76 |
+
padding: 12px 24px;
|
77 |
+
border: none;
|
78 |
+
border-radius: 6px;
|
79 |
+
font-size: 16px;
|
80 |
+
cursor: pointer;
|
81 |
+
transition: all 0.3s ease;
|
82 |
+
}
|
83 |
+
|
84 |
+
.btn-primary {
|
85 |
+
background-color: #007bff;
|
86 |
+
color: white;
|
87 |
+
}
|
88 |
+
|
89 |
+
.btn-success {
|
90 |
+
background-color: #28a745;
|
91 |
+
color: white;
|
92 |
+
}
|
93 |
+
|
94 |
+
.btn:hover {
|
95 |
+
transform: translateY(-2px);
|
96 |
+
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
97 |
+
}
|
98 |
+
|
99 |
+
/* Rest of the styles remain the same */
|
100 |
+
.content-grid {
|
101 |
+
max-width: 1200px;
|
102 |
+
margin: 0 auto;
|
103 |
+
padding: 20px;
|
104 |
+
}
|
105 |
+
|
106 |
+
.section {
|
107 |
+
margin-bottom: 40px;
|
108 |
+
}
|
109 |
+
|
110 |
+
.section-title {
|
111 |
+
font-size: 24px;
|
112 |
+
color: #333;
|
113 |
+
margin-bottom: 20px;
|
114 |
+
text-align: center;
|
115 |
+
font-weight: bold;
|
116 |
+
position: relative;
|
117 |
+
padding-bottom: 10px;
|
118 |
+
}
|
119 |
+
|
120 |
+
.section-title:after {
|
121 |
+
content: '';
|
122 |
+
position: absolute;
|
123 |
+
bottom: 0;
|
124 |
+
left: 50%;
|
125 |
+
transform: translateX(-50%);
|
126 |
+
width: 60px;
|
127 |
+
height: 3px;
|
128 |
+
background-color: #007bff;
|
129 |
+
border-radius: 2px;
|
130 |
+
}
|
131 |
+
|
132 |
+
.image-row {
|
133 |
+
display: flex;
|
134 |
+
justify-content: center;
|
135 |
+
gap: 20px;
|
136 |
+
flex-wrap: wrap;
|
137 |
+
margin-bottom: 30px;
|
138 |
+
}
|
139 |
+
|
140 |
+
.image-card {
|
141 |
+
background: white;
|
142 |
+
border-radius: 12px;
|
143 |
+
overflow: hidden;
|
144 |
+
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.1);
|
145 |
+
transition: transform 0.3s ease;
|
146 |
+
width: 300px;
|
147 |
+
height: 300px;
|
148 |
+
}
|
149 |
+
|
150 |
+
.image-card img {
|
151 |
+
width: 100%;
|
152 |
+
height: 100%;
|
153 |
+
object-fit: cover;
|
154 |
+
transition: transform 0.3s ease;
|
155 |
+
}
|
156 |
+
|
157 |
+
.image-card:hover img {
|
158 |
+
transform: scale(1.05);
|
159 |
+
}
|
160 |
+
|
161 |
+
.video-container {
|
162 |
+
width: 100%;
|
163 |
+
max-width: 800px;
|
164 |
+
margin: 0 auto;
|
165 |
+
background: white;
|
166 |
+
border-radius: 12px;
|
167 |
+
overflow: hidden;
|
168 |
+
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.1);
|
169 |
+
}
|
170 |
+
|
171 |
+
.video-container video {
|
172 |
+
width: 100%;
|
173 |
+
display: block;
|
174 |
+
}
|
175 |
+
|
176 |
+
.loading {
|
177 |
+
position: fixed;
|
178 |
+
top: 50%;
|
179 |
+
left: 50%;
|
180 |
+
transform: translate(-50%, -50%);
|
181 |
+
background: rgba(255, 255, 255, 0.9);
|
182 |
+
padding: 20px 40px;
|
183 |
+
border-radius: 8px;
|
184 |
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
185 |
+
z-index: 1000;
|
186 |
+
}
|
187 |
+
|
188 |
+
.success-message {
|
189 |
+
position: fixed;
|
190 |
+
top: 20px;
|
191 |
+
right: 20px;
|
192 |
+
background: #28a745;
|
193 |
+
color: white;
|
194 |
+
padding: 15px 25px;
|
195 |
+
border-radius: 6px;
|
196 |
+
animation: slideIn 0.3s ease-out;
|
197 |
+
z-index: 1001;
|
198 |
+
}
|
199 |
+
|
200 |
+
@keyframes slideIn {
|
201 |
+
from { transform: translateX(100%); }
|
202 |
+
to { transform: translateX(0); }
|
203 |
+
}
|
204 |
+
</style>
|
205 |
+
</head>
|
206 |
+
<body>
|
207 |
+
<div class="navbar">
|
208 |
+
<a href="index.html">Generation</a>
|
209 |
+
<a href="preview.html">Preview</a>
|
210 |
+
</div>
|
211 |
+
|
212 |
+
<div class="form-container">
|
213 |
+
<h2>Preview Content</h2>
|
214 |
+
<div class="form-group">
|
215 |
+
<select id="necklaceSelect">
|
216 |
+
<option value="">Select Necklace ID</option>
|
217 |
+
</select>
|
218 |
+
<select id="modelSelect" disabled>
|
219 |
+
<option value="">Select Model</option>
|
220 |
+
</select>
|
221 |
+
</div>
|
222 |
+
<div class="button-group">
|
223 |
+
<button class="btn btn-primary" id="previewBtn">Preview</button>
|
224 |
+
<button class="btn btn-success" id="approveBtn">Approve</button>
|
225 |
+
</div>
|
226 |
+
</div>
|
227 |
+
|
228 |
+
<div id="contentContainer" class="content-grid"></div>
|
229 |
+
<div id="loading" class="loading" style="display: none;">Loading...</div>
|
230 |
+
|
231 |
+
<script>
|
232 |
+
let necklaceData = {};
|
233 |
+
|
234 |
+
async function fetchNecklaceIds() {
|
235 |
+
try {
|
236 |
+
const response = await fetch('http://0.0.0.0:7860/list_necklace_id');
|
237 |
+
necklaceData = await response.json();
|
238 |
+
const select = document.getElementById('necklaceSelect');
|
239 |
+
|
240 |
+
Object.keys(necklaceData).forEach(id => {
|
241 |
+
const option = document.createElement('option');
|
242 |
+
option.value = id;
|
243 |
+
option.textContent = id;
|
244 |
+
select.appendChild(option);
|
245 |
+
});
|
246 |
+
} catch (error) {
|
247 |
+
console.error('Error fetching necklace IDs:', error);
|
248 |
+
alert('Failed to load necklace IDs');
|
249 |
+
}
|
250 |
+
}
|
251 |
+
|
252 |
+
document.getElementById('necklaceSelect').addEventListener('change', function() {
|
253 |
+
const modelSelect = document.getElementById('modelSelect');
|
254 |
+
modelSelect.innerHTML = '<option value="">Select Model</option>';
|
255 |
+
|
256 |
+
if (this.value) {
|
257 |
+
const models = necklaceData[this.value];
|
258 |
+
models.forEach(model => {
|
259 |
+
const option = document.createElement('option');
|
260 |
+
option.value = model;
|
261 |
+
option.textContent = model;
|
262 |
+
modelSelect.appendChild(option);
|
263 |
+
});
|
264 |
+
modelSelect.disabled = false;
|
265 |
+
} else {
|
266 |
+
modelSelect.disabled = true;
|
267 |
+
}
|
268 |
+
});
|
269 |
+
|
270 |
+
function showLoading(show) {
|
271 |
+
document.getElementById('loading').style.display = show ? 'block' : 'none';
|
272 |
+
}
|
273 |
+
|
274 |
+
function showSuccessMessage(message) {
|
275 |
+
const messageDiv = document.createElement('div');
|
276 |
+
messageDiv.className = 'success-message';
|
277 |
+
messageDiv.textContent = message;
|
278 |
+
document.body.appendChild(messageDiv);
|
279 |
+
setTimeout(() => messageDiv.remove(), 3000);
|
280 |
+
}
|
281 |
+
|
282 |
+
function createSection(title) {
|
283 |
+
const section = document.createElement('div');
|
284 |
+
section.className = 'section';
|
285 |
+
const titleElement = document.createElement('h2');
|
286 |
+
titleElement.className = 'section-title';
|
287 |
+
titleElement.textContent = title;
|
288 |
+
section.appendChild(titleElement);
|
289 |
+
return section;
|
290 |
+
}
|
291 |
+
|
292 |
+
function createImageCard(url) {
|
293 |
+
const card = document.createElement('div');
|
294 |
+
card.className = 'image-card';
|
295 |
+
const img = document.createElement('img');
|
296 |
+
img.src = url;
|
297 |
+
img.alt = 'Preview Image';
|
298 |
+
img.loading = 'lazy';
|
299 |
+
card.appendChild(img);
|
300 |
+
return card;
|
301 |
+
}
|
302 |
+
|
303 |
+
function displayContent(data) {
|
304 |
+
const container = document.getElementById('contentContainer');
|
305 |
+
container.innerHTML = '';
|
306 |
+
|
307 |
+
// NTO Section
|
308 |
+
if (data.nto && data.nto.length > 0) {
|
309 |
+
const ntoSection = createSection('Necklace Try-On (NTO)');
|
310 |
+
const ntoRow = document.createElement('div');
|
311 |
+
ntoRow.className = 'image-row';
|
312 |
+
data.nto.forEach(url => {
|
313 |
+
ntoRow.appendChild(createImageCard(url));
|
314 |
+
});
|
315 |
+
ntoSection.appendChild(ntoRow);
|
316 |
+
container.appendChild(ntoSection);
|
317 |
+
}
|
318 |
+
|
319 |
+
// CTO Section
|
320 |
+
if (data.cto && data.cto.length > 0) {
|
321 |
+
const ctoSection = createSection('Clothing Try-On (CTO)');
|
322 |
+
for (let i = 0; i < data.cto.length; i += 2) {
|
323 |
+
const row = document.createElement('div');
|
324 |
+
row.className = 'image-row';
|
325 |
+
row.appendChild(createImageCard(data.cto[i]));
|
326 |
+
if (data.cto[i + 1]) {
|
327 |
+
row.appendChild(createImageCard(data.cto[i + 1]));
|
328 |
+
}
|
329 |
+
ctoSection.appendChild(row);
|
330 |
+
}
|
331 |
+
container.appendChild(ctoSection);
|
332 |
+
}
|
333 |
+
|
334 |
+
// MTO Section
|
335 |
+
if (data.mto && data.mto.length > 0) {
|
336 |
+
const mtoSection = createSection('Makeup Try-On (MTO)');
|
337 |
+
for (let i = 0; i < data.mto.length; i += 2) {
|
338 |
+
const row = document.createElement('div');
|
339 |
+
row.className = 'image-row';
|
340 |
+
row.appendChild(createImageCard(data.mto[i]));
|
341 |
+
if (data.mto[i + 1]) {
|
342 |
+
row.appendChild(createImageCard(data.mto[i + 1]));
|
343 |
+
}
|
344 |
+
mtoSection.appendChild(row);
|
345 |
+
}
|
346 |
+
container.appendChild(mtoSection);
|
347 |
+
}
|
348 |
+
|
349 |
+
// Video Section
|
350 |
+
if (data.video && data.video.length > 0) {
|
351 |
+
const videoSection = createSection('Video Preview');
|
352 |
+
const videoContainer = document.createElement('div');
|
353 |
+
videoContainer.className = 'video-container';
|
354 |
+
const video = document.createElement('video');
|
355 |
+
video.src = data.video[0];
|
356 |
+
video.controls = true;
|
357 |
+
videoContainer.appendChild(video);
|
358 |
+
videoSection.appendChild(videoContainer);
|
359 |
+
container.appendChild(videoSection);
|
360 |
+
}
|
361 |
+
}
|
362 |
+
|
363 |
+
document.getElementById('previewBtn').addEventListener('click', async () => {
|
364 |
+
const necklaceId = document.getElementById('necklaceSelect').value;
|
365 |
+
const modelName = document.getElementById('modelSelect').value;
|
366 |
+
|
367 |
+
if (!necklaceId || !modelName) {
|
368 |
+
alert('Please select both Necklace ID and Model');
|
369 |
+
return;
|
370 |
+
}
|
371 |
+
|
372 |
+
showLoading(true);
|
373 |
+
try {
|
374 |
+
const response = await fetch(
|
375 |
+
`http://0.0.0.0:7860/image_fetch_product_page?necklace_id=${necklaceId}&model_name=${modelName}`,
|
376 |
+
{ method: 'POST' }
|
377 |
+
);
|
378 |
+
const data = await response.json();
|
379 |
+
displayContent(data);
|
380 |
+
} catch (error) {
|
381 |
+
console.error('Error:', error);
|
382 |
+
alert('Error fetching preview data');
|
383 |
+
} finally {
|
384 |
+
showLoading(false);
|
385 |
+
}
|
386 |
+
});
|
387 |
+
|
388 |
+
document.getElementById('approveBtn').addEventListener('click', async () => {
|
389 |
+
const necklaceId = document.getElementById('necklaceSelect').value;
|
390 |
+
if (!necklaceId) {
|
391 |
+
alert('Please select a necklace ID');
|
392 |
+
return;
|
393 |
+
}
|
394 |
+
|
395 |
+
showLoading(true);
|
396 |
+
try {
|
397 |
+
const response = await fetch(
|
398 |
+
`http://0.0.0.0:7860/approve?necklace_id=${necklaceId}&model_name=${document.getElementById('modelSelect').value}`,
|
399 |
+
{ method: 'POST' }
|
400 |
+
);
|
401 |
+
const data = await response.json();
|
402 |
+
showSuccessMessage('Necklace approved successfully!');
|
403 |
+
} catch (error) {
|
404 |
+
console.error('Error:', error);
|
405 |
+
alert('Error approving necklace');
|
406 |
+
} finally {
|
407 |
+
showLoading(false);
|
408 |
+
}
|
409 |
+
});
|
410 |
+
|
411 |
+
// Initialize page
|
412 |
+
fetchNecklaceIds();
|
413 |
+
</script>
|
414 |
+
</body>
|
415 |
+
</html>
|