File size: 7,183 Bytes
e0db531 9869532 e0db531 790883f e0db531 c0d8470 e0db531 c0d8470 790883f e0db531 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 | """Publish the reproduction (code + docs + results + videos + checkpoint) to the Hub.
Deliberately EXCLUDES:
* base model weights (models/, ~115GB, all redownloadable from their own repos)
* our trained checkpoints (checkpoints*/) -- weights are not published
* source/intermediate video data (data/videos, data/raw, data/cache)
* third_party/ (the upstream Wan2.2 checkout)
* the venv and every cache
* .hf_token and any other secret
Run: python scripts/push_to_hf.py --repo yqi19/VIPRA-reproduce
"""
from __future__ import annotations
import argparse
import os
import re
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
ALLOW = [
"viper/**",
"scripts/**",
"*.md",
"env.sh",
# NOTE: .gitignore is deliberately NOT published. The Hub applies the repo's
# own .gitignore server-side during commits, and ours carries "*.mp4"/"*.pt"
# (there to keep 100+GB of local data out of git) -- publishing it makes the
# Hub silently drop every result video from the commit.
"data/*.jsonl",
"results/**",
"logs/*.log",
]
IGNORE = [
"models/**", "checkpoints/**", "checkpoints_interactive/**",
"third_party/**", ".venv/**", ".cache/**", ".tmp/**",
".uv_python/**", "data/videos/**", "data/raw/**", "data/cache/**",
"**/__pycache__/**", "*.pyc", ".hf_token", "**/.hf_token",
"results/smoke_videos/**", # superseded by results/comparison
]
TOKEN_RE = re.compile(r"hf_[A-Za-z0-9]{30,}")
# Directories never worth walking (huge / irrelevant); keeps the scan fast.
PRUNE = {"models", "third_party", ".venv", ".cache", ".tmp", ".uv_python",
"__pycache__", ".git", "checkpoints", "checkpoints_interactive"}
def _select(root: Path) -> list[Path]:
"""Files that upload_folder would send, using its fnmatch semantics."""
from fnmatch import fnmatch
out = []
for dirpath, dirnames, filenames in os.walk(root):
dirnames[:] = [d for d in dirnames if d not in PRUNE]
for fn in filenames:
p = Path(dirpath) / fn
rel = str(p.relative_to(root))
if any(fnmatch(rel, pat) for pat in IGNORE):
continue
if any(fnmatch(rel, pat) for pat in ALLOW):
out.append(p)
return sorted(out)
def secret_scan(root: Path) -> list[str]:
"""Refuse to publish if any tracked text file still contains a token."""
hits = []
exts = {".sh", ".py", ".md", ".json", ".jsonl", ".txt", ".yaml", ".yml"}
skip = {"models", "third_party", ".venv", ".cache", ".tmp", ".uv_python",
"__pycache__", ".uv-cache-tmp"}
for p in root.rglob("*"):
if not p.is_file() or p.suffix not in exts:
continue
if any(part in skip for part in p.parts):
continue
try:
txt = p.read_text(errors="ignore")
except Exception:
continue
for m in TOKEN_RE.findall(txt):
hits.append(f"{p.relative_to(root)}: {m[:8]}...")
return hits
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--repo", required=True)
ap.add_argument("--repo_type", default="model")
ap.add_argument("--token", default=os.environ.get("HF_PUSH_TOKEN"))
ap.add_argument("--dry_run", action="store_true")
ap.add_argument(
"--with_videos", action="store_true",
help="also upload the clips actually referenced by data/pairs_train.jsonl "
"and data/pairs_val.jsonl (not all of data/videos, which is the full "
"raw WISA-80K download cache)",
)
args = ap.parse_args()
if not args.token:
sys.exit("no token: pass --token or set HF_PUSH_TOKEN")
leaks = secret_scan(ROOT)
if leaks:
print("REFUSING TO PUSH -- secrets found in files that would be published:")
for l in leaks:
print(" ", l)
sys.exit(1)
print("secret scan: clean")
# Preview using the SAME matching huggingface_hub applies, so the dry run is
# truthful (its filter is fnmatch over repo-relative paths, where '*' also
# crosses '/', not pathlib.glob semantics).
picked = _select(ROOT)
total = sum(p.stat().st_size for p in picked)
print(f"{len(picked)} files, {total/1e6:.1f} MB")
by_top: dict[str, list] = {}
for p in picked:
by_top.setdefault(p.relative_to(ROOT).parts[0], []).append(p)
for top, files in sorted(by_top.items()):
sz = sum(f.stat().st_size for f in files)
print(f" {top:28s} {len(files):4d} files {sz/1e6:8.1f} MB")
if args.dry_run:
print("\n(dry run -- nothing uploaded)")
return
from huggingface_hub import HfApi
api = HfApi(token=args.token)
api.create_repo(args.repo, repo_type=args.repo_type, exist_ok=True)
print(f"\nuploading -> https://huggingface.co/{args.repo}")
api.upload_folder(
folder_path=str(ROOT),
repo_id=args.repo,
repo_type=args.repo_type,
allow_patterns=ALLOW,
ignore_patterns=IGNORE,
commit_message="VIPER reproduction: code, mini VIPER-19K pipeline, "
"qualitative comparisons and eval",
)
# upload_folder honours the repo's .gitignore, and ours carries "*.mp4"/"*.pt"
# to keep the 100+GB of local data out of git. That silently drops every
# generated video from the pass above, so push the comparison media from its
# own folder (which contains no .gitignore) as a second commit.
media = ROOT / "results" / "comparison"
if media.is_dir():
n = len(list(media.rglob("*.mp4")))
print(f"\nuploading {n} videos from results/comparison ...")
api.upload_folder(
folder_path=str(media),
path_in_repo="results/comparison",
repo_id=args.repo,
repo_type=args.repo_type,
commit_message="qualitative comparison videos "
"(reference / target GT / Wan2.2 baseline / VIPER)",
)
if args.with_videos:
import json as _json
ids = set()
for fn in ("data/pairs_train.jsonl", "data/pairs_val.jsonl"):
for line in (ROOT / fn).read_text().splitlines():
d = _json.loads(line)
ids.add(d["ref_id"])
ids.add(d["tgt_id"])
patterns = [f"{i}{ext}" for i in ids for ext in (".mp4", ".txt", ".json")]
vids_dir = ROOT / "data" / "videos"
size = sum((vids_dir / p).stat().st_size for p in patterns
if (vids_dir / p).exists())
print(f"\nuploading {len(ids)} clips referenced by pairs_train/pairs_val "
f"({size/1e9:.2f} GB) from data/videos ...")
api.upload_folder(
folder_path=str(vids_dir),
path_in_repo="data/videos",
repo_id=args.repo,
repo_type=args.repo_type,
allow_patterns=patterns,
commit_message="training clips referenced by pairs_train.jsonl / "
"pairs_val.jsonl (WISA-80K subset, apache-2.0)",
)
print(f"done: https://huggingface.co/{args.repo}")
if __name__ == "__main__":
main()
|