fix downloaders && mediaservers && notifications

This commit is contained in:
jxxghp
2024-07-02 07:16:33 +08:00
parent b4e1e911fc
commit 9a07d88d41
15 changed files with 142 additions and 190 deletions

View File

@@ -1,5 +1,4 @@
import pickle
import traceback
from concurrent.futures import ThreadPoolExecutor, as_completed
from datetime import datetime
from typing import Dict
@@ -23,6 +22,8 @@ class SearchChain(ChainBase):
"""
站点资源搜索处理链
"""
__result_temp_file = "__search_result__"
def __init__(self):
super().__init__()
@@ -53,9 +54,9 @@ class SearchChain(ChainBase):
}
}
results = self.process(mediainfo=mediainfo, area=area, no_exists=no_exists)
# 保存结果
# 保存到本地文件
bytes_results = pickle.dumps(results)
self.systemconfig.set(SystemConfigKey.SearchResults, bytes_results)
self.save_cache(bytes_results, self.__result_temp_file)
return results
def search_by_title(self, title: str, page: int = 0, site: int = None) -> List[Context]:
@@ -77,23 +78,20 @@ class SearchChain(ChainBase):
# 组装上下文
contexts = [Context(meta_info=MetaInfo(title=torrent.title, subtitle=torrent.description),
torrent_info=torrent) for torrent in torrents]
# 保存结果
# 保存到本地文件
bytes_results = pickle.dumps(contexts)
self.systemconfig.set(SystemConfigKey.SearchResults, bytes_results)
self.save_cache(bytes_results, self.__result_temp_file)
return contexts
def last_search_results(self) -> List[Context]:
"""
获取上次搜索结果
"""
results = self.systemconfig.get(SystemConfigKey.SearchResults)
# 读取本地文件缓存
results = self.load_cache(self.__result_temp_file)
if not results:
return []
try:
return pickle.loads(results)
except Exception as e:
logger.error(f'加载搜索结果失败:{str(e)} - {traceback.format_exc()}')
return []
return results
def process(self, mediainfo: MediaInfo,
keyword: str = None,

View File

@@ -95,100 +95,10 @@ class Settings(BaseSettings):
AUTH_SITE: str = ""
# 交互搜索自动下载用户ID使用,分割
AUTO_DOWNLOAD_USER: Optional[str] = None
# 消息通知渠道 telegram/wechat/slack/synologychat/vocechat/webpush多个通知渠道用,分隔
MESSAGER: str = "webpush"
# WeChat企业ID
WECHAT_CORPID: Optional[str] = None
# WeChat应用Secret
WECHAT_APP_SECRET: Optional[str] = None
# WeChat应用ID
WECHAT_APP_ID: Optional[str] = None
# WeChat代理服务器
WECHAT_PROXY: str = "https://qyapi.weixin.qq.com"
# WeChat Token
WECHAT_TOKEN: Optional[str] = None
# WeChat EncodingAESKey
WECHAT_ENCODING_AESKEY: Optional[str] = None
# WeChat 管理员
WECHAT_ADMINS: Optional[str] = None
# Telegram Bot Token
TELEGRAM_TOKEN: Optional[str] = None
# Telegram Chat ID
TELEGRAM_CHAT_ID: Optional[str] = None
# Telegram 用户ID使用,分隔
TELEGRAM_USERS: str = ""
# Telegram 管理员ID使用,分隔
TELEGRAM_ADMINS: str = ""
# Slack Bot User OAuth Token
SLACK_OAUTH_TOKEN: str = ""
# Slack App-Level Token
SLACK_APP_TOKEN: str = ""
# Slack 频道名称
SLACK_CHANNEL: str = ""
# SynologyChat Webhook
SYNOLOGYCHAT_WEBHOOK: str = ""
# SynologyChat Token
SYNOLOGYCHAT_TOKEN: str = ""
# VoceChat地址
VOCECHAT_HOST: str = ""
# VoceChat ApiKey
VOCECHAT_API_KEY: str = ""
# VoceChat 频道ID
VOCECHAT_CHANNEL_ID: str = ""
# 下载器 qbittorrent/transmission启用多个下载器时使用,分隔,只有第一个会被默认使用
DOWNLOADER: str = "qbittorrent"
# 下载器监控开关
DOWNLOADER_MONITOR: bool = True
# Qbittorrent地址IP:PORT
QB_HOST: Optional[str] = None
# Qbittorrent用户名
QB_USER: Optional[str] = None
# Qbittorrent密码
QB_PASSWORD: Optional[str] = None
# Qbittorrent分类自动管理
QB_CATEGORY: bool = False
# Qbittorrent按顺序下载
QB_SEQUENTIAL: bool = True
# Qbittorrent忽略队列限制强制继续
QB_FORCE_RESUME: bool = False
# Transmission地址IP:PORT
TR_HOST: Optional[str] = None
# Transmission用户名
TR_USER: Optional[str] = None
# Transmission密码
TR_PASSWORD: Optional[str] = None
# 种子标签
TORRENT_TAG: str = "MOVIEPILOT"
# 下载站点字幕
DOWNLOAD_SUBTITLE: bool = True
# 媒体服务器 emby/jellyfin/plex多个媒体服务器,分割
MEDIASERVER: str = "emby"
# 媒体服务器同步间隔(小时)
MEDIASERVER_SYNC_INTERVAL: Optional[int] = 6
# 媒体服务器同步黑名单,多个媒体库名称,分割
MEDIASERVER_SYNC_BLACKLIST: Optional[str] = None
# EMBY服务器地址IP:PORT
EMBY_HOST: Optional[str] = None
# EMBY外网地址http(s)://DOMAIN:PORT未设置时使用EMBY_HOST
EMBY_PLAY_HOST: Optional[str] = None
# EMBY Api Key
EMBY_API_KEY: Optional[str] = None
# Jellyfin服务器地址IP:PORT
JELLYFIN_HOST: Optional[str] = None
# Jellyfin外网地址http(s)://DOMAIN:PORT未设置时使用JELLYFIN_HOST
JELLYFIN_PLAY_HOST: Optional[str] = None
# Jellyfin Api Key
JELLYFIN_API_KEY: Optional[str] = None
# Plex服务器地址IP:PORT
PLEX_HOST: Optional[str] = None
# Plex外网地址http(s)://DOMAIN:PORT未设置时使用PLEX_HOST
PLEX_PLAY_HOST: Optional[str] = None
# Plex Token
PLEX_TOKEN: Optional[str] = None
# 转移方式 link/copy/move/softlink
TRANSFER_TYPE: str = "copy"
# 是否同盘优先
TRANSFER_SAME_DISK: bool = True
# CookieCloud是否启动本地服务
COOKIECLOUD_ENABLE_LOCAL: Optional[bool] = False
# CookieCloud服务器地址
@@ -243,7 +153,6 @@ class Settings(BaseSettings):
@validator("SUBSCRIBE_RSS_INTERVAL",
"COOKIECLOUD_INTERVAL",
"MEDIASERVER_SYNC_INTERVAL",
"META_CACHE_EXPIRE",
pre=True, always=True)
def convert_int(cls, value):
@@ -335,24 +244,6 @@ class Settings(BaseSettings):
}
return {}
@property
def DEFAULT_DOWNLOADER(self):
"""
默认下载器
"""
if not self.DOWNLOADER:
return None
return next((d for d in settings.DOWNLOADER.split(",") if d), None)
@property
def DOWNLOADERS(self):
"""
下载器列表
"""
if not self.DOWNLOADER:
return []
return [d for d in settings.DOWNLOADER.split(",") if d]
@property
def VAPID(self):
return {

20
app/helper/downloader.py Normal file
View File

@@ -0,0 +1,20 @@
from app.db.systemconfig_oper import SystemConfigOper
from app.schemas.types import SystemConfigKey
class DownloaderHelper:
"""
下载器帮助类
"""
def __init__(self):
self.systemconfig = SystemConfigOper()
def get_downloaders(self) -> dict:
"""
获取下载器
"""
downloader_conf: dict = self.systemconfig.get(SystemConfigKey.Downloaders)
if not downloader_conf:
return {}
return downloader_conf

20
app/helper/mediaserver.py Normal file
View File

@@ -0,0 +1,20 @@
from app.db.systemconfig_oper import SystemConfigOper
from app.schemas.types import SystemConfigKey
class MediaServerHelper:
"""
媒体服务器帮助类
"""
def __init__(self):
self.systemconfig = SystemConfigOper()
def get_mediaservers(self) -> dict:
"""
获取媒体服务器
"""
mediaserver_conf: dict = self.systemconfig.get(SystemConfigKey.MediaServers)
if not mediaserver_conf:
return {}
return mediaserver_conf

View File

@@ -0,0 +1,20 @@
from app.db.systemconfig_oper import SystemConfigOper
from app.schemas.types import SystemConfigKey
class NotificationHelper:
"""
消息通知渠道帮助类
"""
def __init__(self):
self.systemconfig = SystemConfigOper()
def get_notifications(self) -> dict:
"""
获取消息通知渠道
"""
notification_conf: dict = self.systemconfig.get(SystemConfigKey.Notifications)
if not notification_conf:
return {}
return notification_conf

View File

@@ -15,14 +15,14 @@ from app.utils.http import RequestUtils
class Emby:
def __init__(self):
self._host = settings.EMBY_HOST
def __init__(self, host: str = None, play_host: str = None, apikey: str = None):
self._host = host
if self._host:
self._host = RequestUtils.standardize_base_url(self._host)
self._playhost = settings.EMBY_PLAY_HOST
self._playhost = play_host
if self._playhost:
self._playhost = RequestUtils.standardize_base_url(self._playhost)
self._apikey = settings.EMBY_API_KEY
self._apikey = apikey
self.user = self.get_user(settings.SUPERUSER)
self.folders = self.get_emby_folders()
self.serverid = self.get_server_id()

View File

@@ -12,14 +12,14 @@ from app.utils.http import RequestUtils
class Jellyfin:
def __init__(self):
self._host = settings.JELLYFIN_HOST
def __init__(self, host: str = None, play_host: str = None, apikey: str = None):
self._host = host
if self._host:
self._host = RequestUtils.standardize_base_url(self._host)
self._playhost = settings.JELLYFIN_PLAY_HOST
self._playhost = play_host
if self._playhost:
self._playhost = RequestUtils.standardize_base_url(self._playhost)
self._apikey = settings.JELLYFIN_API_KEY
self._apikey = apikey
self.user = self.get_user(settings.SUPERUSER)
self.serverid = self.get_server_id()

View File

@@ -19,14 +19,14 @@ class Plex:
_plex = None
_session = None
def __init__(self):
self._host = settings.PLEX_HOST
def __init__(self, host: str = None, play_host: str = None, token: str = None):
self._host = host
if self._host:
self._host = RequestUtils.standardize_base_url(self._host)
self._playhost = settings.PLEX_PLAY_HOST
self._playhost = play_host
if self._playhost:
self._playhost = RequestUtils.standardize_base_url(self._playhost)
self._token = settings.PLEX_TOKEN
self._token = token
if self._host and self._token:
try:
self._plex = PlexServer(self._host, self._token)

View File

@@ -8,7 +8,6 @@ from qbittorrentapi.transfer import TransferInfoDictionary
from app.core.config import settings
from app.log import logger
from app.utils.string import StringUtils
class Qbittorrent:
@@ -23,12 +22,10 @@ class Qbittorrent:
"""
若不设置参数,则创建配置文件设置的下载器
"""
if host and port:
self._host, self._port = host, port
else:
self._host, self._port = StringUtils.get_domain_address(address=settings.QB_HOST, prefix=True)
self._username = username if username else settings.QB_USER
self._password = password if password else settings.QB_PASSWORD
self._host = host
self._port = port
self._username = username
self._password = password
if self._host and self._port:
self.qbc = self.__login_qbittorrent()

View File

@@ -21,22 +21,21 @@ class Slack:
_client: WebClient = None
_service: SocketModeHandler = None
_ds_url = f"http://127.0.0.1:{settings.PORT}/api/v1/message?token={settings.API_TOKEN}"
_channel = ""
def __init__(self):
def __init__(self, oauth_token: str, app_token: str, channel: str = ""):
if not settings.SLACK_OAUTH_TOKEN or not settings.SLACK_APP_TOKEN:
return
try:
slack_app = App(token=settings.SLACK_OAUTH_TOKEN,
slack_app = App(token=oauth_token,
ssl_check_enabled=False,
url_verification_enabled=False)
except Exception as err:
logger.error(f"Slack初始化失败: {str(err)}")
return
self._client = slack_app.client
self._channel = channel
# 注册消息响应
@slack_app.event("message")
@@ -72,7 +71,7 @@ class Slack:
try:
self._service = SocketModeHandler(
slack_app,
settings.SLACK_APP_TOKEN
app_token
)
self._service.connect()
logger.info("Slack消息接收服务启动")
@@ -337,7 +336,7 @@ class Slack:
if conversation_id:
break
for channel in result["channels"]:
if channel.get("name") == (settings.SLACK_CHANNEL or "全体"):
if channel.get("name") == (self._channel or "全体"):
conversation_id = channel.get("id")
break
except Exception as e:

View File

@@ -24,14 +24,14 @@ class Telegram:
_event = Event()
_bot: telebot.TeleBot = None
def __init__(self):
def __init__(self, token: str = None, chat_id: str = None):
"""
初始化参数
"""
# Token
self._telegram_token = settings.TELEGRAM_TOKEN
self._telegram_token = token
# Chat Id
self._telegram_chat_id = settings.TELEGRAM_CHAT_ID
self._telegram_chat_id = chat_id
# 初始化机器人
if self._telegram_token and self._telegram_chat_id:
# bot

View File

@@ -1,12 +1,10 @@
from typing import Optional, Union, Tuple, List, Dict
from typing import Optional, Union, Tuple, List
import transmission_rpc
from transmission_rpc import Client, Torrent, File
from transmission_rpc.session import SessionStats, Session
from app.core.config import settings
from app.log import logger
from app.utils.string import StringUtils
class Transmission:
@@ -28,12 +26,10 @@ class Transmission:
"""
若不设置参数,则创建配置文件设置的下载器
"""
if host and port:
self._host, self._port = host, port
else:
self._host, self._port = StringUtils.get_domain_address(address=settings.TR_HOST, prefix=False)
self._username = username if username else settings.TR_USER
self._password = password if password else settings.TR_PASSWORD
self._host = host
self._port = port
self._username = username
self._password = password
if self._host and self._port:
self.trc = self.__login_transmission()

View File

@@ -2,7 +2,6 @@ import re
import threading
from typing import Optional, List
from app.core.config import settings
from app.core.context import MediaInfo, Context
from app.core.metainfo import MetaInfo
from app.log import logger
@@ -23,18 +22,18 @@ class VoceChat:
# 请求对象
_client = None
def __init__(self):
def __init__(self, host: str = None, apikey: str = None, channel_id: str = None):
"""
初始化
"""
self._host = settings.VOCECHAT_HOST
self._host = host
if self._host:
if not self._host.endswith("/"):
self._host += "/"
if not self._host.startswith("http"):
self._playhost = "http://" + self._host
self._apikey = settings.VOCECHAT_API_KEY
self._channel_id = settings.VOCECHAT_CHANNEL_ID
self._apikey = apikey
self._channel_id = channel_id
if self._apikey and self._host and self._channel_id:
self._client = RequestUtils(headers={
"content-type": "text/markdown",

View File

@@ -4,7 +4,6 @@ import threading
from datetime import datetime
from typing import Optional, List, Dict
from app.core.config import settings
from app.core.context import MediaInfo, Context
from app.core.metainfo import MetaInfo
from app.log import logger
@@ -28,25 +27,34 @@ class WeChat:
_appsecret = None
# 企业微信AppID
_appid = None
# 代理
_proxy = None
# 企业微信发送消息URL
_send_msg_url = f"{settings.WECHAT_PROXY}/cgi-bin/message/send?access_token=%s"
_send_msg_url = "/cgi-bin/message/send?access_token=%s"
# 企业微信获取TokenURL
_token_url = f"{settings.WECHAT_PROXY}/cgi-bin/gettoken?corpid=%s&corpsecret=%s"
_token_url = "/cgi-bin/gettoken?corpid=%s&corpsecret=%s"
# 企业微信创新菜单URL
_create_menu_url = f"{settings.WECHAT_PROXY}/cgi-bin/menu/create?access_token=%s&agentid=%s"
_create_menu_url = "/cgi-bin/menu/create?access_token=%s&agentid=%s"
def __init__(self):
def __init__(self, corpid: str = None, appsecret: str = None, appid: str = None, proxy: str = None):
"""
初始化
"""
self._corpid = settings.WECHAT_CORPID
self._appsecret = settings.WECHAT_APP_SECRET
self._appid = settings.WECHAT_APP_ID
self._corpid = corpid
self._appsecret = appsecret
self._appid = appid
self._proxy = proxy or "https://qyapi.weixin.qq.com"
if self._corpid and self._appsecret and self._appid:
self.__get_access_token()
if self._proxy:
self._proxy = self._proxy.rstrip("/")
self._send_msg_url = f"{self._proxy}/{self._send_msg_url}"
self._token_url = f"{self._proxy}/{self._token_url}"
self._create_menu_url = f"{self._proxy}/{self._create_menu_url}"
def get_state(self):
"""
获取状态

View File

@@ -58,16 +58,24 @@ class EventType(Enum):
# 系统配置Key字典
class SystemConfigKey(Enum):
# 用户已安装的插件
UserInstalledPlugins = "UserInstalledPlugins"
# 搜索结果
SearchResults = "SearchResults"
# 下载器配置
Downloaders = "Downloaders"
# 媒体服务器配置
MediaServers = "MediaServers"
# 消息通知配置
Notifications = "Notifications"
# 下载目录配置
DownloadDirectories = "DownloadDirectories"
# 媒体库目录配置
LibraryDirectories = "LibraryDirectories"
# 阿里云盘认证参数
UserAliyunParams = "UserAliyunParams"
# 115网盘认证参数
User115Params = "User115Params"
# 搜索站点范围
IndexerSites = "IndexerSites"
# 订阅站点范围
RssSites = "RssSites"
# 种子优先级规则
TorrentsPriority = "TorrentsPriority"
# 通知消息渠道设置
NotificationChannels = "NotificationChannels"
# 自定义制作组/字幕组
@@ -76,6 +84,10 @@ class SystemConfigKey(Enum):
Customization = "Customization"
# 自定义识别词
CustomIdentifiers = "CustomIdentifiers"
# 转移屏蔽词
TransferExcludeWords = "TransferExcludeWords"
# 种子优先级规则
TorrentsPriority = "TorrentsPriority"
# 搜索优先级规则
SearchFilterRules = "SearchFilterRules"
# 订阅优先级规则
@@ -86,22 +98,14 @@ class SystemConfigKey(Enum):
DefaultFilterRules = "DefaultFilterRules"
# 默认搜索过滤规则
DefaultSearchFilterRules = "DefaultSearchFilterRules"
# 转移屏蔽词
TransferExcludeWords = "TransferExcludeWords"
# 插件安装统计
PluginInstallReport = "PluginInstallReport"
# 订阅统计
SubscribeReport = "SubscribeReport"
# 用户自定义CSS
UserCustomCSS = "UserCustomCSS"
# 下载目录定义
DownloadDirectories = "DownloadDirectories"
# 媒体库目录定义
LibraryDirectories = "LibraryDirectories"
# 阿里云盘认证参数
UserAliyunParams = "UserAliyunParams"
# 115网盘认证参数
User115Params = "User115Params"
# 用户已安装的插件
UserInstalledPlugins = "UserInstalledPlugins"
# 插件安装统计
PluginInstallReport = "PluginInstallReport"
# 处理进度Key字典