The forward-skip threshold was `cur + 0.5`, which is smaller than
the preroll offset applied when landing on a section. After K placed
the cursor at `s.start - preroll`, the next K press found the same
section again because `s.start > (s.start - preroll) + 0.5` is true
whenever preroll > 0.5. Using `cur + preroll` as the threshold ensures
the current section's start is not > cur + preroll, so only the next
section matches.
Cut metadata: WAV and FLAC are now re-encoded (pcm_s16le / flac)
instead of stream-copied, so the container header is always rewritten
with the correct duration. Lossy formats keep -c copy.
J/K shortcuts: now also update the cut panel start/end fields to match
the section they landed on, and announce the section aloud.
Screen reader: added aria-live polite region; chip clicks and J/K
announce 'Section N of M: start to end'; boundary presses announce
'Beginning of sections' or 'End of sections'.
Pre-roll: configurable 'Pre-roll' seconds input in the controls bar
(default 3 s); all section seeks subtract the pre-roll from the target
position so the listener hears context before the loud section begins.
Adds a Cut panel inside every player row (✂ Cut button in the actions
column opens the row and focuses the Start field). Users type start and
end times in m:ss or h:mm:ss format; Download cut triggers
GET /api/cut which runs ffmpeg -c copy (no re-encode) and streams the
result as an attachment.
Clicking an analysis chip now also pre-fills the cut panel start/end
with the loud-section boundaries.
Server switched to ThreadingHTTPServer so ffmpeg runs don't block
other requests. ffmpeg added to Dockerfile apt install.
Client-side filter bar with live filename search and from/to date
pickers. Rendering is split into renderFiles() + applyFilters() so
filters can be re-applied without re-fetching. Subtitle shows
'N of M shown' when a filter is active. Clear button resets all fields.
Analysis chips are now buttons. Clicking one opens the player (if
not already open) and seeks to the section start. J skips to the
previous loud section, K to the next. Shortcuts are suppressed when
focus is inside an input field.
With preload=none the browser never fetches metadata, so Chrome
cannot populate the duration field for FLAC files. On player open:
set preload=metadata and call audio.load() to trigger a metadata-only
fetch. Also render a server-computed duration label beneath the audio
element as a fallback for formats the browser cannot parse.
WAV nframes and FLAC total_samples are both unfinalized while the
recorder has the file open, producing wildly wrong durations
(e.g. 53375995583:39:01). Return None (shown as —) instead.
2026-04-29 19:54:38 +02:00
3 changed files with 342 additions and 42 deletions
Shows a table of all recordings sorted newest-first. Features:
- **Inline playback** — collapsible `▶ Play` button per row; audio loads lazily via a seekable `/stream/` endpoint with HTTP Range support.
- **Inline playback** — collapsible `▶ Play` button per row; audio loads lazily via a seekable `/stream/` endpoint with HTTP Range support. Metadata is fetched immediately so the duration is visible without pressing play.
- **Waveform analysis** — on demand per file; computes RMS per 100 ms window and highlights loud sections. Supported for WAV and FLAC (FLAC requires `numpy` + `soundfile`). Pure-Python fallback for WAV when numpy is absent.
- **Timestamp jump** — after analysis, click any loud-section chip to seek the player to that position and pre-fill the cut panel. Use **J** / **K** keyboard shortcuts to jump to the previous / next section while audio is playing.
- **Cut & download** — `✂ Cut` button opens the player row and reveals a cut panel. Enter start and end times in `m:ss` or `h:mm:ss` format and click **↓ Download cut** to receive an ffmpeg-trimmed copy without re-encoding. Requires ffmpeg (included in the Docker image).
- **Filters** — live filename search and from/to date pickers above the table; applied client-side with no additional requests. Shows `N of M shown` when a filter is active.
- **Delete** — `✕ Delete` button per row with confirmation prompt; disabled for files currently being recorded; sends `DELETE /api/files/<name>` and removes the row without a full page reload.
- **Live REC badge** — files currently being written by `isr.py` show an animated REC indicator, polled every 5 seconds via `/api/status`.
- **Live REC badge** — files currently being written by `isr.py` show an animated REC indicator, polled every 5 seconds via `/api/status`. Duration for in-progress files shows `—` (header is unfinalized until recording stops).
- **WCAG-compliant** — skip link, `aria-expanded`/`aria-controls` on the player toggle, `aria-live` status, focus management, `role=img` on SVG waveforms.
print(f"ISR Web running → http://{args.host}:{args.port}/")
print(f"Recordings dir → {rec_dir.resolve()}")
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.