mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-09-05 23:47:41 +08:00
Merge pull request #3201 from InfinityPacer/feature/event
This commit is contained in:
+3
-26
@@ -84,7 +84,6 @@ class EventManager(metaclass=Singleton):
|
|||||||
self.__disabled_handlers = set() # 禁用的事件处理器集合
|
self.__disabled_handlers = set() # 禁用的事件处理器集合
|
||||||
self.__disabled_classes = set() # 禁用的事件处理器类集合
|
self.__disabled_classes = set() # 禁用的事件处理器类集合
|
||||||
self.__lock = threading.Lock() # 线程锁
|
self.__lock = threading.Lock() # 线程锁
|
||||||
self.__processing_events = {} # 用于记录当前正在处理的事件 {event_hash: event}
|
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
"""
|
"""
|
||||||
@@ -130,14 +129,6 @@ class EventManager(metaclass=Singleton):
|
|||||||
for handler in handlers.values()
|
for handler in handlers.values()
|
||||||
)
|
)
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def __get_event_hash(event: Event) -> str:
|
|
||||||
"""
|
|
||||||
计算事件的唯一标识符(hash)
|
|
||||||
"""
|
|
||||||
data_string = str(event.event_type.value) + str(event.event_data)
|
|
||||||
return str(uuid.uuid5(uuid.NAMESPACE_DNS, data_string))
|
|
||||||
|
|
||||||
def send_event(self, etype: Union[EventType, ChainEventType], data: Optional[Union[Dict, ChainEventData]] = None,
|
def send_event(self, etype: Union[EventType, ChainEventType], data: Optional[Union[Dict, ChainEventData]] = None,
|
||||||
priority: int = DEFAULT_EVENT_PRIORITY) -> Optional[Event]:
|
priority: int = DEFAULT_EVENT_PRIORITY) -> Optional[Event]:
|
||||||
"""
|
"""
|
||||||
@@ -148,12 +139,6 @@ class EventManager(metaclass=Singleton):
|
|||||||
:return: 如果是链式事件,返回处理后的事件数据;否则返回 None
|
:return: 如果是链式事件,返回处理后的事件数据;否则返回 None
|
||||||
"""
|
"""
|
||||||
event = Event(etype, data, priority)
|
event = Event(etype, data, priority)
|
||||||
event_hash = self.__get_event_hash(event)
|
|
||||||
with self.__lock:
|
|
||||||
if event_hash in self.__processing_events:
|
|
||||||
logger.debug(f"Duplicate event ignored: {event}")
|
|
||||||
return None
|
|
||||||
self.__processing_events[event_hash] = event
|
|
||||||
if isinstance(etype, EventType):
|
if isinstance(etype, EventType):
|
||||||
self.__trigger_broadcast_event(event)
|
self.__trigger_broadcast_event(event)
|
||||||
elif isinstance(etype, ChainEventType):
|
elif isinstance(etype, ChainEventType):
|
||||||
@@ -335,14 +320,9 @@ class EventManager(metaclass=Singleton):
|
|||||||
"""
|
"""
|
||||||
触发链式事件,按顺序调用订阅的处理器,并记录处理耗时
|
触发链式事件,按顺序调用订阅的处理器,并记录处理耗时
|
||||||
"""
|
"""
|
||||||
try:
|
logger.debug(f"Triggering synchronous chain event: {event}")
|
||||||
logger.debug(f"Triggering synchronous chain event: {event}")
|
dispatch = self.__dispatch_chain_event(event)
|
||||||
dispatch = self.__dispatch_chain_event(event)
|
return event if dispatch else None
|
||||||
return event if dispatch else None
|
|
||||||
finally:
|
|
||||||
event_hash = self.__get_event_hash(event)
|
|
||||||
with self.__lock:
|
|
||||||
self.__processing_events.pop(event_hash, None)
|
|
||||||
|
|
||||||
def __trigger_broadcast_event(self, event: Event):
|
def __trigger_broadcast_event(self, event: Event):
|
||||||
"""
|
"""
|
||||||
@@ -383,9 +363,6 @@ class EventManager(metaclass=Singleton):
|
|||||||
return
|
return
|
||||||
for handler_id, handler in handlers.items():
|
for handler_id, handler in handlers.items():
|
||||||
self.__executor.submit(self.__safe_invoke_handler, handler, event)
|
self.__executor.submit(self.__safe_invoke_handler, handler, event)
|
||||||
event_hash = self.__get_event_hash(event)
|
|
||||||
with self.__lock:
|
|
||||||
self.__processing_events.pop(event_hash, None)
|
|
||||||
|
|
||||||
def __safe_invoke_handler(self, handler: Callable, event: Event):
|
def __safe_invoke_handler(self, handler: Callable, event: Event):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ from jinja2 import Template
|
|||||||
|
|
||||||
from app.core.config import settings
|
from app.core.config import settings
|
||||||
from app.core.context import MediaInfo
|
from app.core.context import MediaInfo
|
||||||
|
from app.core.event import eventmanager
|
||||||
from app.core.meta import MetaBase
|
from app.core.meta import MetaBase
|
||||||
from app.core.metainfo import MetaInfo, MetaInfoPath
|
from app.core.metainfo import MetaInfo, MetaInfoPath
|
||||||
from app.helper.directory import DirectoryHelper
|
from app.helper.directory import DirectoryHelper
|
||||||
@@ -16,7 +17,8 @@ from app.log import logger
|
|||||||
from app.modules import _ModuleBase
|
from app.modules import _ModuleBase
|
||||||
from app.modules.filemanager.storages import StorageBase
|
from app.modules.filemanager.storages import StorageBase
|
||||||
from app.schemas import TransferInfo, ExistMediaInfo, TmdbEpisode, TransferDirectoryConf, FileItem, StorageUsage
|
from app.schemas import TransferInfo, ExistMediaInfo, TmdbEpisode, TransferDirectoryConf, FileItem, StorageUsage
|
||||||
from app.schemas.types import MediaType, ModuleType
|
from app.schemas.event import SmartRenameEventData
|
||||||
|
from app.schemas.types import MediaType, ModuleType, ChainEventType
|
||||||
from app.utils.system import SystemUtils
|
from app.utils.system import SystemUtils
|
||||||
|
|
||||||
lock = Lock()
|
lock = Lock()
|
||||||
@@ -130,7 +132,6 @@ class FileManagerModule(_ModuleBase):
|
|||||||
)
|
)
|
||||||
return str(path)
|
return str(path)
|
||||||
|
|
||||||
|
|
||||||
def save_config(self, storage: str, conf: Dict) -> None:
|
def save_config(self, storage: str, conf: Dict) -> None:
|
||||||
"""
|
"""
|
||||||
保存存储配置
|
保存存储配置
|
||||||
@@ -1146,18 +1147,46 @@ class FileManagerModule(_ModuleBase):
|
|||||||
# 文件后缀
|
# 文件后缀
|
||||||
"fileExt": file_ext,
|
"fileExt": file_ext,
|
||||||
# 自定义占位符
|
# 自定义占位符
|
||||||
"customization": meta.customization
|
"customization": meta.customization,
|
||||||
|
# 文件元数据
|
||||||
|
"__meta__": meta,
|
||||||
|
# 识别的媒体信息
|
||||||
|
"__mediainfo__": mediainfo,
|
||||||
|
# 当前季的全部集信息
|
||||||
|
"__episodes_info__": episodes_info,
|
||||||
}
|
}
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_rename_path(template_string: str, rename_dict: dict, path: Path = None) -> Path:
|
def get_rename_path(template_string: str, rename_dict: dict, path: Path = None) -> Path:
|
||||||
"""
|
"""
|
||||||
生成重命名后的完整路径
|
生成重命名后的完整路径,支持智能重命名事件
|
||||||
|
:param template_string: Jinja2 模板字符串
|
||||||
|
:param rename_dict: 渲染上下文,用于替换模板中的变量
|
||||||
|
:param path: 可选的基础路径,如果提供,将在其基础上拼接生成的路径
|
||||||
|
:return: 生成的完整路径
|
||||||
"""
|
"""
|
||||||
# 创建jinja2模板对象
|
# 创建jinja2模板对象
|
||||||
template = Template(template_string)
|
template = Template(template_string)
|
||||||
# 渲染生成的字符串
|
# 渲染生成的字符串
|
||||||
render_str = template.render(rename_dict)
|
render_str = template.render(rename_dict)
|
||||||
|
|
||||||
|
logger.debug(f"Initial render string: {render_str}")
|
||||||
|
# 发送智能重命名事件
|
||||||
|
event_data = SmartRenameEventData(
|
||||||
|
template_string=template_string,
|
||||||
|
rename_dict=rename_dict,
|
||||||
|
render_str=render_str,
|
||||||
|
path=path
|
||||||
|
)
|
||||||
|
event = eventmanager.send_event(ChainEventType.SmartRename, event_data)
|
||||||
|
# 检查事件返回的结果
|
||||||
|
if event and event.event_data:
|
||||||
|
event_data: SmartRenameEventData = event.event_data
|
||||||
|
if event_data.updated and event_data.updated_str:
|
||||||
|
logger.debug(f"Render string updated by event: "
|
||||||
|
f"{render_str} -> {event_data.updated_str} (source: {event_data.source})")
|
||||||
|
render_str = event_data.updated_str
|
||||||
|
|
||||||
# 目的路径
|
# 目的路径
|
||||||
if path:
|
if path:
|
||||||
return path / render_str
|
return path / render_str
|
||||||
|
|||||||
+30
-1
@@ -1,4 +1,5 @@
|
|||||||
from typing import Optional, Dict
|
from pathlib import Path
|
||||||
|
from typing import Optional, Dict, Any
|
||||||
|
|
||||||
from pydantic import BaseModel, Field, root_validator
|
from pydantic import BaseModel, Field, root_validator
|
||||||
|
|
||||||
@@ -114,3 +115,31 @@ class CommandRegisterEventData(ChainEventData):
|
|||||||
# 输出参数
|
# 输出参数
|
||||||
cancel: bool = Field(False, description="是否取消注册")
|
cancel: bool = Field(False, description="是否取消注册")
|
||||||
source: str = Field("未知拦截源", description="拦截源")
|
source: str = Field("未知拦截源", description="拦截源")
|
||||||
|
|
||||||
|
|
||||||
|
class SmartRenameEventData(ChainEventData):
|
||||||
|
"""
|
||||||
|
SmartRename 事件的数据模型
|
||||||
|
|
||||||
|
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(False, description="是否已更新")
|
||||||
|
updated_str: Optional[str] = Field(None, description="更新后的字符串")
|
||||||
|
source: Optional[str] = Field("未知拦截源", description="拦截源")
|
||||||
|
|||||||
@@ -60,14 +60,16 @@ class EventType(Enum):
|
|||||||
|
|
||||||
# 同步链式事件
|
# 同步链式事件
|
||||||
class ChainEventType(Enum):
|
class ChainEventType(Enum):
|
||||||
# 名称识别请求
|
# 名称识别
|
||||||
NameRecognize = "name.recognize"
|
NameRecognize = "name.recognize"
|
||||||
# 认证验证请求
|
# 认证验证
|
||||||
AuthVerification = "auth.verification"
|
AuthVerification = "auth.verification"
|
||||||
# 认证拦截请求
|
# 认证拦截
|
||||||
AuthIntercept = "auth.intercept"
|
AuthIntercept = "auth.intercept"
|
||||||
# 命令注册请求
|
# 命令注册
|
||||||
CommandRegister = "command.register"
|
CommandRegister = "command.register"
|
||||||
|
# 智能重命名
|
||||||
|
SmartRename = "SmartRename"
|
||||||
|
|
||||||
|
|
||||||
# 系统配置Key字典
|
# 系统配置Key字典
|
||||||
|
|||||||
Reference in New Issue
Block a user