mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-08-21 16:23:34 +08:00
feat: expand metadata sources and media server sync (#6129)
This commit is contained in:
@@ -38,6 +38,8 @@ class SystemConfModel(BaseModel):
|
||||
douban: int = 0
|
||||
# Bangumi请求缓存数量
|
||||
bangumi: int = 0
|
||||
# AniList请求缓存数量
|
||||
anilist: int = 0
|
||||
# Fanart请求缓存数量
|
||||
fanart: int = 0
|
||||
# 元数据缓存过期时间(秒)
|
||||
@@ -197,11 +199,11 @@ class ConfigModel(BaseModel):
|
||||
DOH_RESOLVERS: str = "1.0.0.1,1.1.1.1,9.9.9.9,149.112.112.112"
|
||||
|
||||
# ==================== 媒体元数据配置 ====================
|
||||
# 媒体搜索来源 themoviedb/douban/bangumi,多个用,分隔
|
||||
# 媒体搜索来源 themoviedb/douban/bangumi/anilist,多个用,分隔
|
||||
SEARCH_SOURCE: str = "themoviedb"
|
||||
# 媒体识别来源 themoviedb/douban
|
||||
# 媒体识别来源 themoviedb/douban/bangumi/anilist
|
||||
RECOGNIZE_SOURCE: str = "themoviedb"
|
||||
# 刮削来源 themoviedb/douban
|
||||
# 刮削来源 themoviedb/douban/bangumi/anilist
|
||||
SCRAP_SOURCE: str = "themoviedb"
|
||||
# 电视剧动漫的分类genre_ids
|
||||
ANIME_GENREIDS: List[int] = Field(default=[16])
|
||||
|
||||
@@ -10,6 +10,7 @@ from app.schemas.types import MediaType
|
||||
from app.utils.string import StringUtils
|
||||
|
||||
BANGUMI_MOVIE_PLATFORMS = frozenset({"movie", "电影", "剧场版"})
|
||||
ANILIST_MOVIE_FORMATS = frozenset({"MOVIE"})
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -251,8 +252,10 @@ class MediaInfo:
|
||||
|
||||
# 内部标记:是否命中本地识别缓存,不参与序列化
|
||||
recognize_cache_hit = False
|
||||
# 来源:themoviedb、douban、bangumi
|
||||
# 来源:themoviedb、douban、bangumi、anilist
|
||||
source: str = None
|
||||
# 请求级刮削来源;为空时使用系统设置
|
||||
scrape_source: str = None
|
||||
# 类型 电影、电视剧
|
||||
type: MediaType = None
|
||||
# 媒体标题
|
||||
@@ -279,6 +282,10 @@ class MediaInfo:
|
||||
douban_id: str = None
|
||||
# Bangumi ID
|
||||
bangumi_id: int = None
|
||||
# AniList ID
|
||||
anilist_id: int = None
|
||||
# AniDB ID(AniList外部映射)
|
||||
anidb_id: int = None
|
||||
# 合集ID
|
||||
collection_id: int = None
|
||||
# 媒体原语种
|
||||
@@ -315,6 +322,8 @@ class MediaInfo:
|
||||
douban_info: dict = field(default_factory=dict)
|
||||
# Bangumi INFO
|
||||
bangumi_info: dict = field(default_factory=dict)
|
||||
# AniList INFO
|
||||
anilist_info: dict = field(default_factory=dict)
|
||||
# 导演
|
||||
directors: List[dict] = field(default_factory=list)
|
||||
# 演员
|
||||
@@ -380,6 +389,8 @@ class MediaInfo:
|
||||
self.set_douban_info(self.douban_info)
|
||||
if self.bangumi_info:
|
||||
self.set_bangumi_info(self.bangumi_info)
|
||||
if self.anilist_info:
|
||||
self.set_anilist_info(self.anilist_info)
|
||||
|
||||
def __setattr__(self, name: str, value: Any):
|
||||
self.__dict__[name] = value
|
||||
@@ -750,7 +761,7 @@ class MediaInfo:
|
||||
self.source = "bangumi"
|
||||
# 本体
|
||||
self.bangumi_info = info
|
||||
# 豆瓣ID
|
||||
# Bangumi ID
|
||||
self.bangumi_id = info.get("id")
|
||||
# 类型
|
||||
if not self.type:
|
||||
@@ -804,13 +815,166 @@ class MediaInfo:
|
||||
if self.type == MediaType.TV and not self.seasons:
|
||||
meta = MetaInfo(self.title)
|
||||
season = meta.begin_season if meta.begin_season is not None else 1
|
||||
episodes_count = info.get("total_episodes")
|
||||
episodes_count = info.get("total_episodes") or info.get("eps")
|
||||
if episodes_count:
|
||||
self.seasons[season] = list(range(1, episodes_count + 1))
|
||||
self.number_of_episodes = episodes_count
|
||||
self.number_of_seasons = 1
|
||||
# 风格
|
||||
if not self.genres:
|
||||
self.genres = [
|
||||
{"id": tag.get("name"), "name": tag.get("name")}
|
||||
for tag in info.get("tags") or []
|
||||
if tag.get("name")
|
||||
]
|
||||
# 制作公司与导演
|
||||
if info.get("infobox"):
|
||||
companies = []
|
||||
directors = []
|
||||
for item in info.get("infobox"):
|
||||
values = item.get("value")
|
||||
if not isinstance(values, list):
|
||||
values = [values]
|
||||
normalized_values = [
|
||||
value.get("v") if isinstance(value, dict) else value
|
||||
for value in values
|
||||
if value
|
||||
]
|
||||
if item.get("key") in {"动画制作", "制作"}:
|
||||
companies.extend({"name": value} for value in normalized_values)
|
||||
elif item.get("key") == "导演":
|
||||
directors.extend({"name": value} for value in normalized_values)
|
||||
if companies and not self.production_companies:
|
||||
self.production_companies = companies
|
||||
if directors and not self.directors:
|
||||
self.directors = directors
|
||||
# 演员
|
||||
if not self.actors:
|
||||
self.actors = info.get("actors") or []
|
||||
|
||||
@staticmethod
|
||||
def get_anilist_media_type(info: dict) -> MediaType:
|
||||
"""
|
||||
根据 AniList 发布格式获取标准媒体类型。
|
||||
|
||||
:param info: AniList 媒体信息
|
||||
:return: 标准媒体类型
|
||||
"""
|
||||
return (
|
||||
MediaType.MOVIE
|
||||
if str(info.get("format") or "").upper() in ANILIST_MOVIE_FORMATS
|
||||
else MediaType.TV
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _anilist_date(date_info: dict) -> Optional[str]:
|
||||
"""
|
||||
将 AniList 模糊日期转换为标准日期文本。
|
||||
|
||||
:param date_info: AniList FuzzyDate 字段
|
||||
:return: YYYY、YYYY-MM 或 YYYY-MM-DD 日期文本
|
||||
"""
|
||||
if not date_info or not date_info.get("year"):
|
||||
return None
|
||||
values = [str(date_info.get("year"))]
|
||||
if date_info.get("month"):
|
||||
values.append(str(date_info.get("month")).zfill(2))
|
||||
if date_info.get("day"):
|
||||
values.append(str(date_info.get("day")).zfill(2))
|
||||
return "-".join(values)
|
||||
|
||||
def set_anilist_info(self, info: dict) -> None:
|
||||
"""
|
||||
初始化 AniList 媒体信息。
|
||||
|
||||
:param info: AniList 媒体详情
|
||||
"""
|
||||
if not info:
|
||||
return
|
||||
self.source = "anilist"
|
||||
self.anilist_info = info
|
||||
self.anilist_id = info.get("id")
|
||||
self.type = self.type or self.get_anilist_media_type(info)
|
||||
|
||||
titles = info.get("title") or {}
|
||||
self.title = self.title or titles.get("english") or titles.get("romaji") or titles.get("native")
|
||||
self.en_title = self.en_title or titles.get("english")
|
||||
self.original_title = self.original_title or titles.get("native") or titles.get("romaji")
|
||||
self.names = list(
|
||||
dict.fromkeys(
|
||||
value
|
||||
for value in [
|
||||
titles.get("english"),
|
||||
titles.get("romaji"),
|
||||
titles.get("native"),
|
||||
*(info.get("synonyms") or []),
|
||||
]
|
||||
if value and value != self.title
|
||||
)
|
||||
)
|
||||
|
||||
self.release_date = self.release_date or self._anilist_date(info.get("startDate") or {})
|
||||
self.first_air_date = self.first_air_date or self.release_date
|
||||
self.last_air_date = self.last_air_date or self._anilist_date(info.get("endDate") or {})
|
||||
self.year = self.year or (
|
||||
str(info.get("startDate", {}).get("year"))
|
||||
if info.get("startDate", {}).get("year")
|
||||
else str(info.get("seasonYear")) if info.get("seasonYear") else None
|
||||
)
|
||||
|
||||
cover = info.get("coverImage") or {}
|
||||
self.poster_path = self.poster_path or cover.get("extraLarge") or cover.get("large")
|
||||
self.backdrop_path = self.backdrop_path or info.get("bannerImage")
|
||||
self.overview = self.overview or re.sub(
|
||||
r"<[^>]+>",
|
||||
"",
|
||||
str(info.get("description") or "").replace("<br>", "\n").replace("<br />", "\n"),
|
||||
).strip()
|
||||
self.vote_average = self.vote_average or (
|
||||
round(float(info.get("averageScore")) / 10, 1)
|
||||
if info.get("averageScore") is not None
|
||||
else 0
|
||||
)
|
||||
self.popularity = self.popularity or info.get("popularity")
|
||||
self.runtime = self.runtime or info.get("duration")
|
||||
self.adult = self.adult or bool(info.get("isAdult"))
|
||||
self.status = self.status or info.get("status")
|
||||
self.original_language = self.original_language or (
|
||||
"ja" if info.get("countryOfOrigin") == "JP" else None
|
||||
)
|
||||
self.origin_country = self.origin_country or (
|
||||
[info.get("countryOfOrigin")] if info.get("countryOfOrigin") else []
|
||||
)
|
||||
self.production_companies = self.production_companies or [
|
||||
{"name": studio.get("name")}
|
||||
for studio in info.get("studios", {}).get("nodes") or []
|
||||
if studio.get("name")
|
||||
]
|
||||
self.genres = self.genres or [
|
||||
{"id": genre, "name": genre} for genre in info.get("genres") or []
|
||||
]
|
||||
self.actors = self.actors or info.get("actors") or []
|
||||
self.directors = self.directors or info.get("directors") or []
|
||||
|
||||
if self.season is None:
|
||||
self.season = MetaInfo(self.title).begin_season if self.title else None
|
||||
episodes_count = info.get("episodes")
|
||||
if self.type == MediaType.TV and episodes_count:
|
||||
season = self.season if self.season is not None else 1
|
||||
self.seasons[season] = list(range(1, episodes_count + 1))
|
||||
self.number_of_episodes = episodes_count
|
||||
self.number_of_seasons = 1
|
||||
if self.year:
|
||||
self.season_years[season] = self.year
|
||||
|
||||
for external_link in info.get("externalLinks") or []:
|
||||
if str(external_link.get("site") or "").casefold() != "anidb":
|
||||
continue
|
||||
match = re.search(r"\d+", external_link.get("url") or "")
|
||||
if match:
|
||||
self.anidb_id = int(match.group())
|
||||
break
|
||||
|
||||
@property
|
||||
def title_year(self):
|
||||
if self.title:
|
||||
@@ -831,6 +995,8 @@ class MediaInfo:
|
||||
return "https://movie.douban.com/subject/%s" % self.douban_id
|
||||
elif self.bangumi_id:
|
||||
return "http://bgm.tv/subject/%s" % self.bangumi_id
|
||||
elif self.anilist_id:
|
||||
return "https://anilist.co/anime/%s" % self.anilist_id
|
||||
return ""
|
||||
|
||||
@property
|
||||
@@ -895,6 +1061,16 @@ class MediaInfo:
|
||||
dicts["tmdb_info"] = None
|
||||
dicts["douban_info"] = None
|
||||
dicts["bangumi_info"] = None
|
||||
dicts["anilist_info"] = None
|
||||
dicts["mediaid_prefix"] = self.source
|
||||
source_ids = {
|
||||
"themoviedb": self.tmdb_id,
|
||||
"douban": self.douban_id,
|
||||
"bangumi": self.bangumi_id,
|
||||
"anilist": self.anilist_id,
|
||||
}
|
||||
media_id = source_ids.get(self.source)
|
||||
dicts["media_id"] = str(media_id) if media_id is not None else None
|
||||
return dicts
|
||||
|
||||
def clear(self):
|
||||
@@ -904,6 +1080,7 @@ class MediaInfo:
|
||||
self.tmdb_info = {}
|
||||
self.douban_info = {}
|
||||
self.bangumi_info = {}
|
||||
self.anilist_info = {}
|
||||
self.seasons = {}
|
||||
self.genres = []
|
||||
self.season_info = []
|
||||
|
||||
@@ -23,7 +23,7 @@ def should_use_parent_title_for_file_stem(
|
||||
"""
|
||||
if not file_meta.isfile or not stem or not parent_dir_name:
|
||||
return False
|
||||
if file_meta.tmdbid or file_meta.doubanid:
|
||||
if file_meta.tmdbid or file_meta.doubanid or file_meta.media_id:
|
||||
return False
|
||||
if not PARENT_LATIN_TITLE_RE.search(parent_dir_name):
|
||||
return False
|
||||
|
||||
@@ -97,6 +97,8 @@ class MetaBase(object):
|
||||
# 附加信息
|
||||
tmdbid: int = None
|
||||
doubanid: str = None
|
||||
media_source: Optional[str] = None
|
||||
media_id: Optional[str] = None
|
||||
episode_group: Optional[str] = None
|
||||
# 帧率信息(纯数值)
|
||||
fps: Optional[int] = None
|
||||
@@ -683,6 +685,11 @@ class MetaBase(object):
|
||||
# doubanid
|
||||
if not self.doubanid and meta.doubanid:
|
||||
self.doubanid = meta.doubanid
|
||||
# 通用媒体来源与ID
|
||||
if not self.media_source and meta.media_source:
|
||||
self.media_source = meta.media_source
|
||||
if not self.media_id and meta.media_id:
|
||||
self.media_id = meta.media_id
|
||||
# 剧集组
|
||||
if not self.episode_group and meta.episode_group:
|
||||
self.episode_group = meta.episode_group
|
||||
|
||||
@@ -29,6 +29,8 @@ _ANIME_SQUARE_BRACKET_RE = re.compile(r'\[[+0-9XVPI-]+]\s*\[', re.IGNORECASE)
|
||||
_BRACED_METAINFO_RE = re.compile(r'(?<={\[)[\W\w]+(?=]})')
|
||||
_BRACED_TMDBID_RE = re.compile(r'(?<=tmdbid=)\d+')
|
||||
_BRACED_DOUBANID_RE = re.compile(r'(?<=doubanid=)\d+')
|
||||
_BRACED_BANGUMIID_RE = re.compile(r'(?<=bangumiid=)\d+')
|
||||
_BRACED_ANILISTID_RE = re.compile(r'(?<=anilistid=)\d+')
|
||||
_BRACED_TYPE_RE = re.compile(r'(?<=type=)\w+')
|
||||
_BRACED_EPISODE_GROUP_RE = re.compile(r'(?:^|;)g=([0-9a-fA-F]+)(?=;|$)')
|
||||
_BRACED_BEGIN_SEASON_RE = re.compile(r'(?<=s=)\d+')
|
||||
@@ -41,6 +43,24 @@ _EMBY_TMDB_RE_LIST = (
|
||||
re.compile(r'\{tmdbid[=\-](\d+)\}'),
|
||||
re.compile(r'\{tmdb[=\-](\d+)\}'),
|
||||
)
|
||||
_EXTENDED_MEDIA_ID_RE_LIST = {
|
||||
"bangumi": (
|
||||
re.compile(r'\[bangumiid[=\-](\d+)\]'),
|
||||
re.compile(r'\[bangumi[=\-](\d+)\]'),
|
||||
re.compile(r'\{bangumiid[=\-](\d+)\}'),
|
||||
re.compile(r'\{bangumi[=\-](\d+)\}'),
|
||||
),
|
||||
"anilist": (
|
||||
re.compile(r'\[anilistid[=\-](\d+)\]'),
|
||||
re.compile(r'\[anilist[=\-](\d+)\]'),
|
||||
re.compile(r'\{anilistid[=\-](\d+)\}'),
|
||||
re.compile(r'\{anilist[=\-](\d+)\}'),
|
||||
),
|
||||
}
|
||||
_EXTENDED_MEDIA_ID_TAG_RE = re.compile(
|
||||
r'(?:bangumi(?:id)?|anilist(?:id)?)[=\-]\d+',
|
||||
re.IGNORECASE,
|
||||
)
|
||||
_RUST_PARSE_OPTIONS_CACHE_KEY = "_cache_key"
|
||||
|
||||
|
||||
@@ -51,6 +71,10 @@ def _empty_metainfo() -> dict:
|
||||
return {
|
||||
'tmdbid': None,
|
||||
'doubanid': None,
|
||||
'bangumiid': None,
|
||||
'anilistid': None,
|
||||
'media_source': None,
|
||||
'media_id': None,
|
||||
'type': None,
|
||||
'episode_group': None,
|
||||
'begin_season': None,
|
||||
@@ -115,6 +139,14 @@ def _find_metainfo_python(title: str) -> Tuple[str, dict]:
|
||||
doubanid = _BRACED_DOUBANID_RE.search(result)
|
||||
if doubanid and doubanid.group(0).isdigit():
|
||||
metainfo['doubanid'] = doubanid.group(0)
|
||||
# 查找Bangumi ID信息
|
||||
bangumiid = _BRACED_BANGUMIID_RE.search(result)
|
||||
if bangumiid and bangumiid.group(0).isdigit():
|
||||
metainfo['bangumiid'] = bangumiid.group(0)
|
||||
# 查找AniList ID信息
|
||||
anilistid = _BRACED_ANILISTID_RE.search(result)
|
||||
if anilistid and anilistid.group(0).isdigit():
|
||||
metainfo['anilistid'] = anilistid.group(0)
|
||||
# 查找媒体类型
|
||||
mtype = _BRACED_TYPE_RE.search(result)
|
||||
if mtype:
|
||||
@@ -142,7 +174,18 @@ def _find_metainfo_python(title: str) -> Tuple[str, dict]:
|
||||
if end_episode and end_episode.group(0).isdigit():
|
||||
metainfo['end_episode'] = int(end_episode.group(0))
|
||||
# 去除title中该部分
|
||||
if tmdbid or mtype or episode_group or begin_season or end_season or begin_episode or end_episode:
|
||||
if (
|
||||
tmdbid
|
||||
or doubanid
|
||||
or bangumiid
|
||||
or anilistid
|
||||
or mtype
|
||||
or episode_group
|
||||
or begin_season
|
||||
or end_season
|
||||
or begin_episode
|
||||
or end_episode
|
||||
):
|
||||
title = title.replace(f"{{[{result}]}}", '')
|
||||
|
||||
# 支持Emby格式的ID标签;第一个 [tmdbid] 历史上始终优先处理,用于覆盖前面 {[...]} 中的旧标签。
|
||||
@@ -159,6 +202,31 @@ def _find_metainfo_python(title: str) -> Tuple[str, dict]:
|
||||
title = tmdb_re.sub('', title).strip()
|
||||
break
|
||||
|
||||
for source, patterns in _EXTENDED_MEDIA_ID_RE_LIST.items():
|
||||
key = f"{source}id"
|
||||
if metainfo.get(key):
|
||||
continue
|
||||
for media_id_re in patterns:
|
||||
media_id_match = media_id_re.search(title)
|
||||
if not media_id_match:
|
||||
continue
|
||||
metainfo[key] = media_id_match.group(1)
|
||||
title = media_id_re.sub('', title).strip()
|
||||
break
|
||||
|
||||
if metainfo.get('tmdbid'):
|
||||
metainfo['media_source'] = 'themoviedb'
|
||||
metainfo['media_id'] = metainfo['tmdbid']
|
||||
elif metainfo.get('doubanid'):
|
||||
metainfo['media_source'] = 'douban'
|
||||
metainfo['media_id'] = metainfo['doubanid']
|
||||
elif metainfo.get('bangumiid'):
|
||||
metainfo['media_source'] = 'bangumi'
|
||||
metainfo['media_id'] = metainfo['bangumiid']
|
||||
elif metainfo.get('anilistid'):
|
||||
metainfo['media_source'] = 'anilist'
|
||||
metainfo['media_id'] = metainfo['anilistid']
|
||||
|
||||
# 计算季集总数
|
||||
_apply_range_total(metainfo, 'begin_season', 'end_season', 'total_season')
|
||||
_apply_range_total(metainfo, 'begin_episode', 'end_episode', 'total_episode')
|
||||
@@ -202,6 +270,10 @@ def _build_meta_info(
|
||||
logger.warn("tmdbid 必须是数字")
|
||||
if metainfo.get('doubanid'):
|
||||
meta.doubanid = metainfo['doubanid']
|
||||
if metainfo.get('media_source'):
|
||||
meta.media_source = metainfo['media_source']
|
||||
if metainfo.get('media_id'):
|
||||
meta.media_id = str(metainfo['media_id'])
|
||||
if metainfo.get('type'):
|
||||
meta.type = MediaType(metainfo['type']) if isinstance(metainfo['type'], str) else metainfo['type']
|
||||
if metainfo.get('episode_group'):
|
||||
@@ -319,6 +391,8 @@ def _meta_from_rust(parsed: dict) -> Optional[MetaBase]:
|
||||
"apply_words": parsed.get("apply_words") or [],
|
||||
"tmdbid": parsed.get("tmdbid"),
|
||||
"doubanid": parsed.get("doubanid"),
|
||||
"media_source": parsed.get("media_source"),
|
||||
"media_id": parsed.get("media_id"),
|
||||
"episode_group": parsed.get("episode_group"),
|
||||
"fps": parsed.get("fps"),
|
||||
}
|
||||
@@ -327,6 +401,24 @@ def _meta_from_rust(parsed: dict) -> Optional[MetaBase]:
|
||||
return meta
|
||||
|
||||
|
||||
def _requires_python_metainfo(
|
||||
title: str,
|
||||
custom_words: Optional[List[str]] = None,
|
||||
) -> bool:
|
||||
"""
|
||||
判断标题或临时识别词是否包含当前Rust扩展尚未支持的数据源ID标签。
|
||||
|
||||
:param title: 原始标题
|
||||
:param custom_words: 临时识别词
|
||||
:return: 是否必须使用Python解析器
|
||||
"""
|
||||
candidates = [title or "", *(custom_words or [])]
|
||||
contains_extended_id = any(
|
||||
_EXTENDED_MEDIA_ID_TAG_RE.search(candidate) for candidate in candidates
|
||||
)
|
||||
return contains_extended_id and not rust_accel.supports_extended_media_ids()
|
||||
|
||||
|
||||
def MetaInfo(title: str, subtitle: Optional[str] = None, custom_words: List[str] = None) -> MetaBase:
|
||||
"""
|
||||
根据标题和副标题识别元数据
|
||||
@@ -335,9 +427,11 @@ def MetaInfo(title: str, subtitle: Optional[str] = None, custom_words: List[str]
|
||||
:param custom_words: 自定义识别词列表
|
||||
:return: MetaAnime、MetaVideo
|
||||
"""
|
||||
rust_meta = _meta_from_rust(
|
||||
rust_accel.parse_metainfo(title, subtitle, _rust_parse_options(custom_words))
|
||||
)
|
||||
rust_meta = None
|
||||
if not _requires_python_metainfo(title, custom_words):
|
||||
rust_meta = _meta_from_rust(
|
||||
rust_accel.parse_metainfo(title, subtitle, _rust_parse_options(custom_words))
|
||||
)
|
||||
if rust_meta:
|
||||
return rust_meta
|
||||
meta = _build_meta_info(title=title, subtitle=subtitle, custom_words=custom_words)
|
||||
@@ -355,9 +449,14 @@ def MetaInfoPath(path: Path, custom_words: List[str] = None) -> MetaBase:
|
||||
:param path: 路径
|
||||
:param custom_words: 自定义识别词列表
|
||||
"""
|
||||
rust_meta = _meta_from_rust(
|
||||
rust_accel.parse_metainfo_path(str(path), _rust_parse_options(custom_words))
|
||||
path_context = " ".join(
|
||||
[path.name, path.parent.name, path.parent.parent.name]
|
||||
)
|
||||
rust_meta = None
|
||||
if not _requires_python_metainfo(path_context, custom_words):
|
||||
rust_meta = _meta_from_rust(
|
||||
rust_accel.parse_metainfo_path(str(path), _rust_parse_options(custom_words))
|
||||
)
|
||||
if rust_meta:
|
||||
return rust_meta
|
||||
# 文件元数据,不包含后缀
|
||||
@@ -400,7 +499,9 @@ def find_metainfo(title: str) -> Tuple[str, dict]:
|
||||
"""
|
||||
从标题中提取媒体信息
|
||||
"""
|
||||
rust_result = rust_accel.find_metainfo(title)
|
||||
rust_result = None
|
||||
if not _requires_python_metainfo(title):
|
||||
rust_result = rust_accel.find_metainfo(title)
|
||||
if rust_result:
|
||||
return rust_result["title"], rust_result["metainfo"]
|
||||
return _find_metainfo_python(title)
|
||||
|
||||
Reference in New Issue
Block a user