mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-03-20 03:57:30 +08:00
358 lines
14 KiB
Python
358 lines
14 KiB
Python
from pathlib import Path
|
||
from typing import Iterable, Optional, Dict, Any, List, Set, Callable
|
||
|
||
from pydantic import BaseModel, Field, field_validator, model_validator
|
||
|
||
from app.schemas.message import MessageChannel
|
||
from app.schemas.file import FileItem
|
||
|
||
|
||
class Event(BaseModel):
|
||
"""
|
||
事件模型
|
||
"""
|
||
event_type: str = Field(..., description="事件类型")
|
||
event_data: Optional[dict] = Field(default={}, description="事件数据")
|
||
priority: Optional[int] = Field(0, description="事件优先级")
|
||
|
||
|
||
class BaseEventData(BaseModel):
|
||
"""
|
||
事件数据的基类,所有具体事件数据类应继承自此类
|
||
"""
|
||
pass
|
||
|
||
|
||
class ConfigChangeEventData(BaseEventData):
|
||
"""
|
||
ConfigChange 事件的数据模型
|
||
"""
|
||
key: set[str] = Field(..., description="配置项的键(集合类型)")
|
||
value: Optional[Any] = Field(default=None, description="配置项的新值")
|
||
change_type: str = Field(default="update", description="配置项的变更类型,如 'add', 'update', 'delete'")
|
||
|
||
@field_validator('key', mode='before')
|
||
@classmethod
|
||
def convert_to_set(cls, v):
|
||
"""将输入的 str、list、dict.keys() 等转为 set"""
|
||
if v is None:
|
||
return set()
|
||
elif isinstance(v, str):
|
||
return {v}
|
||
elif isinstance(v, dict):
|
||
return set(str(k) for k in v.keys())
|
||
elif isinstance(v, (list, tuple)):
|
||
return set(str(item) for item in v)
|
||
elif isinstance(v, set):
|
||
return set(str(item) for item in v)
|
||
elif isinstance(v, Iterable):
|
||
return set(str(item) for item in v)
|
||
else:
|
||
return {str(v)}
|
||
|
||
|
||
class ChainEventData(BaseEventData):
|
||
"""
|
||
链式事件数据的基类,所有具体事件数据类应继承自此类
|
||
"""
|
||
pass
|
||
|
||
|
||
class AuthCredentials(ChainEventData):
|
||
"""
|
||
AuthVerification 事件的数据模型
|
||
|
||
Attributes:
|
||
username (Optional[str]): 用户名,适用于 "password" grant_type
|
||
password (Optional[str]): 用户密码,适用于 "password" grant_type
|
||
mfa_code (Optional[str]): 一次性密码,目前仅适用于 "password" 认证类型
|
||
code (Optional[str]): 授权码,适用于 "authorization_code" grant_type
|
||
grant_type (str): 认证类型,如 "password", "authorization_code", "client_credentials"
|
||
# scope (List[str]): 权限范围,如 ["read", "write"]
|
||
token (Optional[str]): 认证令牌
|
||
channel (Optional[str]): 认证渠道
|
||
service (Optional[str]): 服务名称
|
||
"""
|
||
# 输入参数
|
||
username: Optional[str] = Field(None, description="用户名,适用于 'password' 认证类型")
|
||
password: Optional[str] = Field(None, description="用户密码,适用于 'password' 认证类型")
|
||
mfa_code: Optional[str] = Field(None, description="一次性密码,目前仅适用于 'password' 认证类型")
|
||
code: Optional[str] = Field(None, description="授权码,适用于 'authorization_code' 认证类型")
|
||
grant_type: str = Field(..., description="认证类型,如 'password', 'authorization_code', 'client_credentials'")
|
||
# scope: List[str] = Field(default_factory=list, description="权限范围,如 ['read', 'write']")
|
||
|
||
# 输出参数
|
||
# grant_type 为 authorization_code 时,输出参数包括 username、token、channel、service
|
||
token: Optional[str] = Field(default=None, description="认证令牌")
|
||
channel: Optional[str] = Field(default=None, description="认证渠道")
|
||
service: Optional[str] = Field(default=None, description="服务名称")
|
||
|
||
@model_validator(mode='before')
|
||
@classmethod
|
||
def check_fields_based_on_grant_type(cls, values): # noqa
|
||
grant_type = values.get("grant_type")
|
||
if not grant_type:
|
||
values["grant_type"] = "password"
|
||
grant_type = "password"
|
||
|
||
if grant_type == "password":
|
||
if not values.get("username") or not values.get("password"):
|
||
raise ValueError("username and password are required for grant_type 'password'")
|
||
|
||
elif grant_type == "authorization_code":
|
||
if not values.get("code"):
|
||
raise ValueError("code is required for grant_type 'authorization_code'")
|
||
|
||
return values
|
||
|
||
|
||
class AuthInterceptCredentials(ChainEventData):
|
||
"""
|
||
AuthIntercept 事件的数据模型
|
||
|
||
Attributes:
|
||
# 输入参数
|
||
username (str): 用户名
|
||
channel (str): 认证渠道
|
||
service (str): 服务名称
|
||
token (str): 认证令牌
|
||
status (str): 认证状态,"triggered" 和 "completed" 两个状态
|
||
|
||
# 输出参数
|
||
source (str): 拦截源,默认值为 "未知拦截源"
|
||
cancel (bool): 是否取消认证,默认值为 False
|
||
"""
|
||
# 输入参数
|
||
username: Optional[str] = Field(..., description="用户名")
|
||
channel: str = Field(..., description="认证渠道")
|
||
service: str = Field(..., description="服务名称")
|
||
status: str = Field(..., description="认证状态, 包含 'triggered' 表示认证触发,'completed' 表示认证成功")
|
||
token: Optional[str] = Field(default=None, description="认证令牌")
|
||
|
||
# 输出参数
|
||
source: str = Field(default="未知拦截源", description="拦截源")
|
||
cancel: bool = Field(default=False, description="是否取消认证")
|
||
|
||
|
||
class CommandRegisterEventData(ChainEventData):
|
||
"""
|
||
CommandRegister 事件的数据模型
|
||
|
||
Attributes:
|
||
# 输入参数
|
||
commands (dict): 菜单命令
|
||
origin (str): 事件源,可以是 Chain 或具体的模块名称
|
||
service (str): 服务名称
|
||
|
||
# 输出参数
|
||
source (str): 拦截源,默认值为 "未知拦截源"
|
||
cancel (bool): 是否取消认证,默认值为 False
|
||
"""
|
||
# 输入参数
|
||
commands: Dict[str, dict] = Field(..., description="菜单命令")
|
||
origin: str = Field(..., description="事件源")
|
||
service: Optional[str] = Field(..., description="服务名称")
|
||
|
||
# 输出参数
|
||
cancel: bool = Field(default=False, description="是否取消注册")
|
||
source: str = Field(default="未知拦截源", description="拦截源")
|
||
|
||
|
||
class TransferRenameEventData(ChainEventData):
|
||
"""
|
||
TransferRename 事件的数据模型
|
||
|
||
Attributes:
|
||
# 输入参数
|
||
template_string (str): Jinja2 模板字符串
|
||
rename_dict (dict): 渲染上下文
|
||
render_str (str): 渲染生成的字符串
|
||
path (Optional[Path]): 当前文件的目标路径
|
||
|
||
# 输出参数
|
||
updated (bool): 是否已更新,默认值为 False
|
||
updated_str (str): 更新后的字符串
|
||
source (str): 拦截源,默认值为 "未知拦截源"
|
||
"""
|
||
# 输入参数
|
||
template_string: str = Field(..., description="模板字符串")
|
||
rename_dict: Dict[str, Any] = Field(..., description="渲染上下文")
|
||
path: Optional[Path] = Field(None, description="文件的目标路径")
|
||
render_str: str = Field(..., description="渲染生成的字符串")
|
||
|
||
# 输出参数
|
||
updated: bool = Field(default=False, description="是否已更新")
|
||
updated_str: Optional[str] = Field(default=None, description="更新后的字符串")
|
||
source: Optional[str] = Field(default="未知拦截源", description="拦截源")
|
||
|
||
|
||
class ResourceSelectionEventData(BaseModel):
|
||
"""
|
||
ResourceSelection 事件的数据模型
|
||
|
||
Attributes:
|
||
# 输入参数
|
||
contexts (List[Context]): 当前待选择的资源上下文列表
|
||
source (str): 事件源,指示事件的触发来源
|
||
|
||
# 输出参数
|
||
updated (bool): 是否已更新,默认值为 False
|
||
updated_contexts (Optional[List[Context]]): 已更新的资源上下文列表,默认值为 None
|
||
source (str): 更新源,默认值为 "未知更新源"
|
||
"""
|
||
# 输入参数
|
||
contexts: Any = Field(None, description="待选择的资源上下文列表")
|
||
downloader: Optional[str] = Field(None, description="下载器")
|
||
origin: Optional[str] = Field(None, description="来源")
|
||
|
||
# 输出参数
|
||
updated: bool = Field(default=False, description="是否已更新")
|
||
updated_contexts: Optional[List[Any]] = Field(default=None, description="已更新的资源上下文列表")
|
||
source: Optional[str] = Field(default="未知拦截源", description="拦截源")
|
||
|
||
|
||
class ResourceDownloadEventData(ChainEventData):
|
||
"""
|
||
ResourceDownload 事件的数据模型
|
||
|
||
Attributes:
|
||
# 输入参数
|
||
context (Context): 当前资源上下文
|
||
episodes (Set[int]): 需要下载的集数
|
||
channel (MessageChannel): 通知渠道
|
||
origin (str): 来源(消息通知、Subscribe、Manual等)
|
||
downloader (str): 下载器
|
||
options (dict): 其他参数
|
||
|
||
# 输出参数
|
||
cancel (bool): 是否取消下载,默认值为 False
|
||
source (str): 拦截源,默认值为 "未知拦截源"
|
||
reason (str): 拦截原因,描述拦截的具体原因
|
||
"""
|
||
# 输入参数
|
||
context: Any = Field(None, description="当前资源上下文")
|
||
episodes: Optional[Set[int]] = Field(None, description="需要下载的集数")
|
||
channel: Optional[MessageChannel] = Field(None, description="通知渠道")
|
||
origin: Optional[str] = Field(None, description="来源")
|
||
downloader: Optional[str] = Field(None, description="下载器")
|
||
options: Optional[dict] = Field(default={}, description="其他参数")
|
||
|
||
# 输出参数
|
||
cancel: bool = Field(default=False, description="是否取消下载")
|
||
source: str = Field(default="未知拦截源", description="拦截源")
|
||
reason: str = Field(default="", description="拦截原因")
|
||
|
||
|
||
class TransferInterceptEventData(ChainEventData):
|
||
"""
|
||
TransferIntercept 事件的数据模型
|
||
|
||
Attributes:
|
||
# 输入参数
|
||
fileitem (FileItem): 源文件
|
||
target_storage (str): 目标存储
|
||
target_path (Path): 目标路径
|
||
transfer_type (str): 整理方式(copy、move、link、softlink等)
|
||
options (dict): 其他参数
|
||
|
||
# 输出参数
|
||
cancel (bool): 是否取消下载,默认值为 False
|
||
source (str): 拦截源,默认值为 "未知拦截源"
|
||
reason (str): 拦截原因,描述拦截的具体原因
|
||
"""
|
||
# 输入参数
|
||
fileitem: FileItem = Field(..., description="源文件")
|
||
mediainfo: Any = Field(..., description="媒体信息")
|
||
target_storage: str = Field(..., description="目标存储")
|
||
target_path: Path = Field(..., description="目标路径")
|
||
transfer_type: str = Field(..., description="整理方式")
|
||
options: Optional[dict] = Field(default=None, description="其他参数")
|
||
|
||
# 输出参数
|
||
cancel: bool = Field(default=False, description="是否取消整理")
|
||
source: str = Field(default="未知拦截源", description="拦截源")
|
||
reason: str = Field(default="", description="拦截原因")
|
||
|
||
|
||
class DiscoverMediaSource(BaseModel):
|
||
"""
|
||
探索媒体数据源的基类
|
||
"""
|
||
name: str = Field(..., description="数据源名称")
|
||
mediaid_prefix: str = Field(..., description="媒体ID的前缀,不含:")
|
||
api_path: str = Field(..., description="媒体数据源API地址")
|
||
filter_params: Optional[Dict[str, Any]] = Field(default=None, description="过滤参数")
|
||
filter_ui: Optional[List[dict]] = Field(default=[], description="过滤参数UI配置")
|
||
depends: Optional[Dict[str, list]] = Field(default=None, description="UI依赖关系字典")
|
||
|
||
|
||
class DiscoverSourceEventData(ChainEventData):
|
||
"""
|
||
DiscoverSource 事件的数据模型
|
||
|
||
Attributes:
|
||
# 输出参数
|
||
extra_sources (List[DiscoverMediaSource]): 额外媒体数据源
|
||
"""
|
||
# 输出参数
|
||
extra_sources: List[DiscoverMediaSource] = Field(default_factory=list, description="额外媒体数据源")
|
||
|
||
|
||
class RecommendMediaSource(BaseModel):
|
||
"""
|
||
推荐媒体数据源的基类
|
||
"""
|
||
name: str = Field(..., description="数据源名称")
|
||
api_path: str = Field(..., description="媒体数据源API地址")
|
||
type: str = Field(..., description="类型")
|
||
|
||
|
||
class RecommendSourceEventData(ChainEventData):
|
||
"""
|
||
RecommendSource 事件的数据模型
|
||
|
||
Attributes:
|
||
# 输出参数
|
||
extra_sources (List[RecommendMediaSource]): 额外媒体数据源
|
||
"""
|
||
# 输出参数
|
||
extra_sources: List[RecommendMediaSource] = Field(default_factory=list, description="额外媒体数据源")
|
||
|
||
|
||
class MediaRecognizeConvertEventData(ChainEventData):
|
||
"""
|
||
MediaRecognizeConvert 事件的数据模型
|
||
|
||
Attributes:
|
||
# 输入参数
|
||
mediaid (str): 媒体ID,格式为`前缀:ID值`,如 tmdb:12345、douban:1234567
|
||
convert_type (str): 转换类型 仅支持:themoviedb/douban,需要转换为对应的媒体数据并返回
|
||
|
||
# 输出参数
|
||
media_dict (dict): TheMovieDb/豆瓣的媒体数据
|
||
"""
|
||
# 输入参数
|
||
mediaid: str = Field(..., description="媒体ID")
|
||
convert_type: str = Field(..., description="转换类型(themoviedb/douban)")
|
||
|
||
# 输出参数
|
||
media_dict: dict = Field(default_factory=dict, description="转换后的媒体信息(TheMovieDb/豆瓣)")
|
||
|
||
|
||
class StorageOperSelectionEventData(ChainEventData):
|
||
"""
|
||
StorageOperSelect 事件的数据模型
|
||
|
||
Attributes:
|
||
# 输入参数
|
||
storage (str): 存储类型
|
||
|
||
# 输出参数
|
||
storage_oper (Callable): 存储操作对象
|
||
"""
|
||
# 输入参数
|
||
storage: Optional[str] = Field(default=None, description="存储类型")
|
||
|
||
# 输出参数
|
||
storage_oper: Optional[Callable] = Field(default=None, description="存储操作对象")
|