mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-09-06 07:56:52 +08:00
refactor(db): 修复异步连接池无界增长,并完成 SQLAlchemy 2.0 迁移与分层归位 (#6320)
This commit is contained in:
@@ -20,7 +20,7 @@ from app.schemas.types import (
|
||||
MediaSource,
|
||||
MediaType,
|
||||
)
|
||||
from app.domain.media import normalize_media_source, resolve_media_identity
|
||||
from app.schemas.media import normalize_media_source, resolve_media_identity
|
||||
from app.domain.string import StringUtils
|
||||
|
||||
BANGUMI_MOVIE_PLATFORMS = frozenset({"movie", "电影", "剧场版"})
|
||||
|
||||
+12
-151
@@ -1,5 +1,16 @@
|
||||
from typing import Any, Callable, Optional, Tuple, Union
|
||||
"""
|
||||
媒体来源的领域策略。
|
||||
|
||||
这里只留「按什么规则挑来源、什么算可订阅的音乐实体」这类策略——它们依赖运行期配置
|
||||
(注入的 SEARCH_SOURCE 提供者)与业务约定,会随产品决策变化。
|
||||
|
||||
身份的**表示规则**(别名归一、ID 去空白拒零、成对写入、媒体键前缀)不随策略变化,
|
||||
已迁至 app/schemas/media.py,与两个身份 Mixin 作伴。持久化层因此不必为一条表示规则
|
||||
反向依赖领域层。本模块不做 re-export:同一个符号只应有一条 import 路径。
|
||||
"""
|
||||
from typing import Callable, Optional, Tuple, Union
|
||||
|
||||
from app.schemas.media import normalize_media_source
|
||||
from app.schemas.types import (
|
||||
MUSIC_ENTITY_TYPES,
|
||||
MUSIC_SUBSCRIBABLE_TYPES,
|
||||
@@ -7,47 +18,6 @@ from app.schemas.types import (
|
||||
MediaSourceSelection,
|
||||
)
|
||||
|
||||
MEDIA_SOURCE_ALIASES = {
|
||||
"tmdb": MediaSource.TMDB,
|
||||
"themoviedb": MediaSource.TMDB,
|
||||
"douban": MediaSource.Douban,
|
||||
"bangumi": MediaSource.Bangumi,
|
||||
"anilist": MediaSource.AniList,
|
||||
"imdb": MediaSource.IMDb,
|
||||
"tvdb": MediaSource.TVDB,
|
||||
"musicbrainz": MediaSource.MusicBrainz,
|
||||
"theaudiodb": MediaSource.TheAudioDB,
|
||||
"audio_db": MediaSource.TheAudioDB,
|
||||
"doubanmusic": MediaSource.DoubanMusic,
|
||||
"douban_music": MediaSource.DoubanMusic,
|
||||
"bilibili": MediaSource.Bilibili,
|
||||
"mangguodiscover": MediaSource.MangoTV,
|
||||
"mango_tv": MediaSource.MangoTV,
|
||||
"migu": MediaSource.MiguVideo,
|
||||
"migu_video": MediaSource.MiguVideo,
|
||||
"tencentvideodiscover": MediaSource.TencentVideo,
|
||||
"tencent_video": MediaSource.TencentVideo,
|
||||
"iqiyi": MediaSource.Iqiyi,
|
||||
"iqiyidiscover": MediaSource.Iqiyi,
|
||||
}
|
||||
|
||||
MEDIA_SOURCE_PREFIXES = {
|
||||
MediaSource.TMDB: "tmdb",
|
||||
MediaSource.Douban: "douban",
|
||||
MediaSource.Bangumi: "bangumi",
|
||||
MediaSource.AniList: "anilist",
|
||||
MediaSource.IMDb: "imdb",
|
||||
MediaSource.TVDB: "tvdb",
|
||||
MediaSource.MusicBrainz: "musicbrainz",
|
||||
MediaSource.TheAudioDB: "theaudiodb",
|
||||
MediaSource.DoubanMusic: "doubanmusic",
|
||||
MediaSource.Bilibili: "bilibili",
|
||||
MediaSource.MangoTV: "mangguodiscover",
|
||||
MediaSource.MiguVideo: "migu",
|
||||
MediaSource.TencentVideo: "tencentvideodiscover",
|
||||
MediaSource.Iqiyi: "iqiyidiscover",
|
||||
}
|
||||
|
||||
MUSIC_MEDIA_SOURCE_ORDER = (
|
||||
MediaSource.MusicBrainz,
|
||||
MediaSource.TheAudioDB,
|
||||
@@ -81,24 +51,6 @@ def is_music_media_source(
|
||||
return normalize_media_source(source) in MUSIC_MEDIA_SOURCES
|
||||
|
||||
|
||||
def normalize_media_source(
|
||||
source: Optional[Union[MediaSource, str]],
|
||||
) -> Optional[MediaSource]:
|
||||
"""将内置别名或插件扩展标识规范化为 MediaSource。"""
|
||||
if not source:
|
||||
return None
|
||||
if isinstance(source, MediaSource):
|
||||
return source
|
||||
normalized = str(source).strip().casefold()
|
||||
builtin_source = MEDIA_SOURCE_ALIASES.get(normalized)
|
||||
if builtin_source:
|
||||
return builtin_source
|
||||
try:
|
||||
return MediaSource(normalized)
|
||||
except ValueError:
|
||||
return None
|
||||
|
||||
|
||||
def parse_media_source_selection(value: Optional[str]) -> Tuple[MediaSource, ...]:
|
||||
"""
|
||||
解析 HTTP 查询参数中的逗号分隔来源,并转换为有序枚举集合。
|
||||
@@ -168,94 +120,3 @@ def is_media_source_enabled(
|
||||
}
|
||||
return source_key in configured_sources
|
||||
return True
|
||||
|
||||
|
||||
def parse_media_key(
|
||||
media_key: Optional[str],
|
||||
) -> Tuple[Optional[MediaSource], Optional[str]]:
|
||||
"""解析带来源前缀的媒体键,返回规范化数据源与原生 ID。"""
|
||||
if not media_key or ":" not in str(media_key):
|
||||
return None, None
|
||||
prefix, media_id = str(media_key).split(":", 1)
|
||||
source = normalize_media_source(prefix)
|
||||
media_id = media_id.strip()
|
||||
if not source or not media_id or media_id == "0":
|
||||
return None, None
|
||||
return source, media_id
|
||||
|
||||
|
||||
def resolve_media_identity(
|
||||
media: Any = None,
|
||||
media_source: Optional[Union[MediaSource, str]] = None,
|
||||
media_id: Optional[Any] = None,
|
||||
) -> Tuple[Optional[MediaSource], Optional[str]]:
|
||||
"""
|
||||
从统一媒体对象或显式字段解析主媒体身份。
|
||||
|
||||
:param media: 包含 ``media_source`` 和 ``media_id`` 的媒体对象
|
||||
:param media_source: 显式媒体来源
|
||||
:param media_id: 显式来源原生 ID
|
||||
:return: 枚举化来源和字符串 ID;任一字段无效时返回空身份
|
||||
"""
|
||||
normalized_source = normalize_media_source(media_source)
|
||||
if media_source is not None or media_id is not None:
|
||||
normalized_id = str(media_id).strip() if media_id is not None else ""
|
||||
if normalized_source and normalized_id and normalized_id != "0":
|
||||
return normalized_source, normalized_id
|
||||
return None, None
|
||||
|
||||
if media is None:
|
||||
return None, None
|
||||
normalized_source = normalize_media_source(
|
||||
getattr(media, "media_source", None)
|
||||
if not isinstance(media, dict)
|
||||
else media.get("media_source")
|
||||
)
|
||||
object_media_id = (
|
||||
getattr(media, "media_id", None)
|
||||
if not isinstance(media, dict)
|
||||
else media.get("media_id")
|
||||
)
|
||||
if normalized_source and object_media_id is not None:
|
||||
normalized_id = str(object_media_id).strip()
|
||||
if normalized_id and normalized_id != "0":
|
||||
return normalized_source, normalized_id
|
||||
return None, None
|
||||
|
||||
|
||||
def normalize_media_identity_payload(
|
||||
payload: dict[str, Any],
|
||||
*,
|
||||
include_empty: bool = False,
|
||||
) -> dict[str, Any]:
|
||||
"""
|
||||
规范化字典中的媒体身份,保证来源与 ID 始终成对写入。
|
||||
|
||||
:param payload: 待写入或传输的字段字典
|
||||
:param include_empty: 字典未声明身份字段时,是否仍补充空身份
|
||||
:return: 复制后的规范字典;非法、半对或零值身份会被清空
|
||||
"""
|
||||
normalized = dict(payload)
|
||||
has_identity = "media_source" in normalized or "media_id" in normalized
|
||||
if not has_identity and not include_empty:
|
||||
return normalized
|
||||
media_source, media_id = resolve_media_identity(
|
||||
media_source=normalized.get("media_source"),
|
||||
media_id=normalized.get("media_id"),
|
||||
)
|
||||
normalized["media_source"] = media_source.value if media_source else None
|
||||
normalized["media_id"] = media_id
|
||||
return normalized
|
||||
|
||||
|
||||
def build_media_key(
|
||||
media_source: Optional[Union[MediaSource, str]],
|
||||
media_id: Optional[Any],
|
||||
) -> str:
|
||||
"""构造 API 使用的带来源前缀媒体键。"""
|
||||
normalized_source = normalize_media_source(media_source)
|
||||
normalized_id = str(media_id).strip() if media_id is not None else ""
|
||||
if not normalized_source or not normalized_id or normalized_id == "0":
|
||||
return ""
|
||||
prefix = MEDIA_SOURCE_PREFIXES.get(normalized_source, normalized_source.value)
|
||||
return f"{prefix}:{normalized_id}"
|
||||
|
||||
@@ -7,7 +7,7 @@ import cn2an
|
||||
import regex as re
|
||||
|
||||
from app.schemas.types import MediaSource, MediaType
|
||||
from app.domain.media import resolve_media_identity
|
||||
from app.schemas.media import resolve_media_identity
|
||||
from app.domain.string import StringUtils
|
||||
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ from typing import Any, Callable, Optional
|
||||
|
||||
from app.domain.meta.metabase import MetaBase
|
||||
from app.schemas.types import MediaSource, MediaType
|
||||
from app.domain.media import resolve_media_identity
|
||||
from app.schemas.media import resolve_media_identity
|
||||
from app.domain.meta.runtime import get_metainfo_accelerator
|
||||
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ from app.domain.meta.runtime import (
|
||||
get_metainfo_accelerator,
|
||||
)
|
||||
from app.schemas.types import MediaSource, MediaType
|
||||
from app.domain.media import normalize_media_source, resolve_media_identity
|
||||
from app.schemas.media import normalize_media_source, resolve_media_identity
|
||||
|
||||
|
||||
_ANIME_BRACKET_RE = re.compile(r'【[+0-9XVPI-]+】\s*【', re.IGNORECASE)
|
||||
|
||||
Reference in New Issue
Block a user