Revolutionary Workflow Changes (v0.7.0): This fundamentally changes how NFOGuard handles timestamps and processing priority. Webhooks = Source of Truth: - First webhook fires → Use current timestamp → Store as permanent database entry - Subsequent webhooks (upgrades) → Check database → Use original first-seen timestamp - No more API calls during webhook processing → Webhook timing is ultimate authority - Movies and TV episodes both use webhook-first approach Manual Scans = Smart Fallback Logic: - Priority 1: Our database (webhook timestamps) - database always wins - Priority 2: Sonarr/Radarr import history (first import only) - Priority 3: Air date as dateadded (final fallback) Technical Implementation: - Enhanced _get_webhook_episode_date() to use current timestamp as source of truth - Added webhook_mode parameter to process_movie() for separate webhook logic - All manual scans prioritize database entries before making API calls - All timestamps converted to container timezone (Eastern Time) - Enhanced debug logging for database lookups and timestamp decisions Expected Workflow: First download at 8:30am → webhook timestamp stored in database Upgrade at 2:00pm → database entry found → original 8:30am timestamp preserved Manual scan → database entry found → 8:30am timestamp used This ensures the first-seen webhook timestamp is the permanent source of truth, with upgrades and manual scans always preserving the original download time.
18 KiB
NFOGuard Project Summary
📋 Current Status: v0.6.1 (Latest Improvements)
🎯 Project Goal
Preserve movie and TV import dates in Emby/Jellyfin/Plex by preventing upgraded files from appearing as "recently added". NFOGuard listens to Radarr/Sonarr webhooks and manages .nfo files and file timestamps to maintain chronological consistency.
🚀 Major Achievements
Database-Only Architecture (v0.5.0+)
- Performance: 10x faster than API-based approach
- Reliability: Direct PostgreSQL database queries eliminate API limitations
- Scalability: Handles 1500+ movies without pagination issues
🎯 Configurable Release Date Priority System (v0.6.0)
- Three-Tier Priority System: Digital → Physical → Theatrical (fully configurable)
- Smart Fallbacks: "The Craft (1996)" gets 1996 theatrical, "Top Gun Maverick (2022)" gets digital
- Per-Movie Intelligence: Automatically adapts to movie era and available release data
- Source Tracking: NFO files annotated with chosen date source (tmdb:theatrical, tmdb:digital, etc.)
- Quality Control: Prevents unrealistic dates through smart comparison logic
🏗 Current Architecture
Core Components:
nfoguard.py- Main FastAPI webhook serverclients/radarr_db_client.py- Direct database access clientclients/external_clients.py- TMDB/OMDb integrationcore/nfo_manager.py- NFO file creation and managementbulk_update_movies.py- Mass movie processing
Data Flow:
- Radarr webhook → NFOGuard processes → Database queries for import dates
- Smart fallback logic for manual imports → TMDB digital release dates
- NFO file creation with preserved dates → File timestamp updates
🔧 Configuration Management
Environment-Based Setup:
.env.template- Generic configuration templatedocker-compose.yml- Production deployment configuration- Supports multiple media paths and database types
Key Settings:
MOVIE_PRIORITY=import_then_digital- Prioritizes real import historyPREFER_RELEASE_DATES_OVER_FILE_DATES=true- Smart fallback for manual importsRELEASE_DATE_PRIORITY=digital,physical,theatrical- Configurable fallback order- Database connection parameters for PostgreSQL/SQLite
📊 Performance Metrics
Before (API-based):
- Took 30+ seconds for complex movies
- Failed on movies with extensive history
- API pagination caused timeouts
After (Database-only):
- Sub-second response times
- Handles any history size
- Reliable July 2025 date detection maintained
🧪 Testing Infrastructure
Comprehensive Test Suite:
test_bulk_update.py- Database connection validationtest_movie_scan.py- Directory scanning logic testingtest_end_to_end.py- Complete workflow validation
Debug Endpoints:
/debug/movie/{imdb_id}- Import date analysis/debug/movie/{imdb_id}/priority- Date selection logic/debug/movie/{imdb_id}/history- Complete import history
🎉 Success Metrics
Production Results:
- ✅ Correct July 2025 dates preserved for legitimate imports
- ✅ Manual imports now use intelligent release date selection:
- "The Craft (1996)" → 1996 theatrical date (not 2025 file date)
- "Top Gun Maverick (2022)" → digital release date per user preference
- ✅ Zero API timeout issues with database-only approach
- ✅ Complete webhook-based operation (no manual CLI required)
- ✅ NFO source annotations for full transparency
🚧 Current Development Focus
Completed (v0.6.0):
- ✅ Revolutionary Priority System: Configurable digital/physical/theatrical fallbacks
- ✅ Per-Movie Intelligence: Adapts to movie era and available release data
- ✅ Complete Documentation: README, testing guides, troubleshooting
- ✅ Source Transparency: NFO annotations show exactly which source was used
Next Priorities:
- TV series processing optimization
- Additional external API integrations (OMDb, Jellyseerr)
- Advanced configuration options
- Community feedback integration
📈 Project Maturity
Ready for Production:
- ✅ Stable database architecture
- ✅ Comprehensive error handling
- ✅ Docker deployment ready
- ✅ Extensive testing coverage
- ✅ Complete documentation
Community Ready:
- ✅ Environment-based configuration
- ✅ Portable Docker setup
- ✅ Detailed API documentation
- ✅ Troubleshooting guides
🆕 v0.6.1 Recent Improvements (September 2025)
🔧 Enhanced Radarr History Detection:
- Smart Upgrade Detection: Detects movies where first event is
movieFileRenamed(upgrade scenarios) - Improved Logic: For movies like The Matrix (1999), prefer digital release dates over recent upgrade dates
- True Import Preservation: Still honors actual import dates when they exist - only enhances edge cases
🎯 Improved IMDb ID Extraction:
- Flexible Regex: Now supports both
[imdb-tt123]and[tt123]formats - NFO Fallback: Scans
.nfofiles when path extraction fails (handles Radarr auto-generated files) - Broader Compatibility: Works with various folder naming conventions
🧠 Enhanced Priority Logic:
- Movies with rename→upgrade history now get better chronological dates
- True import dates always take precedence (no regression)
- Smart fallbacks only engage when appropriate
🆕 v0.6.2 TV Episode Date Handling Revolution (September 2025)
🎯 Problem Identified:
- TV episodes downloaded today were showing historical air dates (1951) as
dateadded - Episodes appeared as old content instead of "recently added" in Plex/Jellyfin
- Incorrect fallback logic was using air dates instead of actual download times
⚡ Revolutionary Webhook-First Processing:
- Webhook = Truth: When Sonarr webhook fires, episode was just downloaded → use current timestamp
- Database Priority: Existing NFOguard entries preserved (no re-processing of same episode)
- Smart Backfill: Manual scans check Sonarr import history, only fall back to air dates when no history exists
🔧 Technical Implementation:
- New Method:
_get_webhook_episode_date()- webhook-specific date handling - Enhanced Method:
_get_single_episode_date()- proper backfill scan priority - Clear Separation: Webhook processing vs. backfill scanning use different logic paths
- Database-First: Always check NFOguard database before external APIs
📋 Correct Processing Flow:
- New Episode Download → Sonarr webhook → NFOguard stores current time as
dateadded - Same Episode Re-download → NFOguard sees existing entry → uses stored date (preserves original)
- Backfill Scan → Check NFOguard DB → Check Sonarr import history → Fall back to air dates only if needed
- First Time Setup → No bulk import - database populated only through webhooks and manual scans
🏆 Results:
- Episodes downloaded today now correctly show today's date as
dateadded - Historical air dates preserved in
airedfield for accuracy - "Recently Added" functionality restored in media servers
- No regression for existing properly-dated episodes
🐛 Bug Fixes:
- Fixed f-string syntax error in
core/nfo_manager.py:324 - Resolved backslash escaping issue preventing startup
🆕 v0.6.3 NFO Management & Date Accuracy Improvements (September 2025)
💾 Smart NFO File Management:
- Problem: NFOguard was rewriting NFO files for every episode on every scan, even untouched ones
- Solution: Intelligent content comparison - only updates NFOs when content actually changes
- Performance: Dramatically reduces unnecessary file system writes and processing time
- Both Media Types: Applied to both TV episodes and movies for comprehensive efficiency
🗓️ Correct Date Field Mapping:
- Problem: Episode NFOs showing import dates (2025) for historical fields
<aired>and<premiered> - Root Cause: Logic incorrectly used
dateaddedfor all date fields instead of proper separation - Fix: Clear separation of date meanings:
<aired>: Historical air date (e.g., 1951-10-15 for I Love Lucy)<premiered>: Historical premiere date (same as aired)<dateadded>: Actual download/import date (e.g., 2025-09-13T21:39:28+00:00)
🎯 Technical Implementation:
- New Method:
_nfo_content_matches()- smart content comparison ignoring timestamps - Enhanced Logic: Episodes now correctly separate historical vs import dates
- Skip Logic: Files marked "already up-to-date" when no meaningful changes detected
- Logging: Clear debug output showing when files are skipped vs updated
🏆 User Experience Improvements:
- Correct Metadata: Shows historically accurate air dates in media servers
- Recently Added: Still works properly using
dateaddedfield - Performance: Faster scans with fewer unnecessary file writes
- Accuracy: No more confusion between when show aired vs when you downloaded it
📋 Example Before/After:
<!-- Before (Wrong) -->
<aired>2025-09-13</aired> <!-- Import date in wrong field -->
<dateadded>2025-09-13T21:39:28+00:00</dateadded>
<premiered>2025-09-13</premiered> <!-- Import date in wrong field -->
<!-- After (Correct) -->
<aired>1951-10-15</aired> <!-- Historical air date ✅ -->
<dateadded>2025-09-13T21:39:28+00:00</dateadded> <!-- When downloaded ✅ -->
<premiered>1951-10-15</premiered> <!-- Historical premiere ✅ -->
🆕 v0.6.4 Timezone-Aware Logging Fix (September 2025)
🕰️ Universal Timezone Consistency:
- Problem: Mixed timestamp formats in logs made troubleshooting difficult
- Some logs:
[2025-09-14T09:37:00.338045](local time without timezone info) - Other logs:
[2025-09-14T13:37:00+00:00](UTC with timezone info) - Docker
TZ=America/New_Yorkenvironment variable not respected by all logging systems
- Some logs:
⚡ Comprehensive Logging Fix:
- Unified Timezone Handling: All logging systems now respect the
TZenvironment variable - Custom TimezoneAwareFormatter: Python's standard logging now uses container timezone
- Enhanced _log Function: Custom logging function updated with robust timezone support
- Centralized Logging: Replaced placeholder logging functions with consistent implementation
🔧 Technical Implementation:
- New Class:
TimezoneAwareFormatterfor Python's standard logging module - Enhanced Function:
_get_local_timezone()with fallbacks for different Python versions - Cross-Compatibility: Supports both
zoneinfo(Python 3.9+) andpytz(older versions) - Consistent Import: All client modules now use centralized
core.logging._log
🏆 User Experience Improvements:
- Consistent Timestamps: All logs AND NFO files now show the same timezone format
- Easier Troubleshooting: Timestamps match user's local environment everywhere
- No More Confusion: No need to mentally convert between UTC and local time
- Production Ready: Robust fallbacks ensure timezone handling works in all environments
- NFO File Accuracy: Movie and TV episode
<dateadded>fields now respect container timezone
📋 Before/After Example:
# Before (Inconsistent)
sonarr-nfo-cache | [2025-09-14T09:37:00.338045] DEBUG: Mapped Radarr path...
sonarr-nfo-cache | [2025-09-14T13:37:00+00:00] INFO: Batched movie webhook...
<dateadded>2025-09-14T13:49:00+00:00</dateadded> <!-- UTC in NFO files -->
# After (Consistent)
sonarr-nfo-cache | [2025-09-14T09:37:00-04:00] DEBUG: Mapped Radarr path...
sonarr-nfo-cache | [2025-09-14T09:37:00-04:00] INFO: Batched movie webhook...
<dateadded>2025-09-14T09:49:00-04:00</dateadded> <!-- Local timezone in NFO files -->
🔧 Additional NFO Timezone Fixes:
- Movie Import Dates: Convert UTC timestamps from Radarr to local timezone in NFO files
- TV Episode Downloads: Webhook-triggered episodes now use local time for
<dateadded> - File Modification Times: Fallback file mtime dates now respect container timezone
- Historical Date Preservation:
<aired>and<premiered>dates remain historically accurate - Centralized Logging: All modules now use consistent timezone-aware logging system
📝 Development Workflow Instructions
For Future Updates:
- VERSION File: Always update the
VERSIONfile to match the release version being documented - Commit Authority: Claude Code is authorized to write commit messages and push changes directly
- SUMMARY Updates: This file serves as the primary project documentation and should be updated with each significant change
- Version Consistency: Ensure VERSION file, SUMMARY.md version, and git tags all align
🆕 v0.6.6 NFO Timestamp & Duplicate Processing Fixes (September 2025)
🕰️ NFO Management Timestamp Fix:
- Problem: NFO management comments still showed UTC timestamps despite container timezone setting
- Fix: All NFO file comments now respect local timezone
- Before:
<!-- managed by NFOGuard at 2025-09-14T13:29:06+00:00 --> - After:
<!-- managed by NFOGuard at 2025-09-14T09:29:06-04:00 -->
🔍 Enhanced Episode Processing Debug:
- Enhanced Logging: Added comprehensive debug logging for episode database lookups
- Duplicate Detection: Improved tracking of why episodes might be processed multiple times
- Database Verification: Added verification logging after database writes
- IMDb ID Tracking: Enhanced logging shows IMDb ID in processing messages
🛠️ Technical Improvements:
- NFO Manager: Updated both movie and TV episode NFO timestamp generation
- Webhook Processing: Added detailed debug logging for episode lookup failures
- Database Integrity: Added immediate verification of database writes
- Troubleshooting Ready: Enhanced logging will help identify duplicate processing causes
🆕 v0.6.7 Webhook Episode Import History Fix (September 2025)
🎯 Critical Webhook Processing Fix:
- Problem: Webhook episodes used current time instead of actual Sonarr import history
- Issue:
<dateadded>2025-09-14T12:40:07+00:00</dateadded>showed webhook time (UTC) instead of real import time (local) - Root Cause:
_get_webhook_episode_date()wasn't querying Sonarr import history like manual scans
⚡ Enhanced Webhook Episode Processing:
- Sonarr History Lookup: Webhooks now query Sonarr import history for real import dates
- Local Timezone Conversion: Import dates converted to container timezone (Eastern Time)
- Proper Source Attribution: Episodes now show
sonarr:history.importinstead ofwebhook:new_downloadwhen history exists - Fallback Logic: Only uses current time when no Sonarr history found (true new downloads)
📋 Expected Results:
<!-- Before (Wrong) -->
<dateadded>2025-09-14T12:40:07+00:00</dateadded> <!-- Current webhook time in UTC -->
<!-- source: TV Episode; webhook:new_download -->
<!-- After (Correct) -->
<dateadded>2025-09-14T08:40:07-04:00</dateadded> <!-- Real import time in Eastern -->
<!-- source: TV Episode; sonarr:history.import -->
🛠️ Technical Implementation:
- Enhanced Logic: Webhooks now follow same import history priority as manual scans
- Timezone Consistency: All import dates converted to local timezone before NFO generation
- Better Logging: Shows whether import history was found or current time used as fallback
- Upgrade Handling: Episodes downloaded multiple times now preserve original import dates
🆕 v0.7.0 MAJOR: Webhook-First Architecture & Database Priority (September 2025)
🎯 Revolutionary Workflow Changes: This is a major architectural change that fundamentally alters how NFOGuard handles timestamps.
📡 Webhooks = Source of Truth:
- First webhook seen → Use current timestamp → Store as permanent source of truth
- Subsequent webhooks (upgrades) → Check database → Use original first-seen timestamp
- No more API calls during webhooks → Webhook timing is the ultimate authority
🔍 Manual Scans = Smart Fallback Logic:
- Priority 1: Our database (webhook timestamps) → Database always wins
- Priority 2: Sonarr/Radarr import history (first import only)
- Priority 3: Air date as dateadded (final fallback)
⚡ Implementation Details:
- TV Episodes: Enhanced
_get_webhook_episode_date()→ uses current timestamp as source of truth - Movies: New webhook mode in
process_movie()→ separate logic for webhooks vs manual scans - Database Priority: Manual scans now always check database first before API calls
- Local Timezone: All timestamps converted to container timezone (Eastern Time)
🛠️ Technical Changes:
- Webhook Sources: Episodes/movies show
webhook:first_seeninstead of complex API sources - Database Verification: Added immediate verification logging after database writes
- Enhanced Debug: Comprehensive logging shows database lookups and timestamp decisions
- Clean Separation: Webhook processing vs manual scan processing use different code paths
📋 Expected Workflow:
# First Download (8:30am EST)
Webhook fires → Use 8:30am timestamp → Store in database → NFO shows 8:30am
# Upgrade Download (2:00pm EST)
Webhook fires → Check database → Find 8:30am entry → NFO still shows 8:30am
# Manual Scan
Curl scan → Check database → Find 8:30am webhook entry → NFO shows 8:30am
🏆 Benefits:
- Consistent Timestamps: First download time is preserved forever
- Simplified Logic: Webhooks don't query APIs, just use current time
- Performance: Faster webhook processing with database-first approach
- Predictable: Clear hierarchy - database beats everything else
Last Updated: September 14, 2025
Version: v0.7.0
Status: Production Ready - Major Architecture