Smart episode management for Sonarr - Get episodes as you watch, clean up automatically when storage gets low. This project started as scratching my own itch - I wanted more granular series management and couldn't find exactly what I wanted. I'm not a programmer by trade, but I had a clear vision for the solution I needed. I used AI as a development tool to help implement my ideas faster, just like any other tool. The creativity, problem-solving, architecture decisions, and feature design are all mine - AI helped with code, syntax and implementation details. Although I run everything in my own production environment first, it is catered to my environment and is use at your own risk. All code is open source for anyone to review and audit. The tool has been useful for me, and I shared it in case others can benefit from it too - but I absolutely understand if some prefer to stick with established solutions.
Episeerr gives you four independent features for TV episode management:
| Feature | What It Does | Use Case |
|---|---|---|
| 🎯 Episode Selection | Choose specific episodes to download | Try pilots, skip seasons, selective downloads |
| ⚡ Viewing Automation | Next episode ready when you watch | Binge watching, always-ready episodes |
| 💾 Storage Management | Automatic cleanup based on time/viewing | Limited storage, inactive show cleanup |
| 🔄 Plex Watchlist Sync | Add to Plex watchlist, Episeerr handles the rest | Zero-effort adding, full selection control |
| 📌 Always Have | Baseline episodes always present and protected | Showcase libraries, permanent pilots, season placeholders |
Use one, some, or all - they work independently!
Get running in 5 minutes:
# 1. Create docker-compose.yml (minimal setup)
services:
episeerr:
image: vansmak/episeerr:latest
volumes:
- ./config:/app/config
- ./logs:/app/logs
- ./data:/app/data
ports:
- "5002:5002"
restart: unless-stopped
# 2. Start container
docker-compose up -d
# 3. Open http://your-server:5002/setup
# 4. Configure Sonarr, TMDB, and optional services
# 5. Create a rule, add a series, start watching!
Restart container for changes to take effect
That's it! No .env file needed - configure everything via the GUI.
For automation: Set up webhooks ⬇️
/setup page, no .env file needed.env file (still supported)Create docker-compose.yml:
services:
episeerr:
image: vansmak/episeerr:latest
container_name: episeerr
environment:
# ============================================
# REQUIRED
# ============================================
- SONARR_URL=http://your-sonarr:8989
- SONARR_API_KEY=your_sonarr_api_key
- TMDB_API_KEY=your_tmdb_read_access_token
# ============================================
# OPTIONAL - For Viewing Automation (pick ONE media server)
# ============================================
# Option A: Plex (recommended — native webhooks, no Tautulli needed)
# Requires Plex Pass for webhook support.
# Configure webhook in Plex: Settings → Webhooks
# URL: http://<episeerr>:5002/api/integration/plex/webhook
# Detection mode + token configured via /setup page (GUI recommended)
#
# - PLEX_URL=http://your-plex:32400
# - PLEX_TOKEN=your_plex_token
# Option A2: Tautulli (Plex without Plex Pass, or for richer watch history)
# ⚠️ Do NOT use alongside Plex native webhooks — configure one or the other.
# Webhook URL: http://<episeerr>:5002/api/integration/tautulli/webhook
# Legacy URL: http://<episeerr>:5002/webhook (still supported)
#
# - TAUTULLI_URL=http://your-tautulli:8181
# - TAUTULLI_API_KEY=your_tautulli_key
# Option B: Jellyfin (choose mode A or B in /setup, not via env vars)
# Webhook URL: http://<episeerr>:5002/api/integration/jellyfin/webhook
# Notification type: PlaybackProgress (real-time) or Session Start (polling)
#
# - JELLYFIN_URL=http://your-jellyfin:8096
# - JELLYFIN_API_KEY=your_jellyfin_api_key
# - JELLYFIN_USER_ID=your_username
# - JELLYFIN_TRIGGER_PERCENTAGE=50.0 # polling mode
# - JELLYFIN_POLL_INTERVAL=900 # polling mode
# - JELLYFIN_TRIGGER_MIN=50.0 # real-time mode
# - JELLYFIN_TRIGGER_MAX=55.0 # real-time mode
# Option C: Emby
# Webhook URL: http://<episeerr>:5002/api/integration/emby/webhook
# Events: playback.start, playback.stop
#
# - EMBY_URL=http://your-emby:8096
# - EMBY_API_KEY=your_emby_api_key
# - EMBY_USER_ID=your_username
# - EMBY_TRIGGER_PERCENTAGE=50.0
# - EMBY_POLL_INTERVAL=900
# ============================================
# OPTIONAL - For Request Integration
# ============================================
- JELLYSEERR_URL=http://your-jellyseerr:5055
- JELLYSEERR_API_KEY=your_jellyseerr_key
# OR
- OVERSEERR_URL=http://your-overseerr:5055
- OVERSEERR_API_KEY=your_overseerr_key
# ============================================
# OPTIONAL - Authentication (Security)
# ============================================
# Uncomment to enable password protection:
# - REQUIRE_AUTH=true
# - AUTH_USERNAME=admin
# - AUTH_PASSWORD=your-password-here
# - SECRET_KEY=generate-random-key # Optional: auto-generated if not set
# ============================================
# OPTIONAL - Quick Links in Sidebar
# ============================================
- CUSTOMAPP_URL=http://192.168.1.100:8080
- CUSTOMAPP_NAME=My Custom App
- CUSTOMAPP_ICON=fas fa-cog
volumes:
- ./config:/app/config # Configuration files
- ./logs:/app/logs # Log files
- ./data:/app/data # Database and temp data
- ./temp:/app/temp # Temporary processing
ports:
- "5002:5002"
restart: unless-stopped
Start:
docker-compose up -d
Access:
http://your-server:5002
1. Add Custom Template
Create /boot/config/plugins/community.applications/private/episeerr/my-episeerr.xml:
<?xml version="1.0"?>
<Container version="2">
<Name>episeerr</Name>
<Repository>vansmak/episeerr:latest</Repository>
<Registry>https://hub.docker.com/r/vansmak/episeerr</Registry>
<Network>bridge</Network>
<Shell>sh</Shell>
<Privileged>false</Privileged>
<Support>https://github.com/Vansmak/episeerr/issues</Support>
<Project>https://github.com/Vansmak/episeerr</Project>
<Overview>Smart episode management for Sonarr</Overview>
<Category>MediaApp:Video</Category>
<WebUI>http://[IP]:[PORT:5002]</WebUI>
<Icon>https://raw.githubusercontent.com/Vansmak/episeerr/main/static/logo_icon.png</Icon>
<Config Name="WebUI Port" Target="5002" Default="5002" Mode="tcp" Description="Episeerr WebUI" Type="Port" Display="always" Required="true" Mask="false"/>
<Config Name="Config" Target="/app/config" Default="/mnt/user/appdata/episeerr/config" Mode="rw" Description="Configuration files" Type="Path" Display="always" Required="true" Mask="false"/>
<Config Name="Logs" Target="/app/logs" Default="/mnt/user/appdata/episeerr/logs" Mode="rw" Description="Log files" Type="Path" Display="always" Required="true" Mask="false"/>
<Config Name="Data" Target="/app/data" Default="/mnt/user/appdata/episeerr/data" Mode="rw" Description="Database files" Type="Path" Display="always" Required="true" Mask="false"/>
<Config Name="Temp" Target="/app/temp" Default="/mnt/user/appdata/episeerr/temp" Mode="rw" Description="Temporary files" Type="Path" Display="always" Required="false" Mask="false"/>
<Config Name="SONARR_URL" Target="SONARR_URL" Default="" Description="Sonarr base URL (e.g., http://sonarr:8989)" Type="Variable" Display="always" Required="true" Mask="false"/>
<Config Name="SONARR_API_KEY" Target="SONARR_API_KEY" Default="" Description="Sonarr API key" Type="Variable" Display="always" Required="true" Mask="true"/>
<Config Name="TMDB_API_KEY" Target="TMDB_API_KEY" Default="" Description="TMDB Read Access Token (not API key)" Type="Variable" Display="always" Required="true" Mask="true"/>
<Config Name="TAUTULLI_URL" Target="TAUTULLI_URL" Default="" Description="Tautulli URL (optional)" Type="Variable" Display="always" Required="false" Mask="false"/>
<Config Name="TAUTULLI_API_KEY" Target="TAUTULLI_API_KEY" Default="" Description="Tautulli API Key (optional)" Type="Variable" Display="always" Required="false" Mask="true"/>
<Config Name="JELLYFIN_URL" Target="JELLYFIN_URL" Default="" Description="Jellyfin URL (optional)" Type="Variable" Display="always" Required="false" Mask="false"/>
<Config Name="JELLYFIN_API_KEY" Target="JELLYFIN_API_KEY" Default="" Description="Jellyfin API Key (optional)" Type="Variable" Display="always" Required="false" Mask="true"/>
<Config Name="JELLYFIN_USER_ID" Target="JELLYFIN_USER_ID" Default="" Description="Jellyfin Username (required if using Jellyfin)" Type="Variable" Display="always" Required="false" Mask="false"/>
<Config Name="JELLYSEERR_URL" Target="JELLYSEERR_URL" Default="" Description="Jellyseerr URL (optional)" Type="Variable" Display="always" Required="false" Mask="false"/>
<Config Name="JELLYSEERR_API_KEY" Target="JELLYSEERR_API_KEY" Default="" Description="Jellyseerr API Key (optional)" Type="Variable" Display="always" Required="false" Mask="true"/>
</Container>
2. Install from Apps
The easiest way to configure Episeerr - no .env file needed!
Access: http://your-server:5002/setup
Configure: 1. Sonarr - URL and API key (required) Initial setup Restart container for changes to take effect 2. TMDB - API Read Access Token (required) 3. Media Server - Choose Plex, Jellyfin, or Emby (optional — needed for viewing automation) 4. Overseerr/Jellyseerr - Request integration (optional)
Features:
- ✅ Test connections before saving
- ✅ Configuration stored in database
- ✅ Auto-populate Quick Links in sidebar
- ✅ No container restart needed
- ✅ Works alongside .env files (database takes priority)
Migration from .env:
1. Open /setup page
2. Your existing .env values appear as defaults
3. Save to migrate to database
4. Delete .env file when ready
Note: As of v3.2.0, environment variables are optional. You can configure everything via the /setup page GUI. Environment variables still work for backward compatibility and can be used alongside database configuration (database takes priority).
| Variable | Required | Description |
|---|---|---|
SONARR_URL |
❌ Optional* | Sonarr base URL (e.g., http://sonarr:8989) |
SONARR_API_KEY |
❌ Optional* | Sonarr API key (Settings → General) |
TMDB_API_KEY |
❌ Optional* | TMDB Read Access Token (Get one free) |
PLEX_URL |
❌ Optional | Plex server URL (e.g. http://plex:32400) |
PLEX_TOKEN |
❌ Optional | Plex authentication token |
TAUTULLI_URL |
❌ Optional | Tautulli — only needed for watch-history override or if you don't have Plex Pass |
TAUTULLI_API_KEY |
❌ Optional | Tautulli API key |
JELLYFIN_URL |
❌ Optional | For Jellyfin viewing automation |
JELLYFIN_API_KEY |
❌ Optional | Jellyfin API key |
JELLYFIN_USER_ID |
⚠️ Required if using Jellyfin | Your Jellyfin username |
EMBY_URL |
❌ Optional | For Emby viewing automation |
EMBY_API_KEY |
❌ Optional | Emby API key |
EMBY_USER_ID |
⚠️ Required if using Emby | Your Emby username |
JELLYSEERR_URL |
❌ Optional | For request integration |
JELLYSEERR_API_KEY |
❌ Optional | Jellyseerr API key |
$ claude mcp add episeerr \
-- python -m otcore.mcp_server <graph>