auto-save 2026-05-14 06:55 (+1, ~3)
@@ -1,19 +1,5 @@
|
||||
{
|
||||
"entries": [
|
||||
{
|
||||
"files_changed": 2,
|
||||
"hash": "684930d",
|
||||
"message": "auto-save 2026-05-12 18:57 (~2)",
|
||||
"ts": "2026-05-12T18:57:53+08:00",
|
||||
"type": "commit"
|
||||
},
|
||||
{
|
||||
"files_changed": 1,
|
||||
"hash": "50d6390",
|
||||
"message": "auto-save 2026-05-12 19:03 (~1)",
|
||||
"ts": "2026-05-12T19:03:35+08:00",
|
||||
"type": "commit"
|
||||
},
|
||||
{
|
||||
"files_changed": 3,
|
||||
"hash": "67bbdae",
|
||||
@@ -3345,6 +3331,19 @@
|
||||
"type": "session-heartbeat",
|
||||
"message": "Codex 会话活跃 · 最近命令:codex · 1 项未提交变更 · 最近提交:auto-save 2026-05-14 06:44 (~4)",
|
||||
"files_changed": 1
|
||||
},
|
||||
{
|
||||
"ts": "2026-05-14T06:50:10+08:00",
|
||||
"type": "commit",
|
||||
"message": "auto-save 2026-05-14 06:49 (~1)",
|
||||
"hash": "9546d5f",
|
||||
"files_changed": 1
|
||||
},
|
||||
{
|
||||
"ts": "2026-05-13T22:53:14Z",
|
||||
"type": "session-heartbeat",
|
||||
"message": "Claude 会话活跃 · 最近命令:claude · 1 项未提交变更 · 最近提交:auto-save 2026-05-14 06:49 (~1)",
|
||||
"files_changed": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
96
api/main.py
@@ -23,6 +23,10 @@ load_dotenv()
|
||||
JOBS_DIR = Path(os.getenv("JOBS_DIR", "./jobs")).resolve()
|
||||
JOBS_DIR.mkdir(parents=True, exist_ok=True)
|
||||
CORS_ORIGINS = [o.strip() for o in os.getenv("CORS_ORIGINS", "http://localhost:4290").split(",") if o.strip()]
|
||||
PRODUCT_LIBRARY_DIR = Path(
|
||||
os.getenv("PRODUCT_LIBRARY_DIR", Path(__file__).resolve().parent / "product_library" / "skg-products")
|
||||
).resolve()
|
||||
PRODUCT_LIBRARY_MANIFEST = PRODUCT_LIBRARY_DIR / "manifest.json"
|
||||
|
||||
LLM_BASE_URL = os.getenv("LLM_BASE_URL", "").strip()
|
||||
LLM_API_KEY = os.getenv("LLM_API_KEY", "").strip()
|
||||
@@ -214,6 +218,24 @@ class SubjectAsset(BaseModel):
|
||||
created_at: float = 0.0
|
||||
|
||||
|
||||
class ProductLibraryItem(BaseModel):
|
||||
id: str
|
||||
handle: str
|
||||
title: str
|
||||
product_type: str = ""
|
||||
image_type: str = "gallery"
|
||||
image_index: int = 0
|
||||
filename: str
|
||||
url: str
|
||||
width: int = 0
|
||||
height: int = 0
|
||||
source_path: str = ""
|
||||
white_score: float = 0.0
|
||||
near_white_score: float = 0.0
|
||||
has_people: bool = False
|
||||
tags: list[str] = Field(default_factory=list)
|
||||
|
||||
|
||||
class KeyElement(BaseModel):
|
||||
"""关键帧里识别 / 用户提取的元素 · 多次提取累积多张图,让用户挑选满意的"""
|
||||
id: str # uuid hex 8
|
||||
@@ -368,6 +390,35 @@ def storyboard_ref_path(job_id: str, ref: dict | None) -> Path | None:
|
||||
return None
|
||||
|
||||
|
||||
def load_product_library_items() -> list[ProductLibraryItem]:
|
||||
if not PRODUCT_LIBRARY_MANIFEST.exists():
|
||||
return []
|
||||
try:
|
||||
data = json.loads(PRODUCT_LIBRARY_MANIFEST.read_text(encoding="utf-8"))
|
||||
return [ProductLibraryItem(**item) for item in data.get("items", [])]
|
||||
except Exception as e:
|
||||
raise HTTPException(500, f"product library manifest invalid: {e}")
|
||||
|
||||
|
||||
def find_product_library_item(product_id: str) -> ProductLibraryItem:
|
||||
product_id = product_id.strip()
|
||||
for item in load_product_library_items():
|
||||
if item.id == product_id:
|
||||
return item
|
||||
raise HTTPException(404, "product library item not found")
|
||||
|
||||
|
||||
def product_library_file(item: ProductLibraryItem) -> Path:
|
||||
p = (PRODUCT_LIBRARY_DIR / item.filename).resolve()
|
||||
try:
|
||||
p.relative_to(PRODUCT_LIBRARY_DIR)
|
||||
except ValueError:
|
||||
raise HTTPException(400, "invalid product library path")
|
||||
if not p.exists():
|
||||
raise HTTPException(404, "product library image missing")
|
||||
return p
|
||||
|
||||
|
||||
def storyboard_ref_url(job_id: str, ref: dict | None) -> str:
|
||||
if not ref:
|
||||
return ""
|
||||
@@ -2783,6 +2834,25 @@ def get_storyboard_video(job_id: str, video_id: str):
|
||||
return FileResponse(p, media_type="video/mp4")
|
||||
|
||||
|
||||
class CopyProductLibraryAssetReq(BaseModel):
|
||||
product_id: str
|
||||
|
||||
|
||||
@app.get("/product-library/skg", response_model=list[ProductLibraryItem])
|
||||
def list_skg_product_library() -> list[ProductLibraryItem]:
|
||||
"""内置 SKG 白底产品图库。来源是本地筛选后的产品图 manifest。"""
|
||||
return load_product_library_items()
|
||||
|
||||
|
||||
@app.get("/product-library/skg/images/{filename}")
|
||||
def get_skg_product_library_image(filename: str):
|
||||
items = load_product_library_items()
|
||||
item = next((x for x in items if Path(x.filename).name == filename), None)
|
||||
if not item:
|
||||
raise HTTPException(404, "product library image not found")
|
||||
return FileResponse(product_library_file(item), media_type="image/jpeg")
|
||||
|
||||
|
||||
@app.post("/jobs/{job_id}/assets")
|
||||
async def upload_storyboard_asset(job_id: str, file: UploadFile = File(...)) -> dict:
|
||||
if job_id not in JOBS:
|
||||
@@ -2813,6 +2883,32 @@ async def upload_storyboard_asset(job_id: str, file: UploadFile = File(...)) ->
|
||||
}
|
||||
|
||||
|
||||
@app.post("/jobs/{job_id}/assets/product-library")
|
||||
def copy_product_library_asset(job_id: str, req: CopyProductLibraryAssetReq) -> dict:
|
||||
if job_id not in JOBS:
|
||||
raise HTTPException(404, "job not found")
|
||||
item = find_product_library_item(req.product_id)
|
||||
src = product_library_file(item)
|
||||
asset_id = uuid.uuid4().hex[:12]
|
||||
out_dir = job_dir(job_id) / "assets"
|
||||
out_dir.mkdir(parents=True, exist_ok=True)
|
||||
out = out_dir / f"{asset_id}.jpg"
|
||||
try:
|
||||
img = Image.open(src).convert("RGB")
|
||||
img.thumbnail((1600, 1600), Image.Resampling.LANCZOS)
|
||||
img.save(out, "JPEG", quality=94)
|
||||
except Exception as e:
|
||||
raise HTTPException(400, f"product library copy failed: {e}")
|
||||
label = f"产品融合 · {item.title} #{item.image_index}"
|
||||
return {
|
||||
"kind": "asset",
|
||||
"frame_idx": -1,
|
||||
"element_id": asset_id,
|
||||
"cutout_id": asset_id,
|
||||
"label": label,
|
||||
}
|
||||
|
||||
|
||||
@app.get("/jobs/{job_id}/assets/{asset_id}.jpg")
|
||||
def get_storyboard_asset(job_id: str, asset_id: str):
|
||||
p = job_dir(job_id) / "assets" / f"{asset_id}.jpg"
|
||||
|
||||
|
After Width: | Height: | Size: 373 KiB |
|
After Width: | Height: | Size: 326 KiB |
|
After Width: | Height: | Size: 52 KiB |
|
After Width: | Height: | Size: 316 KiB |
|
After Width: | Height: | Size: 127 KiB |
|
After Width: | Height: | Size: 94 KiB |
|
After Width: | Height: | Size: 74 KiB |
|
After Width: | Height: | Size: 294 KiB |
BIN
api/product_library/skg-products/images/h7-ultra-05.jpg
Normal file
|
After Width: | Height: | Size: 106 KiB |
BIN
api/product_library/skg-products/images/h7-ultra-11.jpg
Normal file
|
After Width: | Height: | Size: 148 KiB |
|
After Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 428 KiB |
|
After Width: | Height: | Size: 414 KiB |
|
After Width: | Height: | Size: 243 KiB |
|
After Width: | Height: | Size: 431 KiB |
|
After Width: | Height: | Size: 362 KiB |
|
After Width: | Height: | Size: 234 KiB |
|
After Width: | Height: | Size: 300 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 33 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 260 KiB |
|
After Width: | Height: | Size: 231 KiB |
|
After Width: | Height: | Size: 108 KiB |
|
After Width: | Height: | Size: 181 KiB |
|
After Width: | Height: | Size: 87 KiB |
|
After Width: | Height: | Size: 184 KiB |
|
After Width: | Height: | Size: 183 KiB |
|
After Width: | Height: | Size: 154 KiB |
|
After Width: | Height: | Size: 126 KiB |
|
After Width: | Height: | Size: 496 KiB |
|
After Width: | Height: | Size: 435 KiB |
|
After Width: | Height: | Size: 190 KiB |
|
After Width: | Height: | Size: 114 KiB |
|
After Width: | Height: | Size: 269 KiB |
|
After Width: | Height: | Size: 319 KiB |
|
After Width: | Height: | Size: 376 KiB |
|
After Width: | Height: | Size: 269 KiB |
|
After Width: | Height: | Size: 514 KiB |
|
After Width: | Height: | Size: 318 KiB |
866
api/product_library/skg-products/manifest.json
Normal file
@@ -0,0 +1,866 @@
|
||||
{
|
||||
"source": "/Users/kangwan/Desktop/skg/skg_product_downloads",
|
||||
"filter": "all_products/gallery only; border white_score >= 0.78 or white_score >= 0.62 and near_white_score >= 0.90",
|
||||
"count": 41,
|
||||
"items": [
|
||||
{
|
||||
"id": "g7-pro-fold-neck-massager-01",
|
||||
"handle": "g7-pro-fold-neck-massager",
|
||||
"title": "SKG G7 PRO-FOLD Foldable Neck Massager with Gentle Warm Function",
|
||||
"product_type": "Neck Massager",
|
||||
"image_type": "gallery",
|
||||
"image_index": 1,
|
||||
"filename": "images/g7-pro-fold-neck-massager-01.jpg",
|
||||
"url": "/product-library/skg/images/g7-pro-fold-neck-massager-01.jpg",
|
||||
"width": 1800,
|
||||
"height": 1800,
|
||||
"source_path": "all_products/g7-pro-fold-neck-massager/gallery/01-11-3.jpg",
|
||||
"white_score": 0.9762,
|
||||
"near_white_score": 0.9803,
|
||||
"has_people": false,
|
||||
"tags": [
|
||||
"white-bg",
|
||||
"gallery",
|
||||
"Neck Massager"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "g7-pro-fold-neck-massager-05",
|
||||
"handle": "g7-pro-fold-neck-massager",
|
||||
"title": "SKG G7 PRO-FOLD Foldable Neck Massager with Gentle Warm Function",
|
||||
"product_type": "Neck Massager",
|
||||
"image_type": "gallery",
|
||||
"image_index": 5,
|
||||
"filename": "images/g7-pro-fold-neck-massager-05.jpg",
|
||||
"url": "/product-library/skg/images/g7-pro-fold-neck-massager-05.jpg",
|
||||
"width": 1800,
|
||||
"height": 1800,
|
||||
"source_path": "all_products/g7-pro-fold-neck-massager/gallery/05-11-96a45541-70fa-443a-9805-61ccca6b30ca.jpg",
|
||||
"white_score": 0.9753,
|
||||
"near_white_score": 0.9794,
|
||||
"has_people": false,
|
||||
"tags": [
|
||||
"white-bg",
|
||||
"gallery",
|
||||
"Neck Massager"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "g7-pro-neck-massager-01",
|
||||
"handle": "g7-pro-neck-massager",
|
||||
"title": "SKG G7 Pro Smart Neck Massager with Heat",
|
||||
"product_type": "Neck Massager",
|
||||
"image_type": "gallery",
|
||||
"image_index": 1,
|
||||
"filename": "images/g7-pro-neck-massager-01.jpg",
|
||||
"url": "/product-library/skg/images/g7-pro-neck-massager-01.jpg",
|
||||
"width": 1000,
|
||||
"height": 1000,
|
||||
"source_path": "all_products/g7-pro-neck-massager/gallery/01-skg-g7-pro-smart-neck-massager-with-heat-651568.jpg",
|
||||
"white_score": 1.0,
|
||||
"near_white_score": 1.0,
|
||||
"has_people": false,
|
||||
"tags": [
|
||||
"white-bg",
|
||||
"gallery",
|
||||
"Neck Massager"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "g7-pro-neck-massager-02",
|
||||
"handle": "g7-pro-neck-massager",
|
||||
"title": "SKG G7 Pro Smart Neck Massager with Heat",
|
||||
"product_type": "Neck Massager",
|
||||
"image_type": "gallery",
|
||||
"image_index": 2,
|
||||
"filename": "images/g7-pro-neck-massager-02.jpg",
|
||||
"url": "/product-library/skg/images/g7-pro-neck-massager-02.jpg",
|
||||
"width": 1800,
|
||||
"height": 1800,
|
||||
"source_path": "all_products/g7-pro-neck-massager/gallery/02-skg-g7-pro-smart-neck-massager-with-heat-848777.jpg",
|
||||
"white_score": 1.0,
|
||||
"near_white_score": 1.0,
|
||||
"has_people": false,
|
||||
"tags": [
|
||||
"white-bg",
|
||||
"gallery",
|
||||
"Neck Massager"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "g7-pro-neck-massager-04",
|
||||
"handle": "g7-pro-neck-massager",
|
||||
"title": "SKG G7 Pro Smart Neck Massager with Heat",
|
||||
"product_type": "Neck Massager",
|
||||
"image_type": "gallery",
|
||||
"image_index": 4,
|
||||
"filename": "images/g7-pro-neck-massager-04.jpg",
|
||||
"url": "/product-library/skg/images/g7-pro-neck-massager-04.jpg",
|
||||
"width": 1001,
|
||||
"height": 1001,
|
||||
"source_path": "all_products/g7-pro-neck-massager/gallery/04-skg-g7-pro-smart-neck-massager-with-heat-358760.jpg",
|
||||
"white_score": 0.9137,
|
||||
"near_white_score": 0.9317,
|
||||
"has_people": false,
|
||||
"tags": [
|
||||
"white-bg",
|
||||
"gallery",
|
||||
"Neck Massager"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "g7-pro-neck-massager-07",
|
||||
"handle": "g7-pro-neck-massager",
|
||||
"title": "SKG G7 Pro Smart Neck Massager with Heat",
|
||||
"product_type": "Neck Massager",
|
||||
"image_type": "gallery",
|
||||
"image_index": 7,
|
||||
"filename": "images/g7-pro-neck-massager-07.jpg",
|
||||
"url": "/product-library/skg/images/g7-pro-neck-massager-07.jpg",
|
||||
"width": 1001,
|
||||
"height": 1001,
|
||||
"source_path": "all_products/g7-pro-neck-massager/gallery/07-e-2022-g7-pro-g7-pro-d94b1b10-f94c-4618-8ec8-9a89ce1830bb.jpg",
|
||||
"white_score": 0.8281,
|
||||
"near_white_score": 0.8553,
|
||||
"has_people": false,
|
||||
"tags": [
|
||||
"white-bg",
|
||||
"gallery",
|
||||
"Neck Massager"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "g7-pro-neck-massager-11",
|
||||
"handle": "g7-pro-neck-massager",
|
||||
"title": "SKG G7 Pro Smart Neck Massager with Heat",
|
||||
"product_type": "Neck Massager",
|
||||
"image_type": "gallery",
|
||||
"image_index": 11,
|
||||
"filename": "images/g7-pro-neck-massager-11.jpg",
|
||||
"url": "/product-library/skg/images/g7-pro-neck-massager-11.jpg",
|
||||
"width": 1001,
|
||||
"height": 1001,
|
||||
"source_path": "all_products/g7-pro-neck-massager/gallery/11-cb79b725-a90c-439b-9256-0b8915910168-f093e3a1d3efa18c7ca3196b97a05aa7.jpg",
|
||||
"white_score": 0.9942,
|
||||
"near_white_score": 0.9984,
|
||||
"has_people": false,
|
||||
"tags": [
|
||||
"white-bg",
|
||||
"gallery",
|
||||
"Neck Massager"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "g7-pro-neck-massager-12",
|
||||
"handle": "g7-pro-neck-massager",
|
||||
"title": "SKG G7 Pro Smart Neck Massager with Heat",
|
||||
"product_type": "Neck Massager",
|
||||
"image_type": "gallery",
|
||||
"image_index": 12,
|
||||
"filename": "images/g7-pro-neck-massager-12.jpg",
|
||||
"url": "/product-library/skg/images/g7-pro-neck-massager-12.jpg",
|
||||
"width": 1800,
|
||||
"height": 1800,
|
||||
"source_path": "all_products/g7-pro-neck-massager/gallery/12-skg-g7-pro-smart-neck-massager-with-heat-116880.jpg",
|
||||
"white_score": 1.0,
|
||||
"near_white_score": 1.0,
|
||||
"has_people": false,
|
||||
"tags": [
|
||||
"white-bg",
|
||||
"gallery",
|
||||
"Neck Massager"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "h7-ultra-05",
|
||||
"handle": "h7-ultra",
|
||||
"title": "SKG H7 Ultra Neck and Shoulder Massager",
|
||||
"product_type": "Neck Massager",
|
||||
"image_type": "gallery",
|
||||
"image_index": 5,
|
||||
"filename": "images/h7-ultra-05.jpg",
|
||||
"url": "/product-library/skg/images/h7-ultra-05.jpg",
|
||||
"width": 1125,
|
||||
"height": 1500,
|
||||
"source_path": "all_products/h7-ultra/gallery/05-61p7odpmgol-ac-sl1500.jpg",
|
||||
"white_score": 0.9046,
|
||||
"near_white_score": 0.9507,
|
||||
"has_people": false,
|
||||
"tags": [
|
||||
"white-bg",
|
||||
"gallery",
|
||||
"Neck Massager"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "h7-ultra-11",
|
||||
"handle": "h7-ultra",
|
||||
"title": "SKG H7 Ultra Neck and Shoulder Massager",
|
||||
"product_type": "Neck Massager",
|
||||
"image_type": "gallery",
|
||||
"image_index": 11,
|
||||
"filename": "images/h7-ultra-11.jpg",
|
||||
"url": "/product-library/skg/images/h7-ultra-11.jpg",
|
||||
"width": 1125,
|
||||
"height": 1500,
|
||||
"source_path": "all_products/h7-ultra/gallery/11-71py-ebgmjl-ac-sl1500.jpg",
|
||||
"white_score": 0.8133,
|
||||
"near_white_score": 0.8701,
|
||||
"has_people": false,
|
||||
"tags": [
|
||||
"white-bg",
|
||||
"gallery",
|
||||
"Neck Massager"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "k5-pro-neck-massager-02",
|
||||
"handle": "k5-pro-neck-massager",
|
||||
"title": "SKG K5-3 Pro Intelligent Neck Massager with Heat",
|
||||
"product_type": "Neck Massager",
|
||||
"image_type": "gallery",
|
||||
"image_index": 2,
|
||||
"filename": "images/k5-pro-neck-massager-02.jpg",
|
||||
"url": "/product-library/skg/images/k5-pro-neck-massager-02.jpg",
|
||||
"width": 1000,
|
||||
"height": 1000,
|
||||
"source_path": "all_products/k5-pro-neck-massager/gallery/02-skg-k5-pro-intelligent-neck-massager-with-heat-452498.jpg",
|
||||
"white_score": 1.0,
|
||||
"near_white_score": 1.0,
|
||||
"has_people": false,
|
||||
"tags": [
|
||||
"white-bg",
|
||||
"gallery",
|
||||
"Neck Massager"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "skg-flex-massager-h7-ultra-e-01",
|
||||
"handle": "skg-flex-massager-h7-ultra-e",
|
||||
"title": "SKG H7 Ultra E Neck and Shoulder Flex Massager",
|
||||
"product_type": "Neck Massager",
|
||||
"image_type": "gallery",
|
||||
"image_index": 1,
|
||||
"filename": "images/skg-flex-massager-h7-ultra-e-01.jpg",
|
||||
"url": "/product-library/skg/images/skg-flex-massager-h7-ultra-e-01.jpg",
|
||||
"width": 1800,
|
||||
"height": 1800,
|
||||
"source_path": "all_products/skg-flex-massager-h7-ultra-e/gallery/01-image-1-536314fe-3ebc-414d-a03c-6e32d077f608.jpg",
|
||||
"white_score": 0.8289,
|
||||
"near_white_score": 0.838,
|
||||
"has_people": false,
|
||||
"tags": [
|
||||
"white-bg",
|
||||
"gallery",
|
||||
"Neck Massager"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "skg-flex-massager-h7-ultra-e-06",
|
||||
"handle": "skg-flex-massager-h7-ultra-e",
|
||||
"title": "SKG H7 Ultra E Neck and Shoulder Flex Massager",
|
||||
"product_type": "Neck Massager",
|
||||
"image_type": "gallery",
|
||||
"image_index": 6,
|
||||
"filename": "images/skg-flex-massager-h7-ultra-e-06.jpg",
|
||||
"url": "/product-library/skg/images/skg-flex-massager-h7-ultra-e-06.jpg",
|
||||
"width": 1800,
|
||||
"height": 1800,
|
||||
"source_path": "all_products/skg-flex-massager-h7-ultra-e/gallery/06-20250320-101625.jpg",
|
||||
"white_score": 0.7952,
|
||||
"near_white_score": 0.8553,
|
||||
"has_people": false,
|
||||
"tags": [
|
||||
"white-bg",
|
||||
"gallery",
|
||||
"Neck Massager"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "skg-g7-pro-fold-golden-neck-massager-02",
|
||||
"handle": "skg-g7-pro-fold-golden-neck-massager",
|
||||
"title": "SKG G7 Pro Fold-Golden Neck Massager",
|
||||
"product_type": "Neck Massager",
|
||||
"image_type": "gallery",
|
||||
"image_index": 2,
|
||||
"filename": "images/skg-g7-pro-fold-golden-neck-massager-02.jpg",
|
||||
"url": "/product-library/skg/images/skg-g7-pro-fold-golden-neck-massager-02.jpg",
|
||||
"width": 1800,
|
||||
"height": 1800,
|
||||
"source_path": "all_products/skg-g7-pro-fold-golden-neck-massager/gallery/02-2-98b3fae1-5c0e-47d0-8b9a-c5153ff83c3a.png",
|
||||
"white_score": 0.9836,
|
||||
"near_white_score": 0.9885,
|
||||
"has_people": false,
|
||||
"tags": [
|
||||
"white-bg",
|
||||
"gallery",
|
||||
"Neck Massager"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "skg-g7-pro-fold-golden-neck-massager-03",
|
||||
"handle": "skg-g7-pro-fold-golden-neck-massager",
|
||||
"title": "SKG G7 Pro Fold-Golden Neck Massager",
|
||||
"product_type": "Neck Massager",
|
||||
"image_type": "gallery",
|
||||
"image_index": 3,
|
||||
"filename": "images/skg-g7-pro-fold-golden-neck-massager-03.jpg",
|
||||
"url": "/product-library/skg/images/skg-g7-pro-fold-golden-neck-massager-03.jpg",
|
||||
"width": 1800,
|
||||
"height": 1800,
|
||||
"source_path": "all_products/skg-g7-pro-fold-golden-neck-massager/gallery/03-1-35d3f741-a3a5-4893-b076-f86b4cad8edc.png",
|
||||
"white_score": 0.8898,
|
||||
"near_white_score": 0.9005,
|
||||
"has_people": false,
|
||||
"tags": [
|
||||
"white-bg",
|
||||
"gallery",
|
||||
"Neck Massager"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "skg-h5-mini-cordless-shiatsu-neck-and-shoulder-massager-with-heat-01",
|
||||
"handle": "skg-h5-mini-cordless-shiatsu-neck-and-shoulder-massager-with-heat",
|
||||
"title": "SKG H5 Mini Cordless Shiatsu Neck and Shoulder Massager with Heat",
|
||||
"product_type": "Neck Massager",
|
||||
"image_type": "gallery",
|
||||
"image_index": 1,
|
||||
"filename": "images/skg-h5-mini-cordless-shiatsu-neck-and-shoulder-massager-with-heat-01.jpg",
|
||||
"url": "/product-library/skg/images/skg-h5-mini-cordless-shiatsu-neck-and-shoulder-massager-with-heat-01.jpg",
|
||||
"width": 1800,
|
||||
"height": 1800,
|
||||
"source_path": "all_products/skg-h5-mini-cordless-shiatsu-neck-and-shoulder-massager-with-heat/gallery/01-1-8e35c077-3381-444f-9daa-b88610f6402d.jpg",
|
||||
"white_score": 0.9013,
|
||||
"near_white_score": 0.9112,
|
||||
"has_people": false,
|
||||
"tags": [
|
||||
"white-bg",
|
||||
"gallery",
|
||||
"Neck Massager"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "skg-h5-mini-cordless-shiatsu-neck-and-shoulder-massager-with-heat-08",
|
||||
"handle": "skg-h5-mini-cordless-shiatsu-neck-and-shoulder-massager-with-heat",
|
||||
"title": "SKG H5 Mini Cordless Shiatsu Neck and Shoulder Massager with Heat",
|
||||
"product_type": "Neck Massager",
|
||||
"image_type": "gallery",
|
||||
"image_index": 8,
|
||||
"filename": "images/skg-h5-mini-cordless-shiatsu-neck-and-shoulder-massager-with-heat-08.jpg",
|
||||
"url": "/product-library/skg/images/skg-h5-mini-cordless-shiatsu-neck-and-shoulder-massager-with-heat-08.jpg",
|
||||
"width": 1350,
|
||||
"height": 1800,
|
||||
"source_path": "all_products/skg-h5-mini-cordless-shiatsu-neck-and-shoulder-massager-with-heat/gallery/08-6-97e250be-47a6-494b-a4aa-58fb5bd9411e.jpg",
|
||||
"white_score": 0.6735,
|
||||
"near_white_score": 0.9942,
|
||||
"has_people": false,
|
||||
"tags": [
|
||||
"white-bg",
|
||||
"gallery",
|
||||
"Neck Massager"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "skg-h5-mini-cordless-shiatsu-neck-and-shoulder-massager-with-heat-09",
|
||||
"handle": "skg-h5-mini-cordless-shiatsu-neck-and-shoulder-massager-with-heat",
|
||||
"title": "SKG H5 Mini Cordless Shiatsu Neck and Shoulder Massager with Heat",
|
||||
"product_type": "Neck Massager",
|
||||
"image_type": "gallery",
|
||||
"image_index": 9,
|
||||
"filename": "images/skg-h5-mini-cordless-shiatsu-neck-and-shoulder-massager-with-heat-09.jpg",
|
||||
"url": "/product-library/skg/images/skg-h5-mini-cordless-shiatsu-neck-and-shoulder-massager-with-heat-09.jpg",
|
||||
"width": 1350,
|
||||
"height": 1800,
|
||||
"source_path": "all_products/skg-h5-mini-cordless-shiatsu-neck-and-shoulder-massager-with-heat/gallery/09-7-a1c3f484-43ec-4622-9667-2ace292c3d6a.jpg",
|
||||
"white_score": 0.7911,
|
||||
"near_white_score": 0.8684,
|
||||
"has_people": false,
|
||||
"tags": [
|
||||
"white-bg",
|
||||
"gallery",
|
||||
"Neck Massager"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "skg-knee-massager-ws500-01",
|
||||
"handle": "skg-knee-massager-ws500",
|
||||
"title": "SKG WS500 Knee Massager",
|
||||
"product_type": "Knee Massager",
|
||||
"image_type": "gallery",
|
||||
"image_index": 1,
|
||||
"filename": "images/skg-knee-massager-ws500-01.jpg",
|
||||
"url": "/product-library/skg/images/skg-knee-massager-ws500-01.jpg",
|
||||
"width": 500,
|
||||
"height": 500,
|
||||
"source_path": "all_products/skg-knee-massager-ws500/gallery/01-2-3343c351-0c0b-47d8-b8c6-3037e2871335.jpg",
|
||||
"white_score": 0.9589,
|
||||
"near_white_score": 0.9663,
|
||||
"has_people": false,
|
||||
"tags": [
|
||||
"white-bg",
|
||||
"gallery",
|
||||
"Knee Massager"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "skg-knee-massager-ws500-03",
|
||||
"handle": "skg-knee-massager-ws500",
|
||||
"title": "SKG WS500 Knee Massager",
|
||||
"product_type": "Knee Massager",
|
||||
"image_type": "gallery",
|
||||
"image_index": 3,
|
||||
"filename": "images/skg-knee-massager-ws500-03.jpg",
|
||||
"url": "/product-library/skg/images/skg-knee-massager-ws500-03.jpg",
|
||||
"width": 500,
|
||||
"height": 500,
|
||||
"source_path": "all_products/skg-knee-massager-ws500/gallery/03-4-ff0fbc01-a3ae-4c61-82ba-005c161151b9.jpg",
|
||||
"white_score": 0.9753,
|
||||
"near_white_score": 0.9794,
|
||||
"has_people": false,
|
||||
"tags": [
|
||||
"white-bg",
|
||||
"gallery",
|
||||
"Knee Massager"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "skg-knee-massager-ws500-04",
|
||||
"handle": "skg-knee-massager-ws500",
|
||||
"title": "SKG WS500 Knee Massager",
|
||||
"product_type": "Knee Massager",
|
||||
"image_type": "gallery",
|
||||
"image_index": 4,
|
||||
"filename": "images/skg-knee-massager-ws500-04.jpg",
|
||||
"url": "/product-library/skg/images/skg-knee-massager-ws500-04.jpg",
|
||||
"width": 500,
|
||||
"height": 500,
|
||||
"source_path": "all_products/skg-knee-massager-ws500/gallery/04-3-570f8251-7f7e-45e0-be04-81b6aceff8a5.jpg",
|
||||
"white_score": 0.9605,
|
||||
"near_white_score": 0.9696,
|
||||
"has_people": false,
|
||||
"tags": [
|
||||
"white-bg",
|
||||
"gallery",
|
||||
"Knee Massager"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "skg-knee-massager-ws500-05",
|
||||
"handle": "skg-knee-massager-ws500",
|
||||
"title": "SKG WS500 Knee Massager",
|
||||
"product_type": "Knee Massager",
|
||||
"image_type": "gallery",
|
||||
"image_index": 5,
|
||||
"filename": "images/skg-knee-massager-ws500-05.jpg",
|
||||
"url": "/product-library/skg/images/skg-knee-massager-ws500-05.jpg",
|
||||
"width": 500,
|
||||
"height": 500,
|
||||
"source_path": "all_products/skg-knee-massager-ws500/gallery/05-6-452f832b-ea1d-4137-ba79-b99cebb93634.jpg",
|
||||
"white_score": 0.9975,
|
||||
"near_white_score": 0.9992,
|
||||
"has_people": false,
|
||||
"tags": [
|
||||
"white-bg",
|
||||
"gallery",
|
||||
"Knee Massager"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "skg-neck-and-shoulder-massager-hs500-2-01",
|
||||
"handle": "skg-neck-and-shoulder-massager-hs500-2",
|
||||
"title": "SKG HS500-2 Neck and Shoulder Massager",
|
||||
"product_type": "Neck Massager",
|
||||
"image_type": "gallery",
|
||||
"image_index": 1,
|
||||
"filename": "images/skg-neck-and-shoulder-massager-hs500-2-01.jpg",
|
||||
"url": "/product-library/skg/images/skg-neck-and-shoulder-massager-hs500-2-01.jpg",
|
||||
"width": 1800,
|
||||
"height": 1800,
|
||||
"source_path": "all_products/skg-neck-and-shoulder-massager-hs500-2/gallery/01-1-2-c80c3785-9064-4506-b8c2-0ad1d8217cb4.jpg",
|
||||
"white_score": 0.889,
|
||||
"near_white_score": 0.9013,
|
||||
"has_people": false,
|
||||
"tags": [
|
||||
"white-bg",
|
||||
"gallery",
|
||||
"Neck Massager"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "skg-neck-and-shoulder-massager-hs500-2-06",
|
||||
"handle": "skg-neck-and-shoulder-massager-hs500-2",
|
||||
"title": "SKG HS500-2 Neck and Shoulder Massager",
|
||||
"product_type": "Neck Massager",
|
||||
"image_type": "gallery",
|
||||
"image_index": 6,
|
||||
"filename": "images/skg-neck-and-shoulder-massager-hs500-2-06.jpg",
|
||||
"url": "/product-library/skg/images/skg-neck-and-shoulder-massager-hs500-2-06.jpg",
|
||||
"width": 1800,
|
||||
"height": 1800,
|
||||
"source_path": "all_products/skg-neck-and-shoulder-massager-hs500-2/gallery/06-6-35028d3f-bea8-4929-a1e5-dafdd88dba05.jpg",
|
||||
"white_score": 0.8413,
|
||||
"near_white_score": 0.9416,
|
||||
"has_people": false,
|
||||
"tags": [
|
||||
"white-bg",
|
||||
"gallery",
|
||||
"Neck Massager"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "skg-neck-and-shoulder-massager-hs500-2-10",
|
||||
"handle": "skg-neck-and-shoulder-massager-hs500-2",
|
||||
"title": "SKG HS500-2 Neck and Shoulder Massager",
|
||||
"product_type": "Neck Massager",
|
||||
"image_type": "gallery",
|
||||
"image_index": 10,
|
||||
"filename": "images/skg-neck-and-shoulder-massager-hs500-2-10.jpg",
|
||||
"url": "/product-library/skg/images/skg-neck-and-shoulder-massager-hs500-2-10.jpg",
|
||||
"width": 1800,
|
||||
"height": 1800,
|
||||
"source_path": "all_products/skg-neck-and-shoulder-massager-hs500-2/gallery/10-3-f1a09094-f423-427d-bb66-91dbd14b3702.jpg",
|
||||
"white_score": 0.9786,
|
||||
"near_white_score": 0.9868,
|
||||
"has_people": false,
|
||||
"tags": [
|
||||
"white-bg",
|
||||
"gallery",
|
||||
"Neck Massager"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "skg-neck-and-shoulder-massager-hs500-2-11",
|
||||
"handle": "skg-neck-and-shoulder-massager-hs500-2",
|
||||
"title": "SKG HS500-2 Neck and Shoulder Massager",
|
||||
"product_type": "Neck Massager",
|
||||
"image_type": "gallery",
|
||||
"image_index": 11,
|
||||
"filename": "images/skg-neck-and-shoulder-massager-hs500-2-11.jpg",
|
||||
"url": "/product-library/skg/images/skg-neck-and-shoulder-massager-hs500-2-11.jpg",
|
||||
"width": 1800,
|
||||
"height": 1800,
|
||||
"source_path": "all_products/skg-neck-and-shoulder-massager-hs500-2/gallery/11-2-d0dac807-5777-4001-a7fd-1aa92742d17b.jpg",
|
||||
"white_score": 0.9021,
|
||||
"near_white_score": 0.9095,
|
||||
"has_people": false,
|
||||
"tags": [
|
||||
"white-bg",
|
||||
"gallery",
|
||||
"Neck Massager"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "skg-neck-and-shoulder-massager-hs500-2-12",
|
||||
"handle": "skg-neck-and-shoulder-massager-hs500-2",
|
||||
"title": "SKG HS500-2 Neck and Shoulder Massager",
|
||||
"product_type": "Neck Massager",
|
||||
"image_type": "gallery",
|
||||
"image_index": 12,
|
||||
"filename": "images/skg-neck-and-shoulder-massager-hs500-2-12.jpg",
|
||||
"url": "/product-library/skg/images/skg-neck-and-shoulder-massager-hs500-2-12.jpg",
|
||||
"width": 1800,
|
||||
"height": 1800,
|
||||
"source_path": "all_products/skg-neck-and-shoulder-massager-hs500-2/gallery/12-5-6454717b-4570-48d5-9596-378a278cb763.jpg",
|
||||
"white_score": 0.9803,
|
||||
"near_white_score": 0.986,
|
||||
"has_people": false,
|
||||
"tags": [
|
||||
"white-bg",
|
||||
"gallery",
|
||||
"Neck Massager"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "skg-neck-massager-gs500-n-01",
|
||||
"handle": "skg-neck-massager-gs500-n",
|
||||
"title": "SKG GS500-N Neck Massager",
|
||||
"product_type": "Neck Massager",
|
||||
"image_type": "gallery",
|
||||
"image_index": 1,
|
||||
"filename": "images/skg-neck-massager-gs500-n-01.jpg",
|
||||
"url": "/product-library/skg/images/skg-neck-massager-gs500-n-01.jpg",
|
||||
"width": 1800,
|
||||
"height": 1800,
|
||||
"source_path": "all_products/skg-neck-massager-gs500-n/gallery/01-1-e5f77c48-51d9-450a-84e9-7e6502df1f8d.jpg",
|
||||
"white_score": 1.0,
|
||||
"near_white_score": 1.0,
|
||||
"has_people": false,
|
||||
"tags": [
|
||||
"white-bg",
|
||||
"gallery",
|
||||
"Neck Massager"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "skg-neck-massager-gs500-n-03",
|
||||
"handle": "skg-neck-massager-gs500-n",
|
||||
"title": "SKG GS500-N Neck Massager",
|
||||
"product_type": "Neck Massager",
|
||||
"image_type": "gallery",
|
||||
"image_index": 3,
|
||||
"filename": "images/skg-neck-massager-gs500-n-03.jpg",
|
||||
"url": "/product-library/skg/images/skg-neck-massager-gs500-n-03.jpg",
|
||||
"width": 1800,
|
||||
"height": 1800,
|
||||
"source_path": "all_products/skg-neck-massager-gs500-n/gallery/03-2-f27e8b0f-023d-43a2-b934-ed7a13cb54f6.jpg",
|
||||
"white_score": 0.9942,
|
||||
"near_white_score": 0.9975,
|
||||
"has_people": false,
|
||||
"tags": [
|
||||
"white-bg",
|
||||
"gallery",
|
||||
"Neck Massager"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "skg-neck-massager-gs500-n-04",
|
||||
"handle": "skg-neck-massager-gs500-n",
|
||||
"title": "SKG GS500-N Neck Massager",
|
||||
"product_type": "Neck Massager",
|
||||
"image_type": "gallery",
|
||||
"image_index": 4,
|
||||
"filename": "images/skg-neck-massager-gs500-n-04.jpg",
|
||||
"url": "/product-library/skg/images/skg-neck-massager-gs500-n-04.jpg",
|
||||
"width": 1800,
|
||||
"height": 1800,
|
||||
"source_path": "all_products/skg-neck-massager-gs500-n/gallery/04-4-fe265164-3b78-4f45-b1b9-53c47f0db027.jpg",
|
||||
"white_score": 0.9975,
|
||||
"near_white_score": 0.9984,
|
||||
"has_people": false,
|
||||
"tags": [
|
||||
"white-bg",
|
||||
"gallery",
|
||||
"Neck Massager"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "skg-neck-massager-gs500-n-05",
|
||||
"handle": "skg-neck-massager-gs500-n",
|
||||
"title": "SKG GS500-N Neck Massager",
|
||||
"product_type": "Neck Massager",
|
||||
"image_type": "gallery",
|
||||
"image_index": 5,
|
||||
"filename": "images/skg-neck-massager-gs500-n-05.jpg",
|
||||
"url": "/product-library/skg/images/skg-neck-massager-gs500-n-05.jpg",
|
||||
"width": 1800,
|
||||
"height": 1800,
|
||||
"source_path": "all_products/skg-neck-massager-gs500-n/gallery/05-5-d8c18f90-7376-431d-aa53-a24599db08b3.jpg",
|
||||
"white_score": 0.9762,
|
||||
"near_white_score": 0.9877,
|
||||
"has_people": false,
|
||||
"tags": [
|
||||
"white-bg",
|
||||
"gallery",
|
||||
"Neck Massager"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "skg-vs500-back-and-neck-massager-01",
|
||||
"handle": "skg-vs500-back-and-neck-massager",
|
||||
"title": "SKG VS500 Back Massage Pillow",
|
||||
"product_type": "Waist Massager",
|
||||
"image_type": "gallery",
|
||||
"image_index": 1,
|
||||
"filename": "images/skg-vs500-back-and-neck-massager-01.jpg",
|
||||
"url": "/product-library/skg/images/skg-vs500-back-and-neck-massager-01.jpg",
|
||||
"width": 1800,
|
||||
"height": 1800,
|
||||
"source_path": "all_products/skg-vs500-back-and-neck-massager/gallery/01-120.png",
|
||||
"white_score": 1.0,
|
||||
"near_white_score": 1.0,
|
||||
"has_people": false,
|
||||
"tags": [
|
||||
"white-bg",
|
||||
"gallery",
|
||||
"Waist Massager"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "skg-vs500-back-and-neck-massager-03",
|
||||
"handle": "skg-vs500-back-and-neck-massager",
|
||||
"title": "SKG VS500 Back Massage Pillow",
|
||||
"product_type": "Waist Massager",
|
||||
"image_type": "gallery",
|
||||
"image_index": 3,
|
||||
"filename": "images/skg-vs500-back-and-neck-massager-03.jpg",
|
||||
"url": "/product-library/skg/images/skg-vs500-back-and-neck-massager-03.jpg",
|
||||
"width": 1800,
|
||||
"height": 1800,
|
||||
"source_path": "all_products/skg-vs500-back-and-neck-massager/gallery/03-3-1.png",
|
||||
"white_score": 1.0,
|
||||
"near_white_score": 1.0,
|
||||
"has_people": false,
|
||||
"tags": [
|
||||
"white-bg",
|
||||
"gallery",
|
||||
"Waist Massager"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "skg-w9-ultra-lower-back-massager-02",
|
||||
"handle": "skg-w9-ultra-lower-back-massager",
|
||||
"title": "SKG W9 Ultra Lower Back Massager",
|
||||
"product_type": "WAIST MASSAGER",
|
||||
"image_type": "gallery",
|
||||
"image_index": 2,
|
||||
"filename": "images/skg-w9-ultra-lower-back-massager-02.jpg",
|
||||
"url": "/product-library/skg/images/skg-w9-ultra-lower-back-massager-02.jpg",
|
||||
"width": 1800,
|
||||
"height": 1800,
|
||||
"source_path": "all_products/skg-w9-ultra-lower-back-massager/gallery/02-177250074092.png",
|
||||
"white_score": 0.9597,
|
||||
"near_white_score": 0.9605,
|
||||
"has_people": false,
|
||||
"tags": [
|
||||
"white-bg",
|
||||
"gallery",
|
||||
"WAIST MASSAGER"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "skg-w9-ultra-lower-back-massager-03",
|
||||
"handle": "skg-w9-ultra-lower-back-massager",
|
||||
"title": "SKG W9 Ultra Lower Back Massager",
|
||||
"product_type": "WAIST MASSAGER",
|
||||
"image_type": "gallery",
|
||||
"image_index": 3,
|
||||
"filename": "images/skg-w9-ultra-lower-back-massager-03.jpg",
|
||||
"url": "/product-library/skg/images/skg-w9-ultra-lower-back-massager-03.jpg",
|
||||
"width": 1800,
|
||||
"height": 1800,
|
||||
"source_path": "all_products/skg-w9-ultra-lower-back-massager/gallery/03-1772500736668.png",
|
||||
"white_score": 0.9803,
|
||||
"near_white_score": 0.9803,
|
||||
"has_people": false,
|
||||
"tags": [
|
||||
"white-bg",
|
||||
"gallery",
|
||||
"WAIST MASSAGER"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "skg-w9-ultra-lower-back-massager-04",
|
||||
"handle": "skg-w9-ultra-lower-back-massager",
|
||||
"title": "SKG W9 Ultra Lower Back Massager",
|
||||
"product_type": "WAIST MASSAGER",
|
||||
"image_type": "gallery",
|
||||
"image_index": 4,
|
||||
"filename": "images/skg-w9-ultra-lower-back-massager-04.jpg",
|
||||
"url": "/product-library/skg/images/skg-w9-ultra-lower-back-massager-04.jpg",
|
||||
"width": 1800,
|
||||
"height": 1800,
|
||||
"source_path": "all_products/skg-w9-ultra-lower-back-massager/gallery/04-17725007288637.png",
|
||||
"white_score": 0.9712,
|
||||
"near_white_score": 0.9819,
|
||||
"has_people": false,
|
||||
"tags": [
|
||||
"white-bg",
|
||||
"gallery",
|
||||
"WAIST MASSAGER"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "skg-ys100-foot-massager-12",
|
||||
"handle": "skg-ys100-foot-massager",
|
||||
"title": "SKG YS100 Foot Massager",
|
||||
"product_type": "",
|
||||
"image_type": "gallery",
|
||||
"image_index": 12,
|
||||
"filename": "images/skg-ys100-foot-massager-12.jpg",
|
||||
"url": "/product-library/skg/images/skg-ys100-foot-massager-12.jpg",
|
||||
"width": 1600,
|
||||
"height": 1154,
|
||||
"source_path": "all_products/skg-ys100-foot-massager/gallery/12-2-a9933337-b122-4cfb-a4fa-1dc82a5d763d.png",
|
||||
"white_score": 0.7862,
|
||||
"near_white_score": 0.8133,
|
||||
"has_people": false,
|
||||
"tags": [
|
||||
"white-bg",
|
||||
"gallery"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "skg-ys100-foot-massager-black-2-0-02",
|
||||
"handle": "skg-ys100-foot-massager-black-2-0",
|
||||
"title": "SKG YS100 Foot Massager Black 2.0",
|
||||
"product_type": "",
|
||||
"image_type": "gallery",
|
||||
"image_index": 2,
|
||||
"filename": "images/skg-ys100-foot-massager-black-2-0-02.jpg",
|
||||
"url": "/product-library/skg/images/skg-ys100-foot-massager-black-2-0-02.jpg",
|
||||
"width": 1502,
|
||||
"height": 1502,
|
||||
"source_path": "all_products/skg-ys100-foot-massager-black-2-0/gallery/02-1-1-48e79179-3270-45d9-9903-e46c03c97e65.jpg",
|
||||
"white_score": 0.8742,
|
||||
"near_white_score": 0.8775,
|
||||
"has_people": false,
|
||||
"tags": [
|
||||
"white-bg",
|
||||
"gallery"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "w9-pro-waist-massager-01",
|
||||
"handle": "w9-pro-waist-massager",
|
||||
"title": "SKG W9 Pro Lower Back Massager",
|
||||
"product_type": "Waist Massager",
|
||||
"image_type": "gallery",
|
||||
"image_index": 1,
|
||||
"filename": "images/w9-pro-waist-massager-01.jpg",
|
||||
"url": "/product-library/skg/images/w9-pro-waist-massager-01.jpg",
|
||||
"width": 1800,
|
||||
"height": 1800,
|
||||
"source_path": "all_products/w9-pro-waist-massager/gallery/01-2-a06084ba-9c70-4f10-82b7-9612f818c61a.jpg",
|
||||
"white_score": 0.9696,
|
||||
"near_white_score": 0.9753,
|
||||
"has_people": false,
|
||||
"tags": [
|
||||
"white-bg",
|
||||
"gallery",
|
||||
"Waist Massager"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "w9-pro-waist-massager-04",
|
||||
"handle": "w9-pro-waist-massager",
|
||||
"title": "SKG W9 Pro Lower Back Massager",
|
||||
"product_type": "Waist Massager",
|
||||
"image_type": "gallery",
|
||||
"image_index": 4,
|
||||
"filename": "images/w9-pro-waist-massager-04.jpg",
|
||||
"url": "/product-library/skg/images/w9-pro-waist-massager-04.jpg",
|
||||
"width": 1350,
|
||||
"height": 1800,
|
||||
"source_path": "all_products/w9-pro-waist-massager/gallery/04-1-47db56ea-162a-44fc-a9cf-6d44c79930b2.jpg",
|
||||
"white_score": 0.866,
|
||||
"near_white_score": 0.875,
|
||||
"has_people": false,
|
||||
"tags": [
|
||||
"white-bg",
|
||||
"gallery",
|
||||
"Waist Massager"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "w9-pro-waist-massager-05",
|
||||
"handle": "w9-pro-waist-massager",
|
||||
"title": "SKG W9 Pro Lower Back Massager",
|
||||
"product_type": "Waist Massager",
|
||||
"image_type": "gallery",
|
||||
"image_index": 5,
|
||||
"filename": "images/w9-pro-waist-massager-05.jpg",
|
||||
"url": "/product-library/skg/images/w9-pro-waist-massager-05.jpg",
|
||||
"width": 1350,
|
||||
"height": 1800,
|
||||
"source_path": "all_products/w9-pro-waist-massager/gallery/05-2-daa222c5-f098-48cd-a50b-baac4a8c2f31.jpg",
|
||||
"white_score": 0.9375,
|
||||
"near_white_score": 0.9688,
|
||||
"has_people": false,
|
||||
"tags": [
|
||||
"white-bg",
|
||||
"gallery",
|
||||
"Waist Massager"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -118,6 +118,28 @@ export async function uploadStoryboardAsset(jobId: string, file: File): Promise<
|
||||
return res.json()
|
||||
}
|
||||
|
||||
export async function listProductLibrary(): Promise<ProductLibraryItem[]> {
|
||||
const res = await fetch(`${API_BASE}/product-library/skg`)
|
||||
if (!res.ok) {
|
||||
const txt = await res.text().catch(() => "")
|
||||
throw new Error(`listProductLibrary ${res.status} ${txt.slice(0, 300)}`)
|
||||
}
|
||||
return res.json()
|
||||
}
|
||||
|
||||
export async function copyProductLibraryAsset(jobId: string, productId: string): Promise<ImageRef> {
|
||||
const res = await fetch(`${API_BASE}/jobs/${jobId}/assets/product-library`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ product_id: productId }),
|
||||
})
|
||||
if (!res.ok) {
|
||||
const txt = await res.text().catch(() => "")
|
||||
throw new Error(`copyProductLibraryAsset ${res.status} ${txt.slice(0, 300)}`)
|
||||
}
|
||||
return res.json()
|
||||
}
|
||||
|
||||
export interface KeyFrame {
|
||||
index: number
|
||||
timestamp: number
|
||||
@@ -180,6 +202,24 @@ export interface SubjectAsset {
|
||||
created_at: number
|
||||
}
|
||||
|
||||
export interface ProductLibraryItem {
|
||||
id: string
|
||||
handle: string
|
||||
title: string
|
||||
product_type: string
|
||||
image_type: string
|
||||
image_index: number
|
||||
filename: string
|
||||
url: string
|
||||
width: number
|
||||
height: number
|
||||
source_path: string
|
||||
white_score: number
|
||||
near_white_score: number
|
||||
has_people: boolean
|
||||
tags: string[]
|
||||
}
|
||||
|
||||
export interface TranscriptSegment {
|
||||
index: number
|
||||
start: number
|
||||
|
||||