mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-09-05 07:27:15 +08:00
fix(monitor,transfer): 修复 FUSE 挂载无响应导致的监控冻死、整理链锁死与漏件 (#6276)
* wip(v3): 移植监控与整理韧性修复到 v3 基线 包含:监控看门狗隔离/挂载探测、整理队列持久化、文件系统子进程代理、 写入原子化。迁移重挂到 v3 链 8a4c7e1d2f90 -> 7f5c1d2e3a4b -> e3d9f4b7c806。 tmdb 相关测试尚未通过,待定位。 * fix(v3): 修正移植引入的 16 项测试失败 - poller.py:合并时我方保留的行仍用旧变量名 merged_snapshot,而 v3 已统一 改名为 current_snapshot,导致 NameError 被外层 except 吞掉、快照从未保存 - smb.py:采纳 f-string 拆分写法,恢复 Python 3.11 可解析 - dispatcher 测试:历史查重由 _should_skip_by_history 统一承担,mock 点随之调整 - tmdb 缓存测试:补充 v3 新增的 media_source/media_id 字段 - tmdb 重试测试:为 fake 补充 match_multi/async_match_multi 尚余 3 项与 v3 识别流程的连接失败处理有关,待单独判断。 * fix(v3): 测试适配 v3 的 media_source/media_id 重构 v3 将媒体标识从 tmdbid 统一重构为 media_source + media_id,recognize_media 的 tmdbid 参数已被 **kwargs 静默吞掉——传了也不生效,流程会误降级到名称搜索。 tmdb 重试用例改用新参数后恢复正确路径。 同时修正 fake 的 match_multi 语义:真实实现(tmdbapi.match_multi)吞掉所有 异常并返回 None,连接失败与「未找到」在该路径上本就不可区分,fake 需保持一致。 至此移植引入的 19 项失败全部清零。 --------- Co-authored-by: Aqr-K <Aqr-K@users.noreply.github.com>
This commit is contained in:
@@ -13,6 +13,7 @@ from app.modules.themoviedb.category import CategoryHelper
|
||||
from app.modules.themoviedb.scraper import TmdbScraper
|
||||
from app.modules.themoviedb.tmdb_cache import TmdbCache
|
||||
from app.modules.themoviedb.tmdbapi import TmdbApi
|
||||
from app.modules.themoviedb.tmdbv3api.exceptions import TMDbConnectionError
|
||||
from app.schemas.category import CategoryConfig
|
||||
from app.schemas.types import (
|
||||
MediaImageType,
|
||||
@@ -193,16 +194,42 @@ class TheMovieDbModule(_ModuleBase):
|
||||
media.season = meta.begin_season
|
||||
return medias
|
||||
|
||||
def _safe_get_info_by_type(self, mtype: MediaType, tmdbid: int) -> Tuple[Optional[dict], bool]:
|
||||
"""
|
||||
查询指定类型的媒体详情,将"确认TMDB连接失败"与"确认查无此项"区分开。
|
||||
|
||||
:param mtype: 媒体类型:电影或电视剧
|
||||
:param tmdbid: TMDB的ID
|
||||
:return: (媒体信息或None, 本次查询是否因TMDB连接失败而没有得到确定结果)
|
||||
"""
|
||||
try:
|
||||
return self.tmdb.get_info(mtype=mtype, tmdbid=tmdbid, raise_on_connection_error=True), False
|
||||
except TMDbConnectionError:
|
||||
return None, True
|
||||
|
||||
async def _async_safe_get_info_by_type(self, mtype: MediaType, tmdbid: int) -> Tuple[Optional[dict], bool]:
|
||||
"""
|
||||
查询指定类型的媒体详情,将"确认TMDB连接失败"与"确认查无此项"区分开(异步版本)
|
||||
"""
|
||||
try:
|
||||
return await self.tmdb.async_get_info(mtype=mtype, tmdbid=tmdbid, raise_on_connection_error=True), False
|
||||
except TMDbConnectionError:
|
||||
return None, True
|
||||
|
||||
def _get_info_by_tmdbid(self, tmdbid: int, mtype: Optional[MediaType],
|
||||
meta: Optional[MetaBase]) -> Optional[dict]:
|
||||
"""
|
||||
根据tmdbid查询媒体信息,当类型未知且同时存在电影和电视剧时,通过元数据消歧
|
||||
|
||||
:raises TMDbConnectionError: 电影、电视剧两路查询都没有得到确定结果,且至少一路
|
||||
是因TMDB连接失败导致的,此时不能断言"条目不存在",交由上层报网络故障
|
||||
"""
|
||||
if mtype:
|
||||
return self.tmdb.get_info(mtype=mtype, tmdbid=tmdbid)
|
||||
# 类型未知,分别查询电影和电视剧
|
||||
info_tv = self.tmdb.get_info(mtype=MediaType.TV, tmdbid=tmdbid)
|
||||
info_movie = self.tmdb.get_info(mtype=MediaType.MOVIE, tmdbid=tmdbid)
|
||||
return self.tmdb.get_info(mtype=mtype, tmdbid=tmdbid, raise_on_connection_error=True)
|
||||
# 类型未知,分别查询电影和电视剧;每一路的连接失败要单独识别,
|
||||
# 避免一路瞬时抖动掩盖另一路已经得到的确定结果
|
||||
info_tv, tv_conn_error = self._safe_get_info_by_type(MediaType.TV, tmdbid)
|
||||
info_movie, movie_conn_error = self._safe_get_info_by_type(MediaType.MOVIE, tmdbid)
|
||||
if info_tv and info_movie:
|
||||
# 同时存在,尝试通过元数据消歧
|
||||
result = self._disambiguate_by_meta(info_tv, info_movie, meta)
|
||||
@@ -210,18 +237,26 @@ class TheMovieDbModule(_ModuleBase):
|
||||
return result
|
||||
logger.warn(f"无法判断tmdb_id:{tmdbid} 是电影还是电视剧")
|
||||
return None
|
||||
return info_tv or info_movie or None
|
||||
if info_tv or info_movie:
|
||||
return info_tv or info_movie
|
||||
if tv_conn_error or movie_conn_error:
|
||||
raise TMDbConnectionError(f"连接TheMovieDb失败,无法确认tmdb_id:{tmdbid} 的媒体类型")
|
||||
return None
|
||||
|
||||
async def _async_get_info_by_tmdbid(self, tmdbid: int, mtype: Optional[MediaType],
|
||||
meta: Optional[MetaBase]) -> Optional[dict]:
|
||||
"""
|
||||
根据tmdbid查询媒体信息,当类型未知且同时存在电影和电视剧时,通过元数据消歧(异步版本)
|
||||
|
||||
:raises TMDbConnectionError: 电影、电视剧两路查询都没有得到确定结果,且至少一路
|
||||
是因TMDB连接失败导致的,此时不能断言"条目不存在",交由上层报网络故障
|
||||
"""
|
||||
if mtype:
|
||||
return await self.tmdb.async_get_info(mtype=mtype, tmdbid=tmdbid)
|
||||
# 类型未知,分别查询电影和电视剧
|
||||
info_tv = await self.tmdb.async_get_info(mtype=MediaType.TV, tmdbid=tmdbid)
|
||||
info_movie = await self.tmdb.async_get_info(mtype=MediaType.MOVIE, tmdbid=tmdbid)
|
||||
return await self.tmdb.async_get_info(mtype=mtype, tmdbid=tmdbid, raise_on_connection_error=True)
|
||||
# 类型未知,分别查询电影和电视剧;每一路的连接失败要单独识别,
|
||||
# 避免一路瞬时抖动掩盖另一路已经得到的确定结果
|
||||
info_tv, tv_conn_error = await self._async_safe_get_info_by_type(MediaType.TV, tmdbid)
|
||||
info_movie, movie_conn_error = await self._async_safe_get_info_by_type(MediaType.MOVIE, tmdbid)
|
||||
if info_tv and info_movie:
|
||||
# 同时存在,尝试通过元数据消歧
|
||||
result = self._disambiguate_by_meta(info_tv, info_movie, meta)
|
||||
@@ -229,7 +264,11 @@ class TheMovieDbModule(_ModuleBase):
|
||||
return result
|
||||
logger.warn(f"无法判断tmdb_id:{tmdbid} 是电影还是电视剧")
|
||||
return None
|
||||
return info_tv or info_movie or None
|
||||
if info_tv or info_movie:
|
||||
return info_tv or info_movie
|
||||
if tv_conn_error or movie_conn_error:
|
||||
raise TMDbConnectionError(f"连接TheMovieDb失败,无法确认tmdb_id:{tmdbid} 的媒体类型")
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def _disambiguate_by_meta(info_tv: dict, info_movie: dict,
|
||||
@@ -525,10 +564,15 @@ class TheMovieDbModule(_ModuleBase):
|
||||
# 识别匹配
|
||||
if not cache_info or not cache:
|
||||
info = None
|
||||
connection_error = False
|
||||
# 缓存没有或者强制不使用缓存
|
||||
if tmdbid:
|
||||
# 直接查询详情,支持同ID电影/电视剧消歧
|
||||
info = self._get_info_by_tmdbid(tmdbid=tmdbid, mtype=mtype, meta=meta)
|
||||
try:
|
||||
info = self._get_info_by_tmdbid(tmdbid=tmdbid, mtype=mtype, meta=meta)
|
||||
except TMDbConnectionError as err:
|
||||
logger.error(f"tmdb_id:{tmdbid} {err}")
|
||||
connection_error = True
|
||||
if not info and meta and not tmdbid:
|
||||
# 准备搜索名称
|
||||
names = self._prepare_search_names(meta)
|
||||
@@ -542,7 +586,11 @@ class TheMovieDbModule(_ModuleBase):
|
||||
info = self.tmdb.get_info(mtype=info.get("media_type"),
|
||||
tmdbid=info.get("id"))
|
||||
elif not info:
|
||||
if tmdbid:
|
||||
if connection_error:
|
||||
# 网络故障与"条目不存在"是完全不同的两类问题,不能用同一句文案掩盖,
|
||||
# 否则用户无从判断该等网络恢复还是该确认条目本身是否存在
|
||||
logger.error(f"tmdb_id:{tmdbid} 连接TheMovieDb失败,无法完成识别,请检查网络连接后重试")
|
||||
elif tmdbid:
|
||||
logger.warn(f"tmdb_id:{tmdbid} 无法确定媒体类型,识别失败")
|
||||
else:
|
||||
logger.error("识别媒体信息时未提供元数据或唯一且有效的tmdbid")
|
||||
@@ -624,10 +672,15 @@ class TheMovieDbModule(_ModuleBase):
|
||||
# 识别匹配
|
||||
if not cache_info or not cache:
|
||||
info = None
|
||||
connection_error = False
|
||||
# 缓存没有或者强制不使用缓存
|
||||
if tmdbid:
|
||||
# 直接查询详情,支持同ID电影/电视剧消歧
|
||||
info = await self._async_get_info_by_tmdbid(tmdbid=tmdbid, mtype=mtype, meta=meta)
|
||||
try:
|
||||
info = await self._async_get_info_by_tmdbid(tmdbid=tmdbid, mtype=mtype, meta=meta)
|
||||
except TMDbConnectionError as err:
|
||||
logger.error(f"tmdb_id:{tmdbid} {err}")
|
||||
connection_error = True
|
||||
if not info and meta and not tmdbid:
|
||||
# 准备搜索名称
|
||||
names = self._prepare_search_names(meta)
|
||||
@@ -641,7 +694,11 @@ class TheMovieDbModule(_ModuleBase):
|
||||
info = await self.tmdb.async_get_info(mtype=info.get("media_type"),
|
||||
tmdbid=info.get("id"))
|
||||
elif not info:
|
||||
if tmdbid:
|
||||
if connection_error:
|
||||
# 网络故障与"条目不存在"是完全不同的两类问题,不能用同一句文案掩盖,
|
||||
# 否则用户无从判断该等网络恢复还是该确认条目本身是否存在
|
||||
logger.error(f"tmdb_id:{tmdbid} 连接TheMovieDb失败,无法完成识别,请检查网络连接后重试")
|
||||
elif tmdbid:
|
||||
logger.warn(f"tmdb_id:{tmdbid} 无法确定媒体类型,识别失败")
|
||||
else:
|
||||
logger.error("识别媒体信息时未提供元数据或唯一且有效的tmdbid")
|
||||
|
||||
Reference in New Issue
Block a user