fix 全局变量定义

This commit is contained in:
jxxghp
2024-09-09 22:17:49 +08:00
parent f18b9793b4
commit c3ef3dd7d1
14 changed files with 88 additions and 87 deletions
+16 -10
View File
@@ -15,6 +15,7 @@ from app.db import get_db
from app.db.models import User from app.db.models import User
from app.db.models.message import Message from app.db.models.message import Message
from app.db.user_oper import get_current_active_superuser from app.db.user_oper import get_current_active_superuser
from app.helper.notification import NotificationHelper
from app.log import logger from app.log import logger
from app.modules.wechat.WXBizMsgCrypt3 import WXBizMsgCrypt from app.modules.wechat.WXBizMsgCrypt3 import WXBizMsgCrypt
from app.schemas.types import MessageChannel from app.schemas.types import MessageChannel
@@ -75,26 +76,31 @@ def get_web_message(_: schemas.TokenPayload = Depends(verify_token),
return ret_messages return ret_messages
def wechat_verify(echostr: str, msg_signature: str, def wechat_verify(echostr: str, msg_signature: str, timestamp: Union[str, int], nonce: str,
timestamp: Union[str, int], nonce: str) -> Any: source: str = None) -> Any:
""" """
微信验证响应 微信验证响应
""" """
clients = NotificationHelper().get_clients()
if not clients:
return
for client in clients:
if client.type == "wechat" and client.enabled and client.name == source:
try: try:
wxcpt = WXBizMsgCrypt(sToken=settings.WECHAT_TOKEN, wxcpt = WXBizMsgCrypt(sToken=client.config.get('WECHAT_TOKEN'),
sEncodingAESKey=settings.WECHAT_ENCODING_AESKEY, sEncodingAESKey=client.config.get('WECHAT_ENCODING_AESKEY'),
sReceiveId=settings.WECHAT_CORPID) sReceiveId=client.config.get('WECHAT_CORPID'))
except Exception as err:
logger.error(f"微信请求验证失败: {str(err)}")
return str(err)
ret, sEchoStr = wxcpt.VerifyURL(sMsgSignature=msg_signature, ret, sEchoStr = wxcpt.VerifyURL(sMsgSignature=msg_signature,
sTimeStamp=timestamp, sTimeStamp=timestamp,
sNonce=nonce, sNonce=nonce,
sEchoStr=echostr) sEchoStr=echostr)
if ret != 0: if ret == 0:
logger.error("微信请求验证失败 VerifyURL ret: %s" % str(ret))
# 验证URL成功,将sEchoStr返回给企业号 # 验证URL成功,将sEchoStr返回给企业号
return PlainTextResponse(sEchoStr) return PlainTextResponse(sEchoStr)
except Exception as err:
logger.error(f"微信请求验证失败: {str(err)}")
return str(err)
return "未找到对应的消息配置"
def vocechat_verify(token: str) -> Any: def vocechat_verify(token: str) -> Any:
+2 -2
View File
@@ -81,11 +81,11 @@ class MediaServerChain(ChainBase):
if not mediaserver: if not mediaserver:
continue continue
server_name = mediaserver.name server_name = mediaserver.name
sync_blacklist = mediaserver.sync_libraries or [] sync_libraries = mediaserver.sync_libraries or []
logger.info(f"开始同步媒体库 {server_name} 的数据 ...") logger.info(f"开始同步媒体库 {server_name} 的数据 ...")
for library in self.librarys(server_name): for library in self.librarys(server_name):
# 同步黑名单 跳过 # 同步黑名单 跳过
if library.id in sync_blacklist: if library.id not in sync_libraries:
continue continue
logger.info(f"正在同步 {server_name} 媒体库 {library.name} ...") logger.info(f"正在同步 {server_name} 媒体库 {library.name} ...")
library_count = 0 library_count = 0
+11 -2
View File
@@ -63,6 +63,16 @@ class TransferChain(ChainBase):
# 全局锁,避免重复处理 # 全局锁,避免重复处理
with lock: with lock:
# 获取下载器监控目录
download_dirs = self.directoryhelper.get_download_dirs()
# 如果没有下载器监控的目录则不处理
downloader_monitor = False
for dir_info in download_dirs:
if dir_info.monitor_type == "downloader":
downloader_monitor = True
break
if not downloader_monitor:
return True
logger.info("开始整理下载器中已经完成下载的文件 ...") logger.info("开始整理下载器中已经完成下载的文件 ...")
# 从下载器获取种子列表 # 从下载器获取种子列表
torrents: Optional[List[TransferTorrent]] = self.list_torrents(status=TorrentStatus.TRANSFER) torrents: Optional[List[TransferTorrent]] = self.list_torrents(status=TorrentStatus.TRANSFER)
@@ -74,7 +84,6 @@ class TransferChain(ChainBase):
# 检查是否为下载器监控目录中的文件 # 检查是否为下载器监控目录中的文件
need_handle = False need_handle = False
download_dirs = self.directoryhelper.get_download_dirs()
for torrent in torrents: for torrent in torrents:
# 文件路径 # 文件路径
file_path = Path(torrent.path) file_path = Path(torrent.path)
@@ -157,7 +166,7 @@ class TransferChain(ChainBase):
返回:成功标识,错误信息 返回:成功标识,错误信息
""" """
if not transfer_type: if not transfer_type:
transfer_type = settings.TRANSFER_TYPE transfer_type = 'link'
# 自定义格式 # 自定义格式
formaterHandler = FormatParser(eformat=epformat.format, formaterHandler = FormatParser(eformat=epformat.format,
-21
View File
@@ -120,14 +120,8 @@ class Settings(BaseSettings):
'.flac', '.midi', '.opus', '.sfalc'] '.flac', '.midi', '.opus', '.sfalc']
# 下载器临时文件后缀 # 下载器临时文件后缀
DOWNLOAD_TMPEXT: list = ['.!qB', '.part'] DOWNLOAD_TMPEXT: list = ['.!qB', '.part']
# 传输类型
TRANSFER_TYPE: str = ""
# 下载器监视
DOWNLOADER_MONITOR: bool = True
# 下载器监视间隔(小时) # 下载器监视间隔(小时)
MEDIASERVER_SYNC_INTERVAL: int = 1 MEDIASERVER_SYNC_INTERVAL: int = 1
# 下载器监视黑名单
MEDIASERVER_SYNC_BLACKLIST: list = []
# 订阅模式 # 订阅模式
SUBSCRIBE_MODE: str = "spider" SUBSCRIBE_MODE: str = "spider"
# RSS订阅模式刷新时间间隔(分钟) # RSS订阅模式刷新时间间隔(分钟)
@@ -192,21 +186,6 @@ class Settings(BaseSettings):
# 全局图片缓存,将媒体图片缓存到本地 # 全局图片缓存,将媒体图片缓存到本地
GLOBAL_IMAGE_CACHE: bool = False GLOBAL_IMAGE_CACHE: bool = False
# 微信代理
WECHAT_PROXY: str = ""
# 微信token
WECHAT_TOKEN: str = ""
# 微信encoding_aes_key
WECHAT_ENCODING_AESKEY: str = ""
# 微信corpid
WECHAT_CORPID: str = ""
# 微信管理员
WECHAT_ADMINS: str = ""
# plex地址
PLEX_PLAY_HOST: str = ""
# plex token
PLEX_TOKEN: str = ""
@validator("SUBSCRIBE_RSS_INTERVAL", @validator("SUBSCRIBE_RSS_INTERVAL",
"COOKIECLOUD_INTERVAL", "COOKIECLOUD_INTERVAL",
"META_CACHE_EXPIRE", "META_CACHE_EXPIRE",
+1 -1
View File
@@ -22,7 +22,7 @@ class EmbyModule(_ModuleBase, _MediaServerBase):
return return
for server in mediaservers: for server in mediaservers:
if server.type == "emby" and server.enabled: if server.type == "emby" and server.enabled:
self._servers[server.name] = Emby(**server.config) self._servers[server.name] = Emby(**server.config, sync_libraries=server.sync_libraries)
@staticmethod @staticmethod
def get_name() -> str: def get_name() -> str:
+6 -5
View File
@@ -18,9 +18,11 @@ class Emby:
_host: str = None _host: str = None
_playhost: str = None _playhost: str = None
_apikey: str = None _apikey: str = None
_sync_libraries: List[str] = []
user: Optional[Union[str, int]] = None user: Optional[Union[str, int]] = None
def __init__(self, host: str = None, apikey: str = None, play_host: str = None, **kwargs): def __init__(self, host: str = None, apikey: str = None, play_host: str = None,
sync_libraries: list = None, **kwargs):
if not host or not apikey: if not host or not apikey:
logger.error("Emby服务器配置不完整!") logger.error("Emby服务器配置不完整!")
return return
@@ -34,6 +36,7 @@ class Emby:
self.user = self.get_user(settings.SUPERUSER) self.user = self.get_user(settings.SUPERUSER)
self.folders = self.get_emby_folders() self.folders = self.get_emby_folders()
self.serverid = self.get_server_id() self.serverid = self.get_server_id()
self._sync_libraries = sync_libraries or []
def is_inactive(self) -> bool: def is_inactive(self) -> bool:
""" """
@@ -132,9 +135,8 @@ class Emby:
if not self._host or not self._apikey: if not self._host or not self._apikey:
return [] return []
libraries = [] libraries = []
black_list = (settings.MEDIASERVER_SYNC_BLACKLIST or '').split(",")
for library in self.__get_emby_librarys(username) or []: for library in self.__get_emby_librarys(username) or []:
if library.get("Name") in black_list: if self._sync_libraries and library.get("Id") not in self._sync_libraries:
continue continue
match library.get("CollectionType"): match library.get("CollectionType"):
case "movies": case "movies":
@@ -1137,9 +1139,8 @@ class Emby:
if not self._host or not self._apikey: if not self._host or not self._apikey:
return [] return []
library_folders = [] library_folders = []
black_list = (settings.MEDIASERVER_SYNC_BLACKLIST or '').split(",")
for library in self.get_emby_virtual_folders() or []: for library in self.get_emby_virtual_folders() or []:
if library.get("Name") in black_list: if self._sync_libraries and library.get("Id") not in self._sync_libraries:
continue continue
library_folders += [folder for folder in library.get("Path")] library_folders += [folder for folder in library.get("Path")]
return library_folders return library_folders
+1 -1
View File
@@ -22,7 +22,7 @@ class JellyfinModule(_ModuleBase, _MediaServerBase):
return return
for server in mediaservers: for server in mediaservers:
if server.type == "jellyfin" and server.enabled: if server.type == "jellyfin" and server.enabled:
self._servers[server.name] = Jellyfin(**server.config) self._servers[server.name] = Jellyfin(**server.config, sync_libraries=server.sync_libraries)
@staticmethod @staticmethod
def get_name() -> str: def get_name() -> str:
+6 -5
View File
@@ -15,9 +15,11 @@ class Jellyfin:
_host: str = None _host: str = None
_apikey: str = None _apikey: str = None
_playhost: str = None _playhost: str = None
_sync_libraries: List[str] = []
user: Optional[Union[str, int]] = None user: Optional[Union[str, int]] = None
def __init__(self, host: str = None, apikey: str = None, play_host: str = None, **kwargs): def __init__(self, host: str = None, apikey: str = None, play_host: str = None,
sync_libraries: list = None, **kwargs):
if not host or not apikey: if not host or not apikey:
logger.error("Jellyfin服务器配置不完整!!") logger.error("Jellyfin服务器配置不完整!!")
return return
@@ -30,6 +32,7 @@ class Jellyfin:
self._apikey = apikey self._apikey = apikey
self.user = self.get_user(settings.SUPERUSER) self.user = self.get_user(settings.SUPERUSER)
self.serverid = self.get_server_id() self.serverid = self.get_server_id()
self._sync_libraries = sync_libraries or []
def is_inactive(self) -> bool: def is_inactive(self) -> bool:
""" """
@@ -128,9 +131,8 @@ class Jellyfin:
if not self._host or not self._apikey: if not self._host or not self._apikey:
return [] return []
libraries = [] libraries = []
black_list = (settings.MEDIASERVER_SYNC_BLACKLIST or '').split(",")
for library in self.__get_jellyfin_librarys(username) or []: for library in self.__get_jellyfin_librarys(username) or []:
if library.get("Name") in black_list: if self._sync_libraries and library.get("Id") not in self._sync_libraries:
continue continue
match library.get("CollectionType"): match library.get("CollectionType"):
case "movies": case "movies":
@@ -871,9 +873,8 @@ class Jellyfin:
if not self._host or not self._apikey: if not self._host or not self._apikey:
return [] return []
library_folders = [] library_folders = []
black_list = (settings.MEDIASERVER_SYNC_BLACKLIST or '').split(",")
for library in self.get_jellyfin_virtual_folders() or []: for library in self.get_jellyfin_virtual_folders() or []:
if library.get("Name") in black_list: if self._sync_libraries and library.get("Id") not in self._sync_libraries:
continue continue
library_folders += [folder for folder in library.get("Path")] library_folders += [folder for folder in library.get("Path")]
return library_folders return library_folders
+1 -1
View File
@@ -22,7 +22,7 @@ class PlexModule(_ModuleBase, _MediaServerBase):
return return
for server in mediaservers: for server in mediaservers:
if server.type == "plex" and server.enabled: if server.type == "plex" and server.enabled:
self._servers[server.name] = Plex(**server.config) self._servers[server.name] = Plex(**server.config, sync_libraries=server.sync_libraries)
@staticmethod @staticmethod
def get_name() -> str: def get_name() -> str:
+13 -13
View File
@@ -19,8 +19,10 @@ from app.utils.url import UrlUtils
class Plex: class Plex:
_plex = None _plex = None
_session = None _session = None
_sync_libraries: List[str] = []
def __init__(self, host: str = None, token: str = None, play_host: str = None, **kwargs): def __init__(self, host: str = None, token: str = None, play_host: str = None,
sync_libraries: list = None, **kwargs):
if not host or not token: if not host or not token:
logger.error("Plex服务器配置不完整!") logger.error("Plex服务器配置不完整!")
return return
@@ -39,6 +41,7 @@ class Plex:
self._plex = None self._plex = None
logger.error(f"Plex服务器连接失败:{str(e)}") logger.error(f"Plex服务器连接失败:{str(e)}")
self._session = self.__adapt_plex_session() self._session = self.__adapt_plex_session()
self._sync_libraries = sync_libraries or []
def is_inactive(self) -> bool: def is_inactive(self) -> bool:
""" """
@@ -109,9 +112,8 @@ class Plex:
logger.error(f"获取媒体服务器所有媒体库列表出错:{str(err)}") logger.error(f"获取媒体服务器所有媒体库列表出错:{str(err)}")
return [] return []
libraries = [] libraries = []
black_list = (settings.MEDIASERVER_SYNC_BLACKLIST or '').split(",")
for library in self._libraries: for library in self._libraries:
if library.title in black_list: if self._sync_libraries and library.key not in self._sync_libraries:
continue continue
match library.type: match library.type:
case "movie": case "movie":
@@ -287,18 +289,18 @@ class Plex:
return None return None
# 如果配置了外网播放地址以及Token,则默认从Plex媒体服务器获取图片,否则返回有外网地址的图片资源 # 如果配置了外网播放地址以及Token,则默认从Plex媒体服务器获取图片,否则返回有外网地址的图片资源
if self._playhost and self._token: if self._playhost and self._token:
query = {"X-Plex-Token": settings.PLEX_TOKEN} query = {"X-Plex-Token": self._token}
if image_type == "Poster": if image_type == "Poster":
if item.thumb: if item.thumb:
image_url = RequestUtils.combine_url(host=settings.PLEX_PLAY_HOST, path=item.thumb, query=query) image_url = RequestUtils.combine_url(host=self._playhost, path=item.thumb, query=query)
else: else:
# 默认使用art也就是Backdrop进行处理 # 默认使用art也就是Backdrop进行处理
if item.art: if item.art:
image_url = RequestUtils.combine_url(host=settings.PLEX_PLAY_HOST, path=item.art, query=query) image_url = RequestUtils.combine_url(host=self._playhost, path=item.art, query=query)
# 这里对episode进行特殊处理,实际上episode的Backdrop是Poster # 这里对episode进行特殊处理,实际上episode的Backdrop是Poster
# 也有个别情况,比如机智的凡人小子episode就是Poster,因此这里把episode的优先级降低,默认还是取art # 也有个别情况,比如机智的凡人小子episode就是Poster,因此这里把episode的优先级降低,默认还是取art
if not image_url and item.TYPE == "episode" and item.thumb: if not image_url and item.TYPE == "episode" and item.thumb:
image_url = RequestUtils.combine_url(host=settings.PLEX_PLAY_HOST, path=item.thumb, query=query) image_url = RequestUtils.combine_url(host=self._playhost, path=item.thumb, query=query)
else: else:
if image_type == "Poster": if image_type == "Poster":
images = self._plex.fetchItems(ekey=f"{ekey}/posters", images = self._plex.fetchItems(ekey=f"{ekey}/posters",
@@ -811,23 +813,21 @@ class Plex:
logger.error(f"连接Plex出错:" + str(e)) logger.error(f"连接Plex出错:" + str(e))
return None return None
@staticmethod def __get_request_headers(self) -> dict:
def __get_request_headers() -> dict:
"""获取请求头""" """获取请求头"""
return { return {
"X-Plex-Token": settings.PLEX_TOKEN, "X-Plex-Token": self._token,
"Accept": "application/json", "Accept": "application/json",
"Content-Type": "application/json" "Content-Type": "application/json"
} }
@staticmethod def __adapt_plex_session(self) -> Session:
def __adapt_plex_session() -> Session:
""" """
创建并配置一个针对Plex服务的requests.Session实例 创建并配置一个针对Plex服务的requests.Session实例
这个会话包括特定的头部信息用于处理所有的Plex请求 这个会话包括特定的头部信息用于处理所有的Plex请求
""" """
# 设置请求头部,通常包括验证令牌和接受/内容类型头部 # 设置请求头部,通常包括验证令牌和接受/内容类型头部
headers = Plex.__get_request_headers() headers = self.__get_request_headers()
session = Session() session = Session()
session.headers = headers session.headers = headers
return session return session
+3 -1
View File
@@ -275,7 +275,8 @@ class QbittorrentModule(_ModuleBase, _DownloaderBase):
if not server: if not server:
return None return None
server.set_torrents_tag(ids=hashs, tags=['已整理']) server.set_torrents_tag(ids=hashs, tags=['已整理'])
# 移动模式删除种子 # FIXME 移动模式删除种子
"""
if settings.TRANSFER_TYPE in ["move"]: if settings.TRANSFER_TYPE in ["move"]:
if self.remove_torrents(hashs): if self.remove_torrents(hashs):
logger.info(f"移动模式删除种子成功:{hashs} ") logger.info(f"移动模式删除种子成功:{hashs} ")
@@ -285,6 +286,7 @@ class QbittorrentModule(_ModuleBase, _DownloaderBase):
if not files: if not files:
logger.warn(f"删除残留文件夹:{path}") logger.warn(f"删除残留文件夹:{path}")
shutil.rmtree(path, ignore_errors=True) shutil.rmtree(path, ignore_errors=True)
"""
def remove_torrents(self, hashs: Union[str, list], delete_file: bool = True, def remove_torrents(self, hashs: Union[str, list], delete_file: bool = True,
downloader: str = None) -> Optional[bool]: downloader: str = None) -> Optional[bool]:
+3 -1
View File
@@ -270,7 +270,8 @@ class TransmissionModule(_ModuleBase, _DownloaderBase):
else: else:
tags = ['已整理'] tags = ['已整理']
server.set_torrent_tag(ids=hashs, tags=tags) server.set_torrent_tag(ids=hashs, tags=tags)
# 移动模式删除种子 # FIXME 移动模式删除种子
"""
if settings.TRANSFER_TYPE in ["move"]: if settings.TRANSFER_TYPE in ["move"]:
if self.remove_torrents(hashs): if self.remove_torrents(hashs):
logger.info(f"移动模式删除种子成功:{hashs} ") logger.info(f"移动模式删除种子成功:{hashs} ")
@@ -280,6 +281,7 @@ class TransmissionModule(_ModuleBase, _DownloaderBase):
if not files: if not files:
logger.warn(f"删除残留文件夹:{path}") logger.warn(f"删除残留文件夹:{path}")
shutil.rmtree(path, ignore_errors=True) shutil.rmtree(path, ignore_errors=True)
"""
def remove_torrents(self, hashs: Union[str, list], delete_file: bool = True, def remove_torrents(self, hashs: Union[str, list], delete_file: bool = True,
downloader: str = None) -> Optional[bool]: downloader: str = None) -> Optional[bool]:
+8 -6
View File
@@ -1,7 +1,6 @@
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 settings
from app.core.context import Context, MediaInfo from app.core.context import Context, MediaInfo
from app.helper.notification import NotificationHelper from app.helper.notification import NotificationHelper
from app.log import logger from app.log import logger
@@ -66,6 +65,9 @@ class WechatModule(_ModuleBase, _MessageBase):
client: WeChat = self.get_client(source) client: WeChat = self.get_client(source)
if not client: if not client:
return None return None
client_config = self.get_config(source)
if not client_config:
return None
# URL参数 # URL参数
sVerifyMsgSig = args.get("msg_signature") sVerifyMsgSig = args.get("msg_signature")
sVerifyTimeStamp = args.get("timestamp") sVerifyTimeStamp = args.get("timestamp")
@@ -74,9 +76,9 @@ class WechatModule(_ModuleBase, _MessageBase):
logger.debug(f"微信请求参数错误:{args}") logger.debug(f"微信请求参数错误:{args}")
return None return None
# 解密模块 # 解密模块
wxcpt = WXBizMsgCrypt(sToken=settings.WECHAT_TOKEN, wxcpt = WXBizMsgCrypt(sToken=client_config.config.get('WECHAT_TOKEN'),
sEncodingAESKey=settings.WECHAT_ENCODING_AESKEY, sEncodingAESKey=client_config.config.get('WECHAT_ENCODING_AESKEY'),
sReceiveId=settings.WECHAT_CORPID) sReceiveId=client_config.config.get('WECHAT_CORPID'))
# 报文数据 # 报文数据
if not body: if not body:
logger.debug(f"微信请求数据为空") logger.debug(f"微信请求数据为空")
@@ -126,8 +128,8 @@ class WechatModule(_ModuleBase, _MessageBase):
# 解析消息内容 # 解析消息内容
if msg_type == "event" and event == "click": if msg_type == "event" and event == "click":
# 校验用户有权限执行交互命令 # 校验用户有权限执行交互命令
if settings.WECHAT_ADMINS: if client_config.config.get('WECHAT_ADMINS'):
wechat_admins = settings.WECHAT_ADMINS.split(',') wechat_admins = client_config.config.get('WECHAT_ADMINS').split(',')
if wechat_admins and not any( if wechat_admins and not any(
user_id == admin_user for admin_user in wechat_admins): user_id == admin_user for admin_user in wechat_admins):
client.send_msg(title="用户无权限执行菜单命令", userid=user_id) client.send_msg(title="用户无权限执行菜单命令", userid=user_id)
-1
View File
@@ -287,7 +287,6 @@ class Scheduler(metaclass=Singleton):
) )
# 下载器文件转移(每5分钟) # 下载器文件转移(每5分钟)
if settings.DOWNLOADER_MONITOR:
self._scheduler.add_job( self._scheduler.add_job(
self.start, self.start,
"interval", "interval",