Smart media library manager for Emby Gestionnaire intelligent de bibliothèque média pour Emby
Open-source alternative to Maintainerr
Hygie automatically scans your Emby libraries, identifies unused media based on configurable rules, and orchestrates their deletion across your entire *arr stack — Emby, Radarr, Sonarr, Seerr, and qBittorrent — while keeping your users informed via Discord.
- Per-library conditions with AND/OR logic:
- Added since X days
- Not watched since X days
- Never watched
- Play count
- Configurable grace period (days between detection and deletion)
- Per-user Seerr overrides — different grace periods per requester
- Seerr filters — include or exclude specific users per library
- Alerts at 30 days, 7 days, 24 hours before deletion, and at deletion
- Automatic @mention of the requester (via Seerr mapping or manual Discord ID)
- Public TMDB poster — available even after Emby deletion
- Notification sent before Emby deletion so the image is still accessible
Complete workflow in a single operation:
- 📣 Discord notification (image still available)
- 🗑️ Remove hardlink from Emby
- 🗑️ Remove item from Radarr / Sonarr (files preserved)
- 🗑️ Delete request from Seerr / Overseerr / Jellyseerr
- 🏷️ qBittorrent — add tag or delete torrent+files
- Automatic sync of an Emby collection with pending media
- Poster overlay showing "Deleted in Xd" countdown, refreshed nightly
- Overlay automatically removed if media is rescued from the queue
| Page | Description |
|---|---|
| Dashboard | Global stats, lifetime deletions chart, upcoming deletions |
| Calendar | Deletions organized by date |
| Queue | Tabs: All / Pending / Deleted / Errors — persistent sort, bulk actions |
| Libraries | CRUD rules, clone, per-library scan |
| Settings | All service configuration, connection tests |
| Ignored | Excluded media with expiry, one-click restore to queue |
| Storage | Disk metrics from Radarr/Sonarr, reclaimable space |
| Job History | Scan and deletion checks with duration and status |
| Logs | Real-time stream via WebSocket, level/source filters |
- Auto-detection from browser language
- French and English supported
services:
hygie:
image: ghcr.io/carryozor/hygie:latest
container_name: hygie
restart: unless-stopped
ports:
- "8000:8000"
volumes:
- ./data:/app/data
environment:
- TZ=Europe/Paris
healthcheck:
test: ["CMD", "python3", "/app/backend/healthcheck.py"]
interval: 30s
timeout: 10s
retries: 3
start_period: 20sdocker compose up -dOpen http://localhost:8000, create your admin account, then configure your services under Settings.
- Docker & Docker Compose
- Emby Media Server
- Radarr and/or Sonarr
- Seerr / Overseerr / Jellyseerr (optional but recommended)
- qBittorrent (optional)
- Discord webhook (optional)
All configuration is done through the web interface. No config files to edit.
| Service | Required fields |
|---|---|
| Emby | Internal URL, API key, External URL (for Discord posters) |
| Radarr | URL, API key |
| Sonarr | URL, API key |
| Seerr | URL, API key, External URL (for clickable links) |
| qBittorrent | URL (Gluetun compatible), username, password |
| Discord | Webhook URL |
| Environment variable | Default | Description |
|---|---|---|
DB_PATH |
/app/data/hygie.db |
SQLite database path |
HYGIE_VERSION |
dev |
Displayed version (injected via --build-arg) |
TZ |
UTC | Timezone |
Emby Scan
│
▼
Item matches conditions?
│ Yes
▼
Added to queue (delete_at = now + grace_days)
│
├──▶ Discord notification D-30
├──▶ Discord notification D-7
├──▶ Discord notification D-1
│
▼ (delete_at reached)
1. 📣 Discord "Deleted" ← image still on Emby
2. 🗑️ Delete from Emby
3. 🗑️ Delete from Radarr/Sonarr
4. 🗑️ Delete from Seerr
5. 🏷️ qBittorrent (tag or delete)
| Feature | Implementation |
|---|---|
| Password hashing | Argon2id |
| Session tokens | JWT HS256 with auto-generated 48-byte secret |
| Secret storage | data/.secret with 0600 permissions |
| Brute force protection | Rate limiting: 5 attempts / IP / 5 minutes |
| Image proxy | SSRF protection via host whitelist |
| Dynamic SQL | Column/table whitelists on all dynamic queries |
| Input validation | Pydantic with min/max length constraints |
| HTTP headers | X-Frame-Options, X-Content-Type-Options, Referrer-Policy |
| Container | Non-root user (UID 1000), tini as PID 1 |
| Database | WAL mode, integrity check in healthcheck |
⚠️ HTTPS strongly recommended via reverse proxy (Caddy, Traefik, Nginx)
git clone https://github.com/carryozor/hygie.git
cd hygie
docker buildx build \
--build-arg VERSION=1.0 \
--platform linux/amd64 \
-t ghcr.io/carryozor/hygie:latest \
-t ghcr.io/carryozor/hygie:1.0 \
--push .hygie/
├── backend/
│ ├── main.py # FastAPI app, WebSocket, image proxy
│ ├── database.py # SQLite, automatic migrations
│ ├── auth.py # JWT, Argon2id, rate limiting
│ ├── scheduler.py # Scan, deletions, notifications, overlay
│ ├── emby_client.py # Emby API client
│ ├── arr_clients.py # Radarr, Sonarr, Seerr clients
│ ├── qbit_client.py # qBittorrent (Gluetun compatible)
│ ├── discord_client.py # Discord webhooks
│ ├── healthcheck.py # Docker healthcheck script
│ └── routers/ # 10 FastAPI routers
└── frontend/
├── templates/index.html # Single Page App
├── static/js/app.js # Vanilla JS
└── static/js/i18n.js # FR/EN translations
Stack: FastAPI · APScheduler · SQLite (aiosqlite) · httpx · Pillow · Python 3.12
Hygie automatically migrates the database schema on startup. Upgrading from any previous version preserves all your data.
Hygie scanne automatiquement vos bibliothèques Emby, identifie les médias inutilisés selon des règles configurables, et orchestre leur suppression sur toute votre stack *arr — Emby, Radarr, Sonarr, Seerr et qBittorrent — tout en tenant vos utilisateurs informés via Discord.
- Conditions par bibliothèque avec logique ET/OU :
- Ajouté depuis X jours
- Non visionné depuis X jours
- Jamais regardé
- Nombre de lectures
- Délai de grâce configurable (jours entre détection et suppression)
- Règles Seerr par utilisateur — délai personnalisé selon le demandeur
- Filtres Seerr — inclure ou exclure certains utilisateurs par bibliothèque
- Alertes à 30 jours, 7 jours, 24 heures avant suppression, et à la suppression
- Mention automatique du demandeur (via mapping Seerr ou ID Discord manuel)
- Affiche TMDB publique — disponible même après suppression d'Emby
- Notification envoyée avant la suppression Emby pour que l'image soit accessible
Workflow complet en une seule opération :
- 📣 Notification Discord (image encore disponible)
- 🗑️ Retrait du hardlink Emby
- 🗑️ Suppression dans Radarr / Sonarr (fichiers conservés)
- 🗑️ Suppression de la requête Seerr / Overseerr / Jellyseerr
- 🏷️ qBittorrent — ajout d'un tag ou suppression torrent+fichier
- Synchronisation automatique d'une collection Emby avec les médias en attente
- Overlay sur les affiches affichant "Supprimé dans Xj", mis à jour chaque nuit
- Overlay automatiquement retiré si le média est sorti de la file
| Page | Description |
|---|---|
| Tableau de bord | Statistiques globales, graphe historique, prochaines suppressions |
| Calendrier | Suppressions organisées par date |
| File d'attente | Onglets Tous / En attente / Supprimés / Erreurs, tri persistant, actions groupées |
| Bibliothèques | CRUD des règles, clonage, scan individuel |
| Paramètres | Configuration de tous les services, tests de connexion |
| Ignorés | Exclusions avec expiration, remise en file en un clic |
| Stockage | Métriques disque Radarr/Sonarr, espace récupérable |
| Historique jobs | Scans et vérifications avec durée et statut |
| Logs | Flux temps réel via WebSocket, filtres niveau/source |
- Détection automatique depuis la langue du navigateur
- Français et anglais supportés
services:
hygie:
image: ghcr.io/carryozor/hygie:latest
container_name: hygie
restart: unless-stopped
ports:
- "8000:8000"
volumes:
- ./data:/app/data
environment:
- TZ=Europe/Paris
healthcheck:
test: ["CMD", "python3", "/app/backend/healthcheck.py"]
interval: 30s
timeout: 10s
retries: 3
start_period: 20sdocker compose up -dOuvre http://localhost:8000, crée ton compte administrateur, puis configure les services dans Paramètres.
- Docker & Docker Compose
- Emby Media Server
- Radarr et/ou Sonarr
- Seerr / Overseerr / Jellyseerr (optionnel mais recommandé)
- qBittorrent (optionnel)
- Webhook Discord (optionnel)
Toute la configuration se fait depuis l'interface web.
| Service | Champs requis |
|---|---|
| Emby | URL interne, clé API, URL externe (pour les affiches Discord) |
| Radarr | URL, clé API |
| Sonarr | URL, clé API |
| Seerr | URL, clé API, URL externe (pour les liens cliquables) |
| qBittorrent | URL (compatible Gluetun), utilisateur, mot de passe |
| Discord | URL du webhook |
| Variable d'environnement | Défaut | Description |
|---|---|---|
DB_PATH |
/app/data/hygie.db |
Chemin de la base SQLite |
HYGIE_VERSION |
dev |
Version affichée (injectée via --build-arg) |
TZ |
UTC | Fuseau horaire |
Scan Emby
│
▼
L'item remplit les conditions ?
│ Oui
▼
Ajouté à la file (delete_at = maintenant + grace_days)
│
├──▶ Notification Discord J-30
├──▶ Notification Discord J-7
├──▶ Notification Discord J-1
│
▼ (delete_at atteint)
1. 📣 Discord « Supprimé » ← image encore sur Emby
2. 🗑️ Suppression Emby
3. 🗑️ Suppression Radarr/Sonarr
4. 🗑️ Suppression Seerr
5. 🏷️ qBittorrent (tag ou suppression)
| Fonctionnalité | Implémentation |
|---|---|
| Hashage des mots de passe | Argon2id |
| Tokens de session | JWT HS256 avec secret auto-généré de 48 octets |
| Stockage du secret | data/.secret avec permissions 0600 |
| Protection brute force | Rate limiting : 5 tentatives / IP / 5 minutes |
| Proxy d'images | Protection SSRF via whitelist d'hôtes |
| SQL dynamique | Whitelists colonnes/tables sur toutes les requêtes dynamiques |
| Validation des entrées | Pydantic avec contraintes min/max de longueur |
| En-têtes HTTP | X-Frame-Options, X-Content-Type-Options, Referrer-Policy |
| Conteneur | Utilisateur non-root (UID 1000), tini comme PID 1 |
| Base de données | Mode WAL, vérification d'intégrité dans le healthcheck |
⚠️ HTTPS fortement recommandé via reverse proxy (Caddy, Traefik, Nginx)
git clone https://github.com/carryozor/hygie.git
cd hygie
docker buildx build \
--build-arg VERSION=1.0 \
--platform linux/amd64 \
-t ghcr.io/carryozor/hygie:latest \
-t ghcr.io/carryozor/hygie:1.0 \
--push .Hygie applique automatiquement les migrations de schéma au démarrage. La mise à jour depuis n'importe quelle version antérieure préserve toutes vos données.
If Hygie is useful to you, consider supporting its development. Si Hygie vous est utile, pensez à soutenir son développement.
MIT License © carryozor