diff --git a/VERSION b/VERSION index 6a66477..9e69e54 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.0.2-clean +2.0.3-external-fallback diff --git a/processors/tv_series_processor.py b/processors/tv_series_processor.py index 6488e06..a068244 100644 --- a/processors/tv_series_processor.py +++ b/processors/tv_series_processor.py @@ -13,6 +13,7 @@ from core.episode_nfo_manager import EpisodeNFOManager from core.nfo_manager import NFOManager from core.path_mapper import PathMapper from clients.sonarr_client import SonarrClient +from clients.external_clients import ExternalClientManager from core.logging import _log, convert_utc_to_local @@ -26,6 +27,7 @@ class TVSeriesProcessor: self.episode_nfo_manager = EpisodeNFOManager() self.path_mapper = path_mapper self.sonarr = sonarr_client + self.external_manager = ExternalClientManager() def process_series_manual_scan(self, series_path: Path) -> bool: """Process a TV series during manual scan""" @@ -228,6 +230,13 @@ class TVSeriesProcessor: except Exception as e: _log("ERROR", f"Sonarr API error for S{season_num:02d}E{episode_num:02d}: {e}") + # Try external APIs for episode airdate + _log("INFO", f"Trying external APIs for episode S{season_num:02d}E{episode_num:02d} airdate") + aired, source = self._get_episode_airdate_from_external_apis(imdb_id, season_num, episode_num) + if aired: + dateadded = convert_utc_to_local(aired) + return aired, dateadded, source + _log("ERROR", f"Could not get any date information for S{season_num:02d}E{episode_num:02d}") return aired, dateadded, source @@ -251,6 +260,62 @@ class TVSeriesProcessor: return None, None + def _get_episode_airdate_from_external_apis(self, imdb_id: str, season_num: int, episode_num: int) -> Tuple[Optional[str], str]: + """Get episode airdate from external APIs (TMDB, OMDb) as fallback""" + # Try TMDB first + if self.external_manager.tmdb.enabled: + try: + _log("DEBUG", f"Trying TMDB for episode S{season_num:02d}E{episode_num:02d} airdate") + + # First convert IMDb to TMDB TV ID + tv_search = self.external_manager.tmdb._get(f"/find/{imdb_id}", {"external_source": "imdb_id"}) + if tv_search and tv_search.get("tv_results"): + tv_id = tv_search["tv_results"][0].get("id") + if tv_id: + _log("DEBUG", f"Found TMDB TV ID {tv_id} for {imdb_id}") + + # Get episode details + episode_data = self.external_manager.tmdb._get(f"/tv/{tv_id}/season/{season_num}/episode/{episode_num}") + if episode_data and episode_data.get("air_date"): + airdate = episode_data["air_date"] + # Convert to ISO format with UTC timezone + iso_airdate = f"{airdate}T00:00:00Z" + _log("INFO", f"Found TMDB airdate for S{season_num:02d}E{episode_num:02d}: {iso_airdate}") + return iso_airdate, "tmdb:episode.air_date" + except Exception as e: + _log("WARNING", f"TMDB episode lookup failed for S{season_num:02d}E{episode_num:02d}: {e}") + + # Try OMDb as fallback + if self.external_manager.omdb.enabled: + try: + _log("DEBUG", f"Trying OMDb for episode S{season_num:02d}E{episode_num:02d} airdate") + episode_dates = self.external_manager.omdb.get_tv_season_episodes(imdb_id, season_num) + if episode_num in episode_dates: + airdate = episode_dates[episode_num] + # Convert to ISO format + from datetime import datetime, timezone + try: + # Try to parse OMDb date format (usually DD MMM YYYY) + dt = datetime.strptime(airdate, "%d %b %Y").replace(tzinfo=timezone.utc) + iso_airdate = dt.isoformat(timespec="seconds") + _log("INFO", f"Found OMDb airdate for S{season_num:02d}E{episode_num:02d}: {iso_airdate}") + return iso_airdate, "omdb:episode.released" + except ValueError: + # Try other common formats + for fmt in ["%Y-%m-%d", "%d %B %Y"]: + try: + dt = datetime.strptime(airdate, fmt).replace(tzinfo=timezone.utc) + iso_airdate = dt.isoformat(timespec="seconds") + _log("INFO", f"Found OMDb airdate for S{season_num:02d}E{episode_num:02d}: {iso_airdate}") + return iso_airdate, "omdb:episode.released" + except ValueError: + continue + except Exception as e: + _log("WARNING", f"OMDb episode lookup failed for S{season_num:02d}E{episode_num:02d}: {e}") + + _log("WARNING", f"No external API airdate found for S{season_num:02d}E{episode_num:02d}") + return None, "no_external_data" + def _create_series_nfos(self, series_path: Path, imdb_id: str): """Create tvshow.nfo and season.nfo files""" # Create tvshow.nfo