fix: update tv .nfo handling
updated TV .nfo handling
This commit is contained in:
@@ -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
@@ -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
@@ -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
|
# Container paths (what NFOGuard sees inside container)
|
||||||
# Adjust these to match YOUR directory structure
|
|
||||||
TV_PATHS=/media/TV/tv,/media/TV/tv6
|
|
||||||
MOVIE_PATHS=/media/Movies/movies,/media/Movies/movies6
|
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
|
# DATABASE CONFIGURATION
|
||||||
@@ -15,127 +26,99 @@ MOVIE_PATHS=/media/Movies/movies,/media/Movies/movies6
|
|||||||
# NFOGuard SQLite database location
|
# NFOGuard SQLite database location
|
||||||
DB_PATH=/app/data/media_dates.db
|
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
|
# Direct database access for better performance
|
||||||
# NOTE: Database password should be set in .env.secrets file
|
RADARR_DB_TYPE=postgresql
|
||||||
RADARR_DB_HOST=radarr-postgres
|
RADARR_DB_HOST=192.168.255.50
|
||||||
RADARR_DB_PORT=5432
|
RADARR_DB_PORT=5432
|
||||||
RADARR_DB_NAME=radarr
|
RADARR_DB_NAME=radarr-main
|
||||||
RADARR_DB_USER=radarr
|
RADARR_DB_USER=postgres
|
||||||
|
|
||||||
# ===========================================
|
# ===========================================
|
||||||
# RADARR API (OPTIONAL - for fallback)
|
# API CONNECTIONS (OPTIONAL)
|
||||||
# ===========================================
|
# ===========================================
|
||||||
# Only needed if you want API fallback (not recommended for performance)
|
# API keys are stored in .env.secrets for security
|
||||||
# NOTE: API key should be set in .env.secrets file
|
|
||||||
RADARR_URL=http://radarr:7878
|
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
|
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
|
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
|
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
|
FIX_DIR_MTIMES=true
|
||||||
|
|
||||||
# Whether to add <lockdata> tags to prevent metadata drift
|
# Add lockdata tags to prevent metadata overwrites
|
||||||
LOCK_METADATA=true
|
LOCK_METADATA=true
|
||||||
|
|
||||||
# Brand name to add to NFO files
|
# Brand name in NFO comments
|
||||||
MANAGER_BRAND=NFOGuard
|
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
|
BATCH_DELAY=5.0
|
||||||
|
|
||||||
# Maximum concurrent series processing
|
# Maximum concurrent series processing
|
||||||
MAX_CONCURRENT_SERIES=3
|
MAX_CONCURRENT_SERIES=3
|
||||||
|
|
||||||
# Movie date strategy: import_then_digital or digital_then_import
|
# API timeout in seconds
|
||||||
MOVIE_PRIORITY=import_then_digital
|
TIMEOUT_SECONDS=45
|
||||||
|
|
||||||
# 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
|
|
||||||
|
|
||||||
# ===========================================
|
# ===========================================
|
||||||
# TV SERIES PROCESSING
|
# DEBUGGING
|
||||||
# ===========================================
|
# ===========================================
|
||||||
# Season directory naming format (supports {season} placeholder)
|
# Enable verbose logging (true/false)
|
||||||
# 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
|
|
||||||
DEBUG=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
|
PORT=8080
|
||||||
+12
-42
@@ -1,4 +1,4 @@
|
|||||||
name: Docker Build & Deploy (DEV)
|
name: Local Docker Build (Dev)
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
@@ -15,12 +15,13 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
echo "Current workspace: $(pwd)"
|
echo "Current workspace: $(pwd)"
|
||||||
|
|
||||||
# Clean workspace first
|
# Clean workspace and temp directory first
|
||||||
rm -rf * .git* 2>/dev/null || true
|
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
|
# Clone the repository since Gitea runner doesn't auto-checkout
|
||||||
echo "Cloning repository..."
|
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/* .
|
||||||
cp -r /tmp/repo/.* . 2>/dev/null || true
|
cp -r /tmp/repo/.* . 2>/dev/null || true
|
||||||
rm -rf /tmp/repo
|
rm -rf /tmp/repo
|
||||||
@@ -95,6 +96,8 @@ jobs:
|
|||||||
echo "Building DEV image with layer caching..."
|
echo "Building DEV image with layer caching..."
|
||||||
docker build \
|
docker build \
|
||||||
$CACHE_ARGS \
|
$CACHE_ARGS \
|
||||||
|
--build-arg GIT_BRANCH=dev \
|
||||||
|
--build-arg BUILD_SOURCE=gitea \
|
||||||
--tag nfoguard:dev \
|
--tag nfoguard:dev \
|
||||||
--tag nfoguard:dev-${{ github.sha }} \
|
--tag nfoguard:dev-${{ github.sha }} \
|
||||||
--tag "$CACHE_IMAGE" \
|
--tag "$CACHE_IMAGE" \
|
||||||
@@ -135,42 +138,9 @@ jobs:
|
|||||||
echo "❌ DEV local registry login failed - continuing to Docker Hub"
|
echo "❌ DEV local registry login failed - continuing to Docker Hub"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Push DEV to Docker Hub
|
echo ""
|
||||||
if: github.ref == 'refs/heads/dev'
|
echo "🎉 DEV build completed successfully!"
|
||||||
run: |
|
echo ""
|
||||||
echo "=== Pushing DEV image to Docker Hub ==="
|
echo "📦 Available DEV images:"
|
||||||
|
echo " Local only: nfoguard:dev"
|
||||||
# Use temporary config directory to avoid permission issues
|
echo " Local SHA: nfoguard:dev-${{ github.sha }}"
|
||||||
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
|
|
||||||
+11
-56
@@ -1,8 +1,8 @@
|
|||||||
name: Docker Build & Deploy
|
name: Local Docker Build (Main)
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ main, dev ]
|
branches: [ main ]
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [ main ]
|
branches: [ main ]
|
||||||
|
|
||||||
@@ -15,12 +15,13 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
echo "Current workspace: $(pwd)"
|
echo "Current workspace: $(pwd)"
|
||||||
|
|
||||||
# Clean workspace first
|
# Clean workspace and temp directory first
|
||||||
rm -rf * .git* 2>/dev/null || true
|
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
|
# Clone the repository since Gitea runner doesn't auto-checkout
|
||||||
echo "Cloning repository..."
|
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/* .
|
||||||
cp -r /tmp/repo/.* . 2>/dev/null || true
|
cp -r /tmp/repo/.* . 2>/dev/null || true
|
||||||
rm -rf /tmp/repo
|
rm -rf /tmp/repo
|
||||||
@@ -98,6 +99,8 @@ jobs:
|
|||||||
echo "Building with layer caching..."
|
echo "Building with layer caching..."
|
||||||
docker build \
|
docker build \
|
||||||
$CACHE_ARGS \
|
$CACHE_ARGS \
|
||||||
|
--build-arg GIT_BRANCH=main \
|
||||||
|
--build-arg BUILD_SOURCE=gitea \
|
||||||
--tag nfoguard:latest \
|
--tag nfoguard:latest \
|
||||||
--tag nfoguard:${{ github.sha }} \
|
--tag nfoguard:${{ github.sha }} \
|
||||||
--tag "$CACHE_IMAGE" \
|
--tag "$CACHE_IMAGE" \
|
||||||
@@ -177,61 +180,13 @@ jobs:
|
|||||||
|
|
||||||
echo "🎉 Local registry step completed!"
|
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 ""
|
||||||
echo "🎉 Successfully pushed to both registries!"
|
echo "🎉 Local build completed successfully!"
|
||||||
echo ""
|
echo ""
|
||||||
echo "📦 Available images:"
|
echo "📦 Available images:"
|
||||||
echo " Local Gitea: 192.168.253.221:3000/jskala/nfoguard:latest"
|
echo " Local Docker: nfoguard:latest"
|
||||||
echo " Docker Hub: sbcrumb/nfoguard:latest"
|
echo " Local SHA: nfoguard:${{ github.sha }}"
|
||||||
|
echo " Local Gitea: 192.168.253.221:3000/jskala/nfoguard:latest"
|
||||||
|
|
||||||
deploy:
|
deploy:
|
||||||
needs: build
|
needs: build
|
||||||
|
|||||||
+4
-1
@@ -62,11 +62,14 @@ temp/
|
|||||||
# Local development files (not for public repo)
|
# Local development files (not for public repo)
|
||||||
*.local
|
*.local
|
||||||
CODE_STRUCTURE.local
|
CODE_STRUCTURE.local
|
||||||
RESTART_SUMMARY.md
|
sync-to-github.sh
|
||||||
|
|
||||||
# Ignore GitHub workflows when pushing to Gitea
|
# Ignore GitHub workflows when pushing to Gitea
|
||||||
.github/
|
.github/
|
||||||
|
|
||||||
|
# Ignore Gitea workflows when pushing to GitHub
|
||||||
|
.gitea/
|
||||||
|
|
||||||
# Temporary troubleshooting files - to be removed
|
# Temporary troubleshooting files - to be removed
|
||||||
Dockerfile.optimized
|
Dockerfile.optimized
|
||||||
fix-gitea-packages.md
|
fix-gitea-packages.md
|
||||||
|
|||||||
+13
-2
@@ -1,9 +1,16 @@
|
|||||||
FROM python:3.11-slim
|
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
|
# Set environment variables
|
||||||
ENV PYTHONDONTWRITEBYTECODE=1 \
|
ENV PYTHONDONTWRITEBYTECODE=1 \
|
||||||
PYTHONUNBUFFERED=1 \
|
PYTHONUNBUFFERED=1 \
|
||||||
PYTHONIOENCODING=utf-8
|
PYTHONIOENCODING=utf-8 \
|
||||||
|
GIT_BRANCH=${GIT_BRANCH} \
|
||||||
|
BUILD_SOURCE=${BUILD_SOURCE}
|
||||||
|
|
||||||
# Install system dependencies including PostgreSQL client libraries
|
# Install system dependencies including PostgreSQL client libraries
|
||||||
RUN apt-get update && apt-get install -y \
|
RUN apt-get update && apt-get install -y \
|
||||||
@@ -24,6 +31,10 @@ RUN pip install --no-cache-dir --upgrade pip && \
|
|||||||
# Copy application code
|
# Copy application code
|
||||||
COPY . .
|
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
|
# Copy DLL to a dedicated directory and create entrypoint script
|
||||||
RUN mkdir -p /app/emby-plugin && \
|
RUN mkdir -p /app/emby-plugin && \
|
||||||
cp /app/Emby-DLL/NFOGuard.Emby.Plugin.dll /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 "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 ' 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 '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
|
chmod +x /app/deploy-plugin.sh
|
||||||
|
|
||||||
# Set ownership
|
# Set ownership
|
||||||
|
|||||||
@@ -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
@@ -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
|
|
||||||
+27
-4
@@ -439,17 +439,40 @@ class NFOManager:
|
|||||||
if existing_long_nfo and source_nfo_path == existing_long_nfo:
|
if existing_long_nfo and source_nfo_path == existing_long_nfo:
|
||||||
old_nfo_to_delete = existing_long_nfo
|
old_nfo_to_delete = existing_long_nfo
|
||||||
print(f"📦 Migrating episode NFO: {existing_long_nfo.name} -> {episode_filename}")
|
print(f"📦 Migrating episode NFO: {existing_long_nfo.name} -> {episode_filename}")
|
||||||
|
|
||||||
|
# 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)}")
|
||||||
|
|
||||||
# Remove existing NFOGuard-managed elements to avoid duplicates
|
# Preserve existing content fields and store NFOGuard-managed fields for re-adding
|
||||||
# These will be re-added at the bottom
|
preserved_values = {}
|
||||||
|
|
||||||
|
# Extract and preserve NFOGuard-managed fields (we'll re-add these at the bottom)
|
||||||
nfoguard_fields = ["aired", "dateadded", "lockdata", "season", "episode"]
|
nfoguard_fields = ["aired", "dateadded", "lockdata", "season", "episode"]
|
||||||
for tag in nfoguard_fields:
|
for tag in nfoguard_fields:
|
||||||
existing = episode.find(tag)
|
existing = episode.find(tag)
|
||||||
if existing is not None:
|
if existing is not None:
|
||||||
# Store the aired value before removing
|
# Store the value before removing
|
||||||
if tag == "aired" and not aired:
|
if tag == "aired" and not aired:
|
||||||
aired = existing.text # Preserve existing aired date
|
aired = existing.text # Preserve existing aired date
|
||||||
|
preserved_values[tag] = existing.text
|
||||||
episode.remove(existing)
|
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:
|
except (ET.ParseError, ValueError) as e:
|
||||||
print(f"⚠️ Corrupted episode NFO detected: {nfo_path} - {str(e)[:100]}...")
|
print(f"⚠️ Corrupted episode NFO detected: {nfo_path} - {str(e)[:100]}...")
|
||||||
@@ -459,7 +482,7 @@ class NFOManager:
|
|||||||
# Create new NFO structure
|
# Create new NFO structure
|
||||||
episode = ET.Element("episodedetails")
|
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:
|
||||||
if enhanced_metadata.get("title") and not episode.find("title"):
|
if enhanced_metadata.get("title") and not episode.find("title"):
|
||||||
title_elem = ET.SubElement(episode, "title")
|
title_elem = ET.SubElement(episode, "title")
|
||||||
|
|||||||
+29
-6
@@ -19,6 +19,8 @@ from typing import Optional, Dict, Any, List, Set, Tuple
|
|||||||
import threading
|
import threading
|
||||||
from concurrent.futures import ThreadPoolExecutor
|
from concurrent.futures import ThreadPoolExecutor
|
||||||
import uvicorn
|
import uvicorn
|
||||||
|
import signal
|
||||||
|
import sys
|
||||||
|
|
||||||
# Import our new modular components
|
# Import our new modular components
|
||||||
from core.database import NFOGuardDatabase
|
from core.database import NFOGuardDatabase
|
||||||
@@ -1395,6 +1397,12 @@ except Exception:
|
|||||||
# If git detection fails, that's fine - use base version
|
# If git detection fails, that's fine - use base version
|
||||||
pass
|
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(
|
app = FastAPI(
|
||||||
title="NFOGuard",
|
title="NFOGuard",
|
||||||
description="Webhook server for preserving media import dates",
|
description="Webhook server for preserving media import dates",
|
||||||
@@ -2201,7 +2209,16 @@ async def debug_tmdb_lookup(imdb_id: str):
|
|||||||
# Main
|
# 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__":
|
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", "Starting NFOGuard")
|
||||||
_log("INFO", f"Version: {version}")
|
_log("INFO", f"Version: {version}")
|
||||||
_log("INFO", f"TV paths: {[str(p) for p in config.tv_paths]}")
|
_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"Config: manage_nfo={config.manage_nfo}, fix_mtimes={config.fix_dir_mtimes}")
|
||||||
_log("INFO", f"Movie priority: {config.movie_priority}")
|
_log("INFO", f"Movie priority: {config.movie_priority}")
|
||||||
|
|
||||||
uvicorn.run(
|
try:
|
||||||
app,
|
uvicorn.run(
|
||||||
host="0.0.0.0",
|
app,
|
||||||
port=int(os.environ.get("PORT", "8080")),
|
host="0.0.0.0",
|
||||||
reload=False
|
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)
|
||||||
Reference in New Issue
Block a user