Series Recording Scheduler for e2proxy — Automatically records TV series and movies from Enigma2 receivers by monitoring the EPG and delegating every recording to e2proxy.
Single Python file, runs in Docker, no own ffmpeg. It scans the EPG, matches your series via regex, and triggers recordings through the e2proxy /api/record/start endpoint — picking the proxy with the most free tuners.
- Series Scheduler — Define series once via regex, every matching episode is recorded automatically
- Movies & Series — Explicit
kind(movie/series) for Plex-compliant paths and.nfometadata - EPG Browser — Interactive timeline grid with one-click recording, "skip this episode", and TMDB artwork
- Quick Record — Record a single episode, a one-off movie, or set up a recurring series straight from the EPG
- Smart Tuner Management — Waits for your own recordings to finish (via
remaining_sec), never fights its own tuners. Overlapping recordings on the same channel share a single tuner instead of being skipped - Multi-Proxy — SSDP auto-discovery, automatically selects the proxy with the most free tuners
- Per-Series Offsets — Start earlier / run longer per series for channels that air early or late
- Back-to-Back Detection — Consecutive recordings on the same channel start 2 min early to avoid time drift
- Keep Last N — Automatic cleanup of old episodes (including
.nfo), keeping only the newest N - TMDB Search — German titles, aliases, and regex suggestions when adding a series
- File Logging — Daily-rotated logs with configurable retention
- Dark/Light Theme — Switchable in settings, shared look with e2proxy
# docker-compose.e2recorder.yml
services:
e2recorder:
image: python:3.11-slim
container_name: e2recorder
restart: unless-stopped
network_mode: host # required for SSDP discovery + LAN access
volumes:
- ./e2recorder.py:/app/e2recorder.py:ro
- ./data:/data
- /mnt/nvme/recordings:/mnt/nvme/recordings
working_dir: /app
command: python3 e2recorder.py
environment:
- PYTHONUNBUFFERED=1
- E2REC_DATA_DIR=/data
healthcheck:
test: ["CMD", "python3", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8889/api/status', timeout=3)"]
interval: 30s
timeout: 5s
retries: 3docker compose -f docker-compose.e2recorder.yml up -dOpen http://your-server:8889 and add your e2proxy instance in Settings (or let SSDP discovery find it).
No Dockerfile, no build — the python:3.11-slim image runs the single script mounted read-only, so updates are just a file swap and a restart.
- Python 3.11+ (no external dependencies — standard library only)
- A running e2proxy instance (v3.2.0+) on the local network
network_mode: host(required for SSDP UDP multicast on port 1900)
┌──────────────┐
│ Browser │
│ EPG / UI │
└──────┬───────┘
│ HTTP :8889
┌──────┴────────────────────────────┐
│ e2recorder │
│ ┌──────────┐ ┌───────────────┐ │
│ │ EPG Scan │ │ Scheduler / │ │
│ │ (regex) │──▶│ Tuner Wait │ │
│ └──────────┘ └───────┬───────┘ │
└─────────────────────────┼──────────┘
│ POST /api/record/start
┌───────┴────────┐
│ e2proxy │
│ HTTP :8888 │
│ ffmpeg + tuners│
└───────┬────────┘
┌───────────┴───────────┐
┌─────┴──────┐ ┌──────┴─────┐
│ Receiver 1 │ │ Receiver 2 │
│ (Enigma2) │ │ (Enigma2) │
└────────────┘ └────────────┘
e2recorder never writes recordings itself — it decides what and when, e2proxy decides where and does the actual ffmpeg work. The recording file path always comes from the e2proxy response (resp["file"]).
First-time setup via the web UI at http://your-server:8889 → Settings:
- Proxies — SSDP discovery or manual URL. Multiple proxies supported — the one with the most free tuners is chosen automatically.
- Stream Profile — Loaded from e2proxy. Recommended:
remux-ac3. - Pre/Post Buffer — Seconds before/after EPG time to cover inaccurate broadcast times.
- EPG Lookahead — How far ahead to scan (default 72 h).
- TMDB API Key — Optional, for poster artwork and German title/regex suggestions.
- Cleanup — Trigger on every new recording (
on_new) or daily at a fixed hour.
All configuration is stored in /data/config.json.
| Tab | Description |
|---|---|
| overview | EPG timeline grid — click a slot to record, hover for details and "skip" |
| serien | Manage series — regex pattern, channel, keep-last, per-series offsets |
| filme | Manage movies — same form, written to the Plex Movies library |
| aufnahmen | Recordings list — sortable/filterable by status, with detail and playback |
| settings | Proxies, stream profile, buffers, EPG, cleanup, logs |
| /help | Feature overview and changelog |
| Endpoint | Method | Description |
|---|---|---|
/api/series |
GET/POST | List / add a series |
/api/series/<id> |
PUT/DELETE | Edit / delete a series |
/api/series/from-epg |
POST | Create a series or one-off recording from an EPG click |
/api/schedule |
GET | Recording plan (EPG events + matches) |
/api/schedule/<id> |
DELETE | Skip a single scheduled recording |
/api/scan |
POST | Trigger an EPG scan |
| Endpoint | Method | Description |
|---|---|---|
/api/recordings |
GET | All recordings |
/api/recordings/<id>/detail |
GET | File path, size, proxy, stream URL |
/api/recordings/<id>/keep |
POST | Protect a recording from cleanup |
/api/recordings/<id> |
DELETE | Delete the DB entry (file stays on disk) |
/api/cleanup |
POST | Run keep-last cleanup for all series |
| Endpoint | Method | Description |
|---|---|---|
/api/proxies |
GET/POST | List / add-update proxies (+ tuner status) |
/api/proxies/remove |
POST | Remove a proxy |
/api/discover |
POST | SSDP discovery |
/api/channels |
GET | Channel list (e2proxy favorites) |
/api/tmdb/search?q= |
GET | TMDB search |
| Endpoint | Method | Description |
|---|---|---|
/api/version |
GET | Version + build ID |
/api/status |
GET | Service status (series, recordings, proxies online) |
/api/config |
GET/POST | Configuration |
/api/logs |
GET | Live logs (?level=INFO) |
/api/logs/files |
GET | Rotated log files on disk |
/api/tuner/history |
GET | Last 200 tuner decisions |
/api/log/level |
POST | Change log level at runtime |
-
A series has a regex pattern (and optionally a TMDB id), a channel, and a
kind(movie/series). -
The EPG scanner (hourly + on demand) matches upcoming events against your series.
-
A match becomes a
scheduledrecording, with per-series pre/post offsets applied. -
At start time, e2recorder calls
POST /api/record/starton the best proxy:{ "ref": "1_0_19_EF11_421_1_C00000_0_0_0_", "title": "First Dates", "episode_title": "Ein Tisch für zwei", "kind": "series", "season": 1, "episode": 3, "duration": 3600, "profile": "remux-ac3" } -
e2proxy builds the Plex path, runs ffmpeg, writes the
.nfo, and refreshes Plex. The file path comes fromresp["file"]— e2recorder never computes it. -
When all tuners are busy, e2recorder distinguishes its own recordings (waits using
remaining_sec) from foreign tuner usage. -
If a proxy is already streaming the requested channel, no additional tuner is needed: e2proxy attaches a second ffmpeg to the running stream (
shared_tuner: true). This is what makes overlapping recordings — caused by pre/post buffers between back-to-back shows on the same channel — work instead of being skipped.
e2proxy (v3.2) decides the structure from kind, writing everything under a single recordings_path:
/mnt/nvme/recordings/
├── Movies/
│ └── Shooter (2007)/
│ ├── Shooter (2007).ts
│ └── Shooter (2007).nfo
└── TV/
└── First Dates/
├── Season 01/
│ ├── First Dates - S01E03 - Ein Tisch für zwei.ts
│ └── First Dates - S01E03 - Ein Tisch für zwei.nfo
└── tvshow.nfo
- Series with TVDB/explicit S/E → real season/episode numbers
- Daily shows without S/E → day-of-year fallback (
S2026E163= June 12) - Movies →
Movies/<Title> (<Year>)/
e2proxy is the Enigma2 streaming proxy that does the actual streaming and recording. e2recorder is the scheduler on top of it — they are designed to run side by side (e2proxy on :8888, e2recorder on :8889).
| Path | Content |
|---|---|
/data/config.json |
Configuration |
/data/series.json |
Series & movie definitions |
/data/recordings.json |
Recording records |
/data/tuner_history.json |
Last 200 tuner decisions |
/data/logs/e2recorder.log |
System log (daily rotation) |
# Copy new version and restart
scp e2recorder.py user@server:~/e2recorder/
docker compose -f ~/e2recorder/docker-compose.e2recorder.yml restart
# Verify
curl -s http://server:8889/api/versionMIT
Built with Claude by Anthropic.