mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-05-16 13:55:28 +08:00
fix Config reload
This commit is contained in:
@@ -37,6 +37,7 @@ from app.utils.crypto import HashUtils
|
|||||||
from app.utils.http import RequestUtils
|
from app.utils.http import RequestUtils
|
||||||
from app.utils.security import SecurityUtils
|
from app.utils.security import SecurityUtils
|
||||||
from app.utils.url import UrlUtils
|
from app.utils.url import UrlUtils
|
||||||
|
from core.event import eventmanager
|
||||||
from version import APP_VERSION
|
from version import APP_VERSION
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
@@ -230,6 +231,10 @@ def set_env_setting(env: dict,
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if success_updates:
|
||||||
|
for key, value in success_updates.items():
|
||||||
|
eventmanager.send_event()
|
||||||
|
|
||||||
return schemas.Response(
|
return schemas.Response(
|
||||||
success=True,
|
success=True,
|
||||||
message="所有配置项更新成功",
|
message="所有配置项更新成功",
|
||||||
|
|||||||
@@ -4,115 +4,17 @@ import os
|
|||||||
import secrets
|
import secrets
|
||||||
import sys
|
import sys
|
||||||
import threading
|
import threading
|
||||||
from collections import defaultdict
|
|
||||||
from enum import Enum
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Dict, List, Optional, Tuple, Type, Callable
|
from typing import Any, Dict, List, Optional, Tuple, Type
|
||||||
|
|
||||||
from dotenv import set_key
|
from dotenv import set_key
|
||||||
from pydantic import BaseModel, BaseSettings, validator, Field
|
from pydantic import BaseModel, BaseSettings, validator, Field
|
||||||
|
|
||||||
from app.log import logger, log_settings, LogConfigModel
|
from app.log import logger, log_settings, LogConfigModel
|
||||||
from app.utils.object import ObjectUtils
|
|
||||||
from app.utils.system import SystemUtils
|
from app.utils.system import SystemUtils
|
||||||
from app.utils.url import UrlUtils
|
from app.utils.url import UrlUtils
|
||||||
|
from app.schemas.types import EventType
|
||||||
|
from app.schemas import ConfigChangeEventData
|
||||||
class ConfigChangeType(Enum):
|
|
||||||
"""
|
|
||||||
配置变更类型
|
|
||||||
"""
|
|
||||||
ADD = "add"
|
|
||||||
UPDATE = "update"
|
|
||||||
DELETE = "delete"
|
|
||||||
|
|
||||||
|
|
||||||
class ConfigChangeEvent:
|
|
||||||
"""
|
|
||||||
配置变更事件
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, key: str, old_value: Any, new_value: Any,
|
|
||||||
change_type: ConfigChangeType = ConfigChangeType.UPDATE):
|
|
||||||
self.key = key
|
|
||||||
self.old_value = old_value
|
|
||||||
self.new_value = new_value
|
|
||||||
self.change_type = change_type
|
|
||||||
self.timestamp = threading.Event()
|
|
||||||
|
|
||||||
|
|
||||||
class ConfigObserver:
|
|
||||||
"""
|
|
||||||
配置观察者接口
|
|
||||||
"""
|
|
||||||
|
|
||||||
def on_config_changed(self, event: ConfigChangeEvent):
|
|
||||||
"""
|
|
||||||
配置变更回调
|
|
||||||
"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class ConfigNotifier:
|
|
||||||
"""
|
|
||||||
配置变更通知器
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self._observers: Dict[str, List[ConfigObserver]] = defaultdict(list)
|
|
||||||
self._global_observers: List[ConfigObserver] = []
|
|
||||||
self._lock = threading.RLock()
|
|
||||||
|
|
||||||
def add_observer(self, observer: ConfigObserver, config_keys: Optional[List[str]] = None):
|
|
||||||
"""
|
|
||||||
添加观察者
|
|
||||||
:param observer: 观察者对象
|
|
||||||
:param config_keys: 监听的配置键列表,为None时监听所有配置变更
|
|
||||||
"""
|
|
||||||
with self._lock:
|
|
||||||
if config_keys is None:
|
|
||||||
self._global_observers.append(observer)
|
|
||||||
else:
|
|
||||||
for key in config_keys:
|
|
||||||
self._observers[key].append(observer)
|
|
||||||
|
|
||||||
def remove_observer(self, observer: ConfigObserver, config_keys: Optional[List[str]] = None):
|
|
||||||
"""
|
|
||||||
移除观察者
|
|
||||||
:param observer: 观察者对象
|
|
||||||
:param config_keys: 监听的配置键列表,为None时移除全局观察者
|
|
||||||
"""
|
|
||||||
with self._lock:
|
|
||||||
if config_keys is None:
|
|
||||||
if observer in self._global_observers:
|
|
||||||
self._global_observers.remove(observer)
|
|
||||||
else:
|
|
||||||
for key in config_keys:
|
|
||||||
if observer in self._observers[key]:
|
|
||||||
self._observers[key].remove(observer)
|
|
||||||
|
|
||||||
def notify(self, event: ConfigChangeEvent):
|
|
||||||
"""
|
|
||||||
通知观察者配置变更
|
|
||||||
"""
|
|
||||||
with self._lock:
|
|
||||||
# 通知全局观察者
|
|
||||||
for observer in self._global_observers:
|
|
||||||
try:
|
|
||||||
observer.on_config_changed(event)
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"配置观察者 {observer} 处理配置变更时出错: {e}")
|
|
||||||
|
|
||||||
# 通知特定配置键的观察者
|
|
||||||
for observer in self._observers.get(event.key, []):
|
|
||||||
try:
|
|
||||||
observer.on_config_changed(event)
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"配置观察者 {observer} 处理配置变更 {event.key} 时出错: {e}")
|
|
||||||
|
|
||||||
|
|
||||||
# 全局配置通知器
|
|
||||||
config_notifier = ConfigNotifier()
|
|
||||||
|
|
||||||
|
|
||||||
class ConfigModel(BaseModel):
|
class ConfigModel(BaseModel):
|
||||||
@@ -558,8 +460,13 @@ class Settings(BaseSettings, ConfigModel, LogConfigModel):
|
|||||||
if hasattr(log_settings, key):
|
if hasattr(log_settings, key):
|
||||||
setattr(log_settings, key, converted_value)
|
setattr(log_settings, key, converted_value)
|
||||||
# 发送配置变更通知
|
# 发送配置变更通知
|
||||||
event = ConfigChangeEvent(key, old_value, converted_value)
|
from app.core.event import eventmanager
|
||||||
config_notifier.notify(event)
|
eventmanager.send_event(etype=EventType.ConfigChanged, data=ConfigChangeEventData(
|
||||||
|
key=key,
|
||||||
|
old_value=old_value,
|
||||||
|
new_value=converted_value,
|
||||||
|
change_type="update"
|
||||||
|
))
|
||||||
|
|
||||||
return success, message
|
return success, message
|
||||||
return True, ""
|
return True, ""
|
||||||
@@ -791,91 +698,3 @@ class GlobalVar(object):
|
|||||||
|
|
||||||
# 全局标识
|
# 全局标识
|
||||||
global_vars = GlobalVar()
|
global_vars = GlobalVar()
|
||||||
|
|
||||||
|
|
||||||
class HotReloadManager(ConfigObserver):
|
|
||||||
"""
|
|
||||||
配置热更新管理器
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self._reload_handlers: Dict[str, Callable] = {}
|
|
||||||
# 注册为全局配置观察者
|
|
||||||
config_notifier.add_observer(self)
|
|
||||||
|
|
||||||
def register_handler(self, config_keys: List[str], handler: Callable[[Any, Any], None]):
|
|
||||||
"""
|
|
||||||
注册配置变更处理器
|
|
||||||
:param config_keys: 配置键列表
|
|
||||||
:param handler: 处理函数,接收 (old_value, new_value) 参数
|
|
||||||
"""
|
|
||||||
for key in config_keys:
|
|
||||||
self._reload_handlers[key] = handler
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def __get_callable(name: str):
|
|
||||||
"""
|
|
||||||
根据类名获取类实例,首先检查全局变量中是否存在该类,如果不存在则尝试动态导入模块。
|
|
||||||
:param name: 方法名/类名.方法名
|
|
||||||
:return: 类的实例
|
|
||||||
"""
|
|
||||||
# 检查类是否在全局变量中
|
|
||||||
if name in globals():
|
|
||||||
try:
|
|
||||||
class_obj = globals()[name]()
|
|
||||||
return class_obj
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(str(e))
|
|
||||||
return None
|
|
||||||
|
|
||||||
# TODO 如果类不在全局变量中,尝试动态导入模块并创建实例
|
|
||||||
|
|
||||||
return None
|
|
||||||
|
|
||||||
def on_config_changed(self, event: ConfigChangeEvent):
|
|
||||||
"""
|
|
||||||
处理配置变更事件
|
|
||||||
"""
|
|
||||||
if event.key in self._reload_handlers:
|
|
||||||
try:
|
|
||||||
handler = self._reload_handlers[event.key]
|
|
||||||
# 可执行函数
|
|
||||||
func = self.__get_callable(handler.__qualname__)
|
|
||||||
# 参数数量
|
|
||||||
args_num = ObjectUtils.arguments(func)
|
|
||||||
if args_num < 2:
|
|
||||||
func()
|
|
||||||
else:
|
|
||||||
func(event.old_value, event.new_value)
|
|
||||||
logger.info(f"配置 {event.key} 热更新成功:{func}")
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"配置 {event.key} 热更新失败: {e}")
|
|
||||||
|
|
||||||
|
|
||||||
# 初始化热更新管理器
|
|
||||||
hot_reload_manager = HotReloadManager()
|
|
||||||
|
|
||||||
|
|
||||||
def on_config_change(config_keys: List[str]):
|
|
||||||
"""
|
|
||||||
装饰器:用于注册配置变更处理函数
|
|
||||||
|
|
||||||
使用示例:
|
|
||||||
@on_config_change(['PROXY_HOST', 'TMDB_API_KEY'])
|
|
||||||
def handle_config_change(old_value, new_value):
|
|
||||||
pass
|
|
||||||
"""
|
|
||||||
|
|
||||||
def decorator(func: Callable[[Any, Any], None]):
|
|
||||||
hot_reload_manager.register_handler(config_keys, func)
|
|
||||||
return func
|
|
||||||
|
|
||||||
return decorator
|
|
||||||
|
|
||||||
|
|
||||||
@on_config_change(['DEBUG', 'LOG_LEVEL'])
|
|
||||||
def handle_logger_change():
|
|
||||||
"""
|
|
||||||
默认的配置变更处理函数
|
|
||||||
"""
|
|
||||||
logger.update_loggers()
|
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ from functools import lru_cache
|
|||||||
from queue import Empty, PriorityQueue
|
from queue import Empty, PriorityQueue
|
||||||
from typing import Callable, Dict, List, Optional, Union
|
from typing import Callable, Dict, List, Optional, Union
|
||||||
|
|
||||||
from app.helper.message import MessageHelper
|
|
||||||
from app.helper.thread import ThreadHelper
|
from app.helper.thread import ThreadHelper
|
||||||
from app.log import logger
|
from app.log import logger
|
||||||
from app.schemas import ChainEventData
|
from app.schemas import ChainEventData
|
||||||
@@ -75,7 +74,6 @@ class EventManager(metaclass=Singleton):
|
|||||||
__event = threading.Event()
|
__event = threading.Event()
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.__messagehelper = MessageHelper()
|
|
||||||
self.__executor = ThreadHelper() # 动态线程池,用于消费事件
|
self.__executor = ThreadHelper() # 动态线程池,用于消费事件
|
||||||
self.__consumer_threads = [] # 用于保存启动的事件消费者线程
|
self.__consumer_threads = [] # 用于保存启动的事件消费者线程
|
||||||
self.__event_queue = PriorityQueue() # 优先级队列
|
self.__event_queue = PriorityQueue() # 优先级队列
|
||||||
@@ -140,11 +138,12 @@ class EventManager(metaclass=Singleton):
|
|||||||
"""
|
"""
|
||||||
event = Event(etype, data, priority)
|
event = Event(etype, data, priority)
|
||||||
if isinstance(etype, EventType):
|
if isinstance(etype, EventType):
|
||||||
self.__trigger_broadcast_event(event)
|
return self.__trigger_broadcast_event(event)
|
||||||
elif isinstance(etype, ChainEventType):
|
elif isinstance(etype, ChainEventType):
|
||||||
return self.__trigger_chain_event(event)
|
return self.__trigger_chain_event(event)
|
||||||
else:
|
else:
|
||||||
logger.error(f"Unknown event type: {etype}")
|
logger.error(f"Unknown event type: {etype}")
|
||||||
|
return None
|
||||||
|
|
||||||
def add_event_listener(self, event_type: Union[EventType, ChainEventType], handler: Callable,
|
def add_event_listener(self, event_type: Union[EventType, ChainEventType], handler: Callable,
|
||||||
priority: Optional[int] = DEFAULT_EVENT_PRIORITY):
|
priority: Optional[int] = DEFAULT_EVENT_PRIORITY):
|
||||||
@@ -293,7 +292,7 @@ class EventManager(metaclass=Singleton):
|
|||||||
|
|
||||||
# 对于类实例(实现了 __call__ 方法)
|
# 对于类实例(实现了 __call__ 方法)
|
||||||
if not inspect.isfunction(handler) and hasattr(handler, "__call__"):
|
if not inspect.isfunction(handler) and hasattr(handler, "__call__"):
|
||||||
handler_cls = handler.__class__ # noqa
|
handler_cls = handler.__class__ # noqa
|
||||||
return cls.__get_handler_identifier(handler_cls)
|
return cls.__get_handler_identifier(handler_cls)
|
||||||
|
|
||||||
# 对于未绑定方法、静态方法、类方法,使用 __qualname__ 提取类信息
|
# 对于未绑定方法、静态方法、类方法,使用 __qualname__ 提取类信息
|
||||||
@@ -303,6 +302,7 @@ class EventManager(metaclass=Singleton):
|
|||||||
module = inspect.getmodule(handler)
|
module = inspect.getmodule(handler)
|
||||||
module_name = module.__name__ if module else "unknown_module"
|
module_name = module.__name__ if module else "unknown_module"
|
||||||
return f"{module_name}.{class_name}"
|
return f"{module_name}.{class_name}"
|
||||||
|
return None
|
||||||
|
|
||||||
def __is_handler_enabled(self, handler: Callable) -> bool:
|
def __is_handler_enabled(self, handler: Callable) -> bool:
|
||||||
"""
|
"""
|
||||||
@@ -398,16 +398,28 @@ class EventManager(metaclass=Singleton):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
from app.core.plugin import PluginManager
|
from app.core.plugin import PluginManager
|
||||||
|
from app.core.module import ModuleManager
|
||||||
|
|
||||||
if class_name in PluginManager().get_plugin_ids():
|
if class_name in PluginManager().get_plugin_ids():
|
||||||
# 定义一个插件调用函数
|
|
||||||
def plugin_callable():
|
def plugin_callable():
|
||||||
|
"""
|
||||||
|
插件调用函数
|
||||||
|
"""
|
||||||
PluginManager().run_plugin_method(class_name, method_name, event_to_process)
|
PluginManager().run_plugin_method(class_name, method_name, event_to_process)
|
||||||
|
|
||||||
if is_broadcast_event:
|
if is_broadcast_event:
|
||||||
self.__executor.submit(plugin_callable)
|
self.__executor.submit(plugin_callable)
|
||||||
else:
|
else:
|
||||||
plugin_callable()
|
plugin_callable()
|
||||||
|
elif class_name in ModuleManager().get_module_ids():
|
||||||
|
module = ModuleManager().get_running_module(class_name)
|
||||||
|
if module:
|
||||||
|
method = getattr(module, method_name, None)
|
||||||
|
if method:
|
||||||
|
if is_broadcast_event:
|
||||||
|
self.__executor.submit(method, event_to_process)
|
||||||
|
else:
|
||||||
|
method(event_to_process)
|
||||||
else:
|
else:
|
||||||
# 获取全局对象或模块类的实例
|
# 获取全局对象或模块类的实例
|
||||||
class_obj = self.__get_class_instance(class_name)
|
class_obj = self.__get_class_instance(class_name)
|
||||||
@@ -441,11 +453,20 @@ class EventManager(metaclass=Singleton):
|
|||||||
if class_name == "Command":
|
if class_name == "Command":
|
||||||
module_name = "app.command"
|
module_name = "app.command"
|
||||||
module = importlib.import_module(module_name)
|
module = importlib.import_module(module_name)
|
||||||
|
elif class_name == "Monitor":
|
||||||
|
module_name = "app.monitor"
|
||||||
|
module = importlib.import_module(module_name)
|
||||||
|
elif class_name == "Scheduler":
|
||||||
|
module_name = "app.scheduler"
|
||||||
|
module = importlib.import_module(module_name)
|
||||||
|
elif class_name == "PluginManager":
|
||||||
|
module_name = "app.core.plugin"
|
||||||
|
module = importlib.import_module(module_name)
|
||||||
elif class_name.endswith("Chain"):
|
elif class_name.endswith("Chain"):
|
||||||
module_name = f"app.chain.{class_name[:-5].lower()}"
|
module_name = f"app.chain.{class_name[:-5].lower()}"
|
||||||
module = importlib.import_module(module_name)
|
module = importlib.import_module(module_name)
|
||||||
else:
|
else:
|
||||||
logger.debug(f"事件处理出错:无效的 Chain 类名: {class_name},类名必须以 'Chain' 结尾")
|
logger.debug(f"事件处理出错:不支持的类名: {class_name}")
|
||||||
return None
|
return None
|
||||||
if hasattr(module, class_name):
|
if hasattr(module, class_name):
|
||||||
class_obj = getattr(module, class_name)()
|
class_obj = getattr(module, class_name)()
|
||||||
@@ -491,9 +512,11 @@ class EventManager(metaclass=Singleton):
|
|||||||
names = handler.__qualname__.split(".")
|
names = handler.__qualname__.split(".")
|
||||||
class_name, method_name = names[0], names[1]
|
class_name, method_name = names[0], names[1]
|
||||||
|
|
||||||
self.__messagehelper.put(title=f"{event.event_type} 事件处理出错",
|
# 发送系统错误通知
|
||||||
message=f"{class_name}.{method_name}:{str(e)}",
|
from app.helper.message import MessageHelper
|
||||||
role="system")
|
MessageHelper().put(title=f"{event.event_type} 事件处理出错",
|
||||||
|
message=f"{class_name}.{method_name}:{str(e)}",
|
||||||
|
role="system")
|
||||||
self.send_event(
|
self.send_event(
|
||||||
EventType.SystemError,
|
EventType.SystemError,
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import traceback
|
import traceback
|
||||||
from typing import Generator, Optional, Tuple, Any, Union
|
from typing import Generator, Optional, Tuple, Any, Union, List
|
||||||
|
|
||||||
from app.core.config import settings
|
from app.core.config import settings
|
||||||
from app.core.event import eventmanager
|
from app.core.event import eventmanager
|
||||||
@@ -164,3 +164,9 @@ class ModuleManager(metaclass=Singleton):
|
|||||||
获取模块列表
|
获取模块列表
|
||||||
"""
|
"""
|
||||||
return self._modules
|
return self._modules
|
||||||
|
|
||||||
|
def get_module_ids(self) -> List[str]:
|
||||||
|
"""
|
||||||
|
获取模块id列表
|
||||||
|
"""
|
||||||
|
return list(self._modules.keys())
|
||||||
|
|||||||
@@ -15,8 +15,8 @@ from watchdog.events import FileSystemEventHandler
|
|||||||
from watchdog.observers import Observer
|
from watchdog.observers import Observer
|
||||||
|
|
||||||
from app import schemas
|
from app import schemas
|
||||||
from app.core.config import settings, on_config_change
|
from app.core.config import settings
|
||||||
from app.core.event import eventmanager
|
from app.core.event import eventmanager, Event
|
||||||
from app.db.plugindata_oper import PluginDataOper
|
from app.db.plugindata_oper import PluginDataOper
|
||||||
from app.db.systemconfig_oper import SystemConfigOper
|
from app.db.systemconfig_oper import SystemConfigOper
|
||||||
from app.helper.module import ModuleHelper
|
from app.helper.module import ModuleHelper
|
||||||
@@ -241,11 +241,17 @@ class PluginManager(metaclass=Singleton):
|
|||||||
"""
|
"""
|
||||||
return self._plugins
|
return self._plugins
|
||||||
|
|
||||||
@on_config_change(['PLUGIN_AUTO_RELOAD', 'DEV'])
|
@eventmanager.register(EventType.ConfigChanged)
|
||||||
def handle_config_change(self):
|
def handle_config_changed(self, event: Event):
|
||||||
"""
|
"""
|
||||||
处理配置变更事件,重新加载插件监测
|
处理配置变更事件
|
||||||
|
:param event: 事件对象
|
||||||
"""
|
"""
|
||||||
|
if not event:
|
||||||
|
return
|
||||||
|
event_data: schemas.ConfigChangeEventData = event.event_data
|
||||||
|
if event_data.key not in ['DEV', 'PLUGIN_AUTO_RELOAD']:
|
||||||
|
return
|
||||||
self.reload_monitor()
|
self.reload_monitor()
|
||||||
|
|
||||||
def reload_monitor(self):
|
def reload_monitor(self):
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
from typing import Any, Union
|
from typing import Any, Union
|
||||||
|
|
||||||
from app.core.config import ConfigChangeEvent, config_notifier, ConfigChangeType
|
|
||||||
from app.db import DbOper
|
from app.db import DbOper
|
||||||
from app.db.models.systemconfig import SystemConfig
|
from app.db.models.systemconfig import SystemConfig
|
||||||
from app.schemas.types import SystemConfigKey
|
from app.schemas.types import SystemConfigKey, EventType
|
||||||
from app.utils.singleton import Singleton
|
from app.utils.singleton import Singleton
|
||||||
|
from app.schemas import ConfigChangeEventData
|
||||||
|
|
||||||
|
|
||||||
class SystemConfigOper(DbOper, metaclass=Singleton):
|
class SystemConfigOper(DbOper, metaclass=Singleton):
|
||||||
@@ -35,22 +35,34 @@ class SystemConfigOper(DbOper, metaclass=Singleton):
|
|||||||
conf.update(self._db, {"value": value})
|
conf.update(self._db, {"value": value})
|
||||||
# 发送配置变更通知
|
# 发送配置变更通知
|
||||||
if old_value != value:
|
if old_value != value:
|
||||||
event = ConfigChangeEvent(key, old_value=old_value, new_value=value,
|
from app.core.event import eventmanager
|
||||||
change_type=ConfigChangeType.UPDATE)
|
eventmanager.send_event(etype=EventType.ConfigChanged, data=ConfigChangeEventData(
|
||||||
config_notifier.notify(event)
|
key=key,
|
||||||
|
old_value=old_value,
|
||||||
|
new_value=value,
|
||||||
|
change_type="update"
|
||||||
|
))
|
||||||
else:
|
else:
|
||||||
conf.delete(self._db, conf.id)
|
conf.delete(self._db, conf.id)
|
||||||
# 发送配置删除通知
|
# 发送配置删除通知
|
||||||
event = ConfigChangeEvent(key, old_value=old_value, new_value=None,
|
from app.core.event import eventmanager
|
||||||
change_type=ConfigChangeType.DELETE)
|
eventmanager.send_event(etype=EventType.ConfigChanged, data=ConfigChangeEventData(
|
||||||
config_notifier.notify(event)
|
key=key,
|
||||||
|
old_value=old_value,
|
||||||
|
new_value=value,
|
||||||
|
change_type="delete"
|
||||||
|
))
|
||||||
else:
|
else:
|
||||||
conf = SystemConfig(key=key, value=value)
|
conf = SystemConfig(key=key, value=value)
|
||||||
conf.create(self._db)
|
conf.create(self._db)
|
||||||
# 发送配置变更通知
|
# 发送配置变更通知
|
||||||
event = ConfigChangeEvent(key, old_value=None, new_value=value,
|
from app.core.event import eventmanager
|
||||||
change_type=ConfigChangeType.ADD)
|
eventmanager.send_event(etype=EventType.ConfigChanged, data=ConfigChangeEventData(
|
||||||
config_notifier.notify(event)
|
key=key,
|
||||||
|
old_value=old_value,
|
||||||
|
new_value=value,
|
||||||
|
change_type="add"
|
||||||
|
))
|
||||||
|
|
||||||
def get(self, key: Union[str, SystemConfigKey] = None) -> Any:
|
def get(self, key: Union[str, SystemConfigKey] = None) -> Any:
|
||||||
"""
|
"""
|
||||||
@@ -81,9 +93,13 @@ class SystemConfigOper(DbOper, metaclass=Singleton):
|
|||||||
if conf:
|
if conf:
|
||||||
conf.delete(self._db, conf.id)
|
conf.delete(self._db, conf.id)
|
||||||
# 发送配置变更通知
|
# 发送配置变更通知
|
||||||
event = ConfigChangeEvent(key, old_value=old_value, new_value=None,
|
from app.core.event import eventmanager
|
||||||
change_type=ConfigChangeType.ADD)
|
eventmanager.send_event(etype=EventType.ConfigChanged, data=ConfigChangeEventData(
|
||||||
config_notifier.notify(event)
|
key=key,
|
||||||
|
old_value=old_value,
|
||||||
|
new_value=None,
|
||||||
|
change_type="delete"
|
||||||
|
))
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def __del__(self):
|
def __del__(self):
|
||||||
|
|||||||
@@ -1,18 +1,16 @@
|
|||||||
from typing import Any, Generator, List, Optional, Tuple, Union
|
from typing import Any, Generator, List, Optional, Tuple, Union
|
||||||
|
|
||||||
from app import schemas
|
from app import schemas
|
||||||
from app.core.config import on_config_change
|
|
||||||
from app.core.context import MediaInfo
|
from app.core.context import MediaInfo
|
||||||
from app.core.event import eventmanager
|
from app.core.event import eventmanager, Event
|
||||||
from app.log import logger
|
from app.log import logger
|
||||||
from app.modules import _MediaServerBase, _ModuleBase
|
from app.modules import _MediaServerBase, _ModuleBase
|
||||||
from app.modules.emby.emby import Emby
|
from app.modules.emby.emby import Emby
|
||||||
from app.schemas.types import MediaType, ModuleType, ChainEventType, MediaServerType, SystemConfigKey
|
from app.schemas.types import MediaType, ModuleType, ChainEventType, MediaServerType, SystemConfigKey, EventType
|
||||||
|
|
||||||
|
|
||||||
class EmbyModule(_ModuleBase, _MediaServerBase[Emby]):
|
class EmbyModule(_ModuleBase, _MediaServerBase[Emby]):
|
||||||
|
|
||||||
@on_config_change([SystemConfigKey.MediaServers.value])
|
|
||||||
def init_module(self) -> None:
|
def init_module(self) -> None:
|
||||||
"""
|
"""
|
||||||
初始化模块
|
初始化模块
|
||||||
@@ -20,6 +18,19 @@ class EmbyModule(_ModuleBase, _MediaServerBase[Emby]):
|
|||||||
super().init_service(service_name=Emby.__name__.lower(),
|
super().init_service(service_name=Emby.__name__.lower(),
|
||||||
service_type=lambda conf: Emby(**conf.config, sync_libraries=conf.sync_libraries))
|
service_type=lambda conf: Emby(**conf.config, sync_libraries=conf.sync_libraries))
|
||||||
|
|
||||||
|
@eventmanager.register(EventType.ConfigChanged)
|
||||||
|
def handle_config_changed(self, event: Event):
|
||||||
|
"""
|
||||||
|
处理配置变更事件
|
||||||
|
:param event: 事件对象
|
||||||
|
"""
|
||||||
|
if not event:
|
||||||
|
return
|
||||||
|
event_data: schemas.ConfigChangeEventData = event.event_data
|
||||||
|
if event_data.key not in [SystemConfigKey.MediaServers.value]:
|
||||||
|
return
|
||||||
|
self.init_module()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_name() -> str:
|
def get_name() -> str:
|
||||||
return "Emby"
|
return "Emby"
|
||||||
@@ -271,7 +282,8 @@ class EmbyModule(_ModuleBase, _MediaServerBase[Emby]):
|
|||||||
) for season, episodes in seasoninfo.items()]
|
) for season, episodes in seasoninfo.items()]
|
||||||
|
|
||||||
def mediaserver_playing(self, server: str,
|
def mediaserver_playing(self, server: str,
|
||||||
count: Optional[int] = 20, username: Optional[str] = None) -> List[schemas.MediaServerPlayItem]:
|
count: Optional[int] = 20, username: Optional[str] = None) -> List[
|
||||||
|
schemas.MediaServerPlayItem]:
|
||||||
"""
|
"""
|
||||||
获取媒体服务器正在播放信息
|
获取媒体服务器正在播放信息
|
||||||
"""
|
"""
|
||||||
@@ -290,7 +302,8 @@ class EmbyModule(_ModuleBase, _MediaServerBase[Emby]):
|
|||||||
return server_obj.get_play_url(item_id)
|
return server_obj.get_play_url(item_id)
|
||||||
|
|
||||||
def mediaserver_latest(self, server: Optional[str] = None,
|
def mediaserver_latest(self, server: Optional[str] = None,
|
||||||
count: Optional[int] = 20, username: Optional[str] = None) -> List[schemas.MediaServerPlayItem]:
|
count: Optional[int] = 20, username: Optional[str] = None) -> List[
|
||||||
|
schemas.MediaServerPlayItem]:
|
||||||
"""
|
"""
|
||||||
获取媒体服务器最新入库条目
|
获取媒体服务器最新入库条目
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -1,19 +1,17 @@
|
|||||||
from typing import Any, Generator, List, Optional, Tuple, Union
|
from typing import Any, Generator, List, Optional, Tuple, Union
|
||||||
|
|
||||||
from app import schemas
|
from app import schemas
|
||||||
from app.core.config import on_config_change
|
|
||||||
from app.core.context import MediaInfo
|
from app.core.context import MediaInfo
|
||||||
from app.core.event import eventmanager
|
from app.core.event import eventmanager, Event
|
||||||
from app.log import logger
|
from app.log import logger
|
||||||
from app.modules import _MediaServerBase, _ModuleBase
|
from app.modules import _MediaServerBase, _ModuleBase
|
||||||
from app.modules.jellyfin.jellyfin import Jellyfin
|
from app.modules.jellyfin.jellyfin import Jellyfin
|
||||||
from app.schemas import AuthCredentials, AuthInterceptCredentials
|
from app.schemas import AuthCredentials, AuthInterceptCredentials
|
||||||
from app.schemas.types import MediaType, ModuleType, ChainEventType, MediaServerType, SystemConfigKey
|
from app.schemas.types import MediaType, ModuleType, ChainEventType, MediaServerType, SystemConfigKey, EventType
|
||||||
|
|
||||||
|
|
||||||
class JellyfinModule(_ModuleBase, _MediaServerBase[Jellyfin]):
|
class JellyfinModule(_ModuleBase, _MediaServerBase[Jellyfin]):
|
||||||
|
|
||||||
@on_config_change([SystemConfigKey.MediaServers.value])
|
|
||||||
def init_module(self) -> None:
|
def init_module(self) -> None:
|
||||||
"""
|
"""
|
||||||
初始化模块
|
初始化模块
|
||||||
@@ -21,6 +19,19 @@ class JellyfinModule(_ModuleBase, _MediaServerBase[Jellyfin]):
|
|||||||
super().init_service(service_name=Jellyfin.__name__.lower(),
|
super().init_service(service_name=Jellyfin.__name__.lower(),
|
||||||
service_type=lambda conf: Jellyfin(**conf.config, sync_libraries=conf.sync_libraries))
|
service_type=lambda conf: Jellyfin(**conf.config, sync_libraries=conf.sync_libraries))
|
||||||
|
|
||||||
|
@eventmanager.register(EventType.ConfigChanged)
|
||||||
|
def handle_config_changed(self, event: Event):
|
||||||
|
"""
|
||||||
|
处理配置变更事件
|
||||||
|
:param event: 事件对象
|
||||||
|
"""
|
||||||
|
if not event:
|
||||||
|
return
|
||||||
|
event_data: schemas.ConfigChangeEventData = event.event_data
|
||||||
|
if event_data.key not in [SystemConfigKey.MediaServers.value]:
|
||||||
|
return
|
||||||
|
self.init_module()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_name() -> str:
|
def get_name() -> str:
|
||||||
return "Jellyfin"
|
return "Jellyfin"
|
||||||
|
|||||||
@@ -1,19 +1,17 @@
|
|||||||
from typing import Optional, Tuple, Union, Any, List, Generator
|
from typing import Optional, Tuple, Union, Any, List, Generator
|
||||||
|
|
||||||
from app import schemas
|
from app import schemas
|
||||||
from app.core.config import on_config_change
|
|
||||||
from app.core.context import MediaInfo
|
from app.core.context import MediaInfo
|
||||||
from app.core.event import eventmanager
|
from app.core.event import eventmanager, Event
|
||||||
from app.log import logger
|
from app.log import logger
|
||||||
from app.modules import _ModuleBase, _MediaServerBase
|
from app.modules import _ModuleBase, _MediaServerBase
|
||||||
from app.modules.plex.plex import Plex
|
from app.modules.plex.plex import Plex
|
||||||
from app.schemas import AuthCredentials, AuthInterceptCredentials
|
from app.schemas import AuthCredentials, AuthInterceptCredentials
|
||||||
from app.schemas.types import MediaType, ModuleType, ChainEventType, MediaServerType, SystemConfigKey
|
from app.schemas.types import MediaType, ModuleType, ChainEventType, MediaServerType, SystemConfigKey, EventType
|
||||||
|
|
||||||
|
|
||||||
class PlexModule(_ModuleBase, _MediaServerBase[Plex]):
|
class PlexModule(_ModuleBase, _MediaServerBase[Plex]):
|
||||||
|
|
||||||
@on_config_change([SystemConfigKey.MediaServers.value])
|
|
||||||
def init_module(self) -> None:
|
def init_module(self) -> None:
|
||||||
"""
|
"""
|
||||||
初始化模块
|
初始化模块
|
||||||
@@ -21,6 +19,19 @@ class PlexModule(_ModuleBase, _MediaServerBase[Plex]):
|
|||||||
super().init_service(service_name=Plex.__name__.lower(),
|
super().init_service(service_name=Plex.__name__.lower(),
|
||||||
service_type=lambda conf: Plex(**conf.config, sync_libraries=conf.sync_libraries))
|
service_type=lambda conf: Plex(**conf.config, sync_libraries=conf.sync_libraries))
|
||||||
|
|
||||||
|
@eventmanager.register(EventType.ConfigChanged)
|
||||||
|
def handle_config_changed(self, event: Event):
|
||||||
|
"""
|
||||||
|
处理配置变更事件
|
||||||
|
:param event: 事件对象
|
||||||
|
"""
|
||||||
|
if not event:
|
||||||
|
return
|
||||||
|
event_data: schemas.ConfigChangeEventData = event.event_data
|
||||||
|
if event_data.key not in [SystemConfigKey.MediaServers.value]:
|
||||||
|
return
|
||||||
|
self.init_module()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_name() -> str:
|
def get_name() -> str:
|
||||||
return "Plex"
|
return "Plex"
|
||||||
|
|||||||
@@ -5,19 +5,19 @@ from qbittorrentapi import TorrentFilesList
|
|||||||
from torrentool.torrent import Torrent
|
from torrentool.torrent import Torrent
|
||||||
|
|
||||||
from app import schemas
|
from app import schemas
|
||||||
from app.core.config import settings, on_config_change
|
from app.core.config import settings
|
||||||
from app.core.metainfo import MetaInfo
|
from app.core.metainfo import MetaInfo
|
||||||
|
from app.core.event import eventmanager, Event
|
||||||
from app.log import logger
|
from app.log import logger
|
||||||
from app.modules import _ModuleBase, _DownloaderBase
|
from app.modules import _ModuleBase, _DownloaderBase
|
||||||
from app.modules.qbittorrent.qbittorrent import Qbittorrent
|
from app.modules.qbittorrent.qbittorrent import Qbittorrent
|
||||||
from app.schemas import TransferTorrent, DownloadingTorrent
|
from app.schemas import TransferTorrent, DownloadingTorrent
|
||||||
from app.schemas.types import TorrentStatus, ModuleType, DownloaderType, SystemConfigKey
|
from app.schemas.types import TorrentStatus, ModuleType, DownloaderType, SystemConfigKey, EventType
|
||||||
from app.utils.string import StringUtils
|
from app.utils.string import StringUtils
|
||||||
|
|
||||||
|
|
||||||
class QbittorrentModule(_ModuleBase, _DownloaderBase[Qbittorrent]):
|
class QbittorrentModule(_ModuleBase, _DownloaderBase[Qbittorrent]):
|
||||||
|
|
||||||
@on_config_change([SystemConfigKey.Downloaders.value])
|
|
||||||
def init_module(self) -> None:
|
def init_module(self) -> None:
|
||||||
"""
|
"""
|
||||||
初始化模块
|
初始化模块
|
||||||
@@ -25,6 +25,19 @@ class QbittorrentModule(_ModuleBase, _DownloaderBase[Qbittorrent]):
|
|||||||
super().init_service(service_name=Qbittorrent.__name__.lower(),
|
super().init_service(service_name=Qbittorrent.__name__.lower(),
|
||||||
service_type=Qbittorrent)
|
service_type=Qbittorrent)
|
||||||
|
|
||||||
|
@eventmanager.register(EventType.ConfigChanged)
|
||||||
|
def handle_config_changed(self, event: Event):
|
||||||
|
"""
|
||||||
|
处理配置变更事件
|
||||||
|
:param event: 事件对象
|
||||||
|
"""
|
||||||
|
if not event:
|
||||||
|
return
|
||||||
|
event_data: schemas.ConfigChangeEventData = event.event_data
|
||||||
|
if event_data.key not in [SystemConfigKey.Downloaders.value]:
|
||||||
|
return
|
||||||
|
self.init_module()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_name() -> str:
|
def get_name() -> str:
|
||||||
return "Qbittorrent"
|
return "Qbittorrent"
|
||||||
@@ -287,7 +300,8 @@ class QbittorrentModule(_ModuleBase, _DownloaderBase[Qbittorrent]):
|
|||||||
dlspeed=StringUtils.str_filesize(torrent.get('dlspeed')),
|
dlspeed=StringUtils.str_filesize(torrent.get('dlspeed')),
|
||||||
upspeed=StringUtils.str_filesize(torrent.get('upspeed')),
|
upspeed=StringUtils.str_filesize(torrent.get('upspeed')),
|
||||||
left_time=StringUtils.str_secends(
|
left_time=StringUtils.str_secends(
|
||||||
(torrent.get('total_size') - torrent.get('completed')) / torrent.get('dlspeed')) if torrent.get(
|
(torrent.get('total_size') - torrent.get('completed')) / torrent.get(
|
||||||
|
'dlspeed')) if torrent.get(
|
||||||
'dlspeed') > 0 else ''
|
'dlspeed') > 0 else ''
|
||||||
))
|
))
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -2,18 +2,17 @@ import json
|
|||||||
import re
|
import re
|
||||||
from typing import Optional, Union, List, Tuple, Any
|
from typing import Optional, Union, List, Tuple, Any
|
||||||
|
|
||||||
from app.core.config import on_config_change
|
|
||||||
from app.core.context import MediaInfo, Context
|
from app.core.context import MediaInfo, Context
|
||||||
|
from app.core.event import eventmanager, Event
|
||||||
from app.log import logger
|
from app.log import logger
|
||||||
from app.modules import _ModuleBase, _MessageBase
|
from app.modules import _ModuleBase, _MessageBase
|
||||||
from app.modules.slack.slack import Slack
|
from app.modules.slack.slack import Slack
|
||||||
from app.schemas import MessageChannel, CommingMessage, Notification
|
from app.schemas import MessageChannel, CommingMessage, Notification, ConfigChangeEventData
|
||||||
from app.schemas.types import ModuleType, SystemConfigKey
|
from app.schemas.types import ModuleType, SystemConfigKey, EventType
|
||||||
|
|
||||||
|
|
||||||
class SlackModule(_ModuleBase, _MessageBase[Slack]):
|
class SlackModule(_ModuleBase, _MessageBase[Slack]):
|
||||||
|
|
||||||
@on_config_change([SystemConfigKey.Notifications.value])
|
|
||||||
def init_module(self) -> None:
|
def init_module(self) -> None:
|
||||||
"""
|
"""
|
||||||
初始化模块
|
初始化模块
|
||||||
@@ -22,6 +21,19 @@ class SlackModule(_ModuleBase, _MessageBase[Slack]):
|
|||||||
service_type=Slack)
|
service_type=Slack)
|
||||||
self._channel = MessageChannel.Slack
|
self._channel = MessageChannel.Slack
|
||||||
|
|
||||||
|
@eventmanager.register(EventType.ConfigChanged)
|
||||||
|
def handle_config_changed(self, event: Event):
|
||||||
|
"""
|
||||||
|
处理配置变更事件
|
||||||
|
:param event: 事件对象
|
||||||
|
"""
|
||||||
|
if not event:
|
||||||
|
return
|
||||||
|
event_data: ConfigChangeEventData = event.event_data
|
||||||
|
if event_data.key not in [SystemConfigKey.Notifications.value]:
|
||||||
|
return
|
||||||
|
self.init_module()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_name() -> str:
|
def get_name() -> str:
|
||||||
return "Slack"
|
return "Slack"
|
||||||
|
|||||||
@@ -1,17 +1,16 @@
|
|||||||
from typing import Optional, Union, List, Tuple, Any
|
from typing import Optional, Union, List, Tuple, Any
|
||||||
|
|
||||||
from app.core.config import on_config_change
|
|
||||||
from app.core.context import MediaInfo, Context
|
from app.core.context import MediaInfo, Context
|
||||||
|
from app.core.event import eventmanager, Event
|
||||||
from app.log import logger
|
from app.log import logger
|
||||||
from app.modules import _ModuleBase, _MessageBase
|
from app.modules import _ModuleBase, _MessageBase
|
||||||
from app.modules.synologychat.synologychat import SynologyChat
|
from app.modules.synologychat.synologychat import SynologyChat
|
||||||
from app.schemas import MessageChannel, CommingMessage, Notification
|
from app.schemas import MessageChannel, CommingMessage, Notification, ConfigChangeEventData
|
||||||
from app.schemas.types import ModuleType, SystemConfigKey
|
from app.schemas.types import ModuleType, SystemConfigKey, EventType
|
||||||
|
|
||||||
|
|
||||||
class SynologyChatModule(_ModuleBase, _MessageBase[SynologyChat]):
|
class SynologyChatModule(_ModuleBase, _MessageBase[SynologyChat]):
|
||||||
|
|
||||||
@on_config_change([SystemConfigKey.Notifications.value])
|
|
||||||
def init_module(self) -> None:
|
def init_module(self) -> None:
|
||||||
"""
|
"""
|
||||||
初始化模块
|
初始化模块
|
||||||
@@ -20,6 +19,19 @@ class SynologyChatModule(_ModuleBase, _MessageBase[SynologyChat]):
|
|||||||
service_type=SynologyChat)
|
service_type=SynologyChat)
|
||||||
self._channel = MessageChannel.SynologyChat
|
self._channel = MessageChannel.SynologyChat
|
||||||
|
|
||||||
|
@eventmanager.register(EventType.ConfigChanged)
|
||||||
|
def handle_config_changed(self, event: Event):
|
||||||
|
"""
|
||||||
|
处理配置变更事件
|
||||||
|
:param event: 事件对象
|
||||||
|
"""
|
||||||
|
if not event:
|
||||||
|
return
|
||||||
|
event_data: ConfigChangeEventData = event.event_data
|
||||||
|
if event_data.key not in [SystemConfigKey.Notifications.value]:
|
||||||
|
return
|
||||||
|
self.init_module()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_name() -> str:
|
def get_name() -> str:
|
||||||
return "Synology Chat"
|
return "Synology Chat"
|
||||||
|
|||||||
@@ -1,21 +1,21 @@
|
|||||||
import copy
|
import copy
|
||||||
import json
|
import json
|
||||||
from typing import Optional, Union, List, Tuple, Any, Dict
|
from typing import Dict
|
||||||
|
from typing import Optional, Union, List, Tuple, Any
|
||||||
|
|
||||||
from app.core.config import on_config_change
|
|
||||||
from app.core.context import MediaInfo, Context
|
from app.core.context import MediaInfo, Context
|
||||||
|
from app.core.event import Event
|
||||||
from app.core.event import eventmanager
|
from app.core.event import eventmanager
|
||||||
from app.log import logger
|
from app.log import logger
|
||||||
from app.modules import _ModuleBase, _MessageBase
|
from app.modules import _ModuleBase, _MessageBase
|
||||||
from app.modules.telegram.telegram import Telegram
|
from app.modules.telegram.telegram import Telegram
|
||||||
from app.schemas import MessageChannel, CommingMessage, Notification, CommandRegisterEventData
|
from app.schemas import MessageChannel, CommingMessage, Notification, CommandRegisterEventData, ConfigChangeEventData
|
||||||
from app.schemas.types import ModuleType, ChainEventType, SystemConfigKey
|
from app.schemas.types import ModuleType, ChainEventType, SystemConfigKey, EventType
|
||||||
from app.utils.structures import DictUtils
|
from app.utils.structures import DictUtils
|
||||||
|
|
||||||
|
|
||||||
class TelegramModule(_ModuleBase, _MessageBase[Telegram]):
|
class TelegramModule(_ModuleBase, _MessageBase[Telegram]):
|
||||||
|
|
||||||
@on_config_change([SystemConfigKey.Notifications.value])
|
|
||||||
def init_module(self) -> None:
|
def init_module(self) -> None:
|
||||||
"""
|
"""
|
||||||
初始化模块
|
初始化模块
|
||||||
@@ -24,6 +24,19 @@ class TelegramModule(_ModuleBase, _MessageBase[Telegram]):
|
|||||||
service_type=Telegram)
|
service_type=Telegram)
|
||||||
self._channel = MessageChannel.Telegram
|
self._channel = MessageChannel.Telegram
|
||||||
|
|
||||||
|
@eventmanager.register(EventType.ConfigChanged)
|
||||||
|
def handle_config_changed(self, event: Event):
|
||||||
|
"""
|
||||||
|
处理配置变更事件
|
||||||
|
:param event: 事件对象
|
||||||
|
"""
|
||||||
|
if not event:
|
||||||
|
return
|
||||||
|
event_data: ConfigChangeEventData = event.event_data
|
||||||
|
if event_data.key not in [SystemConfigKey.Notifications.value]:
|
||||||
|
return
|
||||||
|
self.init_module()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_name() -> str:
|
def get_name() -> str:
|
||||||
return "Telegram"
|
return "Telegram"
|
||||||
|
|||||||
@@ -5,19 +5,19 @@ from torrentool.torrent import Torrent
|
|||||||
from transmission_rpc import File
|
from transmission_rpc import File
|
||||||
|
|
||||||
from app import schemas
|
from app import schemas
|
||||||
from app.core.config import settings, on_config_change
|
from app.core.config import settings
|
||||||
from app.core.metainfo import MetaInfo
|
from app.core.metainfo import MetaInfo
|
||||||
|
from app.core.event import eventmanager, Event
|
||||||
from app.log import logger
|
from app.log import logger
|
||||||
from app.modules import _ModuleBase, _DownloaderBase
|
from app.modules import _ModuleBase, _DownloaderBase
|
||||||
from app.modules.transmission.transmission import Transmission
|
from app.modules.transmission.transmission import Transmission
|
||||||
from app.schemas import TransferTorrent, DownloadingTorrent
|
from app.schemas import TransferTorrent, DownloadingTorrent
|
||||||
from app.schemas.types import TorrentStatus, ModuleType, DownloaderType, SystemConfigKey
|
from app.schemas.types import TorrentStatus, ModuleType, DownloaderType, SystemConfigKey, EventType
|
||||||
from app.utils.string import StringUtils
|
from app.utils.string import StringUtils
|
||||||
|
|
||||||
|
|
||||||
class TransmissionModule(_ModuleBase, _DownloaderBase[Transmission]):
|
class TransmissionModule(_ModuleBase, _DownloaderBase[Transmission]):
|
||||||
|
|
||||||
@on_config_change([SystemConfigKey.Downloaders.value])
|
|
||||||
def init_module(self) -> None:
|
def init_module(self) -> None:
|
||||||
"""
|
"""
|
||||||
初始化模块
|
初始化模块
|
||||||
@@ -25,6 +25,19 @@ class TransmissionModule(_ModuleBase, _DownloaderBase[Transmission]):
|
|||||||
super().init_service(service_name=Transmission.__name__.lower(),
|
super().init_service(service_name=Transmission.__name__.lower(),
|
||||||
service_type=Transmission)
|
service_type=Transmission)
|
||||||
|
|
||||||
|
@eventmanager.register(EventType.ConfigChanged)
|
||||||
|
def handle_config_changed(self, event: Event):
|
||||||
|
"""
|
||||||
|
处理配置变更事件
|
||||||
|
:param event: 事件对象
|
||||||
|
"""
|
||||||
|
if not event:
|
||||||
|
return
|
||||||
|
event_data: schemas.ConfigChangeEventData = event.event_data
|
||||||
|
if event_data.key not in [SystemConfigKey.Downloaders.value]:
|
||||||
|
return
|
||||||
|
self.init_module()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_name() -> str:
|
def get_name() -> str:
|
||||||
return "Transmission"
|
return "Transmission"
|
||||||
|
|||||||
@@ -1,19 +1,17 @@
|
|||||||
from typing import Any, Generator, List, Optional, Tuple, Union
|
from typing import Any, Generator, List, Optional, Tuple, Union
|
||||||
|
|
||||||
from app import schemas
|
from app import schemas
|
||||||
from app.core.config import on_config_change
|
|
||||||
from app.core.context import MediaInfo
|
from app.core.context import MediaInfo
|
||||||
from app.core.event import eventmanager
|
from app.core.event import eventmanager, Event
|
||||||
from app.log import logger
|
from app.log import logger
|
||||||
from app.modules import _MediaServerBase, _ModuleBase
|
from app.modules import _MediaServerBase, _ModuleBase
|
||||||
from app.modules.trimemedia.trimemedia import TrimeMedia
|
from app.modules.trimemedia.trimemedia import TrimeMedia
|
||||||
from app.schemas import AuthCredentials, AuthInterceptCredentials
|
from app.schemas import AuthCredentials, AuthInterceptCredentials
|
||||||
from app.schemas.types import ChainEventType, MediaServerType, MediaType, ModuleType, SystemConfigKey
|
from app.schemas.types import ChainEventType, MediaServerType, MediaType, ModuleType, SystemConfigKey, EventType
|
||||||
|
|
||||||
|
|
||||||
class TrimeMediaModule(_ModuleBase, _MediaServerBase[TrimeMedia]):
|
class TrimeMediaModule(_ModuleBase, _MediaServerBase[TrimeMedia]):
|
||||||
|
|
||||||
@on_config_change([SystemConfigKey.MediaServers.value])
|
|
||||||
def init_module(self) -> None:
|
def init_module(self) -> None:
|
||||||
"""
|
"""
|
||||||
初始化模块
|
初始化模块
|
||||||
@@ -25,6 +23,19 @@ class TrimeMediaModule(_ModuleBase, _MediaServerBase[TrimeMedia]):
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@eventmanager.register(EventType.ConfigChanged)
|
||||||
|
def handle_config_changed(self, event: Event):
|
||||||
|
"""
|
||||||
|
处理配置变更事件
|
||||||
|
:param event: 事件对象
|
||||||
|
"""
|
||||||
|
if not event:
|
||||||
|
return
|
||||||
|
event_data: schemas.ConfigChangeEventData = event.event_data
|
||||||
|
if event_data.key not in [SystemConfigKey.MediaServers.value]:
|
||||||
|
return
|
||||||
|
self.init_module()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_name() -> str:
|
def get_name() -> str:
|
||||||
return "飞牛影视"
|
return "飞牛影视"
|
||||||
|
|||||||
@@ -1,18 +1,17 @@
|
|||||||
import json
|
import json
|
||||||
from typing import Optional, Union, List, Tuple, Any, Dict
|
from typing import Optional, Union, List, Tuple, Any, Dict
|
||||||
|
|
||||||
from app.core.config import on_config_change
|
|
||||||
from app.core.context import Context, MediaInfo
|
from app.core.context import Context, MediaInfo
|
||||||
|
from app.core.event import eventmanager, Event
|
||||||
from app.log import logger
|
from app.log import logger
|
||||||
from app.modules import _ModuleBase, _MessageBase
|
from app.modules import _ModuleBase, _MessageBase
|
||||||
from app.modules.vocechat.vocechat import VoceChat
|
from app.modules.vocechat.vocechat import VoceChat
|
||||||
from app.schemas import MessageChannel, CommingMessage, Notification
|
from app.schemas import MessageChannel, CommingMessage, Notification, ConfigChangeEventData
|
||||||
from app.schemas.types import ModuleType, SystemConfigKey
|
from app.schemas.types import ModuleType, SystemConfigKey, EventType
|
||||||
|
|
||||||
|
|
||||||
class VoceChatModule(_ModuleBase, _MessageBase[VoceChat]):
|
class VoceChatModule(_ModuleBase, _MessageBase[VoceChat]):
|
||||||
|
|
||||||
@on_config_change([SystemConfigKey.Notifications.value])
|
|
||||||
def init_module(self) -> None:
|
def init_module(self) -> None:
|
||||||
"""
|
"""
|
||||||
初始化模块
|
初始化模块
|
||||||
@@ -21,6 +20,19 @@ class VoceChatModule(_ModuleBase, _MessageBase[VoceChat]):
|
|||||||
service_type=VoceChat)
|
service_type=VoceChat)
|
||||||
self._channel = MessageChannel.VoceChat
|
self._channel = MessageChannel.VoceChat
|
||||||
|
|
||||||
|
@eventmanager.register(EventType.ConfigChanged)
|
||||||
|
def handle_config_changed(self, event: Event):
|
||||||
|
"""
|
||||||
|
处理配置变更事件
|
||||||
|
:param event: 事件对象
|
||||||
|
"""
|
||||||
|
if not event:
|
||||||
|
return
|
||||||
|
event_data: ConfigChangeEventData = event.event_data
|
||||||
|
if event_data.key not in [SystemConfigKey.Notifications.value]:
|
||||||
|
return
|
||||||
|
self.init_module()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_name() -> str:
|
def get_name() -> str:
|
||||||
return "VoceChat"
|
return "VoceChat"
|
||||||
|
|||||||
@@ -3,16 +3,16 @@ from typing import Union, Tuple
|
|||||||
|
|
||||||
from pywebpush import webpush, WebPushException
|
from pywebpush import webpush, WebPushException
|
||||||
|
|
||||||
from app.core.config import global_vars, settings, on_config_change
|
from app.core.config import global_vars, settings
|
||||||
|
from app.core.event import eventmanager, Event
|
||||||
from app.log import logger
|
from app.log import logger
|
||||||
from app.modules import _ModuleBase, _MessageBase
|
from app.modules import _ModuleBase, _MessageBase
|
||||||
from app.schemas import Notification
|
from app.schemas import Notification, ConfigChangeEventData
|
||||||
from app.schemas.types import ModuleType, MessageChannel, SystemConfigKey
|
from app.schemas.types import ModuleType, MessageChannel, SystemConfigKey, EventType
|
||||||
|
|
||||||
|
|
||||||
class WebPushModule(_ModuleBase, _MessageBase):
|
class WebPushModule(_ModuleBase, _MessageBase):
|
||||||
|
|
||||||
@on_config_change([SystemConfigKey.Notifications.value])
|
|
||||||
def init_module(self) -> None:
|
def init_module(self) -> None:
|
||||||
"""
|
"""
|
||||||
初始化模块
|
初始化模块
|
||||||
@@ -20,6 +20,19 @@ class WebPushModule(_ModuleBase, _MessageBase):
|
|||||||
super().init_service(service_name=self.get_name().lower())
|
super().init_service(service_name=self.get_name().lower())
|
||||||
self._channel = MessageChannel.WebPush
|
self._channel = MessageChannel.WebPush
|
||||||
|
|
||||||
|
@eventmanager.register(EventType.ConfigChanged)
|
||||||
|
def handle_config_changed(self, event: Event):
|
||||||
|
"""
|
||||||
|
处理配置变更事件
|
||||||
|
:param event: 事件对象
|
||||||
|
"""
|
||||||
|
if not event:
|
||||||
|
return
|
||||||
|
event_data: ConfigChangeEventData = event.event_data
|
||||||
|
if event_data.key not in [SystemConfigKey.Notifications.value]:
|
||||||
|
return
|
||||||
|
self.init_module()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_name() -> str:
|
def get_name() -> str:
|
||||||
return "WebPush"
|
return "WebPush"
|
||||||
|
|||||||
@@ -2,22 +2,20 @@ import copy
|
|||||||
import xml.dom.minidom
|
import xml.dom.minidom
|
||||||
from typing import Optional, Union, List, Tuple, Any, Dict
|
from typing import Optional, Union, List, Tuple, Any, Dict
|
||||||
|
|
||||||
from app.core.config import on_config_change
|
|
||||||
from app.core.context import Context, MediaInfo
|
from app.core.context import Context, MediaInfo
|
||||||
from app.core.event import eventmanager
|
from app.core.event import Event, eventmanager
|
||||||
from app.log import logger
|
from app.log import logger
|
||||||
from app.modules import _ModuleBase, _MessageBase
|
from app.modules import _ModuleBase, _MessageBase
|
||||||
from app.modules.wechat.WXBizMsgCrypt3 import WXBizMsgCrypt
|
from app.modules.wechat.WXBizMsgCrypt3 import WXBizMsgCrypt
|
||||||
from app.modules.wechat.wechat import WeChat
|
from app.modules.wechat.wechat import WeChat
|
||||||
from app.schemas import MessageChannel, CommingMessage, Notification, CommandRegisterEventData
|
from app.schemas import MessageChannel, CommingMessage, Notification, CommandRegisterEventData, ConfigChangeEventData
|
||||||
from app.schemas.types import ModuleType, ChainEventType, SystemConfigKey
|
from app.schemas.types import ModuleType, ChainEventType, SystemConfigKey, EventType
|
||||||
from app.utils.dom import DomUtils
|
from app.utils.dom import DomUtils
|
||||||
from app.utils.structures import DictUtils
|
from app.utils.structures import DictUtils
|
||||||
|
|
||||||
|
|
||||||
class WechatModule(_ModuleBase, _MessageBase[WeChat]):
|
class WechatModule(_ModuleBase, _MessageBase[WeChat]):
|
||||||
|
|
||||||
@on_config_change([SystemConfigKey.Notifications.value])
|
|
||||||
def init_module(self) -> None:
|
def init_module(self) -> None:
|
||||||
"""
|
"""
|
||||||
初始化模块
|
初始化模块
|
||||||
@@ -26,6 +24,19 @@ class WechatModule(_ModuleBase, _MessageBase[WeChat]):
|
|||||||
service_type=WeChat)
|
service_type=WeChat)
|
||||||
self._channel = MessageChannel.Wechat
|
self._channel = MessageChannel.Wechat
|
||||||
|
|
||||||
|
@eventmanager.register(EventType.ConfigChanged)
|
||||||
|
def handle_config_changed(self, event: Event):
|
||||||
|
"""
|
||||||
|
处理配置变更事件
|
||||||
|
:param event: 事件对象
|
||||||
|
"""
|
||||||
|
if not event:
|
||||||
|
return
|
||||||
|
event_data: ConfigChangeEventData = event.event_data
|
||||||
|
if event_data.key not in [SystemConfigKey.Notifications.value]:
|
||||||
|
return
|
||||||
|
self.init_module()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_name() -> str:
|
def get_name() -> str:
|
||||||
return "微信"
|
return "微信"
|
||||||
|
|||||||
@@ -14,14 +14,16 @@ from watchdog.observers.polling import PollingObserver
|
|||||||
from app.chain import ChainBase
|
from app.chain import ChainBase
|
||||||
from app.chain.storage import StorageChain
|
from app.chain.storage import StorageChain
|
||||||
from app.chain.transfer import TransferChain
|
from app.chain.transfer import TransferChain
|
||||||
from app.core.config import settings, on_config_change
|
from app.core.config import settings
|
||||||
from app.db.systemconfig_oper import SystemConfigOper
|
from app.db.systemconfig_oper import SystemConfigOper
|
||||||
from app.helper.directory import DirectoryHelper
|
from app.helper.directory import DirectoryHelper
|
||||||
from app.helper.message import MessageHelper
|
from app.helper.message import MessageHelper
|
||||||
from app.log import logger
|
from app.log import logger
|
||||||
from app.schemas import FileItem
|
from app.schemas import FileItem
|
||||||
from app.schemas.types import SystemConfigKey
|
from app.schemas.types import SystemConfigKey, EventType
|
||||||
from app.utils.singleton import Singleton
|
from app.utils.singleton import Singleton
|
||||||
|
from app.core.event import Event, eventmanager
|
||||||
|
from app.schemas import ConfigChangeEventData
|
||||||
|
|
||||||
lock = Lock()
|
lock = Lock()
|
||||||
snapshot_lock = Lock()
|
snapshot_lock = Lock()
|
||||||
@@ -86,7 +88,19 @@ class Monitor(metaclass=Singleton):
|
|||||||
# 启动目录监控和文件整理
|
# 启动目录监控和文件整理
|
||||||
self.init()
|
self.init()
|
||||||
|
|
||||||
@on_config_change([SystemConfigKey.Directories.value])
|
@eventmanager.register(EventType.ConfigChanged)
|
||||||
|
def handle_config_changed(self, event: Event):
|
||||||
|
"""
|
||||||
|
处理配置变更事件
|
||||||
|
:param event: 事件对象
|
||||||
|
"""
|
||||||
|
if not event:
|
||||||
|
return
|
||||||
|
event_data: ConfigChangeEventData = event.event_data
|
||||||
|
if event_data.key not in [SystemConfigKey.Directories.value]:
|
||||||
|
return
|
||||||
|
self.init()
|
||||||
|
|
||||||
def init(self):
|
def init(self):
|
||||||
"""
|
"""
|
||||||
启动监控
|
启动监控
|
||||||
|
|||||||
@@ -18,18 +18,17 @@ from app.chain.subscribe import SubscribeChain
|
|||||||
from app.chain.tmdb import TmdbChain
|
from app.chain.tmdb import TmdbChain
|
||||||
from app.chain.transfer import TransferChain
|
from app.chain.transfer import TransferChain
|
||||||
from app.chain.workflow import WorkflowChain
|
from app.chain.workflow import WorkflowChain
|
||||||
from app.core.config import settings, on_config_change
|
from app.core.config import settings
|
||||||
from app.core.event import EventManager
|
from app.core.event import EventManager, eventmanager, Event
|
||||||
from app.core.plugin import PluginManager
|
from app.core.plugin import PluginManager
|
||||||
from app.db.systemconfig_oper import SystemConfigOper
|
from app.db.systemconfig_oper import SystemConfigOper
|
||||||
from app.helper.sites import SitesHelper
|
from app.helper.sites import SitesHelper
|
||||||
from app.log import logger
|
from app.log import logger
|
||||||
from app.schemas import Notification, NotificationType, Workflow
|
from app.schemas import Notification, NotificationType, Workflow, ConfigChangeEventData
|
||||||
from app.schemas.types import EventType, SystemConfigKey
|
from app.schemas.types import EventType, SystemConfigKey
|
||||||
from app.utils.singleton import Singleton
|
from app.utils.singleton import Singleton
|
||||||
from app.utils.timer import TimerUtils
|
from app.utils.timer import TimerUtils
|
||||||
|
|
||||||
|
|
||||||
lock = threading.Lock()
|
lock = threading.Lock()
|
||||||
|
|
||||||
|
|
||||||
@@ -57,8 +56,20 @@ class Scheduler(metaclass=Singleton):
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.init()
|
self.init()
|
||||||
|
|
||||||
@on_config_change(['DEV', 'COOKIECLOUD_INTERVAL', 'MEDIASERVER_SYNC_INTERVAL', 'SUBSCRIBE_SEARCH',
|
@eventmanager.register(EventType.ConfigChanged)
|
||||||
'SUBSCRIBE_MODE', 'SUBSCRIBE_RSS_INTERVAL', 'SITEDATA_REFRESH_INTERVAL'])
|
def handle_config_changed(self, event: Event):
|
||||||
|
"""
|
||||||
|
处理配置变更事件
|
||||||
|
:param event: 事件对象
|
||||||
|
"""
|
||||||
|
if not event:
|
||||||
|
return
|
||||||
|
event_data: ConfigChangeEventData = event.event_data
|
||||||
|
if event_data.key not in ['DEV', 'COOKIECLOUD_INTERVAL', 'MEDIASERVER_SYNC_INTERVAL', 'SUBSCRIBE_SEARCH',
|
||||||
|
'SUBSCRIBE_MODE', 'SUBSCRIBE_RSS_INTERVAL', 'SITEDATA_REFRESH_INTERVAL']:
|
||||||
|
return
|
||||||
|
self.init()
|
||||||
|
|
||||||
def init(self):
|
def init(self):
|
||||||
"""
|
"""
|
||||||
初始化定时服务
|
初始化定时服务
|
||||||
|
|||||||
@@ -22,6 +22,16 @@ class BaseEventData(BaseModel):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class ConfigChangeEventData(BaseEventData):
|
||||||
|
"""
|
||||||
|
ConfigChange 事件的数据模型
|
||||||
|
"""
|
||||||
|
key: str = Field(..., description="配置项的键")
|
||||||
|
old_value: Optional[Any] = Field(default=None, description="配置项的旧值")
|
||||||
|
new_value: Optional[Any] = Field(default=None, description="配置项的新值")
|
||||||
|
change_type: str = Field(default="update", description="配置项的变更类型,如 'add', 'update', 'delete'")
|
||||||
|
|
||||||
|
|
||||||
class ChainEventData(BaseEventData):
|
class ChainEventData(BaseEventData):
|
||||||
"""
|
"""
|
||||||
链式事件数据的基类,所有具体事件数据类应继承自此类
|
链式事件数据的基类,所有具体事件数据类应继承自此类
|
||||||
|
|||||||
@@ -61,6 +61,8 @@ class EventType(Enum):
|
|||||||
MetadataScrape = "metadata.scrape"
|
MetadataScrape = "metadata.scrape"
|
||||||
# 模块需要重载
|
# 模块需要重载
|
||||||
ModuleReload = "module.reload"
|
ModuleReload = "module.reload"
|
||||||
|
# 配置项更新
|
||||||
|
ConfigChanged = "config.updated"
|
||||||
|
|
||||||
|
|
||||||
# 同步链式事件
|
# 同步链式事件
|
||||||
|
|||||||
Reference in New Issue
Block a user