feat: intial commits

This commit is contained in:
2025-09-07 11:15:19 -04:00
committed by sbcrumb
parent 51b263ffe7
commit 5782b3970d
3 changed files with 166 additions and 47 deletions
+99 -17
View File
@@ -312,8 +312,11 @@ class RadarrClient:
"sortKey": "date", "sortDirection": "ascending"
})
if not data:
_log("DEBUG", f"No history data returned for movie_id {movie_id}")
return None
items = data if isinstance(data, list) else data.get("records") or []
_log("DEBUG", f"Found {len(items)} history items for movie_id {movie_id}")
first_grab = None
for it in items:
ev = (it.get("eventType") or it.get("type") or "").lower()
@@ -323,13 +326,27 @@ class RadarrClient:
date_iso = datetime.fromisoformat(it["date"].replace("Z", "+00:00")).astimezone(timezone.utc).isoformat(timespec="seconds")
except Exception:
date_iso = None
if ev in ("downloadfolderimported", "moviefileimported"):
_log("DEBUG", f"History event: type='{ev}', date={date_iso}, data={it.get('data', {})}")
# Expanded event types for Radarr v4/v5 compatibility
import_events = {
"downloadfolderimported", "moviefileimported", "imported",
"downloadimported", "fileimported", "movieimported"
}
if ev in import_events:
if date_iso:
_log("INFO", f"Found import event '{ev}' at {date_iso} for movie_id {movie_id}")
return date_iso
if ev == "grabbed" and date_iso and first_grab is None:
first_grab = date_iso
if first_grab:
_log("INFO", f"Using grab date {first_grab} as fallback for movie_id {movie_id}")
return first_grab
_log("WARNING", f"No usable import/grab events found for movie_id {movie_id}")
return None
# ---------------------------
@@ -523,7 +540,11 @@ def upsert_movie_nfo(
_ensure_child_text(root, "year", ymd.split("-")[0], overwrite=False)
if dateadded:
_ensure_child_text(root, "dateadded", dateadded, overwrite=True)
if dateadded == "MANUAL_REVIEW_NEEDED":
_ensure_child_text(root, "dateadded", "MANUAL_REVIEW_NEEDED", overwrite=True)
_log("WARNING", f"Set MANUAL_REVIEW_NEEDED marker in NFO: {movie_dir}/movie.nfo")
else:
_ensure_child_text(root, "dateadded", dateadded, overwrite=True)
if lock_metadata:
_ensure_child_text(root, "lockdata", "true", overwrite=True)
@@ -552,9 +573,16 @@ def _parse_date_to_iso(d: str) -> Optional[str]:
try:
if re.fullmatch(r"\d{4}-\d{2}-\d{2}", d):
dt = datetime.fromisoformat(d).replace(tzinfo=timezone.utc)
return dt.isoformat(timespec="seconds")
dt = datetime.fromisoformat(d.replace("Z", "+00:00"))
return dt.astimezone(timezone.utc).isoformat(timespec="seconds")
else:
dt = datetime.fromisoformat(d.replace("Z", "+00:00")).astimezone(timezone.utc)
# Reject dates that are too recent (prevent bogus timestamps)
now = datetime.now(timezone.utc)
if (now - dt).days < 1:
_log("WARNING", f"Rejecting recent timestamp (less than 1 day old): {d} -> {dt.isoformat()}")
return None
return dt.isoformat(timespec="seconds")
except Exception:
return None
@@ -587,21 +615,32 @@ def _strategy_movie_date_from_movie_obj(movie_obj: Dict[str, Any], which: str) -
def _collect_digital_candidates(imdb_id: str, movie_obj: Optional[Dict[str, Any]]) -> List[Tuple[str, str]]:
cands: List[Tuple[str, str]] = []
_log("DEBUG", f"Collecting digital dates for {imdb_id}")
if movie_obj:
iso = _strategy_movie_date_from_movie_obj(movie_obj, "digital")
if iso:
cands.append((iso, "radarr:digitalRelease"))
_log("DEBUG", f"Found Radarr digital date: {iso}")
iso = tmdb_digital_date_from_imdb(imdb_id)
if iso:
cands.append((iso, "tmdb:digital"))
_log("DEBUG", f"Found TMDB digital date: {iso}")
tmdb_id = _tmdb_id_from_imdb(imdb_id)
if tmdb_id:
iso = jellyseerr_digital_date_from_tmdb(tmdb_id)
if iso:
cands.append((iso, "jellyseerr:digital"))
_log("DEBUG", f"Found Jellyseerr digital date: {iso}")
iso = omdb_dvd_date(imdb_id)
if iso:
cands.append((iso, "omdb:dvd"))
_log("DEBUG", f"Found OMDb DVD date: {iso}")
_log("INFO", f"Found {len(cands)} digital date candidates: {[f'{src}={date}' for date, src in cands]}")
return cands
def _pick_earliest(cands: List[Tuple[str, str]]) -> Optional[Tuple[str, str]]:
@@ -654,15 +693,24 @@ def _decide_movie_date(imdb_id: str, movie_dir: Path, poll_external: bool) -> Tu
def from_import_history() -> Tuple[Optional[str], str]:
ensure_movie()
if poll_external and movie_id:
iso = _radarr_earliest_import_history_iso(movie_id)
if iso:
return iso, "radarr:history.import"
if poll_external and movie_id:
iso2 = _radarr_earliest_file_dateadded_iso(movie_id)
if iso2:
return iso2, "radarr:moviefile.dateAdded"
return None, "radarr:history"
if not (poll_external and movie_id):
_log("DEBUG", f"Skipping import history: poll_external={poll_external}, movie_id={movie_id}")
return None, "radarr:history.skipped"
# Try import history events first (most reliable)
iso = _radarr_earliest_import_history_iso(movie_id)
if iso:
_log("INFO", f"Using Radarr import history date: {iso}")
return iso, "radarr:history.import"
# Fallback to moviefile dateAdded
iso2 = _radarr_earliest_file_dateadded_iso(movie_id)
if iso2:
_log("INFO", f"Using Radarr moviefile dateAdded: {iso2}")
return iso2, "radarr:moviefile.dateAdded"
_log("WARNING", f"No import dates found for movie_id {movie_id}")
return None, "radarr:history.none"
def from_radarr_field(which: str) -> Tuple[Optional[str], str]:
ensure_movie()
@@ -715,33 +763,67 @@ def _decide_movie_date(imdb_id: str, movie_dir: Path, poll_external: bool) -> Tu
return digi_iso, "radarr:digitalRelease"
# Priority flows
_log("INFO", f"Date selection for {imdb_id}: priority={config.movie_priority}, poll_external={poll_external}")
if config.movie_priority == "import_then_digital":
_log("DEBUG", "Trying import history first (import_then_digital mode)")
imp_iso, imp_src = from_import_history()
if imp_iso:
_log("INFO", f"Selected import date: {imp_iso} ({imp_src})")
return imp_iso, imp_src, released_iso
_log("DEBUG", "No import date found, trying digital release dates")
digi_iso, digi_src = from_external_digital_bundle()
digi_iso, digi_src = maybe_sanitize_digital(digi_iso)
if digi_iso:
_log("INFO", f"Selected digital date: {digi_iso} ({digi_src})")
return digi_iso, digi_src, released_iso
_log("DEBUG", "No digital date found, trying cinema release")
cine_iso, cine_src = from_radarr_field("cinema")
if cine_iso:
_log("INFO", f"Selected cinema date: {cine_iso} ({cine_src})")
return cine_iso, cine_src, released_iso or cine_iso
_log("DEBUG", "No external dates found, falling back to file mtime")
mtime_iso, mtime_src = from_file_mtime()
return mtime_iso, mtime_src, released_iso
if mtime_iso:
_log("WARNING", f"Using file mtime as last resort: {mtime_iso} ({mtime_src})")
return mtime_iso, mtime_src, released_iso
# No valid date found - create manual review marker
_log("ERROR", f"No valid dateadded found for {imdb_id} - marking for MANUAL_REVIEW_NEEDED")
return "MANUAL_REVIEW_NEEDED", "manual_review_required", released_iso
# digital_then_import
_log("DEBUG", "Trying digital release dates first (digital_then_import mode)")
digi_iso, digi_src = from_external_digital_bundle()
digi_iso, digi_src = maybe_sanitize_digital(digi_iso)
if digi_iso:
_log("INFO", f"Selected digital date: {digi_iso} ({digi_src})")
return digi_iso, digi_src, released_iso
_log("DEBUG", "No digital date found, trying import history")
imp_iso, imp_src = from_import_history()
if imp_iso:
_log("INFO", f"Selected import date: {imp_iso} ({imp_src})")
return imp_iso, imp_src, released_iso
_log("DEBUG", "No import date found, trying cinema release")
cine_iso, cine_src = from_radarr_field("cinema")
if cine_iso:
_log("INFO", f"Selected cinema date: {cine_iso} ({cine_src})")
return cine_iso, cine_src, released_iso or cine_iso
_log("DEBUG", "No external dates found, falling back to file mtime")
mtime_iso, mtime_src = from_file_mtime()
return mtime_iso, mtime_src, released_iso
if mtime_iso:
_log("WARNING", f"Using file mtime as last resort: {mtime_iso} ({mtime_src})")
return mtime_iso, mtime_src, released_iso
# No valid date found - create manual review marker
_log("ERROR", f"No valid dateadded found for {imdb_id} - marking for MANUAL_REVIEW_NEEDED")
return "MANUAL_REVIEW_NEEDED", "manual_review_required", released_iso
# ---------------------------
# Movie apply
@@ -854,7 +936,7 @@ def apply_movie(movie_dir: Path, conn: sqlite3.Connection):
brand=config.manager_brand,
)
if config.fix_dir_mtimes and dateadded_iso:
if config.fix_dir_mtimes and dateadded_iso and dateadded_iso != "MANUAL_REVIEW_NEEDED":
set_movie_file_mtime(movie_dir, dateadded_iso)
conn.commit()