backblaze-proofframe / scripts /final_video_publish_kit.py
ADJCJH's picture
Add final video publish kit review hardening
a7a715a verified
Raw
History Blame Contribute Delete
12.3 kB
#!/usr/bin/env python3
"""Build a public-safe final video publishing kit for Devpost submission."""
from __future__ import annotations
import argparse
import json
from datetime import datetime, timezone
from pathlib import Path
from typing import Any
ROOT = Path(__file__).resolve().parents[1]
SCHEMA = "proofframe.final_video_publish_kit.v1"
DEFAULT_JSON = ROOT / "docs" / "assets" / "final-video-publish-kit.json"
DEFAULT_MD = ROOT / "docs" / "assets" / "final-video-publish-kit.md"
PUBLIC_DEMO_URL = "https://adjcjh-backblaze-proofframe.hf.space/?judge=1"
REPOSITORY_URL = "https://github.com/adjcjh777/backblaze-proofframe"
ALLOWED_HOSTS = ["YouTube", "Vimeo", "Youku"]
def utc_now() -> str:
return datetime.now(timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z")
def load_json(path: Path) -> dict[str, Any]:
try:
data = json.loads(path.read_text(encoding="utf-8"))
except (FileNotFoundError, json.JSONDecodeError):
return {}
return data if isinstance(data, dict) else {}
def source_summary(root: Path, relative_path: str, schema: str) -> dict[str, Any]:
data = load_json(root / relative_path)
return {
"path": relative_path,
"present": bool(data),
"schema": data.get("schema"),
"schema_ok": data.get("schema") == schema,
"mode": data.get("mode"),
"ok": data.get("ok"),
"safe_to_submit": data.get("safe_to_submit"),
}
def build_chapters(storyboard: dict[str, Any]) -> list[dict[str, Any]]:
shots = storyboard.get("shots")
if not isinstance(shots, list) or not shots:
shots = [
{"id": "opening", "title": "ProofFrame judge-mode slate", "seconds": 20},
{"id": "workflow", "title": "Generate and review a provenance packet", "seconds": 55},
{"id": "evidence", "title": "Manifest, checksum, storage route, and evidence ZIP", "seconds": 35},
{"id": "gates", "title": "Final B2, Genblaze, video, and audit gates", "seconds": 25},
]
chapters: list[dict[str, Any]] = []
cursor = 0
for shot in shots:
if not isinstance(shot, dict):
continue
seconds = int(shot.get("seconds") or shot.get("duration_seconds") or 0)
title = str(shot.get("title") or shot.get("screen") or shot.get("id") or "ProofFrame section")
chapters.append(
{
"timecode": f"{cursor // 60}:{cursor % 60:02d}",
"title": title,
"duration_seconds": seconds,
}
)
cursor += max(0, seconds)
return chapters
def description_text(*, public_demo_url: str, repository_url: str, claim_boundary: str) -> str:
return "\n".join(
[
"ProofFrame is a provenance-first media operations desk for generated campaign assets.",
"",
"This demo walks through one creator workflow: open the judge-mode demo, create a media packet, inspect prompt/provider/model/storage/checksum metadata, approve an asset, and download the evidence ZIP.",
"",
f"Public demo: {public_demo_url}",
f"Repository: {repository_url}",
"",
"What to look for:",
"- Generated media is reviewed as an evidence packet, not a loose image.",
"- Prompt, provider/model metadata, storage pointer, checksum, approval state, and risk note stay together.",
"- Optional Backblaze B2 and Genblaze paths are implemented and fail closed until live proof is captured.",
"",
f"Claim boundary: {claim_boundary}",
]
)
def build_upload_checklist(public_video_check: dict[str, Any]) -> list[dict[str, Any]]:
url_analysis = public_video_check.get("url_analysis") or {}
return [
{
"id": "host_family",
"label": "Upload to an official public video host",
"ok": bool(url_analysis.get("official_host")),
"detail": "Allowed host families: YouTube, Vimeo, or Youku.",
},
{
"id": "public_visibility",
"label": "Use public or unlisted visibility that Devpost judges can access",
"ok": bool(public_video_check.get("safe_to_submit")),
"detail": "The final URL must be reachable without login, cookies, tokens, or signed query parameters.",
},
{
"id": "duration",
"label": "Keep the final video under the event time limit",
"ok": True,
"detail": "Use the generated storyboard and chapter plan; rerun public_video_check after upload.",
},
{
"id": "devpost_field",
"label": "Paste the final video URL into Devpost",
"ok": bool(public_video_check.get("safe_to_submit")),
"detail": "After upload, set PROOFFRAME_PUBLIC_VIDEO_URL and regenerate final reports.",
},
]
def source_is_final_ready(source: dict[str, Any]) -> bool:
return bool(source["schema_ok"] and source.get("ok") is True and source.get("safe_to_submit") is True)
def final_control_is_ready(source: dict[str, Any]) -> bool:
return bool(source["schema_ok"] and source.get("ok") is True and source.get("safe_to_submit") is True)
def build_kit(root: Path = ROOT) -> dict[str, Any]:
root = root.resolve()
packet = load_json(root / "docs" / "assets" / "devpost-submission-packet.json")
storyboard = load_json(root / "docs" / "assets" / "demo-storyboard.json")
draft = load_json(root / "docs" / "assets" / "demo-video-draft.json")
public_video_check = load_json(root / "docs" / "assets" / "public-video-check.json")
evidence_index = load_json(root / "docs" / "assets" / "judge-evidence-index.json")
title = "ProofFrame: Provenance-first generated media vault"
claim_boundary = (
"Use this title and description for final upload only after live B2 and Genblaze proof are recorded; "
"until then, the public demo remains local/mock and safe_to_submit=false."
)
sources = {
"storyboard": source_summary(root, "docs/assets/demo-storyboard.json", "proofframe.demo_storyboard.v1"),
"draft_video": source_summary(root, "docs/assets/demo-video-draft.json", "proofframe.demo_video_draft.v1"),
"public_video_check": source_summary(root, "docs/assets/public-video-check.json", "proofframe.public_video_check.v1"),
"final_control": source_summary(root, "docs/assets/final-submission-control.json", "proofframe.final_submission_control.v1"),
"evidence_index": source_summary(root, "docs/assets/judge-evidence-index.json", "proofframe.judge_evidence_index.v1"),
}
ok = all(source["schema_ok"] for source in sources.values()) and bool(evidence_index.get("ok"))
video_ready = source_is_final_ready(sources["public_video_check"])
control_ready = final_control_is_ready(sources["final_control"])
safe_to_submit = bool(ok and video_ready and control_ready)
return {
"schema": SCHEMA,
"created_at": utc_now(),
"ok": ok,
"mode": "public_video_ready" if video_ready else "ready_for_final_upload",
"safe_to_share": ok,
"safe_to_submit": safe_to_submit,
"final_video_ready": video_ready,
"allowed_hosts": ALLOWED_HOSTS,
"video_url": public_video_check.get("video_url") or packet.get("video_url"),
"title": title,
"description": description_text(
public_demo_url=packet.get("demo_url") or PUBLIC_DEMO_URL,
repository_url=packet.get("repository_url") or REPOSITORY_URL,
claim_boundary=claim_boundary,
),
"chapters": build_chapters(storyboard),
"devpost_field": {
"field_id": "video_url",
"value": public_video_check.get("video_url") if video_ready else "TBD after final upload.",
"ready": video_ready,
"source": "docs/assets/public-video-check.json",
},
"upload_checklist": build_upload_checklist(public_video_check),
"source_reports": sources,
"draft_video": {
"path": draft.get("video_path"),
"public_url": draft.get("public_video_draft_url"),
"safe_to_submit": draft.get("safe_to_submit"),
"final_video_ready": draft.get("final_video_ready"),
},
"evidence_index_url": (
"https://huggingface.co/spaces/ADJCJH/backblaze-proofframe/raw/main/"
"docs/assets/judge-evidence-index.md"
),
"claim_boundary": claim_boundary,
"next_actions": [
"Record the final narrated video after live B2 and Genblaze proof is visible.",
"Upload to YouTube, Vimeo, or Youku with public or unlisted judge-accessible visibility.",
"Set PROOFFRAME_PUBLIC_VIDEO_URL to the final URL and run scripts/public_video_check.py --verify-url --strict-final.",
"Regenerate Devpost form kit, final control, submission audit, preview, and bundle before submit.",
],
}
def render_markdown(kit: dict[str, Any]) -> str:
lines = [
"# ProofFrame Final Video Publish Kit",
"",
f"Mode: `{kit['mode']}`",
f"OK: `{str(kit['ok']).lower()}`",
f"Safe to submit: `{str(kit['safe_to_submit']).lower()}`",
f"Final video ready: `{str(kit['final_video_ready']).lower()}`",
f"Created: `{kit['created_at']}`",
"",
"## Upload Copy",
"",
f"Title: {kit['title']}",
"",
"Description:",
"",
"```text",
kit["description"],
"```",
"",
"## Chapters",
"",
]
lines.extend(
f"- `{chapter['timecode']}` {chapter['title']} ({chapter['duration_seconds']}s)"
for chapter in kit["chapters"]
)
lines.extend(["", "## Upload Checklist", ""])
for item in kit["upload_checklist"]:
status = "OK" if item["ok"] else "PENDING"
lines.append(f"- {status} `{item['id']}`: {item['label']} - {item['detail']}")
lines.extend(
[
"",
"## Devpost Field",
"",
f"- Field: `{kit['devpost_field']['field_id']}`",
f"- Ready: `{str(kit['devpost_field']['ready']).lower()}`",
f"- Value: `{kit['devpost_field']['value']}`",
"",
"## Claim Boundary",
"",
kit["claim_boundary"],
"",
"## Next Actions",
"",
]
)
lines.extend(f"- {action}" for action in kit["next_actions"])
lines.append("")
return "\n".join(lines)
def write_outputs(kit: dict[str, Any], json_path: Path, markdown_path: Path) -> None:
json_path.parent.mkdir(parents=True, exist_ok=True)
markdown_path.parent.mkdir(parents=True, exist_ok=True)
json_path.write_text(json.dumps(kit, indent=2) + "\n", encoding="utf-8")
markdown_path.write_text(render_markdown(kit), encoding="utf-8")
def build_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(description="Build the ProofFrame final video publishing kit.")
parser.add_argument("--root", type=Path, default=ROOT)
parser.add_argument("--json-out", type=Path, default=DEFAULT_JSON)
parser.add_argument("--markdown-out", type=Path, default=DEFAULT_MD)
parser.add_argument("--strict-final", action="store_true", help="Fail unless the final video URL is ready.")
return parser
def main() -> None:
args = build_parser().parse_args()
kit = build_kit(args.root)
write_outputs(kit, args.json_out, args.markdown_out)
print(
json.dumps(
{
"ok": kit["safe_to_submit"] if args.strict_final else kit["ok"],
"mode": kit["mode"],
"safe_to_submit": kit["safe_to_submit"],
"final_video_ready": kit["final_video_ready"],
"json": str(args.json_out),
"markdown": str(args.markdown_out),
},
indent=2,
)
)
if args.strict_final and not kit["safe_to_submit"]:
raise SystemExit(2)
if not kit["ok"]:
raise SystemExit(1)
if __name__ == "__main__":
main()