| |
| """Generate SEO stubs for ALL products in index.html - run during build.sh""" |
| import json, re, unicodedata, os, time, urllib.request |
| from huggingface_hub import HfApi |
|
|
| SPACE_ID = "bep40/V.AISTUDIO" |
| BASE_URL = "https://bep40-v-aistudio.static.hf.space" |
|
|
| def download_index(): |
| print("Downloading index.html...") |
| api = HfApi() |
| path = api.hf_hub_download(SPACE_ID, "index.html", repo_type="space") |
| with open(path, 'r') as f: |
| return f.read() |
|
|
| def extract_products(html): |
| print("Extracting products...") |
| idx = html.find('var products = ') |
| if idx == -1: |
| idx = html.find('products = [') |
| if idx == -1: |
| raise ValueError("Products not found") |
| start = html.find('[', idx) |
| |
| bracket = 0 |
| end = start |
| in_str = False |
| for i in range(start, len(html)): |
| c = html[i] |
| if c == '"' and html[i-1] != '\\': |
| in_str = not in_str |
| elif not in_str: |
| if c == '[': bracket += 1 |
| elif c == ']': |
| bracket -= 1 |
| if bracket == 0: |
| end = i + 1 |
| break |
| products = json.loads(html[start:end]) |
| print(f"Found {len(products)} products") |
| return products |
|
|
| def slugify(name): |
| s = unicodedata.normalize('NFKD', name).lower() |
| s = re.sub(r'[^a-z0-9]+', '-', s).strip('-') |
| s = re.sub(r'-+', '-', s) |
| return s |
|
|
| def create_stub(p): |
| slug = p.get('slug') or slugify(p.get('name', 'p')) |
| name = p.get('name', 'Product')[:200] |
| brand = p.get('brand', '') |
| price = p.get('price', '') |
| img = p.get('image', BASE_URL + '/logo/logo_600.png') |
| desc = re.sub(r'<[^>]+>', '', str(p.get('description', '')))[:160] |
| title = f"{name} | {brand} - V.AISTUDIO" if brand else f"{name} - V.AISTUDIO" |
| return f'''<!DOCTYPE html> |
| <html lang="vi"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="robots" content="noindex,follow"> |
| <link rel="canonical" href="{BASE_URL}/san-pham/{slug}/"> |
| <meta property="og:type" content="product"> |
| <meta property="og:title" content="{title}"> |
| <meta property="og:description" content="{desc}"> |
| <meta property="og:image" content="{img}"> |
| <meta property="og:url" content="{BASE_URL}/san-pham/{slug}/"> |
| <meta property="og:site_name" content="V.AISTUDIO"> |
| <meta name="twitter:card" content="summary_large_image"> |
| <script>window.location.replace("/?product={slug}");</script> |
| </head> |
| <body><h1><a href="/?product={slug}">{title}</a></h1></body> |
| </html>''' |
|
|
| def main(): |
| html = download_index() |
| products = extract_products(html) |
| api = HfApi() |
| seen = {} |
| uploaded = 0 |
| for p in products: |
| slug = p.get('slug') or slugify(p.get('name', 'p')) |
| if slug in seen: |
| seen[slug] += 1 |
| slug = f"{slug}-{seen[slug]}" |
| else: |
| seen[slug] = 0 |
| stub = create_stub(p) |
| try: |
| api.upload_file( |
| path_or_fileobj=stub.encode(), |
| path_in_repo=f"san-pham/{slug}/index.html", |
| repo_id=SPACE_ID, repo_type="space", |
| commit_message=f"SEO stub: {p.get('name', 'product')[:50]}" |
| ) |
| uploaded += 1 |
| if uploaded % 100 == 0: |
| print(f" {uploaded} uploaded...") |
| except Exception as e: |
| print(f" Error: {e}") |
| print(f"Done! {uploaded} stubs") |
| |
| api.upload_file( |
| path_or_fileobj=f"# rebuild {time.time()}".encode(), |
| path_in_repo=".rebuild", |
| repo_id=SPACE_ID, repo_type="space", |
| commit_message="Trigger rebuild complete" |
| ) |
|
|
| if __name__ == "__main__": |
| main() |