fix: update tv .nfo handling

updated TV .nfo handling
This commit is contained in:
2025-09-22 10:52:20 -04:00
parent b881c7944c
commit b1484641d9
12 changed files with 175 additions and 1008 deletions
-28
View File
@@ -1,28 +0,0 @@
# NFOGuard Environment Variables
# User and Group IDs
PUID=1000
PGID=1000
# Timezone
TZ=UTC
# Media path (for read-only access to scan NFO files)
MEDIA_PATH=/path/to/your/media
# Database settings (if using PostgreSQL)
# DB_USER=nfoguard
# DB_PASSWORD=your_secure_password
# Emby Plugin Deployment - Bind Mount Method (Recommended)
# Set this to the path where Emby plugins are installed on your host
# Common paths:
# - /var/lib/emby/plugins (native Emby install)
# - /path/to/emby/config/plugins (Docker Emby)
# - /config/plugins (linuxserver.io Emby)
EMBY_PLUGINS_PATH=/path/to/emby/plugins
# NFOGuard specific settings
DEBUG=false
PATH_DEBUG=false
SUPPRESS_TVDB_WARNINGS=true
-124
View File
@@ -1,124 +0,0 @@
# ===========================================
# NFOGuard Configuration - CORRECTED VERSION
# ===========================================
# Main configuration file - safe to share for debugging
# Sensitive data (API keys, passwords) are in .env.secrets
# ===========================================
# MEDIA PATHS (REQUIRED) - FIXED
# ===========================================
# Container paths (what NFOGuard sees inside container)
MOVIE_PATHS=/media/Movies/movies,/media/Movies/movies6
TV_PATHS=/media/TV/tv,/media/TV/tv6
# Radarr paths (what Radarr sees on the host system)
RADARR_ROOT_FOLDERS=/mnt/unionfs/Media/Movies/movies,/mnt/unionfs/Media/Movies/movies6
# Sonarr paths (what Sonarr sees on the host system) - FIXED VARIABLE NAME AND PATHS
SONARR_ROOT_FOLDERS=/mnt/unionfs/Media/TV/tv,/mnt/unionfs/Media/TV/tv6
# Download detection paths (for identifying downloads vs existing files)
DOWNLOAD_PATH_INDICATORS=/downloads/,/nzbget/,/completed/,/sabnzbd/,/torrents/
# ===========================================
# DATABASE CONFIGURATION
# ===========================================
# NFOGuard SQLite database location
DB_PATH=/app/data/media_dates.db
# Log file directory
LOG_DIR=/app/data/logs
# ===========================================
# RADARR DATABASE CONNECTION (RECOMMENDED)
# ===========================================
# Direct database access for better performance
RADARR_DB_TYPE=postgresql
RADARR_DB_HOST=192.168.255.50
RADARR_DB_PORT=5432
RADARR_DB_NAME=radarr-main
RADARR_DB_USER=postgres
# ===========================================
# API CONNECTIONS (OPTIONAL)
# ===========================================
# API keys are stored in .env.secrets for security
RADARR_URL=http://radarr:7878
SONARR_URL=http://sonarr:8989
JELLYSEERR_URL=http://jellyseerr:5055
# ===========================================
# RELEASE DATE PROCESSING
# ===========================================
# Priority order for release date fallbacks (digital, physical, theatrical)
RELEASE_DATE_PRIORITY=digital,physical,theatrical
#RELEASE_DATE_PRIORITY=digital,theatrical,physical
ENABLE_SMART_DATE_VALIDATION=true
MAX_RELEASE_DATE_GAP_YEARS=10
# Prefer API release dates over file modification dates for manual imports
PREFER_RELEASE_DATES_OVER_FILE_DATES=true
# Disable file date fallback completely (recommended for clean imports)
ALLOW_FILE_DATE_FALLBACK=false
# TMDB country for regional release date preferences
TMDB_COUNTRY=US
# ===========================================
# NFO FILE MANAGEMENT
# ===========================================
# Create/update .nfo files
MANAGE_NFO=true
# Update file modification times to match import dates
FIX_DIR_MTIMES=true
# Add lockdata tags to prevent metadata overwrites
LOCK_METADATA=true
# Brand name in NFO comments
MANAGER_BRAND=NFOGuard
# ===========================================
# PROCESSING BEHAVIOR
# ===========================================
# Movie date update strategy
MOVIE_DATE_UPDATE_MODE=overwrite
MOVIE_PRESERVE_EXISTING=false
# When to update existing dates (always, missing_only, never)
UPDATE_MODE=always
# File modification time behavior (update, leave_alone)
MTIME_BEHAVIOR=update
# ===========================================
# PERFORMANCE & BATCHING
# ===========================================
# Delay before processing batched events (seconds)
BATCH_DELAY=5.0
# Maximum concurrent series processing
MAX_CONCURRENT_SERIES=3
# API timeout in seconds
TIMEOUT_SECONDS=45
# ===========================================
# DEBUGGING
# ===========================================
# Enable verbose logging (true/false)
DEBUG=false
# Enable path mapping debug output (true/false)
PATH_DEBUG=false
# Suppress TVDB API warnings (true/false) - TVDB failures are common and non-critical
SUPPRESS_TVDB_WARNINGS=true
# ===========================================
# SERVER CONFIGURATION
# ===========================================
# Port for webhook server
PORT=8080
+78 -95
View File
@@ -1,13 +1,24 @@
# NFOGuard Environment Configuration Template
# Copy this to .env and customize for your setup
# ===========================================
# NFOGuard Configuration - CORRECTED VERSION
# ===========================================
# Main configuration file - safe to share for debugging
# Sensitive data (API keys, passwords) are in .env.secrets
# ===========================================
# MEDIA PATHS (REQUIRED)
# MEDIA PATHS (REQUIRED) - FIXED
# ===========================================
# Paths where your movies and TV shows are stored inside the container
# Adjust these to match YOUR directory structure
TV_PATHS=/media/TV/tv,/media/TV/tv6
# Container paths (what NFOGuard sees inside container)
MOVIE_PATHS=/media/Movies/movies,/media/Movies/movies6
TV_PATHS=/media/TV/tv,/media/TV/tv6
# Radarr paths (what Radarr sees on the host system)
RADARR_ROOT_FOLDERS=/mnt/unionfs/Media/Movies/movies,/mnt/unionfs/Media/Movies/movies6
# Sonarr paths (what Sonarr sees on the host system) - FIXED VARIABLE NAME AND PATHS
SONARR_ROOT_FOLDERS=/mnt/unionfs/Media/TV/tv,/mnt/unionfs/Media/TV/tv6
# Download detection paths (for identifying downloads vs existing files)
DOWNLOAD_PATH_INDICATORS=/downloads/,/nzbget/,/completed/,/sabnzbd/,/torrents/
# ===========================================
# DATABASE CONFIGURATION
@@ -15,127 +26,99 @@ MOVIE_PATHS=/media/Movies/movies,/media/Movies/movies6
# NFOGuard SQLite database location
DB_PATH=/app/data/media_dates.db
# Log file directory
LOG_DIR=/app/data/logs
# ===========================================
# RADARR DATABASE CONNECTION (REQUIRED)
# RADARR DATABASE CONNECTION (RECOMMENDED)
# ===========================================
# Connection to Radarr's PostgreSQL database
# NOTE: Database password should be set in .env.secrets file
RADARR_DB_HOST=radarr-postgres
# Direct database access for better performance
RADARR_DB_TYPE=postgresql
RADARR_DB_HOST=192.168.255.50
RADARR_DB_PORT=5432
RADARR_DB_NAME=radarr
RADARR_DB_USER=radarr
RADARR_DB_NAME=radarr-main
RADARR_DB_USER=postgres
# ===========================================
# RADARR API (OPTIONAL - for fallback)
# API CONNECTIONS (OPTIONAL)
# ===========================================
# Only needed if you want API fallback (not recommended for performance)
# NOTE: API key should be set in .env.secrets file
# API keys are stored in .env.secrets for security
RADARR_URL=http://radarr:7878
# ===========================================
# SONARR API (REQUIRED for Enhanced TV NFOs - v0.6.0+)
# ===========================================
# Required for TV processing and enhanced NFO generation with metadata
# NOTE: API key should be set in .env.secrets file
SONARR_URL=http://sonarr:8989
# ===========================================
# EXTERNAL APIs FOR DIGITAL RELEASE DATES
# ===========================================
# NOTE: All API keys should be set in .env.secrets file
# See .env.secrets.template for required keys:
# - TMDB_API_KEY (recommended for digital release dates)
# - OMDB_API_KEY (optional for DVD/digital release dates)
# - JELLYSEERR_API_KEY (optional for additional digital release data)
# Jellyseerr URL (optional)
JELLYSEERR_URL=http://jellyseerr:5055
# ===========================================
# NFO MANAGEMENT
# RELEASE DATE PROCESSING
# ===========================================
# Whether to create/update .nfo files
# Priority order for release date fallbacks (digital, physical, theatrical)
RELEASE_DATE_PRIORITY=digital,physical,theatrical
#RELEASE_DATE_PRIORITY=digital,theatrical,physical
ENABLE_SMART_DATE_VALIDATION=true
MAX_RELEASE_DATE_GAP_YEARS=10
# Prefer API release dates over file modification dates for manual imports
PREFER_RELEASE_DATES_OVER_FILE_DATES=true
# Disable file date fallback completely (recommended for clean imports)
ALLOW_FILE_DATE_FALLBACK=false
# TMDB country for regional release date preferences
TMDB_COUNTRY=US
# ===========================================
# NFO FILE MANAGEMENT
# ===========================================
# Create/update .nfo files
MANAGE_NFO=true
# Whether to fix file modification times to match import dates
# Update file modification times to match import dates
FIX_DIR_MTIMES=true
# Whether to add <lockdata> tags to prevent metadata drift
# Add lockdata tags to prevent metadata overwrites
LOCK_METADATA=true
# Brand name to add to NFO files
# Brand name in NFO comments
MANAGER_BRAND=NFOGuard
# ===========================================
# PROCESSING SETTINGS
# PROCESSING BEHAVIOR
# ===========================================
# Delay before processing batched webhook events (seconds)
# Movie date update strategy
MOVIE_DATE_UPDATE_MODE=overwrite
MOVIE_PRESERVE_EXISTING=false
# When to update existing dates (always, missing_only, never)
UPDATE_MODE=always
# File modification time behavior (update, leave_alone)
MTIME_BEHAVIOR=update
# ===========================================
# PERFORMANCE & BATCHING
# ===========================================
# Delay before processing batched events (seconds)
BATCH_DELAY=5.0
# Maximum concurrent series processing
MAX_CONCURRENT_SERIES=3
# Movie date strategy: import_then_digital or digital_then_import
MOVIE_PRIORITY=import_then_digital
# Smart fallback: prefer release dates over file dates for manual imports (true/false)
PREFER_RELEASE_DATES_OVER_FILE_DATES=true
# Allow file dates as absolute last resort (true/false)
# If false, movies with no valid import AND no release dates will be skipped entirely
ALLOW_FILE_DATE_FALLBACK=false
# Fallback priority order when no valid Radarr import exists (comma-separated)
# Options: digital, physical, theatrical
# digital = VOD/streaming release, physical = DVD/Blu-ray, theatrical = cinema release
RELEASE_DATE_PRIORITY=digital,physical,theatrical
# Smart date validation: automatically prefer theatrical over unreasonably late digital/physical dates
# Example: "The Craft (1996)" theatrical vs "The Craft (2022)" physical → chooses 1996 theatrical
ENABLE_SMART_DATE_VALIDATION=true
# Maximum reasonable gap between theatrical and digital/physical release (in years)
# If digital/physical is more than this many years after theatrical, prefer theatrical instead
MAX_RELEASE_DATE_GAP_YEARS=10
# TMDB country for regional release date preferences
TMDB_COUNTRY=US
# TMDB release type priority (comma-separated numbers)
# 1=Premiere, 2=Limited Theatrical, 3=Theatrical, 4=Digital, 5=Physical, 6=TV Premiere
# Default: 4,5,3,2,6,1 (Digital → Physical → Theatrical → Limited → TV → Premiere)
TMDB_TYPE_PRIORITY=4,5,3,2,6,1
# When to query APIs: always, if_missing, never
MOVIE_POLL_MODE=always
# Update mode: backfill_only or overwrite
MOVIE_DATE_UPDATE_MODE=backfill_only
# API timeout in seconds
TIMEOUT_SECONDS=45
# ===========================================
# TV SERIES PROCESSING
# DEBUGGING
# ===========================================
# Season directory naming format (supports {season} placeholder)
# Examples: "Season {season:02d}" -> "Season 01", "S{season:02d}" -> "S01"
TV_SEASON_DIR_FORMAT=Season {season:02d}
# Season directory detection pattern (lowercase, what to look for at start of dir name)
# Examples: "season " -> matches "Season 01", "s" -> matches "S01", "season" -> matches "Season01"
TV_SEASON_DIR_PATTERN=season
# TV webhook processing mode (v0.6.0+)
# targeted = Only process episodes mentioned in webhook (efficient, recommended)
# series = Process entire series directory (comprehensive, current default)
TV_WEBHOOK_PROCESSING_MODE=targeted
# ===========================================
# LOGGING
# ===========================================
# Enable debug logging
# Enable verbose logging (true/false)
DEBUG=false
# Enable path mapping debug output (true/false)
PATH_DEBUG=false
# Suppress TVDB API warnings (true/false) - TVDB failures are common and non-critical
SUPPRESS_TVDB_WARNINGS=true
# ===========================================
# SERVER
# SERVER CONFIGURATION
# ===========================================
# Port to run the webhook server on
# Port for webhook server
PORT=8080
+12 -42
View File
@@ -1,4 +1,4 @@
name: Docker Build & Deploy (DEV)
name: Local Docker Build (Dev)
on:
push:
@@ -15,12 +15,13 @@ jobs:
run: |
echo "Current workspace: $(pwd)"
# Clean workspace first
# Clean workspace and temp directory first
rm -rf * .git* 2>/dev/null || true
rm -rf /tmp/repo 2>/dev/null || true
# Clone the repository since Gitea runner doesn't auto-checkout
echo "Cloning repository..."
git clone http://${{ secrets.username }}:${{ secrets.token }}@192.168.253.221:3000/jskala/NFOguard.git /tmp/repo
git clone --branch dev http://${{ secrets.username }}:${{ secrets.token }}@192.168.253.221:3000/jskala/NFOguard.git /tmp/repo
cp -r /tmp/repo/* .
cp -r /tmp/repo/.* . 2>/dev/null || true
rm -rf /tmp/repo
@@ -95,6 +96,8 @@ jobs:
echo "Building DEV image with layer caching..."
docker build \
$CACHE_ARGS \
--build-arg GIT_BRANCH=dev \
--build-arg BUILD_SOURCE=gitea \
--tag nfoguard:dev \
--tag nfoguard:dev-${{ github.sha }} \
--tag "$CACHE_IMAGE" \
@@ -135,42 +138,9 @@ jobs:
echo "❌ DEV local registry login failed - continuing to Docker Hub"
fi
- name: Push DEV to Docker Hub
if: github.ref == 'refs/heads/dev'
run: |
echo "=== Pushing DEV image to Docker Hub ==="
# Use temporary config directory to avoid permission issues
export DOCKER_CONFIG=/tmp/docker-config-dev-hub
mkdir -p $DOCKER_CONFIG
if echo "${{ secrets.DOCKER_HUB_TOKEN }}" | docker --config $DOCKER_CONFIG login -u "${{ secrets.DOCKER_HUB_USERNAME }}" --password-stdin; then
echo "✅ Docker Hub login successful"
DOCKER_HUB_REPO="sbcrumb/nfoguard"
# Tag and push dev image
docker tag nfoguard:dev "$DOCKER_HUB_REPO:dev"
docker tag nfoguard:dev "$DOCKER_HUB_REPO:dev-${{ github.sha }}"
echo "=== Pushing DEV tags to Docker Hub ==="
if timeout 300 docker --config $DOCKER_CONFIG push "$DOCKER_HUB_REPO:dev"; then
echo "✅ DEV tag pushed to Docker Hub"
if timeout 180 docker --config $DOCKER_CONFIG push "$DOCKER_HUB_REPO:dev-${{ github.sha }}"; then
echo "✅ DEV SHA tag pushed to Docker Hub"
else
echo "⚠️ DEV SHA tag push failed"
fi
else
echo "❌ Failed to push DEV to Docker Hub"
exit 1
fi
echo ""
echo "🎉 DEV image available on Docker Hub!"
echo "📦 DEV image: sbcrumb/nfoguard:dev"
else
echo "❌ Docker Hub login failed for DEV push"
exit 1
fi
echo ""
echo "🎉 DEV build completed successfully!"
echo ""
echo "📦 Available DEV images:"
echo " Local only: nfoguard:dev"
echo " Local SHA: nfoguard:dev-${{ github.sha }}"
+11 -56
View File
@@ -1,8 +1,8 @@
name: Docker Build & Deploy
name: Local Docker Build (Main)
on:
push:
branches: [ main, dev ]
branches: [ main ]
pull_request:
branches: [ main ]
@@ -15,12 +15,13 @@ jobs:
run: |
echo "Current workspace: $(pwd)"
# Clean workspace first
# Clean workspace and temp directory first
rm -rf * .git* 2>/dev/null || true
rm -rf /tmp/repo 2>/dev/null || true
# Clone the repository since Gitea runner doesn't auto-checkout
echo "Cloning repository..."
git clone http://${{ secrets.username }}:${{ secrets.token }}@192.168.253.221:3000/jskala/NFOguard.git /tmp/repo
git clone --branch main http://${{ secrets.username }}:${{ secrets.token }}@192.168.253.221:3000/jskala/NFOguard.git /tmp/repo
cp -r /tmp/repo/* .
cp -r /tmp/repo/.* . 2>/dev/null || true
rm -rf /tmp/repo
@@ -98,6 +99,8 @@ jobs:
echo "Building with layer caching..."
docker build \
$CACHE_ARGS \
--build-arg GIT_BRANCH=main \
--build-arg BUILD_SOURCE=gitea \
--tag nfoguard:latest \
--tag nfoguard:${{ github.sha }} \
--tag "$CACHE_IMAGE" \
@@ -177,61 +180,13 @@ jobs:
echo "🎉 Local registry step completed!"
- name: Login and Push to Docker Hub
run: |
echo "=== Pushing to Docker Hub ==="
# Use temporary config directory to avoid permission issues
export DOCKER_CONFIG=/tmp/docker-config-hub
mkdir -p $DOCKER_CONFIG
# Login to Docker Hub
if echo "${{ secrets.DOCKER_HUB_TOKEN }}" | docker --config $DOCKER_CONFIG login -u "${{ secrets.DOCKER_HUB_USERNAME }}" --password-stdin; then
echo "✅ Docker Hub login successful"
# Tag images for Docker Hub
DOCKER_HUB_REPO="sbcrumb/nfoguard"
docker tag nfoguard:latest "$DOCKER_HUB_REPO:latest"
docker tag nfoguard:latest "$DOCKER_HUB_REPO:${{ github.sha }}"
docker tag nfoguard:latest "$DOCKER_HUB_REPO:v$(cat VERSION)"
echo "Images tagged for Docker Hub: $DOCKER_HUB_REPO"
# Push to Docker Hub
echo "=== Pushing latest tag to Docker Hub ==="
if timeout 300 docker --config $DOCKER_CONFIG push "$DOCKER_HUB_REPO:latest"; then
echo "✅ Latest tag pushed to Docker Hub"
echo "=== Pushing version tag to Docker Hub ==="
if timeout 180 docker --config $DOCKER_CONFIG push "$DOCKER_HUB_REPO:v$(cat VERSION)"; then
echo "✅ Version tag pushed to Docker Hub"
else
echo "⚠️ Version tag push failed"
fi
echo "=== Pushing SHA tag to Docker Hub ==="
if timeout 180 docker --config $DOCKER_CONFIG push "$DOCKER_HUB_REPO:${{ github.sha }}"; then
echo "✅ SHA tag pushed to Docker Hub"
else
echo "⚠️ SHA tag push failed"
fi
else
echo "❌ Failed to push to Docker Hub"
exit 1
fi
else
echo "❌ Docker Hub login failed"
exit 1
fi
echo ""
echo "🎉 Successfully pushed to both registries!"
echo "🎉 Local build completed successfully!"
echo ""
echo "📦 Available images:"
echo " Local Gitea: 192.168.253.221:3000/jskala/nfoguard:latest"
echo " Docker Hub: sbcrumb/nfoguard:latest"
echo " Local Docker: nfoguard:latest"
echo " Local SHA: nfoguard:${{ github.sha }}"
echo " Local Gitea: 192.168.253.221:3000/jskala/nfoguard:latest"
deploy:
needs: build
+4 -1
View File
@@ -62,11 +62,14 @@ temp/
# Local development files (not for public repo)
*.local
CODE_STRUCTURE.local
RESTART_SUMMARY.md
sync-to-github.sh
# Ignore GitHub workflows when pushing to Gitea
.github/
# Ignore Gitea workflows when pushing to GitHub
.gitea/
# Temporary troubleshooting files - to be removed
Dockerfile.optimized
fix-gitea-packages.md
+13 -2
View File
@@ -1,9 +1,16 @@
FROM python:3.11-slim
#Udates for local builds
# Build argument for git branch and build source
ARG GIT_BRANCH=main
ARG BUILD_SOURCE=
# Set environment variables
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
PYTHONIOENCODING=utf-8
PYTHONIOENCODING=utf-8 \
GIT_BRANCH=${GIT_BRANCH} \
BUILD_SOURCE=${BUILD_SOURCE}
# Install system dependencies including PostgreSQL client libraries
RUN apt-get update && apt-get install -y \
@@ -24,6 +31,10 @@ RUN pip install --no-cache-dir --upgrade pip && \
# Copy application code
COPY . .
# Create git metadata for version detection based on build arg
RUN mkdir -p .git && \
echo "ref: refs/heads/${GIT_BRANCH}" > .git/HEAD
# Copy DLL to a dedicated directory and create entrypoint script
RUN mkdir -p /app/emby-plugin && \
cp /app/Emby-DLL/NFOGuard.Emby.Plugin.dll /app/emby-plugin/ && \
@@ -40,7 +51,7 @@ RUN mkdir -p /app/emby-plugin && \
echo ' echo "No Emby plugins directory found - skipping plugin deployment"' >> /app/deploy-plugin.sh && \
echo ' echo "To enable plugin deployment, bind mount your Emby plugins directory to /emby-plugins"' >> /app/deploy-plugin.sh && \
echo 'fi' >> /app/deploy-plugin.sh && \
echo 'exec python nfoguard.py' >> /app/deploy-plugin.sh && \
echo 'exec python -u nfoguard.py' >> /app/deploy-plugin.sh && \
chmod +x /app/deploy-plugin.sh
# Set ownership
-153
View File
@@ -1,153 +0,0 @@
# NFOGuard Setup Guide
## 🔐 Secure Configuration Setup
NFOGuard now uses a **two-file configuration system** for better security and easier troubleshooting:
- **`.env`** - Main configuration (safe to share for debugging)
- **`.env.secrets`** - Sensitive API keys and passwords (never commit to git)
### Step 1: Copy Configuration Templates
```bash
# Copy main configuration
cp .env.template .env
# Copy secrets configuration
cp .env.secrets.template .env.secrets
```
### Step 2: Configure Main Settings
Edit your `.env` file with your specific paths and preferences:
```bash
# Media paths (adjust to your directory structure)
TV_PATHS=/media/TV/tv,/media/TV/tv6
MOVIE_PATHS=/media/Movies/movies,/media/Movies/movies6
# Database connection details
RADARR_DB_HOST=radarr-postgres
RADARR_DB_PORT=5432
RADARR_DB_NAME=radarr
RADARR_DB_USER=radarr
# Processing preferences
PREFER_RELEASE_DATES_OVER_FILE_DATES=true
ALLOW_FILE_DATE_FALLBACK=false
RELEASE_DATE_PRIORITY=digital,physical,theatrical
# TV webhook processing mode (v0.6.0+)
TV_WEBHOOK_PROCESSING_MODE=targeted
```
### Step 3: Configure Secrets
Edit your `.env.secrets` file with your actual API keys and passwords:
```bash
# Database password
RADARR_DB_PASSWORD=your_actual_radarr_password
# TMDB API key (required for release date detection)
TMDB_API_KEY=your_actual_tmdb_api_key
# Sonarr API key (REQUIRED for v0.6.0+ Enhanced TV NFO Generation)
SONARR_API_KEY=your_actual_sonarr_api_key
# Optional API keys
RADARR_API_KEY=your_radarr_api_key
OMDB_API_KEY=your_omdb_api_key
```
### Step 4: Verify Configuration
Test your setup:
```bash
# Test database connections
curl -X POST "http://localhost:8080/test/bulk-update"
# Test movie scanning
curl -X POST "http://localhost:8080/test/movie-scan"
# Check system health
curl "http://localhost:8080/health"
```
## 🔒 Security Features
### API Key Masking
All API keys and passwords are automatically masked in logs:
```
[2025-09-09T12:34:56] INFO: TMDB API call with key=***masked***
[2025-09-09T12:34:56] INFO: Database connection password=***masked***
```
### Sensitive Data Separation
- **Main `.env`**: Paths, preferences, URLs (safe to share)
- **`.env.secrets`**: API keys, passwords (never commit to version control)
- **Automatic loading**: Both files loaded automatically at startup
### Git Protection
The `.gitignore` file prevents accidental commits:
```
.env
.env.secrets
.env.local
```
## 🛠 Troubleshooting
### "Environment files not loaded" Warning
Install python-dotenv:
```bash
pip install python-dotenv==1.0.0
# or
docker-compose build # rebuilds with updated requirements.txt
```
### Sharing Configuration for Help
You can safely share your `.env` file for debugging since it contains no sensitive data. The `.env.secrets` file should never be shared.
### Migration from Old Setup
If you have an existing `.env` with API keys:
1. Move all `*_API_KEY` and `*_PASSWORD` variables to `.env.secrets`
2. Remove sensitive data from `.env`
3. Restart NFOGuard
## 🎯 Docker Compose Example
```yaml
version: '3.8'
services:
nfoguard:
image: sbcrumb/nfoguard:latest
container_name: nfoguard
ports:
- "8080:8080"
volumes:
- /path/to/your/media:/media:rw
- ./data:/app/data
- ./.env:/app/.env:ro # Main configuration
- ./.env.secrets:/app/.env.secrets:ro # Secrets
environment:
- PORT=8080
depends_on:
- radarr-postgres
```
## 📋 Configuration Reference
### Main Configuration (.env)
- **Paths**: `TV_PATHS`, `MOVIE_PATHS`, `DB_PATH`
- **Processing**: `MOVIE_PRIORITY`, `RELEASE_DATE_PRIORITY`
- **Features**: `MANAGE_NFO`, `FIX_DIR_MTIMES`, `LOCK_METADATA`
- **URLs**: `RADARR_URL`, `SONARR_URL`, `JELLYSEERR_URL`
### Secrets Configuration (.env.secrets)
- **Database**: `RADARR_DB_PASSWORD`
- **APIs**: `TMDB_API_KEY`, `OMDB_API_KEY`, `RADARR_API_KEY`, `SONARR_API_KEY`
- **Optional**: `JELLYSEERR_API_KEY`
This setup makes NFOGuard more secure while keeping configuration manageable for troubleshooting and deployment.
-496
View File
@@ -1,496 +0,0 @@
# NFOGuard Project Summary
## Overview
NFOGuard is a Python-based tool that manages NFO (metadata) files for movies and TV shows in Plex/Emby media servers. It integrates with Radarr and Sonarr to automatically manage metadata, ensuring proper dates and metadata consistency.
## 🎯 CURRENT SESSION ACCOMPLISHMENTS (September 21, 2025)
### ✅ MAJOR ISSUES RESOLVED
1. **Database Save Bug**: Identified and resolved "phantom" database save issue (was logging interpretation error)
2. **Enhanced Movie Detection**: Movies without IMDb IDs in directory names now detected via filenames/NFO content
3. **Enhanced Logging**: Crystal-clear source attribution showing NFOGuard DB vs Radarr vs External APIs
4. **Priority Flow Confirmed**: Perfect implementation of user's requested flow
### ✅ KEY IMPROVEMENTS MADE
**🔍 Enhanced Movie Detection**:
- Added `find_movie_imdb_id()` function with comprehensive detection
- Supports directory names, filenames, and NFO file content parsing
- Movies like "Ick (2024)" now properly processed without directory IMDb tags
**📊 Crystal-Clear Logging System**:
- Added emoji-based logging for easy visual scanning
- Clear source attribution: `nfoguard_db:`, `radarr:db.`, `tmdb:`, etc.
- Priority flow display shows exact decision path
- Final completion logs show chosen date and source
**🔄 Perfect Priority Flow**:
1. NFOGuard Database (cached entries) →
2. Radarr Import History (real import dates) →
3. External APIs (TMDB/OMDb with configurable priority) →
4. Save to NFOGuard Database (cache for future)
**🚀 Manual Scan Commands**:
- Movies only: `curl -X POST "http://nfoguard:8080/manual/scan?scan_type=movies"`
- TV only: `curl -X POST "http://nfoguard:8080/manual/scan?scan_type=tv"`
- Both: `curl -X POST "http://nfoguard:8080/manual/scan?scan_type=both"`
### ✅ CONFIGURATION MAINTAINED
All existing .env settings fully preserved and respected:
- `RELEASE_DATE_PRIORITY=digital,physical,theatrical`
- `MOVIE_PRIORITY=import_then_digital`
- `PREFER_RELEASE_DATES_OVER_FILE_DATES=true`
- `ALLOW_FILE_DATE_FALLBACK=false`
### ✅ DEBUGGING COMPLETED
- **Root Cause**: "Phantom database save" was actually two different movie executions in logs
- **Database Save**: Working correctly in both early-exit and full-processing paths
- **NFO Processing**: Fixed to execute before date validation (preserves existing metadata)
- **File Detection**: Enhanced to handle movies without directory IMDb tags
## Core Architecture
### Main Components
- **nfoguard.py**: Main application file containing the core logic
- **core/nfo_manager.py**: Handles NFO file creation and updates
- **clients/**: External API integrations (Radarr, Sonarr, etc.)
### Key Features
- Creates and updates movie.nfo files with proper metadata
- Manages TV show NFO files (tvshow.nfo, season.nfo, episode NFOs)
- Preserves existing metadata while adding NFOGuard-managed fields
- Supports date management from multiple sources (Radarr, file dates, etc.)
- Webhook support for real-time processing
## Current Issue Analysis
### Problem
When a movie.nfo file already exists (e.g., from Radarr), NFOGuard is **not** processing the file to:
1. Move date fields to the bottom
2. Add NFOGuard comment
3. Add NFOGuard-managed fields (dateadded, lockdata, etc.)
### Root Cause Identified
The issue is in `nfoguard.py:1009-1012`. The processing logic has a premature exit condition:
```python
# Skip processing if no valid date found and file dates disabled
if dateadded is None:
_log("WARNING", f"Skipping movie {movie_path.name} - no valid date source available")
self.db.upsert_movie_dates(imdb_id, released, None, source, True)
return # <-- This exits BEFORE NFO processing
```
**The NFO processing code (lines 1015-1018) never executes when dateadded is None.**
### Analysis of NFO Processing Logic
The `create_movie_nfo` function in `core/nfo_manager.py:52-147` is actually well-designed:
- Lines 60-78: Properly loads and preserves existing NFO content
- Lines 69-78: Removes NFOGuard-managed fields to re-add them at the bottom
- Lines 96-123: Adds all NFOGuard fields at the end
- Lines 124-141: Adds NFOGuard comment and proper formatting
**The NFO manager code is correct - it's just not being called due to the early exit.**
## Files That Need Updates
1. `nfoguard.py` - Fix the early exit logic around line 1009-1012
## Status
- ✅ NFO Processing Issue: Fixed in nfoguard.py:1008-1025
- 🔍 **NEW ISSUE**: Database not persisting dateadded values on manual scans
## Fix Summary
### Movie NFO Processing (✅ Fixed)
**Problem**: NFO processing was skipped entirely when `dateadded` was None due to early return.
**Solution**: Moved NFO processing (`create_movie_nfo`) to execute **before** the `dateadded` check, ensuring that:
1. Existing NFO files are always processed and standardized
2. NFOGuard comment is added
3. Date fields are moved to bottom
4. lockdata is added if configured
5. Only file mtime updates are skipped when no valid date is found
### TV Episode NFO Processing (✅ Enhanced)
**Enhancement**: Added smart episode NFO migration for long-named files.
**New Functionality**:
1. **Detection**: `find_existing_episode_nfo()` scans for non-standard NFO files that match season/episode
2. **Migration**: Extracts metadata from long-named files (e.g., `Episode.Name.Here.S01E05.nfo`)
3. **Standardization**: Creates standard `S01E05.nfo` with preserved metadata + NFOGuard enhancements
4. **Cleanup**: Automatically deletes old long-named NFO files after successful migration
5. **Preservation**: All existing metadata is preserved, dates moved to bottom, NFOGuard fields added
## 🔍 DATABASE SAVE BUG RESOLVED! ✅
### Problem Analysis COMPLETED
**Root Cause Identified**: The "phantom database save bug" was actually a **logging interpretation error**.
### What Actually Happened
The debug logs showing these two lines:
```
DEBUG: Movie processing reached post-mtime section: Dear Santa (2024) [imdb-tt2396431]
INFO: Completed processing movie: Dear Santa (2024) [imdb-tt2396431] (source: tmdb:digital)
```
**Cannot be from the same function execution!** Here's why:
### The Real Execution Flow
**Line 1014-1018**: Early exit when `dateadded is None`:
```python
if dateadded is None:
_log("WARNING", f"Movie {movie_path.name} - no valid date source available, but NFO was still processed")
self.db.upsert_movie_dates(imdb_id, released, None, source, True)
return # <-- EXITS HERE, never reaches line 1029!
```
**Line 1029**: Debug log that only executes when `dateadded` has a valid value:
```python
_log("DEBUG", f"Movie processing reached post-mtime section: {movie_path.name}")
```
**Line 1045**: Completion log that only executes after successful database save:
```python
_log("INFO", f"Completed processing movie: {movie_path.name} (source: {source})")
```
### Key Insight
**The logs are from TWO different executions:**
1. **First run**: `dateadded = None` → early exit at line 1018 → database save executes BUT for NULL value
2. **Second run**: `dateadded` has value → reaches line 1029 → full processing → line 1045
### Database Save Status
**Database saves ARE working correctly!**
- Early exit path (line 1018): Saves NULL dateadded when no valid date found
- Full processing path (lines 1038-1043): Saves valid dateadded when available
- Both paths call `upsert_movie_dates()` appropriately
### Investigation Status
**Enhanced Movie Detection**: Working (Ick 2024 now processed)
**NFO Processing**: Working (files get updated)
**File Timestamp Updates**: Working (mtimes get set)
**Database Persistence**: **WORKING CORRECTLY** - saves happen in both execution paths
## 📊 ENHANCED LOGGING SYSTEM (September 21, 2025) ✅
### New Crystal-Clear Source Attribution
**Problem**: Logs didn't clearly show whether dates came from NFOGuard DB vs Radarr vs external APIs.
**Solution**: Added comprehensive logging with emojis and clear source identification.
### New Logging Format
**Priority Flow Display**:
```
🔄 PRIORITY FLOW: NFOGuard DB → Radarr import → External APIs (digital,physical,theatrical) → Save to NFOGuard DB
```
**NFOGuard Database Lookups**:
```
🔍 NFOGuard DB lookup for tt1234567: FOUND - dateadded=2025-06-15T11:10:31-04:00, source=radarr:db.history.import
✅ MANUAL SCAN: Using existing NFOGuard database entry: 2025-06-15T11:10:31-04:00 (original source: radarr:db.history.import)
```
**External Source Queries**:
```
🔍 RADARR DATABASE: Checking import history for movie ID 123 (tt1234567)...
📦 RADARR DATABASE: Found import date=2025-07-20T14:30:00Z, source=radarr:db.history.import
🔍 EXTERNAL APIs: Trying digital release date fallback...
🌐 EXTERNAL APIs: Found digital release date=2025-06-15T00:00:00+00:00, source=tmdb:digital
```
**Final Decision Tracking**:
```
✅ FINAL CHOICE: Using Radarr import date 2025-07-20T14:30:00-04:00 from radarr:db.history.import
🎬 COMPLETED: Movie Name (2024) | Final date: 2025-07-20T14:30:00-04:00 | Final source: radarr:db.history.import
```
### Source Attribution System
**NFOGuard Database**: `nfoguard_db:radarr:db.history.import` (shows original source)
**Radarr Database**: `radarr:db.history.import` (fresh query)
**External APIs**: `tmdb:digital`, `omdb:dvd`, etc.
### Perfect Priority Flow Confirmed
1.**NFOGuard DB First**: Check cached entries, use if available
2.**Radarr Import History**: Query for real import dates if not cached
3.**External APIs**: TMDB/OMDb fallback with configurable priority (`RELEASE_DATE_PRIORITY`)
4.**Save to NFOGuard DB**: Cache result for future lookups
**This ensures we don't repeatedly query Radarr/external APIs for the same movies!**
## 🚀 MANUAL SCAN API COMMANDS
### Available Scan Types
**Movies Only:**
```bash
curl -X POST "http://nfoguard:8080/manual/scan?scan_type=movies"
```
**TV Shows Only:**
```bash
curl -X POST "http://nfoguard:8080/manual/scan?scan_type=tv"
```
**Both Movies AND TV Shows (Default):**
```bash
curl -X POST "http://nfoguard:8080/manual/scan?scan_type=both"
# OR simply:
curl -X POST "http://nfoguard:8080/manual/scan"
```
**Specific Path Scan:**
```bash
curl -X POST "http://nfoguard:8080/manual/scan?path=/media/Movies/Movie.Name.2024&scan_type=movies"
```
### What Each Scan Does
- **Movies**: Processes all movie directories in `MOVIE_PATHS`, follows NFOGuard priority flow
- **TV**: Processes all TV series in `TV_PATHS`, updates episode NFO files with proper dates
- **Both**: Processes both movies and TV shows in sequence (recommended for full refresh)
All scans run in background and return immediately with status confirmation.
### Movie IMDb ID Detection (✅ Enhanced)
**Enhancement**: Added comprehensive IMDb ID detection for movies without directory tags.
**New Detection Methods**:
1. **Directory Name**: `[imdb-tt123456]` format (existing)
2. **Filename Patterns**: IMDb ID in any video file name `Movie.Name.2024.[tt123456].mkv`
3. **NFO File Content**: `<uniqueid type="imdb">tt123456</uniqueid>` (NEW)
4. **Legacy NFO Tags**: `<imdbid>` and `<imdb>` tags (NEW)
**Implementation**:
- Added `find_movie_imdb_id()` function that tries all methods in priority order
- Extended `parse_imdb_from_nfo()` to handle XML parsing
- Updated movie processing to use comprehensive detection
**Example**: Ick (2024) directory without IMDb in folder name will now be detected via NFO file content.
# NFOGuard Development Summary
## Current Status: v1.6.6 - Docker Plugin Deployment! 🐳
### Latest Updates (v1.6.6) - September 19, 2025
- **🐳 DOCKER PLUGIN DEPLOYMENT**: Added automatic Emby plugin deployment via Docker
- **📦 DLL PACKAGING**: NFOGuard.Emby.Plugin.dll now packaged with Docker image
- **🔗 BIND MOUNT SUPPORT**: Primary method using bind mount to Emby plugins directory
- **⚙️ ENVIRONMENT FALLBACK**: Secondary method using EMBY_PLUGINS_PATH environment variable
- **🚀 AUTO-UPDATE**: Plugin automatically updates when new Docker image is deployed
- **📋 CONFIGURATION**: Added .env.example with plugin deployment settings
- **🔄 STARTUP SCRIPT**: Container automatically deploys plugin on startup
- **🎯 WORKFLOW INTEGRATION**: Compatible with Gitea → DockerHub deployment pipeline
### Docker Plugin Deployment Flow
**Bind Mount Method (Recommended):**
1. Set `EMBY_PLUGINS_PATH=/path/to/emby/plugins` in .env
2. Docker bind mounts host directory to `/emby-plugins` in container
3. On startup, NFOGuard copies DLL to mounted directory
4. Plugin updates automatically with each new image deployment
**Benefits:**
- Works with separate Emby/NFOGuard containers
- Compatible with native Emby installations
- Secure - only exposes plugins directory
- Automatic updates via CI/CD pipeline
## Previous Status: v1.6.0 - NFO-Based Identification! 📄
### Latest Updates (v1.6.0) - September 16, 2025
- **📄 NFO IDENTIFICATION**: Added support for identifying movies/TV shows from NFO files
- **🔄 DUAL METHOD**: Directory name IMDb IDs (primary) + NFO file IMDb IDs (fallback)
- **📋 NFO FORMATS**: Supports `<uniqueid type="imdb">`, `<imdbid>`, and `<imdb>` XML tags
- **🎬 MOVIE SUPPORT**: Reads movie.nfo files for IMDb ID extraction
- **📺 TV SUPPORT**: Reads tvshow.nfo files for series identification
- **✅ SMART FALLBACK**: Only checks NFO files when directory name lacks IMDb ID
- **📝 DOCUMENTATION**: Updated README with NFO identification examples and formats
- **🔄 NFO RENAMING**: Added smart episode NFO file renaming to standardized `S##E##.nfo` format
- **🎯 SONARR INTEGRATION**: Detects existing NFO files created by Sonarr/other tools, extracts metadata, and renames to standard format
- **📁 METADATA PRESERVATION**: Preserves all existing metadata when renaming non-standard NFO files
- **🧹 CLEANUP**: Removes old non-standard NFO files after successful rename to prevent duplicates
### Previous Updates (v1.5.5) - September 15, 2025
- **🔧 DATABASE BUG FIX**: Fixed `upsert_movie_dates` to be a proper upsert operation
- **💾 DATA INTEGRITY**: Manual scans now properly save `dateadded` to database
- **🎯 ROOT CAUSE**: Fixed UPDATE-only operation that was leaving `dateadded` fields NULL
- **✅ WEBHOOK FIX**: Webhooks now find existing database entries correctly
- **📺 TV SHOWS**: NOT affected - already using proper `INSERT OR REPLACE` operations
### Previous Updates (v1.5.4) - September 15, 2025
- **🎯 WEBHOOK DATE FIX**: Fixed webhook processing to use proper date decision logic
- **🔍 SMART FALLBACK**: Webhooks now check database → Radarr import dates → release dates → timestamp
- **❌ ISSUE RESOLVED**: Upgrade webhooks no longer incorrectly use current timestamp when database exists
- **✅ PROPER FLOW**: Webhook mode now uses same date logic as manual scans for missing entries
### Previous Updates (v1.5.3) - September 15, 2025
- **🔧 RADARR RENAME FIX**: Added missing event type filtering to Radarr webhooks
- **✅ CONSISTENCY**: Radarr now matches Sonarr's event handling (Download, Upgrade, Rename)
- **🎯 FIXED ISSUE**: Radarr rename events now properly trigger NFO updates with correct dates
- **🔄 WORKFLOW**: Both Sonarr and Radarr now follow identical webhook processing flows
### Previous Updates (v1.5.2)
- **🔇 TVDB WARNINGS**: Added SUPPRESS_TVDB_WARNINGS configuration option
- **✅ CLEAN LOGS**: Can now suppress non-critical TVDB API failure warnings
- **🧹 PRODUCTION**: Cleaner logs for production monitoring
- **🎯 FUNCTIONAL**: System works perfectly without TVDB (uses IMDb/Sonarr data)
### TVDB Warning Suppression
**Added new environment variable:**
```bash
# Suppress TVDB API warnings (true/false) - TVDB failures are common and non-critical
SUPPRESS_TVDB_WARNINGS=true
```
### Why Suppress TVDB Warnings?
- **❌ Common Failures**: Many series don't exist in TVDB database
- **✅ Non-Critical**: NFOGuard works perfectly without TVDB data
- **📊 Primary Data**: Uses IMDb IDs and Sonarr metadata (more reliable)
- **🧹 Log Noise**: Reduces repetitive warning messages
### Before vs After
**Before (noisy):**
```
INFO: All episodes found in cache
WARNING: GET https://api4.thetvdb.com/v4/search/remoteid?remoteId=tt0804484&type=series failed: HTTP Error 400: Bad Request
INFO: Completed processing TV series: Foundation (2021) [imdb-tt0804484]
WARNING: GET https://api4.thetvdb.com/v4/search/remoteid?remoteId=tt9737326&type=series failed: HTTP Error 400: Bad Request
```
**After (clean):**
```
INFO: All episodes found in cache
INFO: Completed processing TV series: Foundation (2021) [imdb-tt0804484]
INFO: Completed processing TV series: Invasion (2021) [imdb-tt9737326]
```
### Your Configuration
```bash
DEBUG=false # Clean production logging
PATH_DEBUG=false # No path mapping debug
SUPPRESS_TVDB_WARNINGS=true # Hide non-critical TVDB failures
```
### System Status
-**Processing**: All TV series processing successfully
-**Episodes**: All episodes found and processed
-**NFO Files**: Being created/updated with proper metadata
-**Functionality**: TVDB failures don't affect operation
## 🔇 NFOGuard v1.5.2 - Ultra-clean production logs! 🔇
### Copy SUPPRESS_TVDB_WARNINGS=true to your server .env file and restart!
---
## 🔧 FIXED: Database Upsert Bug (September 15, 2025) - **CRITICAL**
### Issue Discovery
**The Real Root Cause**: Manual scans were NOT actually saving `dateadded` to database due to faulty upsert operation!
### Problem Details
**Database Method Bug** in `core/database.py:173-182`:
- `upsert_movie_dates()` was doing UPDATE-only, not proper upsert
- If movie row didn't exist properly, UPDATE would affect 0 rows
- `dateadded` field remained NULL even after manual scan
- Webhooks found database entries but with NULL `dateadded` → fell back to current timestamp
### 🔧 **Fix Applied**
- **Location**: `core/database.py:173-186`
- **Changed**: UPDATE-only → proper `INSERT OR REPLACE` upsert
- **Result**: Manual scans now properly save `dateadded` to database
- **Webhook Impact**: Now finds existing dates correctly, no more false timestamps
### Before vs After Database Operations
**Before (BROKEN):**
```sql
UPDATE movies SET dateadded = '2025-06-15...' WHERE imdb_id = 'tt123';
-- If row doesn't exist properly: 0 rows affected, dateadded stays NULL
```
**After (FIXED):**
```sql
INSERT OR REPLACE INTO movies (...) VALUES (...);
-- Always works: Creates row OR replaces with all data including dateadded
```
---
## 🎯 FIXED: Webhook Date Logic Issue (September 15, 2025)
### Issue Discovery
Upgrade webhooks were incorrectly using current timestamp (`webhook:first_seen`) instead of checking Radarr for proper import dates when no database entry existed.
**Example Problem:**
- Database populated via manual scan: `2025-06-15T11:10:31-04:00` (radarr:db.history.import)
- Upgrade webhook comes in: `2025-09-15T12:04:23-04:00` (webhook:first_seen) ❌ **WRONG!**
### Root Cause
Webhook mode was bypassing the full date decision logic (`_decide_movie_dates`) and jumping straight to current timestamp as fallback.
### ✅ **Fix Applied**
- **Location**: `nfoguard.py:975-990`
- **Logic**: Webhook mode now uses same date priority as manual scans:
1. **Database first** (if exists)
2. **Full date logic** (Radarr import → release dates → fallbacks)
3. **Current timestamp** (only as absolute last resort)
### Result
Webhook upgrades now properly find and use existing Radarr import dates instead of creating false "first seen" timestamps.
---
## 🔧 FIXED: Radarr Rename Event Issue (September 15, 2025)
### Issue Resolution
**COMPLETED**: Fixed missing Radarr rename event handling by adding proper event type filtering.
### ✅ **Both Sonarr & Radarr Webhook Handling** - **NOW WORKING CORRECTLY**
- **Sonarr Location**: `nfoguard.py:1429`
- **Radarr Location**: `nfoguard.py:1479` **(FIXED)**
- **Event Filtering**: `if event_type not in ["Download", "Upgrade", "Rename"]:`
- **✅ STATUS**: **Rename events ARE processed for both systems**
- **✅ BEHAVIOR**: Both systems now update NFO files with proper date metadata on rename
### What Was Fixed
1. **Added Event Type Filtering**: Radarr webhooks now explicitly filter for supported events
2. **Consistent Processing**: Both Sonarr and Radarr now follow identical webhook workflows
3. **Proper Rename Handling**: Rename events trigger full NFO processing pipeline
---
## 📋 NFOGuard Webhook Workflow Documentation
### 🎬 **Radarr Webhook Workflow**
1. **Import Event**:
- Check database - if not seen before, current timestamp = truth of first import
- Update movie.nfo with proper date metadata
2. **Rename Event**:
- Check database - if we have the date, use existing date
- Update movie.nfo file as always (preserving original import date)
3. **Rename Event (No Database Entry)**:
- Check Radarr database for earliest import date
- If found, use earliest import date → update database + movie.nfo
- If no import date found, use digital/theatrical release date (fallback logic)
### 📺 **Sonarr Webhook Workflow**
1. **Import Event**:
- Check database - if not seen before, current timestamp = truth of first import
- Update episode.nfo with proper date metadata
2. **Rename Event**:
- Check database - if we have the date, use existing date
- Update episode.nfo file as always (preserving original import date)
3. **Rename Event (No Database Entry)**:
- Check Sonarr API for earliest import date
- If found, use earliest import date → update database + episode.nfo
- If no import date found, use air date (fallback logic)
### 🔄 **Key Processing Logic**
- **Database First**: Always check NFOGuard database for existing dates
- **Import Truth**: Webhook import events establish "source of truth" timestamps
- **Date Preservation**: Rename events preserve original import dates, never overwrite
- **Smart Fallbacks**: API queries → Release dates → Air dates → File dates (configurable)
- **Batch Processing**: All webhooks go through batching system with configurable delays
+1 -1
View File
@@ -1 +1 @@
1.6.8
1.6.7
+27 -4
View File
@@ -440,17 +440,40 @@ class NFOManager:
old_nfo_to_delete = existing_long_nfo
print(f"📦 Migrating episode NFO: {existing_long_nfo.name} -> {episode_filename}")
# Remove existing NFOGuard-managed elements to avoid duplicates
# These will be re-added at the bottom
# Show what content fields are being preserved
content_fields = ["title", "plot", "runtime", "premiered"]
preserved_content = []
for field in content_fields:
elem = episode.find(field)
if elem is not None and elem.text:
preserved_content.append(field)
if preserved_content:
print(f" 📄 Preserving content: {', '.join(preserved_content)}")
# Preserve existing content fields and store NFOGuard-managed fields for re-adding
preserved_values = {}
# Extract and preserve NFOGuard-managed fields (we'll re-add these at the bottom)
nfoguard_fields = ["aired", "dateadded", "lockdata", "season", "episode"]
for tag in nfoguard_fields:
existing = episode.find(tag)
if existing is not None:
# Store the aired value before removing
# Store the value before removing
if tag == "aired" and not aired:
aired = existing.text # Preserve existing aired date
preserved_values[tag] = existing.text
episode.remove(existing)
# Important: DO NOT remove content fields like title, plot, runtime, premiered, etc.
# These should be preserved from the long-named NFO files
# Debug: Show what fields are preserved after removing NFOGuard fields
preserved_fields = [elem.tag for elem in episode]
if preserved_fields:
print(f" 🔍 Content preserved after cleanup: {', '.join(preserved_fields)}")
else:
print(f" ⚠️ No content fields found after cleanup!")
except (ET.ParseError, ValueError) as e:
print(f"⚠️ Corrupted episode NFO detected: {nfo_path} - {str(e)[:100]}...")
print(f" Creating new clean episode NFO file to replace corrupted one")
@@ -459,7 +482,7 @@ class NFOManager:
# Create new NFO structure
episode = ET.Element("episodedetails")
# Enhanced metadata should be preserved - only add if not already present
# Add enhanced metadata only if not already present (preserve existing from long-named NFO)
if enhanced_metadata:
if enhanced_metadata.get("title") and not episode.find("title"):
title_elem = ET.SubElement(episode, "title")
+29 -6
View File
@@ -19,6 +19,8 @@ from typing import Optional, Dict, Any, List, Set, Tuple
import threading
from concurrent.futures import ThreadPoolExecutor
import uvicorn
import signal
import sys
# Import our new modular components
from core.database import NFOGuardDatabase
@@ -1395,6 +1397,12 @@ except Exception:
# If git detection fails, that's fine - use base version
pass
# Check for build source (only add -gitea for local Gitea builds)
build_source = os.environ.get("BUILD_SOURCE", "")
if build_source == "gitea":
if "gitea" not in version: # Don't double-add gitea suffix
version = f"{version}-gitea"
app = FastAPI(
title="NFOGuard",
description="Webhook server for preserving media import dates",
@@ -2201,7 +2209,16 @@ async def debug_tmdb_lookup(imdb_id: str):
# Main
# ---------------------------
def signal_handler(signum, frame):
"""Handle shutdown signals gracefully"""
_log("INFO", f"Received signal {signum}, shutting down gracefully...")
sys.exit(0)
if __name__ == "__main__":
# Register signal handlers for graceful shutdown
signal.signal(signal.SIGTERM, signal_handler)
signal.signal(signal.SIGINT, signal_handler)
_log("INFO", "Starting NFOGuard")
_log("INFO", f"Version: {version}")
_log("INFO", f"TV paths: {[str(p) for p in config.tv_paths]}")
@@ -2210,9 +2227,15 @@ if __name__ == "__main__":
_log("INFO", f"Config: manage_nfo={config.manage_nfo}, fix_mtimes={config.fix_dir_mtimes}")
_log("INFO", f"Movie priority: {config.movie_priority}")
uvicorn.run(
app,
host="0.0.0.0",
port=int(os.environ.get("PORT", "8080")),
reload=False
)
try:
uvicorn.run(
app,
host="0.0.0.0",
port=int(os.environ.get("PORT", "8080")),
reload=False
)
except KeyboardInterrupt:
_log("INFO", "NFOGuard stopped by user")
except Exception as e:
_log("ERROR", f"NFOGuard crashed: {e}")
sys.exit(1)