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
|
||||
# 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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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)
|
||||
Reference in New Issue
Block a user