mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-09-06 16:07:01 +08:00
feat(media): support extensible media sources
This commit is contained in:
@@ -482,12 +482,12 @@ class DiscoverMediaSource(BaseModel):
|
||||
探索媒体数据源的基类。
|
||||
|
||||
``mediaid_prefix`` 是既有插件与前端标签使用的稳定标识;
|
||||
``media_source`` 是新的规范媒体来源。模型同时输出两者,并在输入时互相补齐,
|
||||
以兼容尚未升级的已安装插件。
|
||||
``media_source`` 是新的规范媒体来源,可以是内置常量或插件扩展成员。模型同时
|
||||
输出两者,并在输入时互相补齐,以兼容尚未升级的已安装插件。
|
||||
"""
|
||||
|
||||
name: str = Field(..., description="数据源名称")
|
||||
media_source: MediaSource = Field(..., description="媒体来源枚举")
|
||||
media_source: MediaSource = Field(..., description="内置或插件扩展媒体来源")
|
||||
mediaid_prefix: str = Field(..., description="兼容插件使用的媒体ID前缀")
|
||||
api_path: str = Field(..., description="媒体数据源API地址")
|
||||
filter_params: Optional[Dict[str, JsonData]] = Field(
|
||||
@@ -517,7 +517,7 @@ class DiscoverMediaSource(BaseModel):
|
||||
|
||||
@staticmethod
|
||||
def _media_source_from_prefix(mediaid_prefix: str) -> MediaSource:
|
||||
"""将旧插件使用的历史前缀映射为规范媒体来源枚举。"""
|
||||
"""将旧插件前缀映射为内置或插件扩展媒体来源。"""
|
||||
aliases = {
|
||||
"mangguo": MediaSource.MangoTV,
|
||||
"tencentvideo": MediaSource.TencentVideo,
|
||||
@@ -573,9 +573,9 @@ class MediaRecognizeConvertEventData(RequiredMediaIdentityMixin, ChainEventData)
|
||||
|
||||
Attributes:
|
||||
# 输入参数
|
||||
media_source (MediaSource): 输入媒体来源
|
||||
media_source (MediaSource): 输入内置或插件扩展媒体来源
|
||||
media_id (str): 数据源原生 ID
|
||||
target_media_source (MediaSource): 需要转换到的目标媒体来源
|
||||
target_media_source (MediaSource): 需要转换到的内置或插件扩展媒体来源
|
||||
|
||||
# 输出参数
|
||||
media_dict (dict): TheMovieDb/豆瓣的媒体数据
|
||||
|
||||
@@ -4,7 +4,7 @@ from app.schemas.types import MediaSource
|
||||
|
||||
|
||||
class OptionalMediaIdentityMixin:
|
||||
"""为可选媒体身份模型统一校验来源枚举与原生 ID 的成对约束。"""
|
||||
"""为可选媒体身份模型统一校验内置或插件来源与原生 ID 的成对约束。"""
|
||||
|
||||
@model_validator(mode="before")
|
||||
@classmethod
|
||||
@@ -52,7 +52,7 @@ class OptionalMediaIdentityMixin:
|
||||
|
||||
|
||||
class RequiredMediaIdentityMixin:
|
||||
"""为必填媒体身份模型统一校验来源枚举与原生 ID。"""
|
||||
"""为必填媒体身份模型统一校验内置或插件来源与原生 ID。"""
|
||||
|
||||
@model_validator(mode="after")
|
||||
def _validate_required_media_identity(self):
|
||||
|
||||
@@ -224,7 +224,7 @@ class EpisodeFormatRecommendItem(BaseModel):
|
||||
|
||||
|
||||
class ManualTransferItem(OptionalMediaIdentityMixin, BaseModel):
|
||||
"""手动整理请求,媒体身份只接受来源枚举与原生 ID。"""
|
||||
"""手动整理请求,媒体身份接受内置或插件来源与原生 ID。"""
|
||||
|
||||
# 文件项
|
||||
fileitem: FileItem = None
|
||||
|
||||
+47
-2
@@ -1,6 +1,10 @@
|
||||
import re
|
||||
from enum import Enum
|
||||
from typing import Literal, Optional, Tuple, Union
|
||||
|
||||
from pydantic import GetJsonSchemaHandler
|
||||
from pydantic_core import CoreSchema
|
||||
|
||||
|
||||
# 音乐实体命名空间由公共类型模块统一持有,避免模型、接口和工具层重复定义。
|
||||
MUSIC_ENTITY_RECORDING = "recording"
|
||||
@@ -46,8 +50,20 @@ class MediaType(Enum):
|
||||
}.get(self, self.value)
|
||||
|
||||
|
||||
MEDIA_SOURCE_IDENTIFIER_PATTERN = r"^[a-z][a-z0-9._-]{0,63}$"
|
||||
_MEDIA_SOURCE_IDENTIFIER_RE = re.compile(MEDIA_SOURCE_IDENTIFIER_PATTERN)
|
||||
_MEDIA_SOURCE_VALUE_ALIASES = {
|
||||
"tmdb": "themoviedb",
|
||||
"audio_db": "theaudiodb",
|
||||
"douban_music": "doubanmusic",
|
||||
"mango_tv": "mangguodiscover",
|
||||
"migu_video": "migu",
|
||||
"tencent_video": "tencentvideodiscover",
|
||||
}
|
||||
|
||||
|
||||
class MediaSource(str, Enum):
|
||||
"""媒体主身份的数据来源。"""
|
||||
"""媒体主身份的数据来源,内置来源为常量,插件来源为动态扩展成员。"""
|
||||
|
||||
TMDB = "themoviedb"
|
||||
Douban = "douban"
|
||||
@@ -67,8 +83,37 @@ class MediaSource(str, Enum):
|
||||
"""返回可直接用于 API 和数据库的规范值。"""
|
||||
return self.value
|
||||
|
||||
@classmethod
|
||||
def _missing_(cls, value: object) -> Optional["MediaSource"]:
|
||||
"""将合法插件来源标识解析为动态枚举成员,并规范化内置别名。"""
|
||||
if not isinstance(value, str):
|
||||
return None
|
||||
normalized = value.strip().casefold()
|
||||
normalized = _MEDIA_SOURCE_VALUE_ALIASES.get(normalized, normalized)
|
||||
known_member = cls._value2member_map_.get(normalized)
|
||||
if known_member:
|
||||
return known_member
|
||||
if not _MEDIA_SOURCE_IDENTIFIER_RE.fullmatch(normalized):
|
||||
return None
|
||||
member = str.__new__(cls, normalized)
|
||||
member._name_ = normalized
|
||||
member._value_ = normalized
|
||||
cls._value2member_map_.setdefault(normalized, member)
|
||||
return cls._value2member_map_[normalized]
|
||||
|
||||
# 搜索可以选择一个或多个来源,但集合中的每一项都必须是固定枚举。
|
||||
@classmethod
|
||||
def __get_pydantic_json_schema__(
|
||||
cls, core_schema: CoreSchema, handler: GetJsonSchemaHandler,
|
||||
) -> dict:
|
||||
"""在 OpenAPI 中声明可扩展标识格式,避免把内置成员误写成完整白名单。"""
|
||||
schema = handler(core_schema)
|
||||
schema.pop("enum", None)
|
||||
schema["pattern"] = MEDIA_SOURCE_IDENTIFIER_PATTERN
|
||||
schema["examples"] = [source.value for source in cls]
|
||||
return schema
|
||||
|
||||
|
||||
# 搜索可以选择一个或多个内置或插件扩展来源。
|
||||
MediaSourceSelection = Union[MediaSource, Tuple[MediaSource, ...]]
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user