From b1484641d955e59d02c0e1fcd723072dedb05196 Mon Sep 17 00:00:00 2001 From: jskala Date: Mon, 22 Sep 2025 10:52:20 -0400 Subject: [PATCH 1/2] fix: update tv .nfo handling updated TV .nfo handling --- .env.example | 28 -- .env.fixed | 124 --------- .env.template | 173 ++++++------- .gitea/workflows/ci-dev.yml | 54 +--- .gitea/workflows/ci.yml | 67 +---- .gitignore | 5 +- Dockerfile | 15 +- SETUP.md | 153 ----------- SUMMARY.md | 496 ------------------------------------ VERSION | 2 +- core/nfo_manager.py | 31 ++- nfoguard.py | 35 ++- 12 files changed, 175 insertions(+), 1008 deletions(-) delete mode 100644 .env.example delete mode 100644 .env.fixed delete mode 100644 SETUP.md delete mode 100644 SUMMARY.md diff --git a/.env.example b/.env.example deleted file mode 100644 index 78ce602..0000000 --- a/.env.example +++ /dev/null @@ -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 \ No newline at end of file diff --git a/.env.fixed b/.env.fixed deleted file mode 100644 index 1a449f0..0000000 --- a/.env.fixed +++ /dev/null @@ -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 \ No newline at end of file diff --git a/.env.template b/.env.template index 4ee8755..1a449f0 100644 --- a/.env.template +++ b/.env.template @@ -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 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 \ No newline at end of file diff --git a/.gitea/workflows/ci-dev.yml b/.gitea/workflows/ci-dev.yml index 2d13d04..8effa89 100644 --- a/.gitea/workflows/ci-dev.yml +++ b/.gitea/workflows/ci-dev.yml @@ -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 \ No newline at end of file + echo "" + echo "🎉 DEV build completed successfully!" + echo "" + echo "📦 Available DEV images:" + echo " Local only: nfoguard:dev" + echo " Local SHA: nfoguard:dev-${{ github.sha }}" \ No newline at end of file diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index 410ccd8..e8094c8 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -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 diff --git a/.gitignore b/.gitignore index 644c789..019c6a2 100644 --- a/.gitignore +++ b/.gitignore @@ -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 diff --git a/Dockerfile b/Dockerfile index 868805b..fcfcb37 100644 --- a/Dockerfile +++ b/Dockerfile @@ -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 diff --git a/SETUP.md b/SETUP.md deleted file mode 100644 index 0798ec5..0000000 --- a/SETUP.md +++ /dev/null @@ -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. \ No newline at end of file diff --git a/SUMMARY.md b/SUMMARY.md deleted file mode 100644 index 181b580..0000000 --- a/SUMMARY.md +++ /dev/null @@ -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**: `tt123456` (NEW) -4. **Legacy NFO Tags**: `` and `` 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 ``, ``, and `` 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 \ No newline at end of file diff --git a/VERSION b/VERSION index d8c5e72..400084b 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.6.8 +1.6.7 diff --git a/core/nfo_manager.py b/core/nfo_manager.py index 8baac7c..1fd370f 100644 --- a/core/nfo_manager.py +++ b/core/nfo_manager.py @@ -439,17 +439,40 @@ class NFOManager: if existing_long_nfo and source_nfo_path == existing_long_nfo: old_nfo_to_delete = existing_long_nfo 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 - # These will be re-added at the bottom + # 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]}...") @@ -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") diff --git a/nfoguard.py b/nfoguard.py index 2b1bd82..dec66d6 100644 --- a/nfoguard.py +++ b/nfoguard.py @@ -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 - ) \ No newline at end of file + 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) \ No newline at end of file From 91e749ccdc5089fca3e28468d0a048f9da076de7 Mon Sep 17 00:00:00 2001 From: SBCrumb Date: Mon, 22 Sep 2025 14:20:22 -0400 Subject: [PATCH 2/2] restore: Add .local development documentation back to Gitea - Restore .local/ directory with all development documentation - Files include SUMMARY.md, commit.md, workflow.md, and setup templates - These files stay local and in Gitea only (excluded from GitHub) --- .local/.env.example | 28 +++ .local/.env.template | 141 +++++++++++ .local/SETUP.md | 153 ++++++++++++ .local/SUMMARY.md | 567 +++++++++++++++++++++++++++++++++++++++++++ .local/commit.md | 67 +++++ .local/workflow.md | 221 +++++++++++++++++ 6 files changed, 1177 insertions(+) create mode 100644 .local/.env.example create mode 100644 .local/.env.template create mode 100644 .local/SETUP.md create mode 100644 .local/SUMMARY.md create mode 100644 .local/commit.md create mode 100644 .local/workflow.md diff --git a/.local/.env.example b/.local/.env.example new file mode 100644 index 0000000..78ce602 --- /dev/null +++ b/.local/.env.example @@ -0,0 +1,28 @@ +# 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 \ No newline at end of file diff --git a/.local/.env.template b/.local/.env.template new file mode 100644 index 0000000..4ee8755 --- /dev/null +++ b/.local/.env.template @@ -0,0 +1,141 @@ +# NFOGuard Environment Configuration Template +# Copy this to .env and customize for your setup + +# =========================================== +# MEDIA PATHS (REQUIRED) +# =========================================== +# 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 +MOVIE_PATHS=/media/Movies/movies,/media/Movies/movies6 + +# =========================================== +# DATABASE CONFIGURATION +# =========================================== +# NFOGuard SQLite database location +DB_PATH=/app/data/media_dates.db + +# =========================================== +# RADARR DATABASE CONNECTION (REQUIRED) +# =========================================== +# Connection to Radarr's PostgreSQL database +# NOTE: Database password should be set in .env.secrets file +RADARR_DB_HOST=radarr-postgres +RADARR_DB_PORT=5432 +RADARR_DB_NAME=radarr +RADARR_DB_USER=radarr + +# =========================================== +# RADARR API (OPTIONAL - for fallback) +# =========================================== +# Only needed if you want API fallback (not recommended for performance) +# NOTE: API key should be set in .env.secrets file +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 +# =========================================== +# Whether to create/update .nfo files +MANAGE_NFO=true + +# Whether to fix file modification times to match import dates +FIX_DIR_MTIMES=true + +# Whether to add tags to prevent metadata drift +LOCK_METADATA=true + +# Brand name to add to NFO files +MANAGER_BRAND=NFOGuard + +# =========================================== +# PROCESSING SETTINGS +# =========================================== +# Delay before processing batched webhook 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 + +# =========================================== +# TV SERIES PROCESSING +# =========================================== +# 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 +DEBUG=false + +# =========================================== +# SERVER +# =========================================== +# Port to run the webhook server on +PORT=8080 \ No newline at end of file diff --git a/.local/SETUP.md b/.local/SETUP.md new file mode 100644 index 0000000..0798ec5 --- /dev/null +++ b/.local/SETUP.md @@ -0,0 +1,153 @@ +# 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. \ No newline at end of file diff --git a/.local/SUMMARY.md b/.local/SUMMARY.md new file mode 100644 index 0000000..5f5e4f0 --- /dev/null +++ b/.local/SUMMARY.md @@ -0,0 +1,567 @@ +# 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 22, 2025) + +### ✅ WORKFLOW AUTOMATION OVERHAUL +1. **Removed Docker Hub Dependencies**: Eliminated all Docker Hub pushes from Gitea workflows +2. **Local-Only Builds**: Configured dev and main branches to build locally only +3. **GitHub Integration**: Set up automated local → GitHub workflow for public testing +4. **Post-Merge Automation**: Created git hook for automatic GitHub sync when merging to main + +### ✅ NFO MIGRATION BUG FIX +**Problem**: Long-named episode NFO files created by Sonarr weren't properly migrated +- ❌ Content (title, plot, runtime) was lost during migration +- ❌ Long-named files weren't being cleaned up +- ❌ Short NFO files had minimal metadata + +**Solution**: Enhanced episode NFO migration process +- ✅ **Content Preservation**: All metadata (title, plot, runtime, premiered) now preserved from long-named NFO files +- ✅ **Smart Migration**: `find_existing_episode_nfo()` detects matching season/episode in long-named files +- ✅ **Automatic Cleanup**: Old long-named NFO files automatically deleted after successful migration +- ✅ **Rich Output**: Standard `S##E##.nfo` files now contain full metadata + NFOGuard fields +- ✅ **Debug Logging**: Added detailed logging to track content preservation during migration +- ✅ **FIXED**: Migration logic now properly prioritizes long-named files over existing short files +- ✅ **WORKING**: Content preservation, cleanup, and migration all functioning correctly + +### ✅ DOCKER SHUTDOWN FIX +**Problem**: Container wouldn't stop gracefully with `docker compose down` +- ❌ 24-second timeout waiting for container to stop +- ❌ Python process not handling SIGTERM signals properly + +**Solution**: Added proper signal handling to NFOGuard +- ✅ **Signal Handlers**: SIGTERM and SIGINT now properly handled +- ✅ **Graceful Shutdown**: Clean exit with logging +- ✅ **Fast Stops**: Container stops in seconds instead of timing out + +### ✅ NEW RELEASE WORKFLOW +**Local Development Flow**: +1. **Local dev branch** → Development and testing +2. **Local main branch** → Merge when ready for release +3. **Auto-trigger** → Post-merge hook pushes main to GitHub dev +4. **GitHub dev branch** → Public testing environment +5. **GitHub main branch** → Production releases (manual PR approval) + +### ✅ AUTOMATION TOOLS CREATED +- **Post-merge hook** (`.git/hooks/post-merge`): Auto-syncs main → GitHub dev + creates PR +- **Enhanced sync script** (`sync-to-github.sh`): Manual control over GitHub sync + **automatic version management** +- **GitHub remote**: Added `git@github.com:sbcrumb/nfoguard.git` for public repository + +### ✅ VERSION MANAGEMENT AUTOMATION +**Never Forget Version Bumps Again!** +- **Automatic version increment** integrated into sync workflow +- **Semantic versioning support**: patch/minor/major increments +- **Smart commit messages**: `🔖 Bump version to X.Y.Z (type)` +- **Flexible options**: Version-only updates or sync with version bump + +### ✅ BUILD IDENTIFICATION SYSTEM +**Always Know What Version You're Running!** +- **Branch Detection**: Automatically detects git branch during build +- **Gitea Branding**: All builds tagged with `-gitea` suffix for clear identification +- **Version Examples**: + - Dev builds: `1.6.8-dev-gitea` + - Main builds: `1.6.8-gitea` +- **Build Source**: All builds performed in Gitea CI (never local manual builds) + +### ✅ WORKFLOW BENEFITS +- ✅ **No more Docker Hub costs/dependencies** +- ✅ **Local builds only** (faster, more secure) +- ✅ **Automated public testing** via GitHub dev branch +- ✅ **Controlled releases** via PR approval process +- ✅ **Maintains local Gitea workflow** for internal development + +--- + +## 🎯 PREVIOUS 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**: `tt123456` (NEW) +4. **Legacy NFO Tags**: `` and `` 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 ``, ``, and `` 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 \ No newline at end of file diff --git a/.local/commit.md b/.local/commit.md new file mode 100644 index 0000000..f7c52ce --- /dev/null +++ b/.local/commit.md @@ -0,0 +1,67 @@ +# = NFOGuard v1.6.9 - Enhanced NFO Migration & Workflow Automation + +## < Major Features + +###  Complete NFO Migration System +- **Smart Content Preservation**: Automatically migrates rich metadata (title, plot, runtime) from long-named Sonarr NFO files to standardized `S##E##.nfo` format +- **Intelligent File Detection**: Enhanced `find_existing_episode_nfo()` detects matching season/episode in any NFO file naming convention +- **Automatic Cleanup**: Long-named NFO files automatically deleted after successful migration +- **Priority Logic Fix**: Migration now correctly prioritizes long-named files over existing short files for content extraction + +###  Workflow Automation Overhaul +- **Local-Only Builds**: Removed all Docker Hub dependencies, builds now local-only for faster, more secure CI/CD +- **GitHub Integration**: Automated local GitHub workflow for public testing and releases +- **Post-Merge Automation**: Git hooks automatically sync local main to GitHub dev branch with PR creation +- **Version Management**: Integrated semantic versioning with automatic increment options (patch/minor/major) + +###  Build Identification System +- **Branch Detection**: Automatic git branch detection during Docker builds +- **Environment Tagging**: Clear version identification (`1.6.9-dev-gitea` vs `1.6.9`) +- **Build Source Tracking**: Distinguishes local Gitea builds from public GitHub releases + +## =' Technical Improvements + +###  Docker Enhancements +- **Graceful Shutdown**: Added proper SIGTERM/SIGINT signal handling for clean container stops +- **Branch-Specific Builds**: Workflow now correctly clones target branch instead of default +- **Build Arguments**: Proper `GIT_BRANCH` and `BUILD_SOURCE` parameter passing + +###  Debug System +- **Conditional Logging**: All debug statements now respect `DEBUG=true/false` environment variable +- **Migration Tracking**: Detailed logging for NFO content preservation process (when enabled) +- **Clean Production Logs**: Debug noise eliminated when `DEBUG=false` + +## < NFO Migration Details + +**Before**: Long-named Sonarr NFO files existed alongside minimal short NFO files, content was lost +**After**: Rich metadata automatically preserved in standardized format, old files cleaned up + +**Example Migration**: +``` +Source: "Invasion (2021)-S03E04-The Mission[WEBDL-1080p][AAC2.0][h264].nfo" +Target: "S03E04.nfo" +Content: title, plot, runtime, premiered All preserved  +Cleanup: Original long file Automatically deleted  +``` + +## = New Release Workflow + +1. **Local Development** dev branch with local Gitea CI +2. **Local Release** Merge dev to main triggers GitHub sync +3. **Public Testing** GitHub dev branch for community testing +4. **Production Release** Manual PR approval to GitHub main + +## = Developer Experience + +- **Version Bumping**: Never forget with integrated `sync-to-github.sh` script +- **Build Clarity**: Always know which environment you're running +- **Fast Iteration**: Local-only builds eliminate external dependencies +- **Debug Control**: Toggle verbose logging with single environment variable + +--- + +**Breaking Changes**: None - fully backward compatible +**Migration**: Automatic - existing NFO files will be migrated on next scan +**Requirements**: No additional dependencies + +Co-authored-by: Claude Code \ No newline at end of file diff --git a/.local/workflow.md b/.local/workflow.md new file mode 100644 index 0000000..fc269b3 --- /dev/null +++ b/.local/workflow.md @@ -0,0 +1,221 @@ +# NFOGuard Development Workflow + +## Overview +NFOGuard uses a dual-repository system: +- **Gitea (Private)**: `origin` - Development, testing, messy commits +- **GitHub (Public)**: `github` - Clean releases, public testing + +## Repository Setup +```bash +# Remotes should be configured as: +origin gitea@192.168.253.221:jskala/NFOguard.git (fetch/push) +github git@github.com:sbcrumb/nfoguard.git (fetch/push) +``` + +## Daily Development Workflow + +### 1. Local Development +```bash +# Work on dev branch (make as many commits as you want) +git checkout dev +# ... make changes, commit frequently ... +git add . +git commit -m "fix: whatever you're working on" + +# Push to Gitea regularly for backup +git push origin dev +``` + +### 2. Feature Complete - Merge to Main +```bash +# When feature/fix is complete, merge to main +git checkout main +git merge dev + +# Push to Gitea main +git push origin main +``` + +### 3. Prepare for GitHub Release + +**Use the sync script for clean releases:** +```bash +# From main branch, run the sync script +./sync-to-github.sh + +# Choose Option 3: "Create clean release branch for GitHub PR (RECOMMENDED)" +# This will: +# 1. Increment version (patch/minor/major) +# 2. Create clean release-vX.X.X branch from GitHub main +# 3. Copy your changes with single clean commit +# 4. Push to GitHub automatically +``` + +**The script will output:** +``` +🔗 Create PR at: https://github.com/sbcrumb/nfoguard/pull/new/release-v1.7.0 +📝 PR will be: release-v1.7.0 → main (clean, single commit) +``` + +### 4. GitHub Testing and Release +```bash +# 1. Create PR: release-vX.X.X → main on GitHub +# 2. Test the release branch thoroughly +# 3. Merge when satisfied (squash merge will be available) +``` + +## Manual Process (If Script Unavailable) + +### Emergency Manual Release +If the sync script isn't available, here's the manual process: + +```bash +# 1. Fetch latest GitHub main +git fetch github + +# 2. Create clean release branch +git checkout -b release-v1.7.0 github/main + +# 3. Copy key files from your local main +git checkout main -- VERSION Dockerfile core/nfo_manager.py nfoguard.py .env.template .env.secrets.template .gitea/ + +# 4. Increment version manually +echo "1.7.0" > VERSION + +# 5. Commit and push +git add . +git commit -m "feat: Release v1.7.0 - [describe key changes]" +git push github release-v1.7.0 + +# 6. Create PR on GitHub: release-v1.7.0 → main +``` + +## Important Files and Their Purpose + +### Local-Only Files (Never Commit to Gitea/GitHub) +- `sync-to-github.sh` - GitHub sync automation +- `.local/` directory - Local documentation and notes +- `.env.secrets` - API keys and passwords + +### Repository-Specific Files +- `.gitea/workflows/` - Gitea CI/CD (ignored by GitHub) +- `.github/workflows/` - GitHub CI/CD (ignored by Gitea) + +### Shared Files +- Core application code +- Dockerfile +- Configuration templates +- VERSION file + +## Version Management + +### Semantic Versioning +- **Patch (X.Y.Z+1)**: Bug fixes, small updates +- **Minor (X.Y+1.0)**: New features, enhancements +- **Major (X+1.0.0)**: Breaking changes, major releases + +### Version Identification +- **Local Gitea builds**: `1.7.0-dev-gitea` +- **GitHub releases**: `1.7.0` + +## Troubleshooting + +### "Messy Commit History" in GitHub PRs +**Problem**: Local dev has 20+ commits that make GitHub PRs hard to review +**Solution**: Always use sync script Option 3 to create clean release branches + +### "No Common History" Error +**Problem**: GitHub and local repos have different histories +**Solution**: Use the clean release branch workflow (creates branches from GitHub main) + +### "Sync Script Not Working" +**Fallback**: Use the manual process outlined above + +### Local Branch "Ahead by X Commits" +**Normal**: Your local dev is ahead of Gitea dev - just push when ready +```bash +git push origin dev # Sync to Gitea +``` + +## Branch Strategy + +### Local Branches +- `dev` - Active development (tracks origin/dev) +- `main` - Stable local code (tracks origin/main) + +### GitHub Branches +- `release-vX.X.X` - Clean release branches (created by sync script) +- `main` - Public stable releases +- `dev` - Optional testing branch + +### Gitea Branches +- `dev` - Development history (all commits) +- `main` - Feature-complete code (merged from dev) + +## CI/CD Behavior + +### Gitea Workflows +- Build locally only (no Docker Hub) +- Version shows as "X.X.X-dev-gitea" +- Triggered on dev/main pushes + +### GitHub Workflows +- Build to GitHub Container Registry +- Version shows as "X.X.X" (clean) +- Triggered on main pushes + +## Emergency Procedures + +### If Sync Script is Lost +1. Recreate from this workflow document +2. Use manual process for immediate needs +3. Script should be in `.local/` directory and ignored by git + +### If GitHub Repo Gets Corrupted +1. Force push clean main from Gitea +2. Recreate release branches as needed +3. Your code is safe in Gitea + +### If Gitea Goes Down +1. Continue development locally +2. Push to GitHub dev branch for backup +3. Sync back to Gitea when restored + +## Success Indicators + +### Good Workflow Signs +- ✅ GitHub PRs have 1-3 clean commits +- ✅ Local history is detailed and preserved +- ✅ Version numbers increment properly +- ✅ CI/CD builds succeed on both platforms + +### Warning Signs +- ❌ GitHub PRs have 20+ messy commits +- ❌ "No common history" errors +- ❌ Version numbers out of sync +- ❌ Sync script getting committed to repos + +## Quick Reference Commands + +```bash +# Daily development +git checkout dev && git pull origin dev +# ... work ... +git add . && git commit -m "fix: description" +git push origin dev + +# Complete feature +git checkout main && git merge dev && git push origin main + +# Release to GitHub +./sync-to-github.sh # Choose option 3 + +# Check status +git status +git log --oneline -5 +git remote -v +``` + +--- + +**Remember**: The sync script Option 3 is your best friend for clean GitHub releases! \ No newline at end of file