mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-09-07 00:16:57 +08:00
fix(transfer): 修复音乐整理因缺少 tmdb_id 字段崩溃
MusicInfo 不含 tmdb_id 等 60 个影视专用字段,整理链访问 task.mediainfo.tmdb_id 直接 AttributeError,导致无分类音乐自动 整理全部失败。 源头修复:为 domain 与 schemas 两层 MusicInfo 增加 __getattr__ 兜底,缺失字段返回 None,避免下游逐点安全访问;dunder 方法 除外以保持 copy/pickle 钩子探测正常。显式声明 recognize_cache_hit 字段保留 getattr(..., False) 默认值语义。
This commit is contained in:
@@ -2509,6 +2509,7 @@ class TransferChain(ChainBase, ConfigReloadMixin, metaclass=Singleton):
|
|||||||
task.target_storage = task.target_directory.library_storage
|
task.target_storage = task.target_directory.library_storage
|
||||||
|
|
||||||
if self._requires_automatic_category(task) and not task.mediainfo.category:
|
if self._requires_automatic_category(task) and not task.mediainfo.category:
|
||||||
|
# MusicInfo 无 tmdb_id 字段,但模型 __getattr__ 已兜底返回 None
|
||||||
if task.mediainfo.tmdb_id:
|
if task.mediainfo.tmdb_id:
|
||||||
error_message = "TMDB 信息未匹配到媒体分类,无法按媒体类别整理"
|
error_message = "TMDB 信息未匹配到媒体分类,无法按媒体类别整理"
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -175,11 +175,26 @@ class MusicInfo:
|
|||||||
detail_link: str | None = None
|
detail_link: str | None = None
|
||||||
listen_count: int | None = None
|
listen_count: int | None = None
|
||||||
raw_data: dict[str, Any] = field(default_factory=dict)
|
raw_data: dict[str, Any] = field(default_factory=dict)
|
||||||
|
# 内部标记:是否命中本地识别缓存,不参与序列化;与 MediaInfo 保持一致,
|
||||||
|
# 显式声明以保留 getattr(..., False) 的默认值语义(__getattr__ 兜底会覆盖它)
|
||||||
|
recognize_cache_hit = False
|
||||||
|
|
||||||
def __post_init__(self) -> None:
|
def __post_init__(self) -> None:
|
||||||
"""将构造参数中的媒体身份规范化为统一成对字段。"""
|
"""将构造参数中的媒体身份规范化为统一成对字段。"""
|
||||||
self.media_source, self.media_id = resolve_media_identity(media=self)
|
self.media_source, self.media_id = resolve_media_identity(media=self)
|
||||||
|
|
||||||
|
def __getattr__(self, name: str) -> None:
|
||||||
|
"""影视专用字段兜底返回 None:音乐模型不存在这些字段,避免下游逐点安全访问。
|
||||||
|
|
||||||
|
兼容影视媒体共享的通用读写路径(通知、整理、历史等),任何缺失字段按
|
||||||
|
空值处理;真实字段仍由类定义接管,不受影响。dunder 特殊方法除外——
|
||||||
|
copy/pickle 等机制依赖 hasattr 探测 __setstate__ 等钩子,兜底返回 None
|
||||||
|
会导致探测误判而调用失败。注意这会掩盖属性名拼写错误,属于权衡取舍。
|
||||||
|
"""
|
||||||
|
if name.startswith("__") and name.endswith("__"):
|
||||||
|
raise AttributeError(name)
|
||||||
|
return None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def artist(self) -> str:
|
def artist(self) -> str:
|
||||||
"""返回兼容现有展示组件的艺术家文本。"""
|
"""返回兼容现有展示组件的艺术家文本。"""
|
||||||
|
|||||||
@@ -83,6 +83,17 @@ class MusicInfo(OptionalMediaIdentityMixin, BaseModel):
|
|||||||
overview: Optional[str] = None
|
overview: Optional[str] = None
|
||||||
vote_average: float = 0.0
|
vote_average: float = 0.0
|
||||||
|
|
||||||
|
def __getattr__(self, name: str) -> None:
|
||||||
|
"""影视专用字段兜底返回 None:音乐模型不存在这些字段,避免下游逐点安全访问。
|
||||||
|
|
||||||
|
与 domain 层的 MusicInfo 保持一致:通知、整理等共享影视字段的读写路径
|
||||||
|
直接访问缺失属性时按空值处理,而不是抛 AttributeError。dunder 特殊方法
|
||||||
|
除外,避免 copy/pickle 等机制对钩子的 hasattr 探测误判。
|
||||||
|
"""
|
||||||
|
if name.startswith("__") and name.endswith("__"):
|
||||||
|
raise AttributeError(name)
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
class MusicRelease(BaseModel):
|
class MusicRelease(BaseModel):
|
||||||
"""音乐专辑下的单个发行版本。"""
|
"""音乐专辑下的单个发行版本。"""
|
||||||
|
|||||||
Reference in New Issue
Block a user