# -*- coding: utf-8 -*- """Audit: every Lubu image task in project 30 (since 2026-10-23) must exist locally. Match by sha256; download missing into 视频资产\\后台补档.""" import json, ssl, pathlib, urllib.request, hashlib, re BASE = "https://192.168.31.88:8042" CFG = pathlib.Path(r"C:\Users\admin\.zcode\cli\config.json") pat = json.loads(CFG.read_text(encoding="utf-8-sig"))["mcp"]["servers"]["sorb"]["env"]["SORB_PAT"] ctx = ssl._create_unverified_context() def get(url): req = urllib.request.Request(url, headers={"Authorization": "Bearer " + pat}) return urllib.request.urlopen(req, context=ctx, timeout=180) # 1. local sha index local = {} for p in pathlib.Path(r"D:\腾讯\吕布").rglob("*"): if p.is_file() and p.suffix.lower() in (".png", ".jpg", ".jpeg", ".webp"): try: local[hashlib.sha256(p.read_bytes()).hexdigest()] = str(p) except Exception as e: print("hash fail:", p, e) print("local image files indexed:", len(local)) # 2. page all tasks (handle both response shapes) tasks, page = [], 1 while page <= 20: o = json.loads(get(f"{BASE}/api/external/v1/tasks?page={page}&page_size=100&project_id=30").read().decode("utf-8")) data = o.get("data") if isinstance(o.get("data"), dict) else o items = data.get("items") or o.get("items") or [] if not items: break tasks.extend(items) total = data.get("total") or o.get("total") or 0 if len(tasks) >= total: break page += 1 print("tasks fetched:", len(tasks)) INCLUDE = ["吕布", "赤兔", "方天画戟", "画戟", "虎牢", "濮阳", "辕门", "下邳", "凤仪亭", "董卓", "貂蝉", "连环计", "三英", "关羽", "张飞", "刘备", "一骑当千", "演武", "古战场", "三国", "束发", "凤翅", "兽面", "三视图", "翎羽", "空场景", "角弓"] EXCLUDE = ["沈惊鸿", "狙击", "装甲", "末世", "废墟", "孙尚香"] cand = [] for it in tasks: ca = (it.get("created_at") or "")[:10] if ca < "2026-10-23": continue if it.get("task_type") != "image" or it.get("status") != "completed" or not it.get("image_url"): continue pr = it.get("prompt") or "" if any(x in pr for x in EXCLUDE): continue if not any(x in pr for x in INCLUDE): continue cand.append(it) print("lubu image task candidates:", len(cand)) missing, seen, matched = [], set(), 0 for it in cand: tid = it["id"] try: d = json.loads(get(f"{BASE}/api/external/v1/tasks/{tid}").read().decode("utf-8")) except Exception as e: print("detail fail", tid, e) continue outs = [x for x in (d.get("outputs") or []) if isinstance(x, dict)] pr = (d.get("prompt") or "").replace("\n", " ") date = (d.get("created_at") or "")[:10].replace("-", "") for oi, out in enumerate(outs): sha = out.get("sha256") url = out.get("url_snapshot") or d.get("image_url") if not url: continue if sha: if sha in local: matched += 1 continue if sha in seen: continue seen.add(sha) missing.append((tid, oi, url, sha, pr, date)) print("outputs already local:", matched) print("missing outputs:", len(missing)) OUT = pathlib.Path(r"D:\腾讯\吕布\视频资产\后台补档") OUT.mkdir(parents=True, exist_ok=True) for tid, oi, url, sha, pr, date in missing: full = url if url.startswith("http") else BASE + url data = get(full).read() if sha and hashlib.sha256(data).hexdigest() != sha: print(" sha mismatch on task", tid, "(saved anyway)") snip = re.sub(r'[\\/:*?"<>|\s]+', "", pr)[:30] fname = f"task{tid}_o{oi}_{date}_{snip}.png" (OUT / fname).write_bytes(data) print("downloaded", fname, len(data)) print("DONE. missing downloaded:", len(missing))