fix: 优化TMDB搜索匹配优先级,title/original_title优先于别名匹配

将搜索结果匹配策略从"逐个结果完整匹配链"改为"两轮优先级匹配":
第一轮遍历所有结果只匹配title/original_title,第二轮再匹配别名译名。
避免排序靠前的无关影片因别名恰好匹配而抢先于正确结果。

Fixes #5719

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
jxxghp
2026-05-21 17:47:59 +08:00
co-authored by Claude Opus 4.7
parent 2bd4a41cbe
commit b6b5529d19
+65 -59
View File
@@ -200,55 +200,45 @@ class TmdbApi:
ret_info['media_type'] = MediaType.MOVIE if ret_info.get("media_type") == "movie" else MediaType.TV ret_info['media_type'] = MediaType.MOVIE if ret_info.get("media_type") == "movie" else MediaType.TV
return ret_info return ret_info
def _match_multi_item(self, name: str, multi: dict, get_info_func) -> Optional[dict]: @staticmethod
def _match_multi_title(name_compare_func, name: str, multi: dict) -> bool:
""" """
匹配单个多媒体搜索结果项 匹配单个多媒体搜索结果项的标题/原标题
:param name: 查询名称 """
:param multi: 搜索结果项 if multi.get("media_type") == "movie":
:param get_info_func: 获取详细信息的函数(同步或异步) return (name_compare_func(name, multi.get('title'))
:return: 匹配的结果或None or name_compare_func(name, multi.get('original_title')))
elif multi.get("media_type") == "tv":
return (name_compare_func(name, multi.get('name'))
or name_compare_func(name, multi.get('original_name')))
return False
def _match_multi_names(self, name: str, multi: dict, get_info_func) -> Optional[dict]:
"""
匹配单个多媒体搜索结果项的别名、译名
""" """
if multi.get("media_type") == "movie": if multi.get("media_type") == "movie":
if self.__compare_names(name, multi.get('title')) \
or self.__compare_names(name, multi.get('original_title')):
return multi
# 匹配别名、译名
if not multi.get("names"): if not multi.get("names"):
multi = get_info_func(mtype=MediaType.MOVIE, tmdbid=multi.get("id")) multi = get_info_func(mtype=MediaType.MOVIE, tmdbid=multi.get("id"))
if multi and self.__compare_names(name, multi.get("names")): if multi and self.__compare_names(name, multi.get("names")):
return multi return multi
elif multi.get("media_type") == "tv": elif multi.get("media_type") == "tv":
if self.__compare_names(name, multi.get('name')) \
or self.__compare_names(name, multi.get('original_name')):
return multi
# 匹配别名、译名
if not multi.get("names"): if not multi.get("names"):
multi = get_info_func(mtype=MediaType.TV, tmdbid=multi.get("id")) multi = get_info_func(mtype=MediaType.TV, tmdbid=multi.get("id"))
if multi and self.__compare_names(name, multi.get("names")): if multi and self.__compare_names(name, multi.get("names")):
return multi return multi
return None return None
async def _async_match_multi_item(self, name: str, multi: dict) -> Optional[dict]: async def _async_match_multi_names(self, name: str, multi: dict) -> Optional[dict]:
""" """
匹配单个多媒体搜索结果项(异步版本) 匹配单个多媒体搜索结果项的别名、译名(异步版本)
:param name: 查询名称
:param multi: 搜索结果项
:return: 匹配的结果或None
""" """
if multi.get("media_type") == "movie": if multi.get("media_type") == "movie":
if self.__compare_names(name, multi.get('title')) \
or self.__compare_names(name, multi.get('original_title')):
return multi
# 匹配别名、译名
if not multi.get("names"): if not multi.get("names"):
multi = await self.async_get_info(mtype=MediaType.MOVIE, tmdbid=multi.get("id")) multi = await self.async_get_info(mtype=MediaType.MOVIE, tmdbid=multi.get("id"))
if multi and self.__compare_names(name, multi.get("names")): if multi and self.__compare_names(name, multi.get("names")):
return multi return multi
elif multi.get("media_type") == "tv": elif multi.get("media_type") == "tv":
if self.__compare_names(name, multi.get('name')) \
or self.__compare_names(name, multi.get('original_name')):
return multi
# 匹配别名、译名
if not multi.get("names"): if not multi.get("names"):
multi = await self.async_get_info(mtype=MediaType.TV, tmdbid=multi.get("id")) multi = await self.async_get_info(mtype=MediaType.TV, tmdbid=multi.get("id"))
if multi and self.__compare_names(name, multi.get("names")): if multi and self.__compare_names(name, multi.get("names")):
@@ -366,18 +356,18 @@ class TmdbApi:
key=lambda x: x.get('release_date') or '0000-00-00', key=lambda x: x.get('release_date') or '0000-00-00',
reverse=True reverse=True
) )
# 过滤年份
if year:
movies = [m for m in movies
if (m.get('release_date') or '')[0:4] == year]
# 第一轮:优先匹配标题、原标题
for movie in movies: for movie in movies:
# 年份
movie_year = movie.get('release_date')[0:4] if movie.get('release_date') else None
if year and movie_year != year:
# 年份不匹配
continue
# 匹配标题、原标题
if self.__compare_names(name, movie.get('title')): if self.__compare_names(name, movie.get('title')):
return movie return movie
if self.__compare_names(name, movie.get('original_title')): if self.__compare_names(name, movie.get('original_title')):
return movie return movie
# 匹配别名、译名 # 第二轮:匹配别名、译名
for movie in movies:
if not movie.get("names"): if not movie.get("names"):
movie = self.get_info(mtype=MediaType.MOVIE, tmdbid=movie.get("id")) movie = self.get_info(mtype=MediaType.MOVIE, tmdbid=movie.get("id"))
if movie and self.__compare_names(name, movie.get("names")): if movie and self.__compare_names(name, movie.get("names")):
@@ -413,17 +403,18 @@ class TmdbApi:
key=lambda x: x.get('first_air_date') or '0000-00-00', key=lambda x: x.get('first_air_date') or '0000-00-00',
reverse=True reverse=True
) )
# 过滤年份
if year:
tvs = [t for t in tvs
if (t.get('first_air_date') or '')[0:4] == year]
# 第一轮:优先匹配标题、原标题
for tv in tvs: for tv in tvs:
tv_year = tv.get('first_air_date')[0:4] if tv.get('first_air_date') else None
if year and tv_year != year:
# 年份不匹配
continue
# 匹配标题、原标题
if self.__compare_names(name, tv.get('name')): if self.__compare_names(name, tv.get('name')):
return tv return tv
if self.__compare_names(name, tv.get('original_name')): if self.__compare_names(name, tv.get('original_name')):
return tv return tv
# 匹配别名、译名 # 第二轮:匹配别名、译名
for tv in tvs:
if not tv.get("names"): if not tv.get("names"):
tv = self.get_info(mtype=MediaType.TV, tmdbid=tv.get("id")) tv = self.get_info(mtype=MediaType.TV, tmdbid=tv.get("id"))
if tv and self.__compare_names(name, tv.get("names")): if tv and self.__compare_names(name, tv.get("names")):
@@ -575,12 +566,19 @@ class TmdbApi:
# 按年份降序排列,电影在前面 # 按年份降序排列,电影在前面
multis = self._sort_multi_results(multis) multis = self._sort_multi_results(multis)
# 第一轮:优先匹配标题、原标题
ret_info = {} ret_info = {}
for multi in multis: for multi in multis:
matched = self._match_multi_item(name, multi, self.get_info) if self._match_multi_title(self.__compare_names, name, multi):
if matched: ret_info = multi
ret_info = matched
break break
# 第二轮:匹配别名、译名
if not ret_info:
for multi in multis:
matched = self._match_multi_names(name, multi, self.get_info)
if matched:
ret_info = matched
break
# 类型变更 # 类型变更
return self._convert_media_type(ret_info) return self._convert_media_type(ret_info)
@@ -1521,18 +1519,18 @@ class TmdbApi:
key=lambda x: x.get('release_date') or '0000-00-00', key=lambda x: x.get('release_date') or '0000-00-00',
reverse=True reverse=True
) )
# 过滤年份
if year:
movies = [m for m in movies
if (m.get('release_date') or '')[0:4] == year]
# 第一轮:优先匹配标题、原标题
for movie in movies: for movie in movies:
# 年份
movie_year = movie.get('release_date')[0:4] if movie.get('release_date') else None
if year and movie_year != year:
# 年份不匹配
continue
# 匹配标题、原标题
if self.__compare_names(name, movie.get('title')): if self.__compare_names(name, movie.get('title')):
return movie return movie
if self.__compare_names(name, movie.get('original_title')): if self.__compare_names(name, movie.get('original_title')):
return movie return movie
# 匹配别名、译名 # 第二轮:匹配别名、译名
for movie in movies:
if not movie.get("names"): if not movie.get("names"):
movie = await self.async_get_info(mtype=MediaType.MOVIE, tmdbid=movie.get("id")) movie = await self.async_get_info(mtype=MediaType.MOVIE, tmdbid=movie.get("id"))
if movie and self.__compare_names(name, movie.get("names")): if movie and self.__compare_names(name, movie.get("names")):
@@ -1568,17 +1566,18 @@ class TmdbApi:
key=lambda x: x.get('first_air_date') or '0000-00-00', key=lambda x: x.get('first_air_date') or '0000-00-00',
reverse=True reverse=True
) )
# 过滤年份
if year:
tvs = [t for t in tvs
if (t.get('first_air_date') or '')[0:4] == year]
# 第一轮:优先匹配标题、原标题
for tv in tvs: for tv in tvs:
tv_year = tv.get('first_air_date')[0:4] if tv.get('first_air_date') else None
if year and tv_year != year:
# 年份不匹配
continue
# 匹配标题、原标题
if self.__compare_names(name, tv.get('name')): if self.__compare_names(name, tv.get('name')):
return tv return tv
if self.__compare_names(name, tv.get('original_name')): if self.__compare_names(name, tv.get('original_name')):
return tv return tv
# 匹配别名、译名 # 第二轮:匹配别名、译名
for tv in tvs:
if not tv.get("names"): if not tv.get("names"):
tv = await self.async_get_info(mtype=MediaType.TV, tmdbid=tv.get("id")) tv = await self.async_get_info(mtype=MediaType.TV, tmdbid=tv.get("id"))
if tv and self.__compare_names(name, tv.get("names")): if tv and self.__compare_names(name, tv.get("names")):
@@ -1897,12 +1896,19 @@ class TmdbApi:
# 按年份降序排列,电影在前面 # 按年份降序排列,电影在前面
multis = self._sort_multi_results(multis) multis = self._sort_multi_results(multis)
# 第一轮:优先匹配标题、原标题
ret_info = {} ret_info = {}
for multi in multis: for multi in multis:
matched = await self._async_match_multi_item(name, multi) if self._match_multi_title(self.__compare_names, name, multi):
if matched: ret_info = multi
ret_info = matched
break break
# 第二轮:匹配别名、译名
if not ret_info:
for multi in multis:
matched = await self._async_match_multi_names(name, multi)
if matched:
ret_info = matched
break
# 类型变更 # 类型变更
return self._convert_media_type(ret_info) return self._convert_media_type(ret_info)