8254ccde86
- Dockerfile + docker-compose.yml: two services (recorder + web) sharing ./recordings bind mount; recorder maps /dev/snd for ALSA soundcard access - requirements.txt: requests, numpy, soundfile - .dockerignore, updated .gitignore (add __pycache__, .pytest_cache) - isr.py: add SIGTERM handler for clean Docker shutdown; fix stale error message that referenced removed PulseAudio/PipeWire/PortAudio backends - web.py: translate all German UI strings to English - config.example.ini: remove PipeWire/PulseAudio/PortAudio backend refs, simplify soundcard tips to ALSA only - README.md: full rewrite as user guide (quick start, config reference, Docker notes, how it works) - CLAUDE.md: update architecture section to reflect ALSA-only backend - Delete changelog.txt and guide.md (internal session notes)
70 lines
3.0 KiB
Markdown
70 lines
3.0 KiB
Markdown
# CLAUDE.md
|
|
|
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
|
|
## 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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
# 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
|
|
|
|
## 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
|
|
|
|
In Docker, set `output_directory = /recordings` and `log_file = /recordings/recorder.log`.
|
|
|
|
## Docker
|
|
|
|
Two services share a `./recordings` bind mount:
|
|
- `recorder` — runs `isr.py`, maps `/dev/snd` for ALSA access
|
|
- `web` — runs `web.py`, read-only access to recordings, exposes port 8080
|