For each asset in the migration manifest, download the Vimeo source master into the archive for re-upload to YouTube. Verify integrity (duration/size) against the expected metadata so nothing corrupt gets re-uploaded.
_data/vimeo_migration_manifest.yml (27 entries). Iterate items[], skip any with migration_state past pending.videos/ (already gitignored: .gitignore has videos/* + !videos/index.html), one file per asset named by id, e.g. videos/masters/<asset_id>__<vimeo_id>.mp4. Do NOT commit binaries.download scope); the public player URL is not a master. Confirm access on 1 asset before batching.Range/Content-Length); yt-dlp/vimeo-dl as fallback for the highest-progressive rendition.<asset_id>__<vimeo_id>. Before downloading: if the target file exists AND passes integrity (below), skip (idempotent). If a .part/partial exists, resume via HTTP Range rather than restart.videos/masters/.tmp/<fp>.part, atomically rename to final path only after integrity passes. Record outcome back into the manifest by flipping migration_state: pending → downloaded (a review-surface update, not raw content).ffprobe and compare to the manifest’s duration_seconds (sourced from the vimeo platform entry in video_assets.yml) within a tolerance (±2s). This is the authoritative expected value.ffprobe width/height to context/interviews-history/metadata/*___<vimeo_id>.json (width/height) where available.Content-Length fully received (bytes == server length), and a plausible-bitrate sanity check (size vs duration×resolution not absurdly low → catches truncated/corrupt)..part, migration_state stays pending) and reported; never promoted.videos/masters/.duration_seconds) + resolution (vs metadata JSON) + full-length/non-zero sanity; failures quarantined & listed.Read-only against repo data; writes only to gitignored videos/ + manifest state field. No upload here (TASK-253). No API calls performed during planning.