digital release date
This commit is contained in:
@@ -79,6 +79,9 @@ MAX_CONCURRENT_SERIES=3
|
|||||||
# Movie date strategy: import_then_digital or digital_then_import
|
# Movie date strategy: import_then_digital or digital_then_import
|
||||||
MOVIE_PRIORITY=import_then_digital
|
MOVIE_PRIORITY=import_then_digital
|
||||||
|
|
||||||
|
# Smart fallback: prefer digital over file dates for manual imports (true/false)
|
||||||
|
PREFER_DIGITAL_OVER_FILE_DATES=true
|
||||||
|
|
||||||
# When to query APIs: always, if_missing, never
|
# When to query APIs: always, if_missing, never
|
||||||
MOVIE_POLL_MODE=always
|
MOVIE_POLL_MODE=always
|
||||||
|
|
||||||
|
|||||||
@@ -73,25 +73,97 @@ BATCH_DELAY 5.0 Delay before processing batched events
|
|||||||
DEBUG false Enable verbose logging
|
DEBUG false Enable verbose logging
|
||||||
|
|
||||||
🔌 API Endpoints
|
🔌 API Endpoints
|
||||||
POST /webhook/sonarr – Sonarr events
|
|
||||||
|
|
||||||
POST /webhook/radarr – Radarr events
|
### Webhook Endpoints
|
||||||
|
```bash
|
||||||
|
# Radarr webhook (automatic processing)
|
||||||
|
POST /webhook/radarr
|
||||||
|
|
||||||
POST /manual/scan?scan_type=both – Manual scan (TV + Movies) [DEFAULT]
|
# Sonarr webhook (automatic processing)
|
||||||
|
POST /webhook/sonarr
|
||||||
|
```
|
||||||
|
|
||||||
POST /manual/scan?scan_type=tv – Manual scan TV only
|
### Manual Processing
|
||||||
|
```bash
|
||||||
|
# Manual scan all media
|
||||||
|
curl -X POST "http://localhost:8080/manual/scan?scan_type=both"
|
||||||
|
|
||||||
POST /manual/scan?scan_type=movies – Manual scan Movies only
|
# Manual scan TV only
|
||||||
|
curl -X POST "http://localhost:8080/manual/scan?scan_type=tv"
|
||||||
|
|
||||||
POST /manual/scan?path=/media/movies – Manual scan specific path
|
# Manual scan movies only
|
||||||
|
curl -X POST "http://localhost:8080/manual/scan?scan_type=movies"
|
||||||
|
|
||||||
GET /health – Health check
|
# Manual scan specific path
|
||||||
|
curl -X POST "http://localhost:8080/manual/scan?path=/media/movies"
|
||||||
|
|
||||||
GET /stats – Database stats
|
# Bulk update all movies from Radarr database
|
||||||
|
curl -X POST "http://localhost:8080/bulk/update"
|
||||||
|
```
|
||||||
|
|
||||||
GET /batch/status – Current batch queue
|
### Testing & Validation
|
||||||
|
```bash
|
||||||
|
# Test database connections
|
||||||
|
curl -X POST "http://localhost:8080/test/bulk-update"
|
||||||
|
|
||||||
GET /debug/movie/{imdb_id} – Debug movie import date detection
|
# Test movie directory scanning
|
||||||
|
curl -X POST "http://localhost:8080/test/movie-scan"
|
||||||
|
|
||||||
|
# System health check
|
||||||
|
curl "http://localhost:8080/health"
|
||||||
|
|
||||||
|
# Database statistics
|
||||||
|
curl "http://localhost:8080/stats"
|
||||||
|
|
||||||
|
# Batch processing queue status
|
||||||
|
curl "http://localhost:8080/batch/status"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Debugging & Analysis
|
||||||
|
```bash
|
||||||
|
# Debug specific movie import date detection
|
||||||
|
curl "http://localhost:8080/debug/movie/tt1674782"
|
||||||
|
|
||||||
|
# Show complete import history analysis
|
||||||
|
curl "http://localhost:8080/debug/movie/tt1674782/history"
|
||||||
|
|
||||||
|
# Show date priority logic and available sources
|
||||||
|
curl "http://localhost:8080/debug/movie/tt1674782/priority"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Response Examples
|
||||||
|
|
||||||
|
**Health Check**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"status": "healthy",
|
||||||
|
"version": "0.5.1",
|
||||||
|
"database_status": "healthy",
|
||||||
|
"radarr_database": {"status": "healthy", "movies": 1500}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Movie Debug**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"detected_import_date": "2025-07-08T03:30:04+00:00",
|
||||||
|
"import_source": "radarr:history.import",
|
||||||
|
"movie_title": "Movie Name"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Priority Logic Debug**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"movie_priority": "import_then_digital",
|
||||||
|
"priority_explanation": "1st: Radarr import history, 2nd: TMDB digital release, 3rd: file mtime",
|
||||||
|
"date_sources": {
|
||||||
|
"radarr_import": {"date": "2025-07-08T03:30:04+00:00", "source": "radarr:history.import"},
|
||||||
|
"digital_release": {"date": "2023-05-15T00:00:00+00:00", "source": "tmdb:digital"}
|
||||||
|
},
|
||||||
|
"selected_date": "2025-07-08T03:30:04+00:00"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
📖 Example Workflow
|
📖 Example Workflow
|
||||||
You add The Blacklist in Sonarr.
|
You add The Blacklist in Sonarr.
|
||||||
|
|||||||
+86
-11
@@ -96,6 +96,7 @@ class NFOGuardConfig:
|
|||||||
|
|
||||||
# Movie processing
|
# Movie processing
|
||||||
self.movie_priority = os.environ.get("MOVIE_PRIORITY", "import_then_digital").lower()
|
self.movie_priority = os.environ.get("MOVIE_PRIORITY", "import_then_digital").lower()
|
||||||
|
self.prefer_digital_over_file_dates = _bool_env("PREFER_DIGITAL_OVER_FILE_DATES", True)
|
||||||
self.movie_poll_mode = os.environ.get("MOVIE_POLL_MODE", "always").lower()
|
self.movie_poll_mode = os.environ.get("MOVIE_POLL_MODE", "always").lower()
|
||||||
self.movie_update_mode = os.environ.get("MOVIE_DATE_UPDATE_MODE", "backfill_only").lower()
|
self.movie_update_mode = os.environ.get("MOVIE_DATE_UPDATE_MODE", "backfill_only").lower()
|
||||||
|
|
||||||
@@ -460,16 +461,31 @@ class MovieProcessor:
|
|||||||
|
|
||||||
# Try import history first if configured
|
# Try import history first if configured
|
||||||
if config.movie_priority == "import_then_digital":
|
if config.movie_priority == "import_then_digital":
|
||||||
|
import_date, import_source = None, None
|
||||||
if radarr_movie:
|
if radarr_movie:
|
||||||
movie_id = radarr_movie.get("id")
|
movie_id = radarr_movie.get("id")
|
||||||
if movie_id:
|
if movie_id:
|
||||||
import_date, import_source = self.radarr.get_movie_import_date(movie_id)
|
import_date, import_source = self.radarr.get_movie_import_date(movie_id)
|
||||||
if import_date:
|
|
||||||
return import_date, import_source, released
|
|
||||||
|
|
||||||
# Fall back to digital release
|
# Check if we got a real import date or just file date fallback
|
||||||
|
if import_date and import_source != "radarr:db.file.dateAdded":
|
||||||
|
return import_date, import_source, released
|
||||||
|
|
||||||
|
# Get digital release date for comparison
|
||||||
digital_date, digital_source = self._get_digital_release_date(imdb_id)
|
digital_date, digital_source = self._get_digital_release_date(imdb_id)
|
||||||
if digital_date:
|
|
||||||
|
# If we only have file date and digital date exists, prefer digital if reasonable and enabled
|
||||||
|
if import_date and import_source == "radarr:db.file.dateAdded" and digital_date and config.prefer_digital_over_file_dates:
|
||||||
|
# Compare dates - prefer digital if it's not way older than theatrical
|
||||||
|
if self._should_prefer_digital_over_file_date(digital_date, released, imdb_id):
|
||||||
|
return digital_date, digital_source, released
|
||||||
|
else:
|
||||||
|
return import_date, import_source, released
|
||||||
|
|
||||||
|
# Use whichever we have
|
||||||
|
if import_date:
|
||||||
|
return import_date, import_source, released
|
||||||
|
elif digital_date:
|
||||||
return digital_date, digital_source, released
|
return digital_date, digital_source, released
|
||||||
|
|
||||||
else: # digital_then_import
|
else: # digital_then_import
|
||||||
@@ -519,6 +535,44 @@ class MovieProcessor:
|
|||||||
|
|
||||||
return "MANUAL_REVIEW_NEEDED", "manual_review_required", None
|
return "MANUAL_REVIEW_NEEDED", "manual_review_required", None
|
||||||
|
|
||||||
|
def _should_prefer_digital_over_file_date(self, digital_date: str, theatrical_release: Optional[str], imdb_id: str) -> bool:
|
||||||
|
"""
|
||||||
|
Decide if digital release date should be preferred over file date
|
||||||
|
|
||||||
|
Logic:
|
||||||
|
- If digital date is reasonable (not decades before theatrical), prefer it
|
||||||
|
- If digital date is way too old (like 2000 for 1983 movie), use file date
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
digital_dt = datetime.fromisoformat(digital_date.replace("Z", "+00:00"))
|
||||||
|
|
||||||
|
# If we have theatrical release date, compare
|
||||||
|
if theatrical_release:
|
||||||
|
theatrical_dt = datetime.fromisoformat(theatrical_release.replace("Z", "+00:00"))
|
||||||
|
year_diff = digital_dt.year - theatrical_dt.year
|
||||||
|
|
||||||
|
# If digital is more than 10 years before theatrical, it's probably wrong
|
||||||
|
if year_diff < -10:
|
||||||
|
_log("INFO", f"Digital date {digital_date} is {abs(year_diff)} years before theatrical {theatrical_release} for {imdb_id}, using file date instead")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# If digital is within reasonable range (theatrical to +20 years), use it
|
||||||
|
if -2 <= year_diff <= 20:
|
||||||
|
_log("INFO", f"Digital date {digital_date} is reasonable for {imdb_id} (theatrical: {theatrical_release}), preferring over file date")
|
||||||
|
return True
|
||||||
|
|
||||||
|
# If no theatrical date, use digital if it's not absurdly old
|
||||||
|
if digital_dt.year >= 1990: # Reasonable minimum for digital releases
|
||||||
|
_log("INFO", f"Digital date {digital_date} seems reasonable for {imdb_id}, preferring over file date")
|
||||||
|
return True
|
||||||
|
|
||||||
|
_log("INFO", f"Digital date {digital_date} seems too old for {imdb_id}, using file date instead")
|
||||||
|
return False
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
_log("WARNING", f"Error comparing dates for {imdb_id}: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
def _parse_date_to_iso(self, date_str: str) -> Optional[str]:
|
def _parse_date_to_iso(self, date_str: str) -> Optional[str]:
|
||||||
"""Parse date string to ISO format"""
|
"""Parse date string to ISO format"""
|
||||||
if not date_str:
|
if not date_str:
|
||||||
@@ -1152,13 +1206,34 @@ async def debug_movie_priority_logic(imdb_id: str):
|
|||||||
|
|
||||||
# Show priority logic
|
# Show priority logic
|
||||||
if config.movie_priority == "import_then_digital":
|
if config.movie_priority == "import_then_digital":
|
||||||
result["priority_explanation"] = "1st: Radarr import history, 2nd: TMDB/OMDb digital release, 3rd: file mtime"
|
result["priority_explanation"] = "1st: Radarr import history, 2nd: TMDB/OMDb digital release, 3rd: file mtime. Note: If import is only file date, prefer reasonable digital dates."
|
||||||
if result["date_sources"].get("radarr_import"):
|
|
||||||
result["selected_date"] = result["date_sources"]["radarr_import"]["date"]
|
radarr_import = result["date_sources"].get("radarr_import")
|
||||||
result["selected_source"] = result["date_sources"]["radarr_import"]["source"]
|
digital_release = result["date_sources"].get("digital_release")
|
||||||
elif result["date_sources"].get("digital_release"):
|
|
||||||
result["selected_date"] = result["date_sources"]["digital_release"]["date"]
|
# Check for file date fallback logic
|
||||||
result["selected_source"] = result["date_sources"]["digital_release"]["source"]
|
if radarr_import and radarr_import["source"] == "radarr:db.file.dateAdded" and digital_release:
|
||||||
|
# Test the smart logic
|
||||||
|
would_prefer_digital = movie_processor._should_prefer_digital_over_file_date(
|
||||||
|
digital_release["date"],
|
||||||
|
None, # We don't have theatrical date in this debug context
|
||||||
|
imdb_id
|
||||||
|
)
|
||||||
|
result["file_date_detected"] = True
|
||||||
|
result["would_prefer_digital"] = would_prefer_digital
|
||||||
|
|
||||||
|
if would_prefer_digital:
|
||||||
|
result["selected_date"] = digital_release["date"]
|
||||||
|
result["selected_source"] = digital_release["source"] + " (preferred over file date)"
|
||||||
|
else:
|
||||||
|
result["selected_date"] = radarr_import["date"]
|
||||||
|
result["selected_source"] = radarr_import["source"] + " (digital too old)"
|
||||||
|
elif radarr_import and radarr_import["source"] != "radarr:db.file.dateAdded":
|
||||||
|
result["selected_date"] = radarr_import["date"]
|
||||||
|
result["selected_source"] = radarr_import["source"]
|
||||||
|
elif digital_release:
|
||||||
|
result["selected_date"] = digital_release["date"]
|
||||||
|
result["selected_source"] = digital_release["source"]
|
||||||
else: # digital_then_import
|
else: # digital_then_import
|
||||||
result["priority_explanation"] = "1st: TMDB/OMDb digital release, 2nd: Radarr import history, 3rd: file mtime"
|
result["priority_explanation"] = "1st: TMDB/OMDb digital release, 2nd: Radarr import history, 3rd: file mtime"
|
||||||
if result["date_sources"].get("digital_release"):
|
if result["date_sources"].get("digital_release"):
|
||||||
|
|||||||
Reference in New Issue
Block a user