"""Create a complete upgrade rollback copy without touching live project data.""" from datetime import datetime import hashlib import json import os from pathlib import Path import shutil import sqlite3 import subprocess ROOT = Path(__file__).resolve().parent.parent DEST = ROOT / "integration-work/backups" / ("upgrade-" + datetime.now().strftime("%Y%m%d-%H%M%S")) def main(): DEST.mkdir(parents=True, exist_ok=False) subprocess.run(["icacls", str(DEST), "/inheritance:r", "/grant:r", os.environ["USERNAME"] + ":(OI)(CI)(F)", "SYSTEM:(OI)(CI)(F)"], check=True, capture_output=True) data_dest = DEST / "huobao-data" data_dest.mkdir() database = ROOT / "huobao-data/huobao.sqlite3" with sqlite3.connect(database.as_uri() + "?mode=ro", uri=True) as source, sqlite3.connect(data_dest / "huobao.sqlite3") as snapshot: source.backup(snapshot) assert snapshot.execute("PRAGMA integrity_check").fetchone()[0] == "ok" counts = {name: snapshot.execute('SELECT count(*) FROM "' + name + '"').fetchone()[0] for (name,) in snapshot.execute("SELECT name FROM sqlite_master WHERE type='table'")} for item in (ROOT / "huobao-data").iterdir(): if item.name.startswith("huobao.sqlite3") or item.name == "backups": continue target = data_dest / item.name if item.is_dir(): shutil.copytree(item, target) else: shutil.copy2(item, target) shutil.copytree(ROOT / "HuobaoDrama", DEST / "HuobaoDrama") shutil.copytree(ROOT / "model-bridge", DEST / "model-bridge", ignore=shutil.ignore_patterns("__pycache__", "*.log")) shutil.copy2(ROOT / "一键启动火宝.cmd", DEST / "一键启动火宝.cmd") profile = Path(os.environ["APPDATA"]) / "HuobaoDrama" for name in ("storage-config.json", "Preferences", "Local State", "workspace"): source = profile / name if not source.exists(): continue target = DEST / "profile" / name target.parent.mkdir(parents=True, exist_ok=True) if source.is_dir(): shutil.copytree(source, target) else: shutil.copy2(source, target) files = [] for file in data_dest.rglob("*"): if file.is_file(): files.append({"path": file.relative_to(data_dest).as_posix(), "bytes": file.stat().st_size, "sha256": hashlib.file_digest(file.open("rb"), "sha256").hexdigest()}) manifest = {"path": str(DEST), "counts": counts, "data_files": files} (DEST / "manifest.json").write_text(json.dumps(manifest, ensure_ascii=False, indent=2), encoding="utf-8") (ROOT / "integration-work/current-upgrade-backup.txt").write_text(str(DEST), encoding="utf-8") print("Complete rollback backup:", DEST) print("Data files:", len(files), "model configs:", counts["ai_service_configs"], "projects:", counts["dramas"], "styles:", counts["style_presets"]) if __name__ == "__main__": main()