Files
ISR/CLAUDE.md
T
admin f84b36687b docs: update README for Docker fix, FLAC/streaming features; add README rule to CLAUDE.md
- Remove stale instruction to set output_directory = /recordings in Docker
- Document that docker compose down + up --build is required when updating
- Fix output_directory and log_file table descriptions
- Expand Web UI section: inline playback, FLAC waveform, live REC badge, WCAG
- Fix Docker notes: log_file path and log_file in Docker guidance
- Add rule to CLAUDE.md: always update and commit README.md with code changes
2026-04-26 13:17:26 +02:00

4.9 KiB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Rules

  • Always update README.md whenever user-facing behaviour changes (new flags, new endpoints, changed Docker setup, new features). The README is the primary external reference; CLAUDE.md documents internals.
  • Always commit README.md in the same commit as the code changes it documents — never let the README fall behind.

Project Overview

ISR is a Python audio recording application that captures from multiple simultaneous sources (Icecast/HTTP streams and ALSA soundcard devices) with time-based file splitting. All application code is in two files: isr.py (recorder) and web.py (archive browser UI).

Commands

# Run the recorder
python isr.py                     # uses config.ini
python isr.py myconfig.ini        # custom config file
python isr.py --list-devices      # list available ALSA devices

# Run the web UI
python web.py                          # http://localhost:8080
python web.py --dir recordings         # custom recordings directory
python web.py --port 8888              # custom port
python web.py --threshold 0.03         # loudness threshold (0-1, default 0.05)

# Stop: Ctrl+C (or docker compose down)

# Install dependencies
pip install requests              # for stream recording
pip install numpy soundfile       # for FLAC output and web waveform analysis (optional)

# Docker
docker compose up -d
docker compose logs -f
docker compose down

Architecture

Audio Backend System

  • AudioDevice — Dataclass: id, name, channels, sample_rate, backend type
  • AudioBackend (ABC) — Abstract base for audio capture backends
    • ALSABackend — Native ALSA support via arecord subprocess (the only backend)
  • ALSAStream — Context manager that wraps an arecord subprocess and reads PCM in a thread
  • AudioSystem — Discovers available backends, lists devices, resolves device specs

Recorder Classes

  • BaseRecorder (ABC) — Common settings, get_next_split_time(), generate_filename(), record() interface
  • StreamRecorder(BaseRecorder) — Records HTTP/Icecast streams with format auto-detection and OGG/FLAC header injection
  • SoundcardRecorder(BaseRecorder) — Records from ALSA devices; outputs WAV or FLAC via _AudioFileWriter
  • _AudioFileWriter — Unified write/close interface for wave (WAV) and soundfile (FLAC)
  • RecorderManager — Loads config, creates recorders, manages threads, handles shutdown

Key Implementation Details

  • ALSA backend spawns arecord as a subprocess; raw PCM is read in 100 ms chunks via a reader thread
  • Device selection: default, monitor (loopback), partial name match, or exact hw:X,Y ID
  • Thread-safe audio buffering with threading.Lock()
  • OGG/Opus/FLAC headers captured from first ~16 KB of stream and prepended to each split file
  • File splits aligned to time period boundaries (get_next_split_time())
  • SIGTERM handled in main() so Docker docker compose down shuts down cleanly
  • RecorderManager._write_status() atomically writes recordings/status.json every 2 s while running; deleted on clean shutdown so the web UI shows no stale active-recording badges

Web UI (web.py)

  • GET / — Single-page archive table; lists all recordings sorted newest first
  • GET /api/files — JSON list of file metadata (name, size, date, duration, ext, recording flag)
  • GET /api/analyze?file=<path> — RMS loudness analysis for WAV and FLAC files; returns waveform data, loud sections, and duration. Requires numpy and soundfile for FLAC.
  • GET /api/status — Returns {"active": [...]} from status.json; used by the UI to animate the REC badge on in-progress files (polled every 5 s)
  • GET /stream/<path> — Serves audio for inline <audio> playback with full HTTP Range support (seekable). Responds 206 Partial Content for range requests. Files are served with Content-Disposition: inline.
  • GET /download/<path> — Serves audio as a file download (Content-Disposition: attachment)
  • All paths are validated against the recordings directory to prevent path traversal.

Configuration

Copy config.example.ini to config.ini. Each section defines a recording source:

  • type = stream — HTTP/Icecast stream recording
  • type = soundcard — ALSA device recording

The output_directory value is used as-is: a relative path like recordings resolves to recordings/ next to isr.py. No Docker-specific config change is needed — the docker-compose.yml mounts ./recordings at /app/recordings to match this default.

Docker

Two services share a ./recordings bind mount:

  • recorder — runs isr.py; volume mounted at /app/recordings (matches output_directory = recordings); maps /dev/snd for ALSA access; stop_grace_period: 30s so open files are closed before SIGKILL
  • web — runs web.py; same ./recordings bind mounted read-only at /recordings; exposes port 8080