bep40 commited on
Commit
fa7872c
·
verified ·
1 Parent(s): 845b99f

Upload sync_products.py

Browse files
Files changed (1) hide show
  1. sync_products.py +152 -0
sync_products.py ADDED
@@ -0,0 +1,152 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ Product Auto-Sync Script — bep40/V.AISTUDIO ↔ bep40/vai-avatar2
4
+ Mục đích: Tự động so sánh và cập nhật sản phẩm từ bep40/grob-products-updated dataset
5
+ """
6
+ import json
7
+ import os
8
+ import sys
9
+ from datetime import datetime, timezone
10
+
11
+ try:
12
+ from huggingface_hub import HfApi, hf_hub_download
13
+ except ImportError:
14
+ print("Installing huggingface_hub...")
15
+ os.system(f"{sys.executable} -m pip install huggingface_hub --quiet")
16
+ from huggingface_hub import HfApi, hf_hub_download
17
+
18
+ def log(msg, level="INFO"):
19
+ ts = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S UTC")
20
+ print(f"[{level}] [{ts}] {msg}", flush=True)
21
+
22
+ def slug_id(p):
23
+ for key in ("slug", "_source_alias", "sku", "mod", "model"):
24
+ val = p.get(key)
25
+ if val and str(val).strip():
26
+ return str(val).strip().lower()
27
+ return ""
28
+
29
+ def load_dataset():
30
+ api = HfApi()
31
+ try:
32
+ path = hf_hub_download(repo_id="bep40/grob-products-updated", filename="products_with_slugs.json", repo_type="dataset")
33
+ with open(path, 'r', encoding='utf-8') as f:
34
+ products = json.load(f)
35
+ log(f"Loaded {len(products)} products from grob-products-updated dataset")
36
+ return products
37
+ except Exception as e:
38
+ log(f"Failed to load dataset: {e}", "ERROR")
39
+ return []
40
+
41
+ def get_space_slugs(api, space_id):
42
+ slugs = set()
43
+ candidates = ["products_with_slugs.json", "vaix-products.json"]
44
+ for fname in candidates:
45
+ try:
46
+ path = hf_hub_download(repo_id=space_id, repo_type="space", filename=fname)
47
+ with open(path, 'r', encoding='utf-8') as f:
48
+ content = f.read()
49
+ if fname.endswith('.js'):
50
+ import re
51
+ match = re.search(r'\[.*\]', content, re.DOTALL | re.IGNORECASE)
52
+ if match:
53
+ items = json.loads(match.group())
54
+ for item in items:
55
+ sid = slug_id(item)
56
+ if sid: slugs.add(sid)
57
+ else:
58
+ items = json.loads(content)
59
+ for item in items:
60
+ sid = slug_id(item)
61
+ if sid: slugs.add(sid)
62
+ if slugs:
63
+ log(f"Found {len(slugs)} slugs in {space_id}/{fname}")
64
+ break
65
+ except:
66
+ continue
67
+ return slugs
68
+
69
+ def sync_to_space(api, space_id, products, products_list=None):
70
+ """Sync a single space."""
71
+ if isinstance(products, dict):
72
+ product_list = products_list or []
73
+ else:
74
+ product_list = products
75
+
76
+ payload = json.dumps(product_list, ensure_ascii=False, indent=2).encode('utf-8') if isinstance(product_list, list) else products.encode('utf-8') if isinstance(products, bytes) else json.dumps(products, ensure_ascii=False, indent=2).encode('utf-8')
77
+
78
+ api.upload_file(
79
+ path_or_fileobj=payload,
80
+ path_in_repo="products_with_slugs.json",
81
+ repo_id=space_id,
82
+ repo_type="space",
83
+ commit_message=f"[AUTO-SYNC] Update products ({len(product_list) if isinstance(product_list, list) else len(json.loads(payload))} items)"
84
+ )
85
+ log(f"Updated {space_id}")
86
+ return True
87
+
88
+ def sync_api_response(api, space_id, products_list=None):
89
+ """API Response mode for /api/vaix/json endpoint."""
90
+ payload = json.dumps(products_list, ensure_ascii=False, indent=2).encode('utf-8')
91
+ api.upload_file(
92
+ path_or_fileobj=payload,
93
+ path_in_repo="/api/vaix/json",
94
+ repo_id=space_id,
95
+ repo_type="space",
96
+ commit_message=f"[AUTO-SYNC] Update /api/vaix/json ({len(products_list)} items)"
97
+ )
98
+ log(f"Updated /api/vaix/json in {space_id}")
99
+ return True
100
+
101
+ def trigger_rebuild(api, space_id):
102
+ ts = datetime.now(timezone.utc).isoformat()
103
+ content = f"# Sync rebuild at {ts}\nSpace: {space_id}\nSource: bep40/grob-products-updated\n"
104
+ flag = ".cache_bust" if "avatar" in space_id else "build-trigger"
105
+ try:
106
+ api.upload_file(
107
+ path_or_fileobj=content.encode('utf-8'),
108
+ path_in_repo=flag,
109
+ repo_id=space_id,
110
+ repo_type="space",
111
+ commit_message=f"[SYNC] Rebuild at {ts}"
112
+ )
113
+ log(f"Rebuild triggered for {space_id}")
114
+ except:
115
+ pass
116
+
117
+ def main():
118
+ log("=" * 50)
119
+ log("V.AISTUDIO ↔ vai-avatar2 Auto-Sync")
120
+ log("=" * 50)
121
+
122
+ api = HfApi()
123
+
124
+ products = load_dataset()
125
+ if not products:
126
+ log("No products loaded", "ERROR")
127
+ sys.exit(1)
128
+
129
+ total_prods = len(products)
130
+ prods_list = products if isinstance(products, list) else [products] if products else []
131
+
132
+ # Update V.AISTUDIO with full products list
133
+ log("\n=== Updating V.AISTUDIO ===")
134
+ sync_to_space(api, "bep40/V.AISTUDIO", prods_list, prods_list)
135
+ trigger_rebuild(api, "bep40/V.AISTUDIO")
136
+
137
+ # Also update /api/vaix/json for API endpoint compatibility
138
+ sync_api_response(api, "bep40/V.AISTUDIO", prods_list)
139
+
140
+ # Update vai-avatar2
141
+ log("\n=== Updating vai-avatar2 ===")
142
+ sync_to_space(api, "bep40/vai-avatar2", prods_list, prods_list)
143
+ trigger_rebuild(api, "bep40/vai-avatar2")
144
+
145
+ # Also update /api/vaix/json for API endpoint compatibility
146
+ sync_api_response(api, "bep40/vai-avatar2", prods_list)
147
+
148
+ log(f"\n✅ Sync complete — Updated both spaces with {total_prods} products!")
149
+ log("=" * 50)
150
+
151
+ if __name__ == "__main__":
152
+ main()