feat: async clip jobs, Siap-unduh state, cross-page ready toast
ci / smoke (push) Canceled after 0s

This commit is contained in:
andrew committed 2026-09-29 09:28:51 +07:00
1 parent 14f5ed4aac
commit 12f921f81c
6 files changed
+878 -66

No files matched your search

+37 -9
View File
@@ -88,17 +88,45 @@ Pages: `/` + `/monitoring`, `/operator` (manual start/stop; DO panel in
`PUT/DELETE /api/do/<id>`, `/api/summary`, `/api/daily-data`,
`/api/day-detail/<date>`, `/api/recent-batches`, `/api/available-dates`,
`/api/export-daily-csv`, `/api/export-day-csv/<date>` (Excel via openpyxl),
`/api/batch-clip/<date>/<batch_number>` (office-only, see below).
`/api/batch-clip/<date>/<batch_number>` + `/status`, `/file`,
`/api/batch-clip/pending` (office-only, see below).
Port split: mode / model_mode / require_plate / require_do / ocr_engine POSTs →
**office 5721 only** (403 on 5000). Smartphones open `http://<host>:5000/operator` for camera capture.
## Batch clip download (motionEye)
History page `Klip` button → `GET /api/batch-clip/<date>/<batch_number>`
(**office port 5721 only**, 403 on 5000) trims + concatenates motionEye
recordings for the batch window (`clip_batch` in `src/clip.py`, ffmpeg
under the hood) and returns the file as an attachment.
History page `Klip` button runs an **async job** (all endpoints **office port
5721 only**, 403 on 5000):
- `POST /api/batch-clip/<date>/<batch_number>` — idempotent: starts the job
or returns the existing one → `200 {"success": true, "status":
"running"|"ready", "job": "<date>/<n>"}`.
- `GET …/<date>/<batch_number>/status` → `{"success": true, "status":
"running"|"ready"|"error", "error": "<msg|empty>", "elapsed": <s>}`
(404 if no job).
- `GET …/<date>/<batch_number>/file` → mp4 attachment (409 while running,
404 if error/none).
- `GET /api/batch-clip/pending` → all jobs, newest first — restores button
state after reload/navigation and feeds the cross-page toast.
Button states: `Klip` → `Memproses… <s> dtk` (disabled; status polled every
1.5 s, `pending` refreshed every 3 s while a job on the page runs) →
`Siap — unduh` → `Unduh lagi` after the first download (instant, file already
exists); failure → `Gagal` (click shows the error). Jobs for other dates are
left to the toast.
Toast: every office page (`templates/base.html`) polls `pending` every 3 s —
bootstrap once, a 403 on the operator port disables polling for that tab —
and shows a non-blocking bottom-right **`Klip siap`** / **`Klip gagal`** card
(auto-dismiss 8 s, click → `/history`). Shown once per job per tab
(`sessionStorage clipNotified`, capped at 50 keys).
Rendering itself is unchanged (`clip_batch` in `src/clip.py`, ffmpeg under
the hood). The finished temp file is kept **≤1 h** so `Unduh lagi`
re-downloads without re-rendering; older `clip_*.mp4` files are swept then.
Jobs live in dashboard **memory** — a `karung-counter-dashboard` restart
drops them (status 404 → button falls back to `Klip`, press again).
- URL keys (`.env`): `MOTIONEYE_URL` (base URL; empty → 404
`motionEye belum dikonfigurasi (MOTIONEYE_URL)`), `MOTIONEYE_CAMERA_ID`
@@ -122,10 +150,10 @@ under the hood) and returns the file as an attachment.
- **Recording retention: motionEye `preserve_movies: 3` → only the last 3 days
of recordings exist.** Older batches return 404 (`ClipError`, no recording in
window).
- Errors: `ClipError` "no recording" → **404**, other `ClipError`
(motionEye/ffmpeg failure) → **502**, unexpected exception → **500**;
`src.clip` missing → **503**. Temp file `$(dirname DB)/tmp/clip_<uuid>.mp4`
is always unlinked (stale `clip_*.mp4` older than 1 h swept on each request).
- Errors are surfaced on the job: `…/status` reports `status: "error"` with
the `error` message (`…/file` → 404), and a failing `POST` answers non-2xx
`{"success": false, "error": "<msg>"}`. Finished file lives at
`$(dirname DB)/tmp/clip_<uuid>.mp4` (kept ≤1 h, then swept).
- Output filename built from the batch **start_time**:
`b{batch}-{YYYY-MM-DD}-{HH-MM-SS}.mp4`
(e.g. batch 21 starting `2026-09-26T14:25:09.123456` →