ishworrsubedii commited on
Commit
21ba534
·
1 Parent(s): b017f2d

add: prodctpage batch cto, mto, nto, video and static file

Browse files
.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>