From fa3f41e6fd3431b6e23a50748e5a776aba5c814d Mon Sep 17 00:00:00 2001 From: SBCrumb Date: Sun, 21 Sep 2025 09:32:27 -0400 Subject: [PATCH 01/15] update Dev --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index d8c5e72..400084b 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.6.8 +1.6.7 From 09b28abcdc9c4cba895ab1545a4da9ed30e91f25 Mon Sep 17 00:00:00 2001 From: SBCrumb Date: Mon, 22 Sep 2025 08:36:46 -0400 Subject: [PATCH 02/15] make local dir for ignores in local docs --- .env.example | 28 --- .env.fixed | 124 ------------- .env.template | 173 ++++++++---------- .gitignore | 1 - SETUP.md | 153 ---------------- SUMMARY.md | 496 -------------------------------------------------- 6 files changed, 78 insertions(+), 897 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/.gitignore b/.gitignore index 644c789..ff21fc2 100644 --- a/.gitignore +++ b/.gitignore @@ -62,7 +62,6 @@ temp/ # Local development files (not for public repo) *.local CODE_STRUCTURE.local -RESTART_SUMMARY.md # Ignore GitHub workflows when pushing to Gitea .github/ 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 From a4b1042b6517df15446752d757ac0a5af9f40334 Mon Sep 17 00:00:00 2001 From: SBCrumb Date: Mon, 22 Sep 2025 09:01:21 -0400 Subject: [PATCH 03/15] update local workflow --- .gitea/workflows/ci-dev.yml | 47 +++++------------------------- .gitea/workflows/ci.yml | 58 ++++--------------------------------- .gitignore | 4 +++ 3 files changed, 16 insertions(+), 93 deletions(-) diff --git a/.gitea/workflows/ci-dev.yml b/.gitea/workflows/ci-dev.yml index 2d13d04..4c5cab3 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: @@ -135,42 +135,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..3c375da 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -1,4 +1,4 @@ -name: Docker Build & Deploy +name: Local Docker Build (Main) on: push: @@ -177,61 +177,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 ff21fc2..019c6a2 100644 --- a/.gitignore +++ b/.gitignore @@ -62,10 +62,14 @@ temp/ # Local development files (not for public repo) *.local CODE_STRUCTURE.local +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 From 927d7cdeff99da576ebefacb3295d4ea16302d8c Mon Sep 17 00:00:00 2001 From: SBCrumb Date: Mon, 22 Sep 2025 09:04:01 -0400 Subject: [PATCH 04/15] another workflow upate local --- .gitea/workflows/ci-dev.yml | 3 ++- .gitea/workflows/ci.yml | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.gitea/workflows/ci-dev.yml b/.gitea/workflows/ci-dev.yml index 4c5cab3..718327f 100644 --- a/.gitea/workflows/ci-dev.yml +++ b/.gitea/workflows/ci-dev.yml @@ -15,8 +15,9 @@ 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..." diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index 3c375da..8af839d 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -15,8 +15,9 @@ 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..." From 93cee77ce0051868bfdb3918c64ba7b0ac48368c Mon Sep 17 00:00:00 2001 From: SBCrumb Date: Mon, 22 Sep 2025 09:13:08 -0400 Subject: [PATCH 05/15] stop main flow when dev runs --- .gitea/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index 8af839d..9398ee0 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -2,7 +2,7 @@ name: Local Docker Build (Main) on: push: - branches: [ main, dev ] + branches: [ main ] pull_request: branches: [ main ] From f563c6740590fa63803a2545fcff997f0dcb6611 Mon Sep 17 00:00:00 2001 From: SBCrumb Date: Mon, 22 Sep 2025 09:25:03 -0400 Subject: [PATCH 06/15] update sonarr .nfo flow --- core/nfo_manager.py | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/core/nfo_manager.py b/core/nfo_manager.py index 8baac7c..faf8ba5 100644 --- a/core/nfo_manager.py +++ b/core/nfo_manager.py @@ -439,17 +439,33 @@ 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 except (ET.ParseError, ValueError) as e: print(f"⚠️ Corrupted episode NFO detected: {nfo_path} - {str(e)[:100]}...") @@ -459,7 +475,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") From e7749620f6a3c5b5a34a20977893da8928dc345f Mon Sep 17 00:00:00 2001 From: SBCrumb Date: Mon, 22 Sep 2025 09:30:50 -0400 Subject: [PATCH 07/15] better docker handling for shutdown --- Dockerfile | 2 +- nfoguard.py | 29 +++++++++++++++++++++++------ 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/Dockerfile b/Dockerfile index 868805b..573c48b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -40,7 +40,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/nfoguard.py b/nfoguard.py index 2b1bd82..369228e 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 @@ -2201,7 +2203,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 +2221,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 a74feeb67e907d266cac2949f16a4ab93411f4bc Mon Sep 17 00:00:00 2001 From: SBCrumb Date: Mon, 22 Sep 2025 10:03:59 -0400 Subject: [PATCH 08/15] udpates to flow and nfo manager for TV --- .gitea/workflows/ci-dev.yml | 1 + .gitea/workflows/ci.yml | 1 + Dockerfile | 10 +++++++++- core/nfo_manager.py | 7 +++++++ 4 files changed, 18 insertions(+), 1 deletion(-) diff --git a/.gitea/workflows/ci-dev.yml b/.gitea/workflows/ci-dev.yml index 718327f..fa44ee1 100644 --- a/.gitea/workflows/ci-dev.yml +++ b/.gitea/workflows/ci-dev.yml @@ -96,6 +96,7 @@ jobs: echo "Building DEV image with layer caching..." docker build \ $CACHE_ARGS \ + --build-arg GIT_BRANCH=dev \ --tag nfoguard:dev \ --tag nfoguard:dev-${{ github.sha }} \ --tag "$CACHE_IMAGE" \ diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index 9398ee0..ae112d9 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -99,6 +99,7 @@ jobs: echo "Building with layer caching..." docker build \ $CACHE_ARGS \ + --build-arg GIT_BRANCH=main \ --tag nfoguard:latest \ --tag nfoguard:${{ github.sha }} \ --tag "$CACHE_IMAGE" \ diff --git a/Dockerfile b/Dockerfile index 573c48b..b48c511 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,9 +1,13 @@ FROM python:3.11-slim +# Build argument for git branch +ARG GIT_BRANCH=main + # Set environment variables ENV PYTHONDONTWRITEBYTECODE=1 \ PYTHONUNBUFFERED=1 \ - PYTHONIOENCODING=utf-8 + PYTHONIOENCODING=utf-8 \ + GIT_BRANCH=${GIT_BRANCH} # Install system dependencies including PostgreSQL client libraries RUN apt-get update && apt-get install -y \ @@ -24,6 +28,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/ && \ diff --git a/core/nfo_manager.py b/core/nfo_manager.py index faf8ba5..1fd370f 100644 --- a/core/nfo_manager.py +++ b/core/nfo_manager.py @@ -466,6 +466,13 @@ class NFOManager: # 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]}...") From 95273be0b062fd6c97b4a306c9b90af463a94cc6 Mon Sep 17 00:00:00 2001 From: SBCrumb Date: Mon, 22 Sep 2025 10:07:39 -0400 Subject: [PATCH 09/15] update flow --- Dockerfile | 3 ++- nfoguard.py | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index b48c511..e49defa 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,7 +7,8 @@ ARG GIT_BRANCH=main ENV PYTHONDONTWRITEBYTECODE=1 \ PYTHONUNBUFFERED=1 \ PYTHONIOENCODING=utf-8 \ - GIT_BRANCH=${GIT_BRANCH} + GIT_BRANCH=${GIT_BRANCH} \ + GITEA_BUILD=true # Install system dependencies including PostgreSQL client libraries RUN apt-get update && apt-get install -y \ diff --git a/nfoguard.py b/nfoguard.py index 369228e..83040ce 100644 --- a/nfoguard.py +++ b/nfoguard.py @@ -1397,6 +1397,11 @@ except Exception: # If git detection fails, that's fine - use base version pass +# Check for Gitea build environment variable +if os.environ.get("GITEA_BUILD") == "true": + if "-" not in version: # Don't double-add suffixes + version = f"{version}-gitea" + app = FastAPI( title="NFOGuard", description="Webhook server for preserving media import dates", From 7de99ef9453cecb123afcc27b11fa6e76935822e Mon Sep 17 00:00:00 2001 From: SBCrumb Date: Mon, 22 Sep 2025 10:12:07 -0400 Subject: [PATCH 10/15] trying to add gitea --- .gitea/workflows/ci-dev.yml | 1 + .gitea/workflows/ci.yml | 1 + Dockerfile | 5 +++-- nfoguard.py | 7 ++++--- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/.gitea/workflows/ci-dev.yml b/.gitea/workflows/ci-dev.yml index fa44ee1..fe78142 100644 --- a/.gitea/workflows/ci-dev.yml +++ b/.gitea/workflows/ci-dev.yml @@ -97,6 +97,7 @@ jobs: 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" \ diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index ae112d9..2ca778b 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -100,6 +100,7 @@ jobs: docker build \ $CACHE_ARGS \ --build-arg GIT_BRANCH=main \ + --build-arg BUILD_SOURCE=gitea \ --tag nfoguard:latest \ --tag nfoguard:${{ github.sha }} \ --tag "$CACHE_IMAGE" \ diff --git a/Dockerfile b/Dockerfile index e49defa..4077c32 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,14 +1,15 @@ FROM python:3.11-slim -# Build argument for git branch +# 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 \ GIT_BRANCH=${GIT_BRANCH} \ - GITEA_BUILD=true + BUILD_SOURCE=${BUILD_SOURCE} # Install system dependencies including PostgreSQL client libraries RUN apt-get update && apt-get install -y \ diff --git a/nfoguard.py b/nfoguard.py index 83040ce..dec66d6 100644 --- a/nfoguard.py +++ b/nfoguard.py @@ -1397,9 +1397,10 @@ except Exception: # If git detection fails, that's fine - use base version pass -# Check for Gitea build environment variable -if os.environ.get("GITEA_BUILD") == "true": - if "-" not in version: # Don't double-add suffixes +# 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( From 96de17e0c52c538c654b66e7661ad37232976093 Mon Sep 17 00:00:00 2001 From: SBCrumb Date: Mon, 22 Sep 2025 10:31:26 -0400 Subject: [PATCH 11/15] update docker for force build --- Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Dockerfile b/Dockerfile index 4077c32..fcfcb37 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,5 @@ FROM python:3.11-slim +#Udates for local builds # Build argument for git branch and build source ARG GIT_BRANCH=main From 07b3110c722021b79fa973b00a76ba6ab4162f46 Mon Sep 17 00:00:00 2001 From: SBCrumb Date: Mon, 22 Sep 2025 10:35:05 -0400 Subject: [PATCH 12/15] fix branch issue in workflow --- .gitea/workflows/ci-dev.yml | 2 +- .gitea/workflows/ci.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitea/workflows/ci-dev.yml b/.gitea/workflows/ci-dev.yml index fe78142..8effa89 100644 --- a/.gitea/workflows/ci-dev.yml +++ b/.gitea/workflows/ci-dev.yml @@ -21,7 +21,7 @@ jobs: # 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 diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index 2ca778b..e8094c8 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -21,7 +21,7 @@ jobs: # 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 From de3896b8c370caabe6ffa8b573bf91f1e8badbee Mon Sep 17 00:00:00 2001 From: SBCrumb Date: Mon, 22 Sep 2025 10:54:22 -0400 Subject: [PATCH 13/15] fix: add debug for .nfo ussues --- VERSION | 2 +- core/nfo_manager.py | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 400084b..d8c5e72 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.6.7 +1.6.8 diff --git a/core/nfo_manager.py b/core/nfo_manager.py index 1fd370f..2d12c89 100644 --- a/core/nfo_manager.py +++ b/core/nfo_manager.py @@ -422,9 +422,12 @@ class NFOManager: try: # First, check for existing long-named NFO files that need migration existing_long_nfo = self.find_existing_episode_nfo(season_dir, season_num, episode_num) + print(f" 🔍 Debug: existing_long_nfo = {existing_long_nfo}") + print(f" 🔍 Debug: nfo_path.exists() = {nfo_path.exists()}") # Try to load existing NFO file (either standard or long-named) source_nfo_path = nfo_path if nfo_path.exists() else existing_long_nfo + print(f" 🔍 Debug: source_nfo_path = {source_nfo_path}") if source_nfo_path: try: From 3e5c80279f40502a6415128e9d1eaeab9289da7b Mon Sep 17 00:00:00 2001 From: SBCrumb Date: Mon, 22 Sep 2025 11:02:12 -0400 Subject: [PATCH 14/15] fix: nfo logic long vs shortname --- core/nfo_manager.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/nfo_manager.py b/core/nfo_manager.py index 2d12c89..2c1ae6c 100644 --- a/core/nfo_manager.py +++ b/core/nfo_manager.py @@ -425,8 +425,8 @@ class NFOManager: print(f" 🔍 Debug: existing_long_nfo = {existing_long_nfo}") print(f" 🔍 Debug: nfo_path.exists() = {nfo_path.exists()}") - # Try to load existing NFO file (either standard or long-named) - source_nfo_path = nfo_path if nfo_path.exists() else existing_long_nfo + # Prioritize long-named file for migration, otherwise use standard file + source_nfo_path = existing_long_nfo if existing_long_nfo else nfo_path if nfo_path.exists() else None print(f" 🔍 Debug: source_nfo_path = {source_nfo_path}") if source_nfo_path: From c04fe8d0838ced48698b5d534af1fab7108ddf29 Mon Sep 17 00:00:00 2001 From: SBCrumb Date: Mon, 22 Sep 2025 12:18:40 -0400 Subject: [PATCH 15/15] debug optional --- VERSION | 2 +- core/nfo_manager.py | 19 +++++++++---------- nfoguard.py | 2 +- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/VERSION b/VERSION index d8c5e72..15d45d4 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.6.8 +1.6.9 diff --git a/core/nfo_manager.py b/core/nfo_manager.py index 2c1ae6c..5113157 100644 --- a/core/nfo_manager.py +++ b/core/nfo_manager.py @@ -14,8 +14,9 @@ import re class NFOManager: """Manages NFO file creation and updates""" - def __init__(self, manager_brand: str = "NFOGuard"): + def __init__(self, manager_brand: str = "NFOGuard", debug: bool = False): self.manager_brand = manager_brand + self.debug = debug def parse_imdb_from_path(self, path: Path) -> Optional[str]: """Extract IMDb ID from directory path or filename""" @@ -422,12 +423,9 @@ class NFOManager: try: # First, check for existing long-named NFO files that need migration existing_long_nfo = self.find_existing_episode_nfo(season_dir, season_num, episode_num) - print(f" 🔍 Debug: existing_long_nfo = {existing_long_nfo}") - print(f" 🔍 Debug: nfo_path.exists() = {nfo_path.exists()}") # Prioritize long-named file for migration, otherwise use standard file source_nfo_path = existing_long_nfo if existing_long_nfo else nfo_path if nfo_path.exists() else None - print(f" 🔍 Debug: source_nfo_path = {source_nfo_path}") if source_nfo_path: try: @@ -470,12 +468,13 @@ class NFOManager: # 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!") + # Debug: Show what fields are preserved after removing NFOGuard fields (if DEBUG enabled) + if self.debug: + 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]}...") diff --git a/nfoguard.py b/nfoguard.py index dec66d6..2a79fbd 100644 --- a/nfoguard.py +++ b/nfoguard.py @@ -1413,7 +1413,7 @@ start_time = datetime.now(timezone.utc) # Initialize components db = NFOGuardDatabase(config.db_path) -nfo_manager = NFOManager(config.manager_brand) +nfo_manager = NFOManager(config.manager_brand, config.debug) path_mapper = PathMapper(config) # FIXED: Pass config to PathMapper tv_processor = TVProcessor(db, nfo_manager, path_mapper) movie_processor = MovieProcessor(db, nfo_manager, path_mapper)