Spaces:
Sleeping
Sleeping
| # Hit review and progress streaming | |
| Last updated: 2026-05-12 | |
| ## Purpose | |
| This pass moves the app closer to a review workstation by making detected hits individually inspectable and by replacing frontend-only polling with a server-sent-events progress channel. | |
| ## Implemented behavior | |
| | Area | Implementation | Files | | |
| |---|---|---| | |
| | Review hit artifacts | Every accepted detected hit is written as an individual WAV under `review/hits/`. | `pipeline_runner.py` | | |
| | Manifest hit rows | `manifest.json` now includes a top-level `hits` array with onset, duration, label, cluster, representative flag, and relative file path. | `pipeline_runner.py` | | |
| | Hit URLs | API serialization adds direct download/playback URLs to every hit row. | `app.py` | | |
| | Waveform selection | Clicking the waveform selects the nearest detected onset marker. | `web/app.js` | | |
| | Hit audition | Clicking a hit row or waveform marker loads that hit into the selected-hit audio player. | `web/index.html`, `web/app.js` | | |
| | Sample audition | Representative sample rows now have explicit Audition buttons and a dedicated selected-sample player. | `web/index.html`, `web/app.js` | | |
| | SSE progress | `GET /api/jobs/{job_id}/events` streams job snapshots whenever state changes. | `app.py`, `web/app.js` | | |
| | Poll fallback | The frontend falls back to polling if `EventSource` is unavailable or errors. | `web/app.js` | | |
| | Artifact serving hardening | File downloads now use `Path.relative_to()` against the resolved run output directory. | `app.py` | | |
| ## Manifest shape additions | |
| Completed results now include: | |
| ```json | |
| { | |
| "hits": [ | |
| { | |
| "index": 0, | |
| "label": "kick", | |
| "cluster_id": 3, | |
| "cluster_label": "kick_0", | |
| "is_representative": true, | |
| "onset_sec": 0.002993, | |
| "duration_ms": 255.0, | |
| "rms_energy": 0.141768, | |
| "spectral_centroid_hz": 773.4, | |
| "file": "review/hits/hit_00000_kick.wav" | |
| } | |
| ] | |
| } | |
| ``` | |
| API responses add `url` to each hit row, for example: | |
| ```json | |
| { | |
| "file": "review/hits/hit_00000_kick.wav", | |
| "url": "/api/jobs/<job-id>/files/review/hits/hit_00000_kick.wav" | |
| } | |
| ``` | |
| The `overview.onsets` entries now also carry `index` and `duration_sec`, allowing the waveform to map markers back to review hit rows. | |
| ## Streaming endpoint | |
| `GET /api/jobs/{job_id}/events` returns `text/event-stream`. | |
| Each emitted event has type `job` and contains the same serialized shape as `GET /api/jobs/{job_id}`: | |
| ```text | |
| event: job | |
| data: {"id":"...","status":"running",...} | |
| ``` | |
| The stream ends after `complete` or `error`. Completed historical jobs stream one final event and then close. | |
| ## Current limitations | |
| - Hit review is read-only. It does not yet support delete/shift/relabel actions. | |
| - Every accepted hit is exported as a WAV. This is correct for review UX, but large files with thousands of hits may produce many small artifacts. | |
| - SSE streams job snapshots, not fine-grained internal Demucs progress. | |
| - The waveform is an overview canvas, not an editable detailed waveform yet. | |
| ## Next editor step | |
| Add an edit state layer on top of the hit manifest: | |
| 1. Mark hit deleted/restored. | |
| 2. Shift onset and duration bounds. | |
| 3. Reassign cluster label. | |
| 4. Merge/split clusters. | |
| 5. Re-render/repack from edited manifest without rerunning Demucs or onset detection. | |