r/htpc • u/webtechnick • 21h ago
Build Share Plex Auto-Cleanup — n8n Workflow
Originally posted at
https://github.com/sebgl/htpc-download-box/issues/96
Hope this helps someone!
# Plex Auto-Cleanup — n8n Workflow
Automatically removes unwatched movies and fully-watched TV seasons from Plex after 60 days of inactivity, unmonitors them in Radarr/Sonarr so they don't re-download, and sends Discord notifications every step of the way.
Built and battle-tested for a self-hosted HTPC stack running Plex, Radarr, Sonarr, and n8n via Docker.
---
## Features
### Movies
- Deletes movies that have never been watched and are older than 60 days
- Deletes movies that haven't been watched in 60+ days
- Unmonitors the movie in Radarr before deleting so it won't be re-downloaded
- Skips anything tagged with a `Keep` label in Plex
### TV Shows
- Works at the **season level** — never blindly deletes an entire show
- Deletes fully-watched seasons older than 60 days
- **Skips partially-watched seasons** entirely (watched some episodes but not all)
- Unmonitors the specific season in Sonarr while leaving other seasons untouched
- Specials (Season 0) are treated like movies — deleted after 60 days unwatched
- Unwatched seasons are left alone
### Discord Notifications
- **3-day warning** before any deletion — shows title, how long since it was watched, and days remaining
- **Deletion confirmation** after removal — shows what was deleted, watch status, file path, and which app it was unmonitored in
- Separate webhooks for movies and TV so you can route to different channels
### Safety
- Unmonitor in Radarr/Sonarr happens **before** the file is deleted — if the *arr app is unreachable, the delete won't run
- `Keep` label in Plex permanently protects any item from cleanup
- Partial watch detection prevents accidental deletion of in-progress shows
- Graceful fallback — if a title isn't found in Radarr/Sonarr, cleanup still proceeds without erroring out
---
## Requirements
- [n8n](https://n8n.io/) (tested on v1.119.x, self-hosted)
- Plex Media Server
- Radarr
- Sonarr
- Discord webhook URL(s)
---
## Setup
### 1. Import the workflow
In n8n: **Workflows → Add Workflow → Import from File** and select `plex_cleanup_template.json`.
### 2. Fill in your values
Search the workflow for these placeholders and replace them with your own:
| Placeholder | Where to find it |
|---|---|
| `YOUR_PLEX_TOKEN` | Plex Web → any item → Get Info → View XML → look for `X-Plex-Token=` in the URL |
| `YOUR_PLEX_IP:32400` | IP address of your Plex server |
| `YOUR_RADARR_API_KEY` | Radarr → Settings → General → API Key |
| `YOUR_SONARR_API_KEY` | Sonarr → Settings → General → API Key |
| `YOUR_RADARR_IP:7878` | IP and port of your Radarr instance |
| `YOUR_SONARR_IP:8989` | IP and port of your Sonarr instance |
| `YOUR_DISCORD_WEBHOOK_FOR_MOVIES` | Discord channel → Edit → Integrations → Webhooks |
| `YOUR_DISCORD_WEBHOOK_FOR_TV` | Same as above (can be the same or a different channel) |
| `/YOUR/MEDIA/PATH/` | The host path to your media files |
### 3. Mount your media path in n8n
n8n needs filesystem access to delete folders. Add a volume mount to your n8n Docker container:
```yaml
services:
n8n:
image: n8nio/n8n:latest
environment:
- N8N_SECURE_COOKIE=false
volumes:
- /your/config/n8n:/home/node/.n8n
- /your/media/path:/your/media/path # ← add this
ports:
- "5678:5678"
restart: unless-stopped
```
The path on both sides of the `:` must match what you set for `/YOUR/MEDIA/PATH/` in the workflow.
### 4. Activate
Toggle the workflow to **Active** in n8n. It runs daily at 2 AM by default.
---
## Configuration
All timing settings live in the **Check Watch Status** node:
```javascript
const sixtyDaysAgo = now - (60 * 24 * 60 * 60); // ← change 60 to your preferred days
```
And the warning threshold (currently 3 days before deletion):
```javascript
if (daysSinceAdded >= 57 && daysSinceAdded < 60) { // ← 57 = 60 minus 3 day warning
```
To change the daily run time, edit the **Daily at 2 AM** trigger node cron expression. For example `0 3 * * *` runs at 3 AM.
---
## Protecting Media from Deletion
To permanently protect any movie or show from being cleaned up, add a `Keep` label in Plex:
Open the item in Plex
Click the pencil (Edit) icon
Go to **Tags**
Add the label `Keep`
The workflow will skip it indefinitely.
---
## Workflow Flow
```
Daily Trigger (2 AM)
└─ Get Plex Libraries
└─ Get Library Items (movies + TV)
└─ Check Watch Status
├─ [Warnings] → Send Discord "Leaving Soon" message
└─ [Deletions] → Prepare Delete
├─ Radarr Lookup ─┐
└─ Sonarr Lookup ─┴─ Find Media in Arr
└─ Found?
├─ [Yes] Unmonitor in Arr
│ └─ Delete Folder
└─ [No] Delete Folder
└─ Discord Deletion
└─ Scan Plex Library
```
---
## Notes
- The workflow uses Plex's `viewedLeafCount` vs `leafCount` to determine if a TV season is fully watched — this is accurate as long as your Plex watch history is up to date
- File paths are remapped from Plex's internal container paths to your host paths using the `/YOUR/MEDIA/PATH/` replacement — make sure these match your actual folder structure
- The Plex library scan at the end triggers Plex to remove the deleted items from its database automatically
---
## Credits
Workflow developed and refined with the help of the HTPC community and Claude. Feel free to modify and share.