Web service that serves individual frames of Stingray (towed shadowgraph imager) videos to browsers. Sits in front of source AVIs, encodes frames on demand, and (Phase 2+) caches encoded frames in VAST S3.
Design overview: DESIGN.md.
Phase 1 (on-the-fly extraction) works end-to-end: manifest ingest, frame extraction, PNG/JPEG encoding, and the public HTTP contract are all implemented and tested. The Phase 2 VAST S3 cache, the /neighbors endpoint, and the bulk backfill are still to come — see DESIGN.md §3.
python3 -m venv .venv
.venv/bin/pip install -e ".[dev]"The amplify-db-utils and amplify-storage-utils dependencies are pinned to their git repositories in pyproject.toml; pip resolves them automatically. No separate editable installs of sibling checkouts are required.
.venv/bin/pytestSTINGRAY_STORE_ROOT=./.dev-store \
.venv/bin/uvicorn stingray_frame_viewer.app:create_app --factory --port 8000Then:
curl http://localhost:8000/health→{"status":"ok"}curl http://localhost:8000/videos/<video_id>→ JSON metadatacurl -o frame.png http://localhost:8000/frames/<video_id>/0→ PNG (default)curl -o frame.jpg http://localhost:8000/frames/<video_id>/0?format=jpeg→ JPEG
.venv/bin/python scripts/inspect_avi.py /path/to/sample.aviPrints dimensions, dtype, channel count, and total frame count. Used once during initial bring-up to confirm Stingray AVIs are 8-bit grayscale; the measured values are recorded as a comment block at the top of src/stingray_frame_viewer/extractor.py.
.venv/bin/python -m stingray_frame_viewer.ingest \
--csv "/path/to/cruise/*.csv" \
--store-root ./.dev-store \
[--exclude "*_fast.csv"] \
[--frames]--csv and --exclude are both repeatable. --exclude takes an fnmatch pattern tested against each CSV's full path (* spans /), so --exclude "*_fast.csv" drops fast-export variants and --exclude "*/ISIIS2/20250418_AR88.csv" drops one specific file without affecting same-named files in other directories.
The ingest CLI is append-only and refuses to write to a (cruise, camera) partition that already exists in the manifest. Re-ingesting an existing cruise requires manually clearing the store and starting over.
See .env.example. The ingest CLI honors STINGRAY_STORE_ROOT as a fallback for --store-root; the service requires it.