auto-save 2026-05-14 06:55 (+1, ~3)

This commit is contained in:
2026-05-14 06:55:41 +08:00
parent 9546d5f60b
commit aff05b89dd
45 changed files with 1015 additions and 14 deletions

View File

@@ -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
}
]
}

View File

@@ -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"

Binary file not shown.

After

Width:  |  Height:  |  Size: 373 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 326 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 316 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 127 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 294 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 428 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 414 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 243 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 431 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 362 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 234 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 300 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 260 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 231 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 181 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 183 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 496 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 435 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 190 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 269 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 319 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 376 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 269 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 514 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 318 KiB

View 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"
]
}
]
}

View File

@@ -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