initial update

This commit is contained in:
2025-09-21 09:25:36 -04:00
parent f5fd954ebb
commit 22483717c4
11 changed files with 994 additions and 613 deletions
+330 -588
View File
@@ -1,164 +1,150 @@
# NFOGuard
**NFOGuard** is a lightweight webhook service that locks in the *true import date* of your movies and TV episodes, ensuring that upgrades, renames, or reimports never bubble old media up as "new" in Emby, Jellyfin, or Plex.
[![Docker Pulls](https://img.shields.io/docker/pulls/sbcrumb/nfoguard.svg)](https://hub.docker.com/r/sbcrumb/nfoguard)
[![Docker Image Version](https://img.shields.io/docker/v/sbcrumb/nfoguard?sort=semver)](https://hub.docker.com/r/sbcrumb/nfoguard)
[![Docker Image Size](https://img.shields.io/docker/image-size/sbcrumb/nfoguard/latest)](https://hub.docker.com/r/sbcrumb/nfoguard)
It integrates with **Sonarr** and **Radarr**, listens for import/rename/upgrade events, and automatically manages `.nfo` files and filesystem timestamps to keep your library chronology consistent.
## Companion Plugin
For **Emby users**, check out the companion [NFOGuard Emby Plugin](https://github.com/jskala/NFOGuard-Emby-Plugin) that provides real-time date synchronization within Emby itself.
**Automated NFO file management for Radarr and Sonarr with intelligent date handling**
---
> **⚠️ ALPHA SOFTWARE NOTICE ⚠️**
>
> NFOGuard is currently in **Alpha** stage. While functional, it may have bugs or missing features.
>
> **🔌 Emby Plugin Included**: The Emby companion plugin is now bundled directly into the Docker image — no extra steps required.
>
> **💬 Community Feedback**: Join our Discord if youd like to share feedback, test new features early, or discuss improvements with other users:
>
> **[Join Discord: https://discord.gg/bbD9Pmtr](https://discord.gg/bbD9Pmtr)**
>
> *If the Discord link has expired, please [open an issue](https://github.com/sbcrumb/NFOguard/issues) and we'll provide an updated link.*
---
NFOGuard automatically updates movie and TV show NFO files with proper release dates and metadata when triggered by Radarr/Sonarr webhooks. It preserves existing metadata while adding clean, accurate date information at the bottom of NFO files.
## ✨ Features
- **Import Date Locking**
Captures the original date a movie or episode was imported and preserves it across upgrades.
- 🎬 **Movie & TV Support** - Works with both Radarr and Sonarr
- 📅 **Smart Date Handling** - Prioritizes digital, physical, and theatrical release dates
- 🔄 **Webhook Integration** - Triggers automatically on import, upgrade, and rename
- 🗄️ **Database Integration** - Direct PostgreSQL access for better performance
- 📝 **NFO Preservation** - Maintains existing metadata, adds fields cleanly at bottom
- 🔒 **Metadata Locking** - Prevents overwrites with lockdata tags
-**Batch Processing** - Efficient handling of multiple files
- 🐳 **Docker Ready** - Easy deployment with Docker Compose
- **Enhanced NFO Management**
Creates and updates `.nfo` files for movies and TV episodes with consistent `<dateadded>` fields.
**NEW**: Full metadata integration from Sonarr API (episode titles, plots, ratings, runtime).
Optionally adds `<lockdata>` to prevent metadata drift from scrapers.
**NEW**: Timestamps all NFO files with creation/update dates.
## 🚀 Quick Start
- **Filesystem Time Fixing**
Updates file and directory modification times (`mtime`) to match the preserved import date.
- **Batching Support**
Groups multiple webhook events (e.g. whole-season downloads) to avoid thrashing.
- **Database Backing**
Uses SQLite to remember previously-seen imports, so dates persist across restarts.
- **Manual Scans**
Exposes endpoints to rescan and reconcile TV or movie libraries on demand.
---
## 🏗 How It Works
1. **Webhooks**
- Add NFOGuard as a webhook in Sonarr and Radarr.
- Supported event types: `Download`, `Upgrade`, `Rename`.
2. **Processing**
- Maps Sonarr/Radarr paths to container paths.
- Looks up IMDb IDs from folder names or payloads.
- Writes `.nfo` files and updates file/dir mtimes.
- Records everything in `media_dates.db`.
3. **Playback Apps**
- Emby/Jellyfin/Plex see consistent `dateadded` values.
- Upgrades dont bubble to the top of “Recently Added.”
---
## 🚀 Quick Start (Docker Compose)
### 1. Download Configuration Files
```bash
# 1. Copy configuration templates
wget https://raw.githubusercontent.com/sbcrumb/NFOguard/main/.env.template
wget https://raw.githubusercontent.com/sbcrumb/NFOguard/main/.env.secrets.template
wget https://raw.githubusercontent.com/sbcrumb/NFOguard/main/docker-compose.example.yml
```
### 2. Configure Environment
```bash
# Copy and edit main configuration
cp .env.template .env
nano .env
# Copy and edit secrets (API keys, passwords)
cp .env.secrets.template .env.secrets
nano .env.secrets
chmod 600 .env.secrets
# 2. Edit your configuration files
# .env - paths and preferences (safe to share)
# .env.secrets - API keys and passwords (never commit)
# Copy and edit Docker Compose
cp docker-compose.example.yml docker-compose.yml
nano docker-compose.yml
```
# 3. Start NFOGuard
### 3. Deploy
```bash
# Create data directory
mkdir -p ./data
# Start NFOGuard
docker-compose up -d
# Check logs
docker-compose logs -f nfoguard
# Verify health
curl http://localhost:8080/health
```
**Docker Compose (Recommended)**:
```yaml
version: '3.8'
services:
nfoguard:
image: ghcr.io/your-org/nfoguard:latest
container_name: nfoguard
ports:
- "8080:8080"
volumes:
- /mnt/unionfs/Media:/media:rw
- ./data:/app/data
- ./.env:/app/.env:ro # Main configuration
- ./.env.secrets:/app/.env.secrets:ro # Secure API keys
restart: unless-stopped
```
## ⚙️ Configuration
**Docker Run (Alternative)**:
### Environment Files
| File | Purpose | Contains |
|------|---------|----------|
| `.env` | Main configuration | Paths, behavior settings, non-sensitive options |
| `.env.secrets` | Sensitive data | API keys, passwords, database credentials |
### Key Configuration Options
**Media Paths** (Required):
```bash
docker run -d \
--name=NFOGuard \
-p 8080:8080 \
-v /mnt/unionfs/Media:/media:rw \
-v /opt/NFOGuard/data:/app/data \
-v /opt/NFOGuard/.env:/app/.env:ro \
-v /opt/NFOGuard/.env.secrets:/app/.env.secrets:ro \
ghcr.io/your-org/NFOGuard:latest
```
## 🔧 Configuration Files
NFOGuard uses a **secure two-file configuration system**:
### **`.env` - Main Configuration** (safe to share)
```bash
# Media paths
# Container paths (what NFOGuard sees)
MOVIE_PATHS=/media/Movies/movies,/media/Movies/movies6
TV_PATHS=/media/TV/tv,/media/TV/tv6
# Release date priority (digital, physical, theatrical)
# *arr application paths (what your apps see)
RADARR_ROOT_FOLDERS=/mnt/unionfs/Media/Movies/movies
SONARR_ROOT_FOLDERS=/mnt/unionfs/Media/TV/tv
```
**Release Date Priority**:
```bash
RELEASE_DATE_PRIORITY=digital,physical,theatrical
PREFER_RELEASE_DATES_OVER_FILE_DATES=true
ALLOW_FILE_DATE_FALLBACK=false
# Smart date validation: prefer theatrical over unreasonably late dates
ENABLE_SMART_DATE_VALIDATION=true
MAX_RELEASE_DATE_GAP_YEARS=10
# TV webhook processing mode (v0.6.0+)
# targeted = Only process episodes in webhook (efficient)
# series = Process entire series directory (comprehensive)
TV_WEBHOOK_PROCESSING_MODE=targeted
# Database connection
RADARR_DB_HOST=radarr-postgres
RADARR_DB_PORT=5432
RADARR_DB_NAME=radarr
RADARR_DB_USER=postgres
```
### **`.env.secrets` - Sensitive Data** (never commit)
**Debug Mode**:
```bash
# Database password
RADARR_DB_PASSWORD=your_actual_password
# API keys
TMDB_API_KEY=your_tmdb_api_key
TVDB_API_KEY=your_tvdb_api_key
RADARR_API_KEY=your_radarr_api_key
SONARR_API_KEY=your_sonarr_api_key
DEBUG=false # Clean production logs
SUPPRESS_TVDB_WARNINGS=true # Hide non-critical API failures
```
**Security Features**:
- 🔐 **API key masking** in logs automatically
- 🚫 **Git protection** - secrets never committed
- 🛠️ **Easy debugging** - main .env safe to share
## 🐳 Docker Images
🔌 API Endpoints
### Webhook Endpoints
```bash
# Radarr webhook (automatic processing)
POST /webhook/radarr
# Sonarr webhook (automatic processing)
POST /webhook/sonarr
### Production (Stable)
```yaml
image: sbcrumb/nfoguard:latest
```
### Manual Processing
### Development (Latest Features)
```yaml
image: sbcrumb/nfoguard:dev
```
### Specific Version
```yaml
image: sbcrumb/nfoguard:v1.5.5
```
## 🔗 Webhook Setup
Configure these webhook URLs in your applications:
**Radarr**: `http://nfoguard:8080/webhook/radarr`
**Sonarr**: `http://nfoguard:8080/webhook/sonarr`
**Triggers**: On Import, On Upgrade, On Rename
## 🔄 Manual Operations
### Manual Scanning
Trigger manual scans via API endpoints:
```bash
# Manual scan all media
# Manual scan all media (movies and TV)
curl -X POST "http://localhost:8080/manual/scan?scan_type=both"
# Manual scan TV only
@@ -174,475 +160,231 @@ curl -X POST "http://localhost:8080/manual/scan?path=/media/movies"
curl -X POST "http://localhost:8080/bulk/update"
```
### TV Show Processing (NEW in v0.6.0+)
### API Endpoints
| Endpoint | Method | Purpose |
|----------|--------|---------|
| `/health` | GET | Health check |
| `/webhook/radarr` | POST | Radarr webhook handler |
| `/webhook/sonarr` | POST | Sonarr webhook handler |
| `/manual/scan` | POST | Manual media scanning |
| `/bulk/update` | POST | Bulk movie updates from Radarr DB |
### Manual Scan Parameters
- `scan_type`: `both`, `movies`, `tv`
- `path`: Specific directory path to scan
- Use for initial setup or fixing existing media
## 📁 Volume Mapping
Critical: Update `docker-compose.yml` with your actual paths:
```yaml
volumes:
- ./data:/app/data # NFOGuard data
- /your/movies/path:/media/Movies/movies:rw # Movie library
- /your/tv/path:/media/TV/tv:rw # TV library
```
### Common Examples
**Unraid**:
```yaml
- /mnt/user/Media/Movies:/media/Movies/movies:rw
- /mnt/user/Media/TV:/media/TV/tv:rw
```
**Synology**:
```yaml
- /volume1/Media/Movies:/media/Movies/movies:rw
- /volume1/Media/TV:/media/TV/tv:rw
```
**Standard Linux**:
```yaml
- /home/user/media/movies:/media/Movies/movies:rw
- /home/user/media/tv:/media/TV/tv:rw
```
## 🔧 Troubleshooting
### Check Logs
```bash
# Process a specific TV season (URL-safe)
curl -X POST "http://localhost:8080/tv/scan-season" \
-H "Content-Type: application/json" \
-d '{
"series_path": "/media/TV/tv/Chicago Fire (2012) [imdb-tt2261391]",
"season_name": "Season 13"
}'
# Process a specific TV episode (URL-safe)
curl -X POST "http://localhost:8080/tv/scan-episode" \
-H "Content-Type: application/json" \
-d '{
"series_path": "/media/TV/tv/Chicago Fire (2012) [imdb-tt2261391]",
"season_name": "Season 13",
"episode_name": "Chicago Fire (2012)-S13E21-The Bad Guy[WEBDL-1080p][AAC2.0][h264].mkv"
}'
# Process single season via manual scan (URL encoding required for spaces)
curl -X POST "http://localhost:8080/manual/scan?path=/media/TV/tv/Chicago%20Fire%20(2012)%20%5Bimdb-tt2261391%5D/Season%2013"
# Process single episode via manual scan (URL encoding required for spaces)
curl -X POST "http://localhost:8080/manual/scan?path=/media/TV/tv/Chicago%20Fire%20(2012)%20%5Bimdb-tt2261391%5D/Season%2013/episode.mkv"
docker-compose logs -f nfoguard
```
### Testing & Validation
### Enable Debug Mode
```bash
# Test database connections
curl -X POST "http://localhost:8080/test/bulk-update"
# Test movie directory scanning
curl -X POST "http://localhost:8080/test/movie-scan"
# System health check
curl "http://localhost:8080/health"
# Database statistics
curl "http://localhost:8080/stats"
# Batch processing queue status
curl "http://localhost:8080/batch/status"
```
### Debugging & Analysis
```bash
# Debug specific movie import date detection
curl "http://localhost:8080/debug/movie/tt1674782"
# Show complete import history analysis
curl "http://localhost:8080/debug/movie/tt1674782/history"
# Show date priority logic and available sources
curl "http://localhost:8080/debug/movie/tt1674782/priority"
# Debug TMDB API lookup step by step
curl "http://localhost:8080/debug/tmdb/tt1674782"
```
### Response Examples
**Health Check**:
```json
{
"status": "healthy",
"version": "0.6.0",
"database_status": "healthy",
"radarr_database": {"status": "healthy", "movies": 1500}
}
```
**Movie Debug**:
```json
{
"detected_import_date": "2025-07-08T03:30:04+00:00",
"import_source": "radarr:history.import",
"movie_title": "Movie Name"
}
```
**Priority Logic Debug**:
```json
{
"movie_priority": "import_then_digital",
"release_date_priority": ["digital", "physical", "theatrical"],
"priority_explanation": "1st: Radarr import history, 2nd: Release dates (digital → physical → theatrical), 3rd: file mtime",
"date_sources": {
"radarr_import": {"date": "2025-08-29T12:14:28+00:00", "source": "radarr:db.file.dateAdded"},
"digital_release": {"date": "1996-05-03T00:00:00+00:00", "source": "tmdb:theatrical"}
},
"file_date_detected": true,
"would_prefer_digital": true,
"selected_date": "1996-05-03T00:00:00+00:00",
"selected_source": "tmdb:theatrical (preferred over file date)"
}
```
**TV Season Processing**:
```json
{
"status": "started",
"message": "Season scan started for Season 13"
}
```
**TV Episode Processing**:
```json
{
"status": "started",
"message": "Episode scan started for Chicago Fire (2012)-S13E21-The Bad Guy[WEBDL-1080p][AAC2.0][h264].mkv"
}
```
## 🎯 Date Selection Priority System
NFOGuard uses a **comprehensive fallback hierarchy** to find the best possible date for each movie:
### Movie Priority: `import_then_digital` (Default)
**1. Radarr Import History** *(Highest Priority)*
- `radarr:db.history.import` - Real download/import events from Radarr database
- `radarr:db.history.grab` - Fallback to grab events if no import events exist
- **Skip if**: First event is rename (indicates upgrades, not original import)
**2. TMDB Release Dates** *(When import unavailable/unreliable)*
- `tmdb:premiere` - Type 1: World premieres, film festivals
- `tmdb:limited` - Type 2: Limited theatrical releases
- `tmdb:theatrical` - Type 3: Wide theatrical releases
- `tmdb:digital` - Type 4: Digital/streaming releases (Netflix, iTunes, etc.)
- `tmdb:physical` - Type 5: Physical media (DVD, Blu-ray)
- `tmdb:tv` - Type 6: TV broadcasts, TV movie premieres
- **Priority Order**: Uses `RELEASE_DATE_PRIORITY=digital,physical,theatrical`
**3. Radarr NFO Premiered Date** *(Radarr's own research)*
- `radarr:nfo.premiered` - Extracts `<premiered>YYYY-MM-DD</premiered>` from existing movie.nfo
- **Source**: Radarr's metadata providers (TMDB, IMDb, etc.)
**4. File Modification Time** *(Last Resort)*
- `file:mtime` - Earliest video file modification time in directory
- **Only if**: `ALLOW_FILE_DATE_FALLBACK=true`
### Movie Priority: `digital_then_import`
Same hierarchy, but **TMDB Release Dates** are tried **before** Radarr Import History.
## 🧠 Smart Date Validation
**NEW**: Automatically detects unreasonable date gaps and chooses the most logical release date.
### How It Works
- **Compares release dates**: Checks if digital/physical releases are unreasonably far from theatrical
- **Automatic fallback**: If gap exceeds threshold, prefers theatrical date instead
- **Configurable threshold**: `MAX_RELEASE_DATE_GAP_YEARS=10` (default: 10 years)
### Real Example: "The Craft (1996)"
```bash
# Without smart validation:
Physical Release: 2022-05-17 (26 years after theatrical!)
Selected: 2022
# With smart validation:
Theatrical: 1996-05-03
Physical: 2022-05-17 (26 year gap > 10 year limit)
Selected: 1996 theatrical ✅ (smart fallback)
```
**Configuration**:
```bash
# Enable/disable smart validation
ENABLE_SMART_DATE_VALIDATION=true
# Maximum reasonable gap in years
MAX_RELEASE_DATE_GAP_YEARS=10
```
### Configuration Examples
**For "The Craft (1996)"** (predates digital):
- ❌ Digital: Not available (movie too old)
- ❌ Physical: DVD from 2000 (too late)
-**Theatrical: 1996-05-03****Selected**
**For "Top Gun Maverick (2022)"** (modern movie):
-**Digital: 2022-08-23****Selected** (your priority)
- ⏭️ Physical: 2022-10-31 (skipped due to priority)
- ⏭️ Theatrical: 2022-05-27 (skipped due to priority)
## 🐛 Troubleshooting
### Manual Imports Show File Dates Instead of Release Dates
**Problem**: Movies manually imported show recent file dates like `2025-08-29T12:14:28+00:00`
**Solution**:
1. Enable TMDB integration and configure fallback priority:
```bash
# Add to your .env file
TMDB_API_KEY=your_tmdb_api_key
PREFER_RELEASE_DATES_OVER_FILE_DATES=true
RELEASE_DATE_PRIORITY=digital,physical,theatrical
```
2. Test the priority logic:
```bash
curl "http://localhost:8080/debug/movie/tt0115963/priority"
```
3. Should show release date selection based on your priority order
### "0 Movies Processed" During Manual Scan
**Problem**: Manual scan reports processing 0 movies
**Solution**:
1. Test directory scanning:
```bash
curl -X POST "http://localhost:8080/test/movie-scan"
```
2. Verify paths in your `.env` file match your actual structure:
```bash
# Your structure: /media/Movies/movies/
MOVIE_PATHS=/media/Movies/movies,/media/Movies/movies6
```
3. Ensure movie directories have IMDb tags:
```
Movie Name [imdb-tt1234567] (2023)/
```
### Database Connection Issues
**Problem**: "Radarr database connection failed"
**Solution**:
1. Test database connectivity:
```bash
curl -X POST "http://localhost:8080/test/bulk-update"
```
2. Verify database credentials in `.env`:
```bash
RADARR_DB_HOST=radarr-postgres
RADARR_DB_PASSWORD=your_actual_password
```
### Release Dates Not Found
**Problem**: `"external_apis": {"tmdb_enabled": false}` or no release dates detected
**Solution**:
1. Add TMDB API key and configure fallback order:
```bash
# Add to your .env
TMDB_API_KEY=your_api_key
RELEASE_DATE_PRIORITY=digital,physical,theatrical
```
2. Restart container: `docker-compose restart`
3. Test all release types: `curl "http://localhost:8080/debug/tmdb/tt0115963"`
4. Verify priority logic: `curl "http://localhost:8080/debug/movie/tt0115963/priority"`
### Customizing Release Date Priority
**Default Order**: `digital,physical,theatrical` (your preference)
**Alternative Orders**:
```bash
# Prefer theatrical dates for older movies
RELEASE_DATE_PRIORITY=theatrical,digital,physical
# Physical media collector preference
RELEASE_DATE_PRIORITY=physical,digital,theatrical
# Digital-first modern preference
RELEASE_DATE_PRIORITY=digital,theatrical,physical
```
### Disabling File Date Fallbacks
**Problem**: Don't want file modification dates used at all
**Solution**: Disable file date fallbacks completely
```bash
# Add to your .env
ALLOW_FILE_DATE_FALLBACK=false
```
**Result**:
- ✅ Movies with release dates get processed normally
- ⚠️ Movies with NO release dates get skipped (no NFO created)
- 🔇 No more "Using file dateAdded as fallback" warnings
## 📱 Media Server Compatibility
### Movies
- **Emby**: ✅ Full support - reads NFO `<dateadded>` fields
- **Jellyfin**: ✅ Full support - reads NFO `<dateadded>` fields
- **Plex**: ✅ Full support - reads NFO `<dateadded>` fields
### TV Episodes
- **Emby**: ⚠️ **Partial support** - reads metadata (titles, plots, ratings) but **ignores all NFO date fields**
- **Jellyfin**: ✅ Full support - reads NFO `<dateadded>` fields
- **Plex**: ✅ Full support - reads NFO `<dateadded>` fields
> **Note**: Emby has an architectural limitation where TV episodes always use filesystem dates instead of NFO dates. This is a known Emby behavior that cannot be worked around. NFOGuard still provides valuable enhanced metadata for TV episodes in Emby, just not corrected dates.
---
## 🔑 API Keys Configuration
NFOGuard integrates with multiple external APIs to provide enhanced metadata and release date information.
### Required API Keys
| Service | Environment Variable | Purpose | How to Get |
|---------|---------------------|---------|------------|
| **TMDB** | `TMDB_API_KEY` | Movie release dates, metadata fallbacks | Free at [themoviedb.org](https://www.themoviedb.org/settings/api) |
| **TVDB** | `TVDB_API_KEY` | TV show metadata, Emby compatibility | Free at [thetvdb.com](https://thetvdb.com/api-information) |
| **Radarr** | `RADARR_API_KEY` | Movie import history, database access | Found in Radarr Settings → General |
| **Sonarr** | `SONARR_API_KEY` | TV episode import history | Found in Sonarr Settings → General |
### Configuration Example
Add to your `.env.secrets` file:
```bash
# External API keys for enhanced metadata
TMDB_API_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
TVDB_API_KEY=your-tvdb-api-key-here
# *arr application API keys
RADARR_API_KEY=a1b2c3d4e5f6...
SONARR_API_KEY=f6e5d4c3b2a1...
```
### API Key Warnings
If you see these warnings in logs, add the corresponding API key:
```bash
[WARNING] TMDB API key not configured - release date fallbacks disabled
[WARNING] TVDB API key not configured, skipping TVDB ID lookup
```
**Note**: TMDB and TVDB keys are **optional** but highly recommended for:
- ✅ Better release date accuracy
- ✅ Enhanced TV show metadata
- ✅ Improved Emby/Plex compatibility
---
## 📖 Example Workflow
You add The Blacklist in Sonarr.
Sonarr downloads S01E01 → NFOGuard logs the import date in DB and .nfo.
Months later, a better-quality upgrade replaces the file.
NFOGuard updates the .nfo and mtime, but keeps the original import date.
Emby/Jellyfin/Plex see the file as unchanged in chronology.
Result: no more "old shows" showing up in "Recently Added."
## 📺 Enhanced NFO Generation (NEW)
NFOGuard now creates **full-featured NFO files** with rich metadata from Sonarr:
### Before (Basic Format):
```xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<episodedetails>
<season>13</season>
<episode>2</episode>
<aired>2024-10-03</aired>
<dateadded>2025-02-19T23:10:35+00:00</dateadded>
<premiered>2025-02-19</premiered>
<lockdata>true</lockdata>
<!-- source: TV Episode; sonarr:history.import -->
<!-- managed by NFOGuard -->
</episodedetails>
```
### After (Enhanced Format):
```xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<episodedetails>
<season>13</season>
<episode>2</episode>
<title>Ride the Blade</title>
<plot>Firehouse 51 tackles a fire at a local restaurant. Boden has an emotional conversation with his stepson.</plot>
<runtime>42</runtime>
<rating>8.2</rating>
<votes>1543</votes>
<aired>2024-10-03</aired>
<dateadded>2025-02-19T23:10:35+00:00</dateadded>
<premiered>2025-02-19</premiered>
<lockdata>true</lockdata>
<!-- source: TV Episode; sonarr:history.import -->
<!-- managed by NFOGuard at 2025-09-11T13:45:22+00:00 -->
</episodedetails>
```
**Enhanced features:**
- ✅ **Episode titles** from Sonarr API
- ✅ **Plot summaries** for better library browsing
- ✅ **Runtime** and **ratings** data
- ✅ **Timestamps** showing when NFOGuard processed the file
- ✅ **Smart URL-safe endpoints** for processing individual seasons/episodes
- ✅ **Configurable processing modes** for webhook efficiency
## ⚙️ TV Webhook Processing Modes (v0.6.0+)
NFOGuard offers two processing modes for TV show webhooks:
### **Targeted Mode** (Default - `targeted`)
**Best for**: Efficient processing, minimal file operations
```bash
TV_WEBHOOK_PROCESSING_MODE=targeted
```
**Behavior**: Only processes episodes mentioned in the webhook
- ✅ **1 episode downloaded** → **1 episode processed**
- ✅ **Minimal file operations** - only touches new episode
- ✅ **Reduced notifications** to media servers
- ✅ **Faster processing** for single episode downloads
**Example**: Download S41E07 → Only S41E07.nfo gets updated
### **Series Mode** (`series`)
**Best for**: Comprehensive updates, database synchronization
```bash
TV_WEBHOOK_PROCESSING_MODE=series
```
**Behavior**: Processes entire TV series directory
- 📺 **1 episode downloaded****All series episodes processed**
- 🔄 **Complete sync** - ensures all episodes stay current
- 📋 **Database consistency** - all episode dates updated
- 🛠️ **Legacy behavior** - matches previous versions
**Example**: Download S41E07 → All 7 episodes in season get updated
### **Smart Fallback**
- If webhook contains no episode data → automatically uses series mode
- If targeted mode fails → falls back to series processing
- Configuration validated at startup with helpful error messages
🔍 Logging & Debugging
# Enable verbose logging
# In .env file
DEBUG=true
PATH_DEBUG=true
```
# Check container logs
docker logs sonarr-nfo-cache
### Health Check
```bash
curl http://localhost:8080/health
```
# Debug specific movie import detection
curl http://localhost:8080/debug/movie/tt1674782
### Common Issues
# Manual scan with verbose output
curl -X POST "http://localhost:8080/manual/scan?scan_type=movies"
1. **Permission Errors**: Ensure NFOGuard can write to mounted directories
2. **Path Mapping**: Verify container paths match `.env` configuration
3. **Webhooks**: Check URLs and ensure port 8080 is accessible
4. **Database**: Verify PostgreSQL credentials in `.env.secrets`
# File logs (inside container)
/app/data/logs/nfoguard.log
## 📊 What NFOGuard Does
⚙️ Environment Variables (v0.2.1+)
Variable Default Description
RADARR_ROOT_FOLDERS (required) What Radarr sees: /mnt/unionfs/Media/Movies/movies
SONARR_ROOT_FOLDERS (required) What Sonarr sees: /mnt/unionfs/Media/TV/tv
DOWNLOAD_PATH_INDICATORS (see example) Paths that indicate downloads vs existing files
MOVIE_PRIORITY import_then_digital Strategy: import_then_digital or digital_then_import
MOVIE_POLL_MODE always When to query APIs: always, if_missing, never
MOVIE_DATE_UPDATE_MODE backfill_only Update behavior: backfill_only or overwrite
### Before
```xml
<movie>
<title>Movie Title</title>
<year>2023</year>
<!-- Existing Radarr metadata -->
</movie>
```
📜 License
MIT License do what you want, just dont blame us if your timestamps go timey-wimey.
### After
```xml
<movie>
<title>Movie Title</title>
<year>2023</year>
<!-- Existing Radarr metadata preserved -->
<!-- NFOGuard additions at bottom -->
<digital_release_date>2023-03-15</digital_release_date>
<lockdata>true</lockdata>
<!-- Manager: NFOGuard -->
</movie>
```
## 📋 Requirements
- Docker and Docker Compose
- Radarr and/or Sonarr
- Media files in accessible directories
- Network connectivity between services
## 📁 Directory Structure Requirements
NFOGuard identifies movies and TV shows using two methods: directory names with IMDb IDs (primary) or NFO files with IMDb IDs (fallback). Your media should follow these conventions:
### 🎬 **Movies**
**Directory Structure:**
```
/movies/
└── Movie Title (2024) [tt1234567]/
├── movie.mkv
└── movie.nfo (created by NFOGuard)
```
**Identification Methods:**
1. **Primary**: Directory name contains IMDb ID in brackets: `[tt1234567]` or `[imdb-tt1234567]`
2. **Fallback**: NFO file with IMDb ID in movie.nfo file (see NFO format below)
3. **Video file** required: `.mkv`, `.mp4`, `.avi`, `.mov`, `.m4v`
4. **Case insensitive** - `[TT1234567]` works too
**Examples:**
```
✅ Action Film (2024) [tt1234567]/
✅ Drama Movie [imdb-tt7654321]/
✅ SciFi Thriller (2023) [TT9876543]/
❌ Missing IMDB Directory/
```
### 📺 **TV Shows**
**Directory Structure:**
```
/tv/
└── Series Title (2024) [tt1234567]/
├── Season 01/
│ ├── Series S01E01.mkv
│ ├── Series S01E02.mkv
│ ├── S01E01.nfo (created by NFOGuard)
│ └── S01E02.nfo (created by NFOGuard)
├── Season 02/
└── tvshow.nfo (created by NFOGuard)
```
**Identification Methods:**
1. **Primary**: Series directory contains IMDb ID: `[tt1234567]` or `[imdb-tt1234567]`
2. **Fallback**: NFO file with IMDb ID in tvshow.nfo file (see NFO format below)
3. **Season directories** must match pattern: `Season 01`, `Season 1`, `season 01` etc.
4. **Episode files** must contain season/episode info:
- **SxxExx format**: `S01E01`, `S1E1`, `s01e01`
- **Dot format**: `1.1`, `01.01`
5. **Video extensions**: `.mkv`, `.mp4`, `.avi`, `.mov`, `.m4v`, `.ts`, `.m2ts`
**Examples:**
```
✅ Drama Series (2024) [tt1234567]/
├── Season 01/
│ ├── Drama S01E01.mkv
│ └── Drama S01E02.mkv
└── Season 02/
✅ Comedy Show [tt7654321]/
└── Season 1/
├── Comedy 1.1.mkv
└── Comedy 1.2.mkv
❌ Series Without IMDB []/
❌ Series [tt1234567]/Episode01.mkv (no season directory)
❌ Series [tt1234567]/Season 1/RandomName.mkv (no episode pattern)
```
### 📄 **NFO File Identification Format**
NFOGuard can extract IMDb IDs from existing NFO files using these XML tags:
**Movie NFO (movie.nfo):**
```xml
<!-- Method 1: uniqueid tag (preferred) -->
<uniqueid type="imdb">tt1234567</uniqueid>
<!-- Method 2: imdbid tag -->
<imdbid>tt1234567</imdbid>
<!-- Method 3: imdb tag -->
<imdb>tt1234567</imdb>
```
**TV Show NFO (tvshow.nfo):**
```xml
<!-- Same format as movies -->
<uniqueid type="imdb">tt1234567</uniqueid>
<imdbid>tt1234567</imdbid>
<imdb>tt1234567</imdb>
```
**Episode NFO Files:**
NFOGuard creates standardized episode NFO files using the pattern `S##E##.nfo`:
- `S01E01.nfo` for Season 1, Episode 1
- `S02E05.nfo` for Season 2, Episode 5
- Always zero-padded format (S01E01, not S1E1)
- **Smart Rename**: NFOGuard will find existing NFO files (created by Sonarr/other tools), extract their metadata, and rename them to the standard format
### 🚫 **What Gets Skipped**
NFOGuard will ignore:
- Directories without IMDb IDs in brackets AND no NFO files with IMDb IDs
- Directories without video files
- TV episodes without recognizable season/episode patterns
- Season directories that don't match "Season X" format
## 🆘 Support
- **Issues**: [GitHub Issues](https://github.com/sbcrumb/NFOguard/issues)
- **Documentation**: See `SETUP.md` for detailed instructions
- **Docker Hub**: [`sbcrumb/nfoguard`](https://hub.docker.com/r/sbcrumb/nfoguard)
## 📄 License
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
Commercial licensing and enterprise features may be available separately. Contact us for more information.
---
**NFOGuard** - Keeping your media metadata clean and organized! 🎯