diff --git a/VERSION b/VERSION index b16e3275..cc5581d1 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.20.0-develop51 +1.20.0-develop52 diff --git a/modules/cache.py b/modules/cache.py index 3d80ac7d..c95a76ff 100644 --- a/modules/cache.py +++ b/modules/cache.py @@ -214,6 +214,22 @@ class Cache: seasons TEXT, expiration_date TEXT)""" ) + cursor.execute( + """CREATE TABLE IF NOT EXISTS tmdb_episode_data ( + key INTEGER PRIMARY KEY, + tmdb_id INTEGER UNIQUE, + title TEXT, + air_date TEXT, + overview TEXT, + episode_number INTEGER, + season_number INTEGER, + still_url TEXT, + vote_count INTEGER, + vote_average REAL, + imdb_id TEXT, + tvdb_id INTEGER, + expiration_date TEXT)""" + ) cursor.execute( """CREATE TABLE IF NOT EXISTS tvdb_data3 ( key INTEGER PRIMARY KEY, @@ -718,6 +734,49 @@ class Cache: expiration_date.strftime("%Y-%m-%d"), obj.tmdb_id )) + def query_tmdb_episode(self, tmdb_id, season_number, episode_number, expiration): + tmdb_dict = {} + expired = None + with sqlite3.connect(self.cache_path) as connection: + connection.row_factory = sqlite3.Row + with closing(connection.cursor()) as cursor: + cursor.execute( + "SELECT * FROM tmdb_episode_data WHERE tmdb_id = ? AND season_number = ? AND episode_number = ?", + (tmdb_id, season_number, episode_number) + ) + row = cursor.fetchone() + if row: + tmdb_dict["title"] = row["title"] if row["title"] else "" + tmdb_dict["air_date"] = datetime.strptime(row["air_date"], "%Y-%m-%d") if row["air_date"] else None + tmdb_dict["overview"] = row["overview"] if row["overview"] else "" + tmdb_dict["still_url"] = row["still_url"] if row["still_url"] else "" + tmdb_dict["vote_count"] = row["vote_count"] if row["vote_count"] else 0 + tmdb_dict["vote_average"] = row["vote_average"] if row["vote_average"] else 0 + tmdb_dict["imdb_id"] = row["imdb_id"] if row["imdb_id"] else "" + tmdb_dict["tvdb_id"] = row["tvdb_id"] if row["tvdb_id"] else None + datetime_object = datetime.strptime(row["expiration_date"], "%Y-%m-%d") + time_between_insertion = datetime.now() - datetime_object + expired = time_between_insertion.days > expiration + return tmdb_dict, expired + + def update_tmdb_episode(self, expired, obj, expiration): + expiration_date = datetime.now() if expired is True else (datetime.now() - timedelta(days=random.randint(1, expiration))) + with sqlite3.connect(self.cache_path) as connection: + connection.row_factory = sqlite3.Row + with closing(connection.cursor()) as cursor: + cursor.execute( + "INSERT OR IGNORE INTO tmdb_episode_data(tmdb_id, season_number, episode_number) VALUES(?)", + (obj.tmdb_id, obj.season_number, obj.episode_number) + ) + update_sql = "UPDATE tmdb_episode_data SET title = ?, air_date = ?, overview = ?, still_url = ?, " \ + "vote_count = ?, vote_average = ?, imdb_id = ?, tvdb_id = ?, " \ + "expiration_date = ? WHERE tmdb_id = ? AND season_number = ? AND episode_number = ?" + cursor.execute(update_sql, ( + obj.title, obj.air_date.strftime("%Y-%m-%d"), obj.overview, obj.still_url, + obj.vote_count, obj.vote_average, obj.imdb_id, obj.tvdb_id, + expiration_date.strftime("%Y-%m-%d"), obj.tmdb_id, obj.season_number, obj.episode_number + )) + def query_tvdb(self, tvdb_id, is_movie, expiration): tvdb_dict = {} expired = None diff --git a/modules/tmdb.py b/modules/tmdb.py index 2c03b15f..c3178da0 100644 --- a/modules/tmdb.py +++ b/modules/tmdb.py @@ -175,6 +175,43 @@ class TMDbShow(TMDBObj): logger.stacktrace() raise TMDbException(f"TMDb Error: Unexpected Error with TMDb ID: {self.tmdb_id}: {e}") +class TMDbEpisode: + def __init__(self, tmdb, tmdb_id, season_number, episode_number, ignore_cache=False): + self._tmdb = tmdb + self.tmdb_id = tmdb_id + self.season_number = season_number + self.episode_number = episode_number + self.ignore_cache = ignore_cache + expired = None + data = None + if self._tmdb.config.Cache and not ignore_cache: + data, expired = self._tmdb.config.Cache.query_tmdb_episode(self.tmdb_id, self.season_number, self.episode_number, self._tmdb.expiration) + if expired or not data: + data = self.load_episode() + + self.title = data["title"] if isinstance(data, dict) else data.title + self.air_date = data["air_date"] if isinstance(data, dict) else data.air_date + self.overview = data["overview"] if isinstance(data, dict) else data.overview + self.still_url = data["still_url"] if isinstance(data, dict) else data.still_url + self.vote_count = data["vote_count"] if isinstance(data, dict) else data.vote_count + self.vote_average = data["vote_average"] if isinstance(data, dict) else data.vote_average + self.imdb_id = data["imdb_id"] if isinstance(data, dict) else data.imdb_id + self.tvdb_id = data["tvdb_id"] if isinstance(data, dict) else data.tvdb_id + + if self._tmdb.config.Cache and not ignore_cache: + self._tmdb.config.Cache.update_tmdb_episode(expired, self, self._tmdb.expiration) + + @retry(stop_max_attempt_number=6, wait_fixed=10000, retry_on_exception=util.retry_if_not_failed) + def load_episode(self): + try: + return self._tmdb.TMDb.tv_episode(self.tmdb_id, self.season_number, self.episode_number) + except NotFound as e: + raise Failed(f"TMDb Error: No Episode found for TMDb ID {self.tmdb_id} Season {self.season_number} Episode {self.episode_number}: {e}") + except TMDbException as e: + logger.stacktrace() + raise TMDbException(f"TMDb Error: Unexpected Error with TMDb ID: {self.tmdb_id}: {e}") + + class TMDb: def __init__(self, config, params): self.config = config @@ -240,10 +277,8 @@ class TMDb: try: return self.TMDb.tv_season(tmdb_id, season_number, partial=partial) except NotFound as e: raise Failed(f"TMDb Error: No Season found for TMDb ID {tmdb_id} Season {season_number}: {e}") - @retry(stop_max_attempt_number=6, wait_fixed=10000, retry_on_exception=util.retry_if_not_failed) - def get_episode(self, tmdb_id, season_number, episode_number, partial=None): - try: return self.TMDb.tv_episode(tmdb_id, season_number, episode_number, partial=partial) - except NotFound as e: raise Failed(f"TMDb Error: No Episode found for TMDb ID {tmdb_id} Season {season_number} Episode {episode_number}: {e}") + def get_episode(self, tmdb_id, season_number, episode_number, ignore_cache=False): + return TMDbEpisode(self, tmdb_id, season_number, episode_number, ignore_cache=ignore_cache) @retry(stop_max_attempt_number=6, wait_fixed=10000, retry_on_exception=util.retry_if_not_failed) def get_collection(self, tmdb_id, partial=None): diff --git a/requirements.txt b/requirements.txt index 898cbf1c..11e2c0cc 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,10 +1,10 @@ arrapi==1.4.7 -GitPython==3.1.42 -lxml==5.1.0 +GitPython==3.1.43 +lxml==5.2.0 num2words==0.5.13 pathvalidate==3.2.0 -pillow==10.2.0 -PlexAPI==4.15.10 +pillow==10.3.0 +PlexAPI==4.15.11 psutil==5.9.8 python-dotenv==1.0.1 python-dateutil==2.9.0.post0