From bedab9ab923a40871bf76348157b6602ceea5ba3 Mon Sep 17 00:00:00 2001 From: InfinityPacer Date: Sat, 9 May 2026 01:46:29 +0800 Subject: [PATCH] fix(mediaserver): fallback stale tv item ids --- app/modules/emby/emby.py | 12 +++++++ app/modules/jellyfin/jellyfin.py | 12 +++++++ app/modules/plex/plex.py | 50 ++++++++++++++++++++-------- app/modules/trimemedia/trimemedia.py | 8 +++++ app/modules/ugreen/ugreen.py | 20 +++++++++++ 5 files changed, 89 insertions(+), 13 deletions(-) diff --git a/app/modules/emby/emby.py b/app/modules/emby/emby.py index cbde1671..06ee68e4 100644 --- a/app/modules/emby/emby.py +++ b/app/modules/emby/emby.py @@ -407,6 +407,7 @@ class Emby: """ if not self._host or not self._apikey: return None, None + cached_item_id = item_id # 电视剧 if not item_id: item_id = self.__get_emby_series_id_by_name(title, year) @@ -416,6 +417,17 @@ class Emby: return None, {} # 验证tmdbid是否相同 item_info = self.get_iteminfo(item_id) + if not item_info and cached_item_id and title: + # 媒体删除后重新入库会导致缓存ID失效,回退到标题搜索避免误判整部剧缺失。 + logger.warning(f"Emby缓存的电视剧媒体ID {cached_item_id} 已失效,尝试按标题重新搜索:{title}") + item_id = self.__get_emby_series_id_by_name(title, year) + if item_id is None: + return None, None + if not item_id: + return None, {} + item_info = self.get_iteminfo(item_id) + if not item_info: + return None, {} if item_info: if tmdb_id and item_info.tmdbid: if str(tmdb_id) != str(item_info.tmdbid): diff --git a/app/modules/jellyfin/jellyfin.py b/app/modules/jellyfin/jellyfin.py index 7c99307c..3b9b4979 100644 --- a/app/modules/jellyfin/jellyfin.py +++ b/app/modules/jellyfin/jellyfin.py @@ -429,6 +429,7 @@ class Jellyfin: """ if not self._host or not self._apikey or not self.user: return None, None + cached_item_id = item_id # 查TVID if not item_id: item_id = self.__get_jellyfin_series_id_by_name(title, year) @@ -438,6 +439,17 @@ class Jellyfin: return None, {} # 验证tmdbid是否相同 item_info = self.get_iteminfo(item_id) + if not item_info and cached_item_id and title: + # 媒体删除后重新入库会导致缓存ID失效,回退到标题搜索避免误判整部剧缺失。 + logger.warning(f"Jellyfin缓存的电视剧媒体ID {cached_item_id} 已失效,尝试按标题重新搜索:{title}") + item_id = self.__get_jellyfin_series_id_by_name(title, year) + if item_id is None: + return None, None + if not item_id: + return None, {} + item_info = self.get_iteminfo(item_id) + if not item_info: + return None, {} if item_info: if tmdb_id and item_info.tmdbid: if str(tmdb_id) != str(item_info.tmdbid): diff --git a/app/modules/plex/plex.py b/app/modules/plex/plex.py index db0b7bbe..943cac6b 100644 --- a/app/modules/plex/plex.py +++ b/app/modules/plex/plex.py @@ -3,6 +3,7 @@ from pathlib import Path from typing import List, Optional, Dict, Tuple, Generator, Any, Union from urllib.parse import quote_plus +from plexapi.exceptions import NotFound from plexapi.myplex import MyPlexAccount from plexapi.server import PlexServer from requests import Response, Session @@ -260,20 +261,18 @@ class Plex: if not self._plex: return None, {} if item_id: - videos = self.__fetch_item(item_id) + try: + videos = self.__fetch_item(item_id) + except NotFound: + # Plex删除并重新入库后metadata id会变化,缓存的旧item_id失效时回退到搜索路径。 + logger.warning(f"Plex缓存的电视剧媒体ID {item_id} 已失效,尝试按标题重新搜索:{title}") + videos = self.__search_show(title=title, + original_title=original_title, + year=year) else: - # 兼容年份为空的场景 - kwargs = {"year": year} if year else {} - # 根据标题和年份模糊搜索,该结果不够准确 - videos = self._plex.library.search(title=title, - libtype="show", - **kwargs) - if (not videos - and original_title - and str(original_title) != str(title)): - videos = self._plex.library.search(title=original_title, - libtype="show", - **kwargs) + videos = self.__search_show(title=title, + original_title=original_title, + year=year) if not videos: return None, {} @@ -293,6 +292,31 @@ class Plex: season_episodes[episode.seasonNumber].append(episode.index) return videos.key, season_episodes + def __search_show(self, + title: Optional[str] = None, + original_title: Optional[str] = None, + year: Optional[str] = None) -> Any: + """ + 按标题搜索Plex电视剧条目,供常规查询和缓存item_id失效后的回退查询复用。 + :param title: 标题 + :param original_title: 原产地标题 + :param year: 年份,可以为空,为空时不按年份过滤 + :return: Plex搜索返回的电视剧条目列表 + """ + # 兼容年份为空的场景 + kwargs = {"year": year} if year else {} + # 根据标题和年份模糊搜索,该结果不够准确,后续仍会通过tmdb_id校验。 + videos = self._plex.library.search(title=title, + libtype="show", + **kwargs) + if (not videos + and original_title + and str(original_title) != str(title)): + videos = self._plex.library.search(title=original_title, + libtype="show", + **kwargs) + return videos + def get_remote_image_by_id(self, item_id: str, image_type: str, diff --git a/app/modules/trimemedia/trimemedia.py b/app/modules/trimemedia/trimemedia.py index 1249958f..ae683cd5 100644 --- a/app/modules/trimemedia/trimemedia.py +++ b/app/modules/trimemedia/trimemedia.py @@ -307,12 +307,20 @@ class TrimeMedia: if not self.is_authenticated(): return None, None + cached_item_id = item_id if not item_id: item_id = self.__get_series_id_by_name(title, year) if item_id is None: return None, None item_info = self.get_iteminfo(item_id) + if not item_info and cached_item_id and title: + # 媒体删除后重新入库会导致缓存ID失效,回退到标题搜索避免误判整部剧缺失。 + logger.warning(f"飞牛影视缓存的电视剧媒体ID {cached_item_id} 已失效,尝试按标题重新搜索:{title}") + item_id = self.__get_series_id_by_name(title, year) + if item_id is None: + return None, None + item_info = self.get_iteminfo(item_id) if not item_info: return None, {} diff --git a/app/modules/ugreen/ugreen.py b/app/modules/ugreen/ugreen.py index 1eb8c0ce..7911915d 100644 --- a/app/modules/ugreen/ugreen.py +++ b/app/modules/ugreen/ugreen.py @@ -653,9 +653,19 @@ class Ugreen: tmdb_id: Optional[int] = None, season: Optional[int] = None, ) -> tuple[Optional[str], Optional[Dict[int, list]]]: + """ + 根据标题、年份、TMDB ID和季号查询绿联媒体库中的电视剧已入库集数。 + :param item_id: 绿联媒体库中的剧集ID,存在缓存ID时优先使用 + :param title: 标题 + :param year: 年份 + :param tmdb_id: TMDB ID + :param season: 季号 + :return: 命中的剧集ID及每季已入库集数 + """ if not self.is_authenticated() or not self._api: return None, None + cached_item_id = item_id if not item_id: if not title: return None, None @@ -669,6 +679,16 @@ class Ugreen: item_id = str(item_id) item_info = self.get_iteminfo(item_id) + if not item_info and cached_item_id and title: + # 媒体删除后重新入库会导致缓存ID失效,回退到标题搜索避免误判整部剧缺失。 + logger.warning(f"绿联缓存的电视剧媒体ID {cached_item_id} 已失效,尝试按标题重新搜索:{title}") + if not (tv_info := self.__search_tv_item(title, year, tmdb_id)): + return None, {} + found_item_id = tv_info.get("ug_video_info_id") + if found_item_id is None: + return None, {} + item_id = str(found_item_id) + item_info = self.get_iteminfo(item_id) if not item_info: return None, {} if tmdb_id and item_info.tmdbid and tmdb_id != item_info.tmdbid: