Job Dashboard¶
The dashboard is a live view of every download, not a page you reload. Job state is pushed to the browser over Server-Sent Events.
Live updates¶
Two SSE endpoints exist:
| Endpoint | Scope |
|---|---|
GET /events | Every job — powers the dashboard list |
GET /api/jobs/{job_id}/events | One job — powers the job detail page |
| Property | Value |
|---|---|
| Keep-alive interval | 5 s |
| Per-client queue | 32 events |
| Max concurrent connections | 200 |
| Rate limit | 30/minute per client |
The event broker is process-local
Subscribers only receive events published by the same Python process. This is why WORKERS must stay at 1; with a second Gunicorn worker, clients would subscribe to a process that never sees their job. See Resources & Workers.
If a client's queue overflows — a browser tab suspended in the background, for instance — the connection is dropped rather than allowed to grow unbounded. The page reconnects and re-reads the current state from GET /api/jobs.
Job statuses¶
| Status | Terminal | Meaning |
|---|---|---|
queued | Waiting for a worker slot | |
processing | Metadata extraction and format selection | |
downloading | yt-dlp is fetching | |
transcoding | ffmpeg is converting | |
analysis | BPM detection running; the file is already downloadable | |
analysis_done | Finished, with analysis | |
done | Finished, no analysis | |
error | Failed — the message is shown and a retry is offered | |
cancelled | Cancelled by the user |
done and analysis_done both count as completed. analysis additionally counts as downloadable, so an audio file can be fetched while its tempo analysis is still running.
A completed video whose file is VP9 or AV1 (see Universally playable output) carries a Limited playback marker in the job list and the details dialog — it will not open on Safari, iOS, or most TVs.
Job actions¶
| Action | Availability |
|---|---|
| Download | Any downloadable status |
| Open job page | Any job |
| Cancel | While in flight |
| Retry | After error |
| Trim | Audio jobs, downloadable — Audio Trimming |
| Stems | Audio jobs, Lalal.ai connected — Stem Separation |
| Share | Completed jobs — Share Links |
There is no per-job delete action. Jobs are removed either by retention sweeping expired artifacts, or by Settings → System → Remove all jobs, which clears every job at once (see Bulk cleanup below).
Track length¶
duration_seconds is filled twice. At submit time it comes from the source's own metadata, so a job shows its length while it is still queued or downloading. When the download finishes, ffprobe measures the actual file and replaces the value — a probe that comes back empty leaves the source value in place rather than blanking it.
It is rendered as M:SS, or H:MM:SS from an hour up, in the job list (desktop media column and mobile card), the detail dialog, and on the job page. Jobs whose length is unknown show an en dash.
The job page¶
GET /job/{job_id} renders a detail view with:
- The player (in-browser playback of the downloaded file)
- The waveform for audio jobs, with drag-to-select trimming
- Detected BPM and beat confidence, when analysis has run
- Job metadata: source URL, platform, type, quality, duration, file size, timestamps
Statistics¶
GET /api/stats returns aggregate counters for the dashboard — totals by status, bytes downloaded, and the like. The result is cached briefly so a busy dashboard does not re-aggregate the database on every event.
GET /api/stats/bpm-clusters buckets every detected tempo into 5-BPM groups, sorted by count, for the tempo distribution chart.
Settings → System → Reset statistics (POST /api/stats/reset) stamps a reset marker rather than deleting rows, so counters restart without losing job history.
Bulk cleanup¶
Settings → System → Remove all jobs (POST /api/jobs/remove-all, 2/minute) deletes every job row and its on-disk artifacts in one step.
There is no undo
Removing all jobs deletes the downloaded media as well. Any share links pointing at those jobs stop working immediately.
For automatic cleanup by age, use retention instead — see Storage & Retention.
Recovery after a restart¶
Jobs that were processing, downloading, or transcoding when the process stopped cannot be resumed — the subprocess is gone. At startup they are marked cancelled with the message "Cancelled because the application restarted during processing", so they end up in a terminal state you can retry from instead of being stuck in flight forever.