festifun/backend/festival4d/ingest.py
m3ultra cf6a6fb2c8 Foundation (M0 + M3 + frozen contracts): scaffold, synthetic fixtures, full API
What now works:
- M0 scaffold: pyproject (all spec deps), uv/py3.12 env, `python -m festival4d`
  CLI registering synthetic|ingest|sync|reconstruct|events|serve. Vite hello page.
- Synthetic fixture (synthetic.py): 3 shifted-audio videos (offsets 0/+1370/-842 ms),
  camera-arc poses, stage point cloud -> points.ply, seeded events + anchors,
  ground_truth.json. `python -m festival4d synthetic` populates data/ + DB.
- DB schema exactly per spec §2 (db.py) + CRUD helpers all lanes use.
- M3 API (api.py) full against synthetic data: manifest/poses/pointcloud/anchors/
  events/detect/annotations; Range-capable video serving (206 verified); CORS for any
  localhost origin.
- Frozen geometry contract: geometry.colmap_to_threejs (M5 math) + unit test (3 known
  vectors, random round-trip, scipy oracle); mirrored frontend/src/lib/pose.js with
  identical POSE_TEST_VECTORS. Lane-B stubs: slerp_pose, ray_from_pixel, triangulate_rays,
  nearest_point_on_ray.
- Classifier contract (events_ai.py): MomentClassification model + MomentClassifier
  protocol + Gemini/Claude/Local provider stubs.
- Lane-owned modules stubbed with final signatures (ingest, audio_sync, frames, sfm,
  events_ai); cli/api catch NotImplementedError and degrade gracefully.
- plan/CHANGE_REQUESTS.md created; plan/status/foundation.md updated.

Acceptance: pytest 24 passed; serve endpoints verified via curl + browser (video seek,
manifest fetch cross-origin, pose.js self-test, 0 console errors).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 00:45:55 +10:00

32 lines
1.2 KiB
Python

"""Video ingest: probe files in ``data/raw`` and extract audio (spec M1, lane A).
STUB — lane A fills the bodies; the public signatures below are frozen (``cli.py`` calls
``run_ingest``). For each file in ``config.RAW_DIR``: ffprobe it, insert/update the
``videos`` row via ``db`` helpers, and extract a mono 16 kHz WAV into ``config.AUDIO_DIR``.
"""
from __future__ import annotations
import logging
from pathlib import Path
log = logging.getLogger("festival4d.ingest")
def probe_video(path: Path) -> dict:
"""ffprobe a video; return ``{duration_s, fps, width, height}`` (lane A / M1)."""
raise NotImplementedError("lane A (M1): implement probe_video")
def extract_audio(path: Path, out_wav: Path, sample_rate: int = 16_000) -> Path:
"""Extract a mono ``sample_rate`` WAV from ``path`` via ffmpeg (lane A / M1)."""
raise NotImplementedError("lane A (M1): implement extract_audio")
def run_ingest() -> list[dict]:
"""Ingest every video in ``config.RAW_DIR``: probe, register in DB, extract audio.
Entrypoint for ``python -m festival4d ingest``. Returns a per-video summary list.
"""
raise NotImplementedError("lane A (M1): implement run_ingest")