# NFOGuard *** Alpha in progress things are volatile use at your own risk *** **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. 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. --- ## ✨ Features - **Import Date Locking** Captures the original date a movie or episode was imported and preserves it across upgrades. - **NFO Management** Creates and updates `.nfo` files for movies and TV episodes with consistent `` fields. Optionally adds `` to prevent metadata drift from scrapers. - **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 don’t bubble to the top of β€œRecently Added.” --- ## πŸš€ Quick Start (Docker) ```bash docker run -d \ --name=NFOGuard \ -p 8080:8080 \ -v /mnt/unionfs/Media:/media:rw \ -v /opt/NFOGuard/data:/app/data \ -e TV_PATHS="/media/TV/tv,/media/TV/tv6" \ -e MOVIE_PATHS="/media/Movies/movies,/media/Movies/movies6" \ ghcr.io/your-org/NFOGuard:latest βš™οΈ Environment Variables Variable Default Description TV_PATHS /media/tv,/media/tv6 Comma-separated list of TV root paths MOVIE_PATHS /media/movies,/media/movies6 Comma-separated list of Movie root paths DB_PATH /app/data/media_dates.db Path to SQLite database MANAGE_NFO true Write/update NFO files FIX_DIR_MTIMES true Sync directory/file mtimes to import date LOCK_METADATA true Add in NFOs BATCH_DELAY 5.0 Delay before processing batched events DEBUG false Enable verbose logging πŸ”Œ API Endpoints ### Webhook Endpoints ```bash # Radarr webhook (automatic processing) POST /webhook/radarr # Sonarr webhook (automatic processing) POST /webhook/sonarr ``` ### Manual Processing ```bash # Manual scan all media curl -X POST "http://localhost:8080/manual/scan?scan_type=both" # Manual scan TV only curl -X POST "http://localhost:8080/manual/scan?scan_type=tv" # Manual scan movies only curl -X POST "http://localhost:8080/manual/scan?scan_type=movies" # Manual scan specific path curl -X POST "http://localhost:8080/manual/scan?path=/media/movies" # Bulk update all movies from Radarr database curl -X POST "http://localhost:8080/bulk/update" ``` ### Testing & Validation ```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.5.1", "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", "priority_explanation": "1st: Radarr import history, 2nd: TMDB digital release, 3rd: file mtime", "date_sources": { "radarr_import": {"date": "2025-07-08T03:30:04+00:00", "source": "radarr:history.import"}, "digital_release": {"date": "2023-05-15T00:00:00+00:00", "source": "tmdb:digital"} }, "selected_date": "2025-07-08T03:30:04+00:00" } ``` ## πŸ› Troubleshooting ### Manual Imports Show File Dates Instead of Digital Releases **Problem**: Movies manually imported show recent file dates like `2025-08-29T12:14:28+00:00` **Solution**: 1. Enable TMDB integration: ```bash # Add to your .env file TMDB_API_KEY=your_tmdb_api_key PREFER_DIGITAL_OVER_FILE_DATES=true ``` 2. Test the priority logic: ```bash curl "http://localhost:8080/debug/movie/tt0115963/priority" ``` 3. Should show digital date selection when reasonable ### "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 ``` ### Digital Dates Not Found **Problem**: `"external_apis": {"tmdb_enabled": false}` **Solution**: 1. Add TMDB API key to `.env` 2. Restart container: `docker-compose restart` 3. Verify: `curl "http://localhost:8080/debug/movie/tt0115963/priority"` ## πŸ“– 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." πŸ” Logging & Debugging # Enable verbose logging DEBUG=true # Check container logs docker logs sonarr-nfo-cache # Debug specific movie import detection curl http://localhost:8080/debug/movie/tt1674782 # Manual scan with verbose output curl -X POST "http://localhost:8080/manual/scan?scan_type=movies" # File logs (inside container) /app/data/logs/nfoguard.log βš™οΈ 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 πŸ“œ License MIT License – do what you want, just don’t blame us if your timestamps go timey-wimey.