mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-02-03 10:35:15 +08:00
refactor:重构配置热加载
This commit is contained in:
@@ -24,14 +24,13 @@ from app.db.models import User
|
||||
from app.db.systemconfig_oper import SystemConfigOper
|
||||
from app.db.user_oper import get_current_active_superuser
|
||||
from app.helper.mediaserver import MediaServerHelper
|
||||
from app.helper.message import MessageHelper, MessageQueueManager
|
||||
from app.helper.message import MessageHelper
|
||||
from app.helper.progress import ProgressHelper
|
||||
from app.helper.rule import RuleHelper
|
||||
from app.helper.sites import SitesHelper
|
||||
from app.helper.subscribe import SubscribeHelper
|
||||
from app.helper.system import SystemHelper
|
||||
from app.log import logger
|
||||
from app.monitor import Monitor
|
||||
from app.scheduler import Scheduler
|
||||
from app.schemas.types import SystemConfigKey
|
||||
from app.utils.crypto import HashUtils
|
||||
@@ -483,18 +482,6 @@ def restart_system(_: User = Depends(get_current_active_superuser)):
|
||||
return schemas.Response(success=ret, message=msg)
|
||||
|
||||
|
||||
@router.get("/reload", summary="重新加载模块", response_model=schemas.Response)
|
||||
def reload_module(_: User = Depends(get_current_active_superuser)):
|
||||
"""
|
||||
重新加载模块(仅管理员)
|
||||
"""
|
||||
MessageQueueManager().init_config()
|
||||
ModuleManager().reload()
|
||||
Scheduler().init()
|
||||
Monitor().init()
|
||||
return schemas.Response(success=True)
|
||||
|
||||
|
||||
@router.get("/runscheduler", summary="运行服务", response_model=schemas.Response)
|
||||
def run_scheduler(jobid: str,
|
||||
_: User = Depends(get_current_active_superuser)):
|
||||
|
||||
@@ -1,21 +1,120 @@
|
||||
import copy
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import secrets
|
||||
import sys
|
||||
import threading
|
||||
from collections import defaultdict
|
||||
from enum import Enum
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, List, Optional, Tuple, Type
|
||||
from typing import Any, Dict, List, Optional, Tuple, Type, Callable
|
||||
|
||||
from dotenv import set_key
|
||||
from pydantic import BaseModel, BaseSettings, validator, Field
|
||||
|
||||
from app.log import logger, log_settings, LogConfigModel
|
||||
from app.utils.object import ObjectUtils
|
||||
from app.utils.system import SystemUtils
|
||||
from app.utils.url import UrlUtils
|
||||
|
||||
|
||||
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):
|
||||
"""
|
||||
Pydantic 配置模型,描述所有配置项及其类型和默认值
|
||||
@@ -255,30 +354,26 @@ class ConfigModel(BaseModel):
|
||||
# 编码探测的最低置信度阈值
|
||||
ENCODING_DETECTION_MIN_CONFIDENCE: float = 0.8
|
||||
# 允许的图片缓存域名
|
||||
SECURITY_IMAGE_DOMAINS: List[str] = Field(
|
||||
default_factory=lambda: ["image.tmdb.org",
|
||||
"static-mdb.v.geilijiasu.com",
|
||||
"bing.com",
|
||||
"doubanio.com",
|
||||
"lain.bgm.tv",
|
||||
"raw.githubusercontent.com",
|
||||
"github.com",
|
||||
"thetvdb.com",
|
||||
"cctvpic.com",
|
||||
"iqiyipic.com",
|
||||
"hdslb.com",
|
||||
"cmvideo.cn",
|
||||
"ykimg.com",
|
||||
"qpic.cn"]
|
||||
)
|
||||
SECURITY_IMAGE_DOMAINS: list = Field(default=[
|
||||
"image.tmdb.org",
|
||||
"static-mdb.v.geilijiasu.com",
|
||||
"bing.com",
|
||||
"doubanio.com",
|
||||
"lain.bgm.tv",
|
||||
"raw.githubusercontent.com",
|
||||
"github.com",
|
||||
"thetvdb.com",
|
||||
"cctvpic.com",
|
||||
"iqiyipic.com",
|
||||
"hdslb.com",
|
||||
"cmvideo.cn",
|
||||
"ykimg.com",
|
||||
"qpic.cn"
|
||||
])
|
||||
# 允许的图片文件后缀格式
|
||||
SECURITY_IMAGE_SUFFIXES: List[str] = Field(
|
||||
default_factory=lambda: [".jpg", ".jpeg", ".png", ".webp", ".gif", ".svg", ".avif"]
|
||||
)
|
||||
SECURITY_IMAGE_SUFFIXES: list = Field(default=[".jpg", ".jpeg", ".png", ".webp", ".gif", ".svg", ".avif"])
|
||||
# 重命名时支持的S0别名
|
||||
RENAME_FORMAT_S0_NAMES: List[str] = Field(
|
||||
default_factory=lambda: ["Specials", "SPs"]
|
||||
)
|
||||
RENAME_FORMAT_S0_NAMES: list = Field(default=["Specials", "SPs"])
|
||||
# 启用分词搜索
|
||||
TOKENIZED_SEARCH: bool = False
|
||||
# 为指定默认字幕添加.default后缀
|
||||
@@ -333,6 +428,7 @@ class Settings(BaseSettings, ConfigModel, LogConfigModel):
|
||||
raise_exception: bool = False) -> Tuple[Any, bool]:
|
||||
"""
|
||||
通用类型转换函数,根据预期类型转换值。如果转换失败,返回默认值
|
||||
:return: 元组 (转换后的值, 是否需要更新)
|
||||
"""
|
||||
if isinstance(value, (list, dict, set)):
|
||||
value = copy.deepcopy(value)
|
||||
@@ -373,12 +469,8 @@ class Settings(BaseSettings, ConfigModel, LogConfigModel):
|
||||
converted = float(value)
|
||||
return converted, str(converted) != str(original_value)
|
||||
elif expected_type is str:
|
||||
# 清理 value 中所有空白字符的字段
|
||||
fields_not_keep_spaces = {"AUTO_DOWNLOAD_USER", "REPO_GITHUB_TOKEN", "PLUGIN_MARKET"}
|
||||
if field_name in fields_not_keep_spaces:
|
||||
value = re.sub(r"\s+", "", value)
|
||||
return value, str(value) != str(original_value)
|
||||
# 支持 list 类型的处理
|
||||
converted = str(value).strip()
|
||||
return converted, converted != str(original_value)
|
||||
elif expected_type is list:
|
||||
if isinstance(value, list):
|
||||
return value, str(value) != str(original_value)
|
||||
@@ -388,7 +480,6 @@ class Settings(BaseSettings, ConfigModel, LogConfigModel):
|
||||
return items, items != original_value
|
||||
else:
|
||||
return items, str(items) != str(original_value)
|
||||
# 可根据需要添加更多类型处理
|
||||
else:
|
||||
return value, str(value) != str(original_value)
|
||||
except (ValueError, TypeError) as e:
|
||||
@@ -462,9 +553,14 @@ class Settings(BaseSettings, ConfigModel, LogConfigModel):
|
||||
success, message = self.update_env_config(field, value, converted_value)
|
||||
# 仅成功更新配置时,才更新内存
|
||||
if success:
|
||||
old_value = getattr(self, key)
|
||||
setattr(self, key, converted_value)
|
||||
if hasattr(log_settings, key):
|
||||
setattr(log_settings, key, converted_value)
|
||||
# 发送配置变更通知
|
||||
event = ConfigChangeEvent(key, old_value, converted_value)
|
||||
config_notifier.notify(event)
|
||||
|
||||
return success, message
|
||||
return True, ""
|
||||
except Exception as e:
|
||||
@@ -475,21 +571,8 @@ class Settings(BaseSettings, ConfigModel, LogConfigModel):
|
||||
更新多个配置项
|
||||
"""
|
||||
results = {}
|
||||
log_updated, plugin_monitor_updated = False, False
|
||||
for k, v in env.items():
|
||||
results[k] = self.update_setting(k, v)
|
||||
if hasattr(log_settings, k):
|
||||
log_updated = True
|
||||
if k in ["PLUGIN_AUTO_RELOAD", "DEV"]:
|
||||
plugin_monitor_updated = True
|
||||
# 本次更新存在日志配置项更新,需要重新加载日志配置
|
||||
if log_updated:
|
||||
logger.update_loggers()
|
||||
# 本次更新存在插件监控配置项更新,需要重新加载插件监控
|
||||
if plugin_monitor_updated:
|
||||
# 解决顶层循环导入问题
|
||||
from app.core.plugin import PluginManager
|
||||
PluginManager().reload_monitor()
|
||||
return results
|
||||
|
||||
@property
|
||||
@@ -645,6 +728,10 @@ class Settings(BaseSettings, ConfigModel, LogConfigModel):
|
||||
return UrlUtils.combine_url(host=self.APP_DOMAIN, path=url)
|
||||
|
||||
|
||||
# 实例化配置
|
||||
settings = Settings()
|
||||
|
||||
|
||||
class GlobalVar(object):
|
||||
"""
|
||||
全局标识
|
||||
@@ -702,8 +789,93 @@ class GlobalVar(object):
|
||||
return self.is_system_stopped or workflow_id in self.EMERGENCY_STOP_WORKFLOWS
|
||||
|
||||
|
||||
# 实例化配置
|
||||
settings = Settings()
|
||||
|
||||
# 全局标识
|
||||
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()
|
||||
|
||||
@@ -15,7 +15,7 @@ from watchdog.events import FileSystemEventHandler
|
||||
from watchdog.observers import Observer
|
||||
|
||||
from app import schemas
|
||||
from app.core.config import settings
|
||||
from app.core.config import settings, on_config_change
|
||||
from app.core.event import eventmanager
|
||||
from app.db.plugindata_oper import PluginDataOper
|
||||
from app.db.systemconfig_oper import SystemConfigOper
|
||||
@@ -241,6 +241,13 @@ class PluginManager(metaclass=Singleton):
|
||||
"""
|
||||
return self._plugins
|
||||
|
||||
@on_config_change(['PLUGIN_AUTO_RELOAD', 'DEV'])
|
||||
def handle_config_change(self):
|
||||
"""
|
||||
处理配置变更事件,重新加载插件监测
|
||||
"""
|
||||
self.reload_monitor()
|
||||
|
||||
def reload_monitor(self):
|
||||
"""
|
||||
重新加载插件文件修改监测
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
from typing import Any, Union
|
||||
|
||||
from app.core.config import ConfigChangeEvent, config_notifier, ConfigChangeType
|
||||
from app.db import DbOper
|
||||
from app.db.models.systemconfig import SystemConfig
|
||||
from app.schemas.types import SystemConfigKey
|
||||
@@ -24,17 +25,32 @@ class SystemConfigOper(DbOper, metaclass=Singleton):
|
||||
"""
|
||||
if isinstance(key, SystemConfigKey):
|
||||
key = key.value
|
||||
# 旧值
|
||||
old_value = self.__SYSTEMCONF.get(key)
|
||||
# 更新内存
|
||||
self.__SYSTEMCONF[key] = value
|
||||
conf = SystemConfig.get_by_key(self._db, key)
|
||||
if conf:
|
||||
if value:
|
||||
conf.update(self._db, {"value": value})
|
||||
# 发送配置变更通知
|
||||
if old_value != value:
|
||||
event = ConfigChangeEvent(key, old_value=old_value, new_value=value,
|
||||
change_type=ConfigChangeType.UPDATE)
|
||||
config_notifier.notify(event)
|
||||
else:
|
||||
conf.delete(self._db, conf.id)
|
||||
# 发送配置删除通知
|
||||
event = ConfigChangeEvent(key, old_value=old_value, new_value=None,
|
||||
change_type=ConfigChangeType.DELETE)
|
||||
config_notifier.notify(event)
|
||||
else:
|
||||
conf = SystemConfig(key=key, value=value)
|
||||
conf.create(self._db)
|
||||
# 发送配置变更通知
|
||||
event = ConfigChangeEvent(key, old_value=None, new_value=value,
|
||||
change_type=ConfigChangeType.ADD)
|
||||
config_notifier.notify(event)
|
||||
|
||||
def get(self, key: Union[str, SystemConfigKey] = None) -> Any:
|
||||
"""
|
||||
@@ -59,11 +75,15 @@ class SystemConfigOper(DbOper, metaclass=Singleton):
|
||||
if isinstance(key, SystemConfigKey):
|
||||
key = key.value
|
||||
# 更新内存
|
||||
self.__SYSTEMCONF.pop(key, None)
|
||||
old_value = self.__SYSTEMCONF.pop(key, None)
|
||||
# 写入数据库
|
||||
conf = SystemConfig.get_by_key(self._db, key)
|
||||
if conf:
|
||||
conf.delete(self._db, conf.id)
|
||||
# 发送配置变更通知
|
||||
event = ConfigChangeEvent(key, old_value=old_value, new_value=None,
|
||||
change_type=ConfigChangeType.ADD)
|
||||
config_notifier.notify(event)
|
||||
return True
|
||||
|
||||
def __del__(self):
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
from typing import Any, Generator, List, Optional, Tuple, Union
|
||||
|
||||
from app import schemas
|
||||
from app.core.config import on_config_change
|
||||
from app.core.context import MediaInfo
|
||||
from app.core.event import eventmanager
|
||||
from app.log import logger
|
||||
from app.modules import _MediaServerBase, _ModuleBase
|
||||
from app.modules.emby.emby import Emby
|
||||
from app.schemas.types import MediaType, ModuleType, ChainEventType, MediaServerType
|
||||
from app.schemas.types import MediaType, ModuleType, ChainEventType, MediaServerType, SystemConfigKey
|
||||
|
||||
|
||||
class EmbyModule(_ModuleBase, _MediaServerBase[Emby]):
|
||||
|
||||
@on_config_change([SystemConfigKey.MediaServers.value])
|
||||
def init_module(self) -> None:
|
||||
"""
|
||||
初始化模块
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
from typing import Any, Generator, List, Optional, Tuple, Union
|
||||
|
||||
from app import schemas
|
||||
from app.core.config import on_config_change
|
||||
from app.core.context import MediaInfo
|
||||
from app.core.event import eventmanager
|
||||
from app.log import logger
|
||||
from app.modules import _MediaServerBase, _ModuleBase
|
||||
from app.modules.jellyfin.jellyfin import Jellyfin
|
||||
from app.schemas import AuthCredentials, AuthInterceptCredentials
|
||||
from app.schemas.types import MediaType, ModuleType, ChainEventType, MediaServerType
|
||||
from app.schemas.types import MediaType, ModuleType, ChainEventType, MediaServerType, SystemConfigKey
|
||||
|
||||
|
||||
class JellyfinModule(_ModuleBase, _MediaServerBase[Jellyfin]):
|
||||
|
||||
@on_config_change([SystemConfigKey.MediaServers.value])
|
||||
def init_module(self) -> None:
|
||||
"""
|
||||
初始化模块
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
from typing import Optional, Tuple, Union, Any, List, Generator
|
||||
|
||||
from app import schemas
|
||||
from app.core.config import on_config_change
|
||||
from app.core.context import MediaInfo
|
||||
from app.core.event import eventmanager
|
||||
from app.log import logger
|
||||
from app.modules import _ModuleBase, _MediaServerBase
|
||||
from app.modules.plex.plex import Plex
|
||||
from app.schemas import AuthCredentials, AuthInterceptCredentials
|
||||
from app.schemas.types import MediaType, ModuleType, ChainEventType, MediaServerType
|
||||
from app.schemas.types import MediaType, ModuleType, ChainEventType, MediaServerType, SystemConfigKey
|
||||
|
||||
|
||||
class PlexModule(_ModuleBase, _MediaServerBase[Plex]):
|
||||
|
||||
@on_config_change([SystemConfigKey.MediaServers.value])
|
||||
def init_module(self) -> None:
|
||||
"""
|
||||
初始化模块
|
||||
|
||||
@@ -5,18 +5,19 @@ from qbittorrentapi import TorrentFilesList
|
||||
from torrentool.torrent import Torrent
|
||||
|
||||
from app import schemas
|
||||
from app.core.config import settings
|
||||
from app.core.config import settings, on_config_change
|
||||
from app.core.metainfo import MetaInfo
|
||||
from app.log import logger
|
||||
from app.modules import _ModuleBase, _DownloaderBase
|
||||
from app.modules.qbittorrent.qbittorrent import Qbittorrent
|
||||
from app.schemas import TransferTorrent, DownloadingTorrent
|
||||
from app.schemas.types import TorrentStatus, ModuleType, DownloaderType
|
||||
from app.schemas.types import TorrentStatus, ModuleType, DownloaderType, SystemConfigKey
|
||||
from app.utils.string import StringUtils
|
||||
|
||||
|
||||
class QbittorrentModule(_ModuleBase, _DownloaderBase[Qbittorrent]):
|
||||
|
||||
@on_config_change([SystemConfigKey.Downloaders.value])
|
||||
def init_module(self) -> None:
|
||||
"""
|
||||
初始化模块
|
||||
|
||||
@@ -2,16 +2,18 @@ import json
|
||||
import re
|
||||
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.log import logger
|
||||
from app.modules import _ModuleBase, _MessageBase
|
||||
from app.modules.slack.slack import Slack
|
||||
from app.schemas import MessageChannel, CommingMessage, Notification
|
||||
from app.schemas.types import ModuleType
|
||||
from app.schemas.types import ModuleType, SystemConfigKey
|
||||
|
||||
|
||||
class SlackModule(_ModuleBase, _MessageBase[Slack]):
|
||||
|
||||
@on_config_change([SystemConfigKey.Notifications.value])
|
||||
def init_module(self) -> None:
|
||||
"""
|
||||
初始化模块
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
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.log import logger
|
||||
from app.modules import _ModuleBase, _MessageBase
|
||||
from app.modules.synologychat.synologychat import SynologyChat
|
||||
from app.schemas import MessageChannel, CommingMessage, Notification
|
||||
from app.schemas.types import ModuleType
|
||||
from app.schemas.types import ModuleType, SystemConfigKey
|
||||
|
||||
|
||||
class SynologyChatModule(_ModuleBase, _MessageBase[SynologyChat]):
|
||||
|
||||
@on_config_change([SystemConfigKey.Notifications.value])
|
||||
def init_module(self) -> None:
|
||||
"""
|
||||
初始化模块
|
||||
|
||||
@@ -2,18 +2,20 @@ import copy
|
||||
import json
|
||||
from typing import Optional, Union, List, Tuple, Any, Dict
|
||||
|
||||
from app.core.config import on_config_change
|
||||
from app.core.context import MediaInfo, Context
|
||||
from app.core.event import eventmanager
|
||||
from app.log import logger
|
||||
from app.modules import _ModuleBase, _MessageBase
|
||||
from app.modules.telegram.telegram import Telegram
|
||||
from app.schemas import MessageChannel, CommingMessage, Notification, CommandRegisterEventData
|
||||
from app.schemas.types import ModuleType, ChainEventType
|
||||
from app.schemas.types import ModuleType, ChainEventType, SystemConfigKey
|
||||
from app.utils.structures import DictUtils
|
||||
|
||||
|
||||
class TelegramModule(_ModuleBase, _MessageBase[Telegram]):
|
||||
|
||||
@on_config_change([SystemConfigKey.Notifications.value])
|
||||
def init_module(self) -> None:
|
||||
"""
|
||||
初始化模块
|
||||
|
||||
@@ -5,18 +5,19 @@ from torrentool.torrent import Torrent
|
||||
from transmission_rpc import File
|
||||
|
||||
from app import schemas
|
||||
from app.core.config import settings
|
||||
from app.core.config import settings, on_config_change
|
||||
from app.core.metainfo import MetaInfo
|
||||
from app.log import logger
|
||||
from app.modules import _ModuleBase, _DownloaderBase
|
||||
from app.modules.transmission.transmission import Transmission
|
||||
from app.schemas import TransferTorrent, DownloadingTorrent
|
||||
from app.schemas.types import TorrentStatus, ModuleType, DownloaderType
|
||||
from app.schemas.types import TorrentStatus, ModuleType, DownloaderType, SystemConfigKey
|
||||
from app.utils.string import StringUtils
|
||||
|
||||
|
||||
class TransmissionModule(_ModuleBase, _DownloaderBase[Transmission]):
|
||||
|
||||
@on_config_change([SystemConfigKey.Downloaders.value])
|
||||
def init_module(self) -> None:
|
||||
"""
|
||||
初始化模块
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
from typing import Any, Generator, List, Optional, Tuple, Union
|
||||
|
||||
from app import schemas
|
||||
from app.core.config import on_config_change
|
||||
from app.core.context import MediaInfo
|
||||
from app.core.event import eventmanager
|
||||
from app.log import logger
|
||||
from app.modules import _MediaServerBase, _ModuleBase
|
||||
from app.modules.trimemedia.trimemedia import TrimeMedia
|
||||
from app.schemas import AuthCredentials, AuthInterceptCredentials
|
||||
from app.schemas.types import ChainEventType, MediaServerType, MediaType, ModuleType
|
||||
from app.schemas.types import ChainEventType, MediaServerType, MediaType, ModuleType, SystemConfigKey
|
||||
|
||||
|
||||
class TrimeMediaModule(_ModuleBase, _MediaServerBase[TrimeMedia]):
|
||||
|
||||
@on_config_change([SystemConfigKey.MediaServers.value])
|
||||
def init_module(self) -> None:
|
||||
"""
|
||||
初始化模块
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
import json
|
||||
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.log import logger
|
||||
from app.modules import _ModuleBase, _MessageBase
|
||||
from app.modules.vocechat.vocechat import VoceChat
|
||||
from app.schemas import MessageChannel, CommingMessage, Notification
|
||||
from app.schemas.types import ModuleType
|
||||
from app.schemas.types import ModuleType, SystemConfigKey
|
||||
|
||||
|
||||
class VoceChatModule(_ModuleBase, _MessageBase[VoceChat]):
|
||||
|
||||
@on_config_change([SystemConfigKey.Notifications.value])
|
||||
def init_module(self) -> None:
|
||||
"""
|
||||
初始化模块
|
||||
|
||||
@@ -3,15 +3,16 @@ from typing import Union, Tuple
|
||||
|
||||
from pywebpush import webpush, WebPushException
|
||||
|
||||
from app.core.config import global_vars, settings
|
||||
from app.core.config import global_vars, settings, on_config_change
|
||||
from app.log import logger
|
||||
from app.modules import _ModuleBase, _MessageBase
|
||||
from app.schemas import Notification
|
||||
from app.schemas.types import ModuleType, MessageChannel
|
||||
from app.schemas.types import ModuleType, MessageChannel, SystemConfigKey
|
||||
|
||||
|
||||
class WebPushModule(_ModuleBase, _MessageBase):
|
||||
|
||||
@on_config_change([SystemConfigKey.Notifications.value])
|
||||
def init_module(self) -> None:
|
||||
"""
|
||||
初始化模块
|
||||
|
||||
@@ -2,6 +2,7 @@ import copy
|
||||
import xml.dom.minidom
|
||||
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.event import eventmanager
|
||||
from app.log import logger
|
||||
@@ -9,13 +10,14 @@ from app.modules import _ModuleBase, _MessageBase
|
||||
from app.modules.wechat.WXBizMsgCrypt3 import WXBizMsgCrypt
|
||||
from app.modules.wechat.wechat import WeChat
|
||||
from app.schemas import MessageChannel, CommingMessage, Notification, CommandRegisterEventData
|
||||
from app.schemas.types import ModuleType, ChainEventType
|
||||
from app.schemas.types import ModuleType, ChainEventType, SystemConfigKey
|
||||
from app.utils.dom import DomUtils
|
||||
from app.utils.structures import DictUtils
|
||||
|
||||
|
||||
class WechatModule(_ModuleBase, _MessageBase[WeChat]):
|
||||
|
||||
@on_config_change([SystemConfigKey.Notifications.value])
|
||||
def init_module(self) -> None:
|
||||
"""
|
||||
初始化模块
|
||||
|
||||
@@ -14,12 +14,13 @@ from watchdog.observers.polling import PollingObserver
|
||||
from app.chain import ChainBase
|
||||
from app.chain.storage import StorageChain
|
||||
from app.chain.transfer import TransferChain
|
||||
from app.core.config import settings
|
||||
from app.core.config import settings, on_config_change
|
||||
from app.db.systemconfig_oper import SystemConfigOper
|
||||
from app.helper.directory import DirectoryHelper
|
||||
from app.helper.message import MessageHelper
|
||||
from app.log import logger
|
||||
from app.schemas import FileItem
|
||||
from app.schemas.types import SystemConfigKey
|
||||
from app.utils.singleton import Singleton
|
||||
|
||||
lock = Lock()
|
||||
@@ -85,6 +86,7 @@ class Monitor(metaclass=Singleton):
|
||||
# 启动目录监控和文件整理
|
||||
self.init()
|
||||
|
||||
@on_config_change([SystemConfigKey.Directories.value])
|
||||
def init(self):
|
||||
"""
|
||||
启动监控
|
||||
|
||||
@@ -18,7 +18,7 @@ from app.chain.subscribe import SubscribeChain
|
||||
from app.chain.tmdb import TmdbChain
|
||||
from app.chain.transfer import TransferChain
|
||||
from app.chain.workflow import WorkflowChain
|
||||
from app.core.config import settings
|
||||
from app.core.config import settings, on_config_change
|
||||
from app.core.event import EventManager
|
||||
from app.core.plugin import PluginManager
|
||||
from app.db.systemconfig_oper import SystemConfigOper
|
||||
@@ -57,6 +57,8 @@ class Scheduler(metaclass=Singleton):
|
||||
def __init__(self):
|
||||
self.init()
|
||||
|
||||
@on_config_change(['DEV', 'COOKIECLOUD_INTERVAL', 'MEDIASERVER_SYNC_INTERVAL', 'SUBSCRIBE_SEARCH',
|
||||
'SUBSCRIBE_MODE', 'SUBSCRIBE_RSS_INTERVAL', 'SITEDATA_REFRESH_INTERVAL'])
|
||||
def init(self):
|
||||
"""
|
||||
初始化定时服务
|
||||
|
||||
Reference in New Issue
Block a user