- Add VideoCapture with CameraX for MP4 recording via POST /record - Add GET /captures to list all media with sidecar status - Add GET /sidecar/<filename> for JSON sidecar downloads - Add GET /video/last and /video/<filename> endpoints - New Rust TUI dashboard for WiFi auto-sync to ~/timi/capture - Fix duplicate tvQr id in activity_main.xml - Update api_spec.yaml, AGENTS.md, scicam_api_test.sh
6.0 KiB
AGENTS.md — SciCam Logger
What this is
Android camera app (scicam/) for scientific archiving. Captures images + JSON sidecars. Exposes a NanoHTTPD REST API so a host computer can trigger captures remotely.
Build & Deploy
Host build (requires ANDROID_HOME, JDK 17)
cd scicam
./gradlew assembleDebug
adb install app/build/outputs/apk/debug/app-debug.apk
Docker build (requires Docker; adb stays on host)
cd scicam
./docker-build.sh
adb install app/build/outputs/apk/debug/app-debug.apk
- Min SDK 24, target SDK 34, JVM 17.
- Build files are Groovy DSL (
.gradle), not Kotlin DSL.
API
Server runs on device port 8080. Device IP is shown in the app UI.
api_spec.yaml at repo root is the OpenAPI 3.0 source of truth for endpoints.
| Endpoint | Method | Purpose |
|---|---|---|
/api |
GET | API metadata |
/status |
GET | Current item_id, tags, locked, last_qr, last_capture, auto_capture, debug |
/captures |
GET | List all images and videos on device with sidecar status. |
/photo/last |
GET | Download the most recent capture as JPEG. Returns 404 if none. |
/photo/<filename> |
GET | Download a specific photo by filename (e.g. 20260101_120000_ITEM.jpg). Returns 404 if not found. |
/video/last |
GET | Download the most recent video as MP4. Returns 404 if none. |
/video/<filename> |
GET | Download a specific video by filename (e.g. 20260101_120000_ITEM.mp4). Returns 404 if not found. |
/sidecar/<filename> |
GET | Download JSON sidecar for a given base filename. Returns 404 if not found. |
/settings |
POST | Set item_id, tags, lock (boolean), debug (boolean). Returns updated state. |
/capture |
POST | Trigger photo. Use --max-time 15. Returns {success, filename, uri}. |
/record |
POST | Toggle video recording. Returns {recording, filename, duration_ms}. |
/auto-capture |
POST | Set enabled (boolean). Enables/disables QR auto-capture. Returns updated state. |
/events |
GET | SSE stream. Broadcasts capture results as data: <json>\n\n. |
Critical: POST only for /capture, /settings, and /auto-capture. GET returns 404.
Testing
Run the API client test suite against a device on the same network:
./scicam_api_test.sh <device-ip>
- Requires
jq(used to parse capture-filename responses). - Add new API endpoints to this script as they are added to
SciCamApiServer.kt.
Architecture Notes
MainActivity.ktimplementsSciCamApiServer.ApiListener. The server posts camera control to the UI thread but reads state fields directly.- Images →
Pictures/SciCam/(MediaStore, visible over MTP). Sidecars →Documents/SciCam/. MetadataLogger.ktbuilds sidecar JSON with EXIF +camera_settings.- BoofCV is wired in for in-preview QR detection (replaced the ZXing Activity hop). CameraX + MediaStore storage unchanged.
capture()blocks up to 5 s waiting for the CameraX callback; curl callers should still use--max-time 15.
Clients
Cross-platform API clients live in clients/ as Git submodules. Upstreams point to Gitea tami/ org. On a fresh clone, run:
git submodule update --init
Gitea Workflow
- Remotes point to
https://git.telavivmakers.space. The tami org owns all repos (tami/timi,tami/go-scicam,tami/web-scicam). - Token lives at
~/.config/timi/gitea-token.~/.bashrcexports it asGITEA_SERVER_TOKEN. - No
teaCLI installed. For API operations (create/delete repos, transfer), use rawcurlagainst the Gitea REST API:curl -s -X POST "https://git.telavivmakers.space/api/v1/org/tami/repos?token=$(cat ~/.config/timi/gitea-token)" \ -H "Content-Type: application/json" -d '{"name":"repo-name","private":false}' - HTTPS push requires embedding the token in the URL, then stripping it afterward so credentials are not written to
.git/config:git remote set-url origin "https://$(cat ~/.config/timi/gitea-token)@git.telavivmakers.space/tami/REPO.git" git push -u origin master git remote set-url origin https://git.telavivmakers.space/tami/REPO.git
Go CLI (clients/go-scicam/)
cd clients/go-scicam
go build -o go-scicam
go test ./...
# Usage
./go-scicam status 192.168.1.42
./go-scicam settings 192.168.1.42 -item-id=ITEM-001 -tags=archive,lab -lock=true
./go-scicam capture 192.168.1.42
./go-scicam snapshot 192.168.1.42 -o custom.jpg
./go-scicam watch 192.168.1.42 -auto-download
./go-scicam photo -filename=20260101_120000_ITEM.jpg -o output.jpg
The CLI reads default values from scicam/.env in the current working directory (key scicam_host_ip). The SCICAM_HOST environment variable overrides the file. scicam/.env is gitignored.
Web Client (clients/web-scicam/)
Static vanilla-JS page. Open index.html in a browser or serve with any static server.
cd clients/web-scicam
node --test test.js
Dashboard (dashboard/)
Terminal UI for monitoring and auto-syncing multiple SciCam devices to ~/timi/capture.
Built in Rust with ratatui + tokio.
cd dashboard
cargo build --release
./target/release/scicam-dashboard
Key controls:
↑/↓j/k— Navigate devicesa— Add device (enter Name + IP)d— Delete selected devicer/R— Refresh one / all devicess/S— Sync one / all devices noww— Toggle SSE watch (live capture events)q— Quit
The dashboard polls registered devices every 10s. Devices marked auto_sync download missing .jpg, .mp4, and .json sidecars automatically. Manual syncs via s/S are always available.
Device registry lives at ~/.config/scicam-dashboard/config.toml. capture_dir defaults to ~/timi/capture.
Future Roadmap (do not break)
- Database sync is planned. It must be non-destructive to images/sidecars.
- This is an archive and dataset for audio-visual inventory. Treat existing files as immutable.
- The API-first UI (
api-firstbranch heritage) is the primary interface for automated capture.