#!/usr/bin/env python3 """Fail closed until a downloaded video job reaches its required deliverable.""" from __future__ import annotations import argparse import hashlib import json from pathlib import Path import re import sys from typing import Any, Sequence class DeliveryError(RuntimeError): """A malformed or unreadable delivery job.""" def _read_json(path: Path) -> dict[str, Any]: try: value = json.loads(path.read_text(encoding="utf-8")) except FileNotFoundError as exc: raise DeliveryError(f"manifest not found: {path}") from exc except json.JSONDecodeError as exc: raise DeliveryError(f"invalid JSON in {path}: {exc}") from exc if not isinstance(value, dict): raise DeliveryError(f"manifest root must be an object: {path}") return value def _artifact_path(job_dir: Path, value: Any) -> Path | None: if not isinstance(value, dict) or not isinstance(value.get("path"), str): return None path = Path(value["path"]) return path if path.is_absolute() else job_dir / path def _sha256_file(path: Path) -> str: digest = hashlib.sha256() with path.open("rb") as handle: for chunk in iter(lambda: handle.read(1024 * 1024), b""): digest.update(chunk) return digest.hexdigest() def _verify_citation_watermark(job_dir: Path, download: dict[str, Any], burned: Path) -> None: citation = download.get("citation_watermark") if not isinstance(citation, dict) or citation.get("enabled") is not True: return citation_name = citation.get("citation_file") citation_hash = citation.get("citation_sha256") if not isinstance(citation_name, str) or Path(citation_name).name != citation_name: raise DeliveryError("citation_watermark.citation_file must be a plain filename") if not isinstance(citation_hash, str) or not re.fullmatch(r"[0-9a-fA-F]{64}", citation_hash): raise DeliveryError("citation_watermark.citation_sha256 is invalid") citation_path = job_dir / citation_name if not citation_path.is_file() or _sha256_file(citation_path) != citation_hash.lower(): raise DeliveryError("citation watermark text is missing or its checksum changed") receipt_path = burned.with_suffix(burned.suffix + ".citation.json") if not receipt_path.is_file(): raise DeliveryError(f"citation watermark burn receipt is missing: {receipt_path}") receipt = _read_json(receipt_path) expected = { "output_file": burned.name, "output_sha256": _sha256_file(burned), "citation_sha256": citation_hash.lower(), "position": "top-left", } for key, value in expected.items(): if receipt.get(key) != value: raise DeliveryError(f"citation watermark receipt has a stale or invalid {key}") def _existing_video_artifact(job_dir: Path, artifacts: dict[str, Any]) -> Path | None: records = [artifacts.get("lossless_mp4_master"), artifacts.get("intermediate")] fallback = artifacts.get("lossy_mp4_fallback") if isinstance(fallback, dict): records.append(fallback.get("created")) for record in records: path = _artifact_path(job_dir, record) if path is not None and path.is_file() and path.stat().st_size: return path return None DELIVERABLES = ("full", "video", "subs", "bilingual-subs") def assess_delivery(download_manifest: Path) -> dict[str, Any]: download_manifest = download_manifest.expanduser().resolve() download = _read_json(download_manifest) configured_dir = download.get("output_directory") job_dir = ( Path(configured_dir).expanduser().resolve() if isinstance(configured_dir, str) else download_manifest.parent ) deliverable = download.get("deliverable") if deliverable not in DELIVERABLES: deliverable = "full" artifacts = download.get("artifacts") if not isinstance(artifacts, dict): raise DeliveryError("download manifest has no artifacts object") if deliverable in ("full", "video") and _existing_video_artifact(job_dir, artifacts) is None: raise DeliveryError("no declared video artifact exists on disk") def complete(stage: str, **extra: Any) -> dict[str, Any]: return { "complete": True, "stage": stage, "deliverable": deliverable, "job_dir": str(job_dir), "missing": [], **extra, } subtitle_record = artifacts.get("subtitle") subtitle = ( _artifact_path(job_dir, subtitle_record.get("source_srt")) if isinstance(subtitle_record, dict) else None ) if subtitle is not None and not subtitle.is_file(): raise DeliveryError(f"declared source subtitle is missing: {subtitle}") if deliverable in ("subs", "bilingual-subs") and subtitle is None: raise DeliveryError( "a subtitle delivery was requested, but the manifest declares no source subtitle" ) has_dialogue = ( subtitle is not None and isinstance(subtitle_record, dict) and subtitle_record.get("dialogue") is not False ) if deliverable == "video": return complete("video_complete") if deliverable == "subs": return complete("subs_complete") if not has_dialogue: # full falls back to plain video; bilingual-subs still delivered the # source subtitle files even though nothing was translatable. stage = "video_only_complete" if deliverable == "full" else "subs_complete" return complete(stage) subtitle_dir = job_dir / "subtitles" subtitle_manifest_path = subtitle_dir / "subtitle-manifest.json" if not subtitle_manifest_path.is_file(): return { "complete": False, "stage": "subtitle_prepare_required", "job_dir": str(job_dir), "missing": [str(subtitle_manifest_path)], } subtitle_manifest = _read_json(subtitle_manifest_path) batches = subtitle_manifest.get("translation_batches") if not isinstance(batches, list) or not batches: raise DeliveryError("subtitle manifest has no translation batches") output_dir_value = subtitle_manifest.get("translation_output_dir") translation_output_dir = ( Path(output_dir_value) if isinstance(output_dir_value, str) else subtitle_dir / "translation-output" ) missing_batches: list[str] = [] for batch in batches: if not isinstance(batch, dict) or not isinstance(batch.get("path"), str): raise DeliveryError("subtitle manifest has an invalid translation batch") name = Path(batch["path"]).name if not (translation_output_dir / name).is_file(): missing_batches.append(name) if missing_batches: return { "complete": False, "stage": "translation_required", "job_dir": str(job_dir), "missing": missing_batches, } execution = download.get("execution") scientific_review_required = ( isinstance(execution, dict) and execution.get("scientific_review_required") is True ) scientific_review_report_path = subtitle_dir / "scientific-review" / "report.json" scientific_review: dict[str, Any] | None = None if scientific_review_required: reviewed_translations = ( subtitle_dir / "scientific-review" / "reviewed-translations" / "translations.json" ) missing_review = [ str(path) for path in (scientific_review_report_path, reviewed_translations) if not path.is_file() ] if missing_review: return { "complete": False, "stage": "scientific_review_required", "job_dir": str(job_dir), "missing": missing_review, } scientific_review = _read_json(scientific_review_report_path) if scientific_review.get("status") != "complete": raise DeliveryError("scientific review report is incomplete") if scientific_review.get("human_expert_reviewed") is not False: raise DeliveryError("scientific review report makes an invalid human-review claim") if scientific_review.get("subtitle_manifest_sha256") != _sha256_file( subtitle_manifest_path ): raise DeliveryError("scientific review report is bound to a different subtitle manifest") rendered_dir = subtitle_dir / "rendered" required_rendered = [rendered_dir / "bilingual.ass", rendered_dir / "validation.json"] missing_rendered = [str(path) for path in required_rendered if not path.is_file()] if missing_rendered: return { "complete": False, "stage": "render_required", "job_dir": str(job_dir), "missing": missing_rendered, } if scientific_review_required: validation = _read_json(rendered_dir / "validation.json") binding = validation.get("scientific_review") if validation.get("translation_quality_reviewed") is not True or not isinstance(binding, dict): raise DeliveryError("rendered subtitles did not use the scientific-review gate") if binding.get("report_sha256") != _sha256_file(scientific_review_report_path): raise DeliveryError("rendered subtitles use a stale scientific review report") if deliverable == "bilingual-subs": return complete("bilingual_subs_complete", rendered_dir=str(rendered_dir)) delivery_names = download.get("delivery_names") burned_name = ( delivery_names.get("bilingual_video") if isinstance(delivery_names, dict) else None ) if isinstance(burned_name, str) and burned_name: if Path(burned_name).name != burned_name: raise DeliveryError("delivery_names.bilingual_video must be a plain filename") burned = job_dir / burned_name if burned.is_file() and burned.stat().st_size: _verify_citation_watermark(job_dir, download, burned) return complete("bilingual_complete", burned_video=str(burned)) missing = [str(burned)] else: legacy = sorted( path for path in job_dir.glob("*.bilingual.mp4") if path.is_file() and path.stat().st_size ) if legacy: _verify_citation_watermark(job_dir, download, legacy[-1]) return complete("bilingual_complete", burned_video=str(legacy[-1])) missing = ["*.bilingual.mp4"] if missing: return { "complete": False, "stage": "burn_required", "job_dir": str(job_dir), "missing": missing, } raise AssertionError("unreachable") def _parser() -> argparse.ArgumentParser: parser = argparse.ArgumentParser( description="Check whether a video job is video-only complete or bilingual complete." ) parser.add_argument("download_manifest", type=Path) return parser def main(argv: Sequence[str] | None = None) -> int: args = _parser().parse_args(argv) try: result = assess_delivery(args.download_manifest) except (DeliveryError, OSError) as exc: print(f"delivery verification error: {exc}", file=sys.stderr) return 2 print(json.dumps(result, ensure_ascii=False, sort_keys=True)) return 0 if result["complete"] else 3 if __name__ == "__main__": raise SystemExit(main())