fix downloaders

This commit is contained in:
jxxghp
2024-07-02 11:00:55 +08:00
parent dfc5872087
commit c8fe6e4284
19 changed files with 311 additions and 140 deletions

View File

@@ -9,14 +9,14 @@ class DashboardChain(ChainBase, metaclass=Singleton):
"""
各类仪表板统计处理链
"""
def media_statistic(self) -> Optional[List[schemas.Statistic]]:
def media_statistic(self, server: str = None) -> Optional[List[schemas.Statistic]]:
"""
媒体数量统计
"""
return self.run_module("media_statistic")
return self.run_module("media_statistic", server=server)
def downloader_info(self) -> Optional[List[schemas.DownloaderInfo]]:
def downloader_info(self, downloader: str = None) -> Optional[List[schemas.DownloaderInfo]]:
"""
下载器信息
"""
return self.run_module("downloader_info")
return self.run_module("downloader_info", downloader=downloader)

View File

@@ -1,4 +1,7 @@
from typing import List
from app.db.systemconfig_oper import SystemConfigOper
from app.schemas.system import DownloaderConf
from app.schemas.types import SystemConfigKey
@@ -10,11 +13,11 @@ class DownloaderHelper:
def __init__(self):
self.systemconfig = SystemConfigOper()
def get_downloaders(self) -> dict:
def get_downloaders(self) -> List[DownloaderConf]:
"""
获取下载器
"""
downloader_conf: dict = self.systemconfig.get(SystemConfigKey.Downloaders)
if not downloader_conf:
return {}
return downloader_conf
downloader_confs: List[dict] = self.systemconfig.get(SystemConfigKey.Downloaders)
if not downloader_confs:
return []
return [DownloaderConf(**conf) for conf in downloader_confs]

View File

@@ -21,7 +21,6 @@ class EmbyModule(_ModuleBase):
mediaservers = MediaServerHelper().get_mediaservers()
if not mediaservers:
return
# 读取Emby配置
for server in mediaservers:
if server.type == "emby":
self._servers[server.name] = Emby(**server.config)
@@ -142,16 +141,24 @@ class EmbyModule(_ModuleBase):
)
return None
def media_statistic(self) -> List[schemas.Statistic]:
def media_statistic(self, server: str = None) -> Optional[List[schemas.Statistic]]:
"""
媒体数量统计
"""
if server:
server_obj = self.get_server(server)
if not server_obj:
return None
servers = [server_obj]
else:
servers = self._servers.values()
media_statistics = []
for server in self._servers.values():
for server in servers:
media_statistic = server.get_medias_count()
if not media_statistics:
continue
media_statistic.user_count = server.get_user_count()
if media_statistic:
media_statistics.append(media_statistic)
media_statistics.append(media_statistic)
return media_statistics
def mediaserver_librarys(self, server: str,

View File

@@ -15,7 +15,10 @@ from app.utils.http import RequestUtils
class Emby:
def __init__(self, host: str = None, play_host: str = None, apikey: str = None):
def __init__(self, host: str, apikey: str, play_host: str = None, **kwargs):
if not host or not apikey:
logger.error("Emby服务器配置不完整")
return
self._host = host
if self._host:
self._host = RequestUtils.standardize_base_url(self._host)

View File

@@ -21,7 +21,6 @@ class JellyfinModule(_ModuleBase):
mediaservers = MediaServerHelper().get_mediaservers()
if not mediaservers:
return
# 读取Jelly配置
for server in mediaservers:
if server.type == "jellyfin":
self._servers[server.name] = Jellyfin(**server.config)
@@ -140,16 +139,24 @@ class JellyfinModule(_ModuleBase):
)
return None
def media_statistic(self) -> List[schemas.Statistic]:
def media_statistic(self, server: str = None) -> Optional[List[schemas.Statistic]]:
"""
媒体数量统计
"""
if server:
server_obj = self.get_server(server)
if not server_obj:
return None
servers = [server_obj]
else:
servers = self._servers.values()
media_statistics = []
for server in self._servers.values():
for server in servers:
media_statistic = server.get_medias_count()
if not media_statistics:
continue
media_statistic.user_count = server.get_user_count()
if media_statistic:
media_statistics.append(media_statistic)
media_statistics.append(media_statistic)
return media_statistics
def mediaserver_librarys(self, server: str = None,

View File

@@ -12,7 +12,10 @@ from app.utils.http import RequestUtils
class Jellyfin:
def __init__(self, host: str = None, play_host: str = None, apikey: str = None):
def __init__(self, host: str, apikey: str, play_host: str = None, **kwargs):
if not host or not apikey:
logger.error("Jellyfin服务器配置不完整")
return
self._host = host
if self._host:
self._host = RequestUtils.standardize_base_url(self._host)

View File

@@ -21,7 +21,6 @@ class PlexModule(_ModuleBase):
mediaservers = MediaServerHelper().get_mediaservers()
if not mediaservers:
return
# 读取Emby配置
for server in mediaservers:
if server.type == "plex":
self._servers[server.name] = Plex(**server.config)
@@ -130,16 +129,23 @@ class PlexModule(_ModuleBase):
)
return None
def media_statistic(self) -> List[schemas.Statistic]:
def media_statistic(self, server: str = None) -> Optional[List[schemas.Statistic]]:
"""
媒体数量统计
"""
if server:
server_obj = self.get_server(server)
if not server_obj:
return None
servers = [server_obj]
else:
servers = self._servers.values()
media_statistics = []
for server in self._servers.values():
for server in servers:
media_statistic = server.get_medias_count()
media_statistic.user_count = 1
if media_statistic:
media_statistics.append(media_statistic)
if not media_statistics:
continue
media_statistics.append(media_statistic)
return media_statistics
def mediaserver_librarys(self, server: str = None, **kwargs) -> Optional[List[schemas.MediaServerLibrary]]:

View File

@@ -19,7 +19,10 @@ class Plex:
_plex = None
_session = None
def __init__(self, host: str = None, play_host: str = None, token: str = None):
def __init__(self, host: str, token: str, play_host: str = None, **kwargs):
if not host or not token:
logger.error("Plex服务器配置不完整")
return
self._host = host
if self._host:
self._host = RequestUtils.standardize_base_url(self._host)

View File

@@ -1,6 +1,6 @@
import shutil
from pathlib import Path
from typing import Set, Tuple, Optional, Union, List
from typing import Set, Tuple, Optional, Union, List, Dict
from qbittorrentapi import TorrentFilesList
from torrentool.torrent import Torrent
@@ -8,6 +8,7 @@ from torrentool.torrent import Torrent
from app import schemas
from app.core.config import settings
from app.core.metainfo import MetaInfo
from app.helper.downloader import DownloaderHelper
from app.log import logger
from app.modules import _ModuleBase
from app.modules.qbittorrent.qbittorrent import Qbittorrent
@@ -18,15 +19,36 @@ from app.utils.system import SystemUtils
class QbittorrentModule(_ModuleBase):
qbittorrent: Qbittorrent = None
_servers: Dict[str, Qbittorrent] = {}
_default_server: Qbittorrent = None
def init_module(self) -> None:
self.qbittorrent = Qbittorrent()
"""
初始化模块
"""
# 读取下载器配置
self._servers = {}
downloaders = DownloaderHelper().get_downloaders()
if not downloaders:
return
for server in downloaders:
if server.type == "qbittorrent":
self._servers[server.name] = Qbittorrent(**server.config)
if server.default:
self._default_server = self._servers[server.name]
@staticmethod
def get_name() -> str:
return "Qbittorrent"
def get_server(self, name: str = None) -> Optional[Qbittorrent]:
"""
获取服务器name为空则返回默认服务器
"""
if name:
return self._servers.get(name)
return self._default_server
def stop(self):
pass
@@ -34,26 +56,28 @@ class QbittorrentModule(_ModuleBase):
"""
测试模块连接性
"""
if self.qbittorrent.is_inactive():
self.qbittorrent.reconnect()
if not self.qbittorrent.transfer_info():
return False, "无法获取Qbittorrent状态请检查参数配置"
for name, server in self._servers.items():
if server.is_inactive():
server.reconnect()
if not server.transfer_info():
return False, f"无法连接Qbittorrent下载器{name}"
return True, ""
def init_setting(self) -> Tuple[str, Union[str, bool]]:
return "DOWNLOADER", "qbittorrent"
pass
def scheduler_job(self) -> None:
"""
定时任务每10分钟调用一次
"""
# 定时重连
if self.qbittorrent.is_inactive():
self.qbittorrent.reconnect()
for name, server in self._servers.items():
if server.is_inactive():
logger.info(f"Qbittorrent下载器 {name} 连接断开,尝试重连 ...")
server.reconnect()
def download(self, content: Union[Path, str], download_dir: Path, cookie: str,
episodes: Set[int] = None, category: str = None,
downloader: str = settings.DEFAULT_DOWNLOADER) -> Optional[Tuple[Optional[str], str]]:
downloader: str = None) -> Optional[Tuple[Optional[str], str]]:
"""
根据种子文件,选择并添加下载任务
:param content: 种子文件地址或者磁力链接
@@ -79,15 +103,17 @@ class QbittorrentModule(_ModuleBase):
logger.error(f"获取种子名称失败:{e}")
return "", 0
# 不是默认下载器不处理
if downloader != "qbittorrent":
return None
if not content:
return None
return None, "下载内容为空"
if isinstance(content, Path) and not content.exists():
logger.error(f"种子文件不存在:{content}")
return None, f"种子文件不存在:{content}"
# 获取下载器
server = self.get_server(downloader)
if not server:
return None
# 生成随机Tag
tag = StringUtils.generate_random_str(10)
if settings.TORRENT_TAG:
@@ -97,7 +123,7 @@ class QbittorrentModule(_ModuleBase):
# 如果要选择文件则先暂停
is_paused = True if episodes else False
# 添加任务
state = self.qbittorrent.add_torrent(
state = server.add_torrent(
content=content.read_bytes() if isinstance(content, Path) else content,
download_dir=str(download_dir),
is_paused=is_paused,
@@ -111,7 +137,7 @@ class QbittorrentModule(_ModuleBase):
if not torrent_name:
return None, f"添加种子任务失败:无法读取种子文件"
# 查询所有下载器的种子
torrents, error = self.qbittorrent.get_torrents()
torrents, error = server.get_torrents()
if error:
return None, "无法连接qbittorrent下载器"
if torrents:
@@ -123,21 +149,21 @@ class QbittorrentModule(_ModuleBase):
logger.warn(f"下载器中已存在该种子任务:{torrent_hash} - {torrent.get('name')}")
# 给种子打上标签
if "已整理" in torrent_tags:
self.qbittorrent.remove_torrents_tag(ids=torrent_hash, tag=['已整理'])
server.remove_torrents_tag(ids=torrent_hash, tag=['已整理'])
if settings.TORRENT_TAG and settings.TORRENT_TAG not in torrent_tags:
logger.info(f"给种子 {torrent_hash} 打上标签:{settings.TORRENT_TAG}")
self.qbittorrent.set_torrents_tag(ids=torrent_hash, tags=[settings.TORRENT_TAG])
server.set_torrents_tag(ids=torrent_hash, tags=[settings.TORRENT_TAG])
return torrent_hash, f"下载任务已存在"
return None, f"添加种子任务失败:{content}"
else:
# 获取种子Hash
torrent_hash = self.qbittorrent.get_torrent_id_by_tag(tags=tag)
torrent_hash = server.get_torrent_id_by_tag(tags=tag)
if not torrent_hash:
return None, f"下载任务添加成功但获取Qbittorrent任务信息失败{content}"
else:
if is_paused:
# 种子文件
torrent_files = self.qbittorrent.get_files(torrent_hash)
torrent_files = server.get_files(torrent_hash)
if not torrent_files:
return torrent_hash, "获取种子文件失败,下载任务可能在暂停状态"
@@ -157,17 +183,17 @@ class QbittorrentModule(_ModuleBase):
sucess_epidised = list(set(sucess_epidised).union(set(meta_info.episode_list)))
if sucess_epidised and file_ids:
# 选择文件
self.qbittorrent.set_files(torrent_hash=torrent_hash, file_ids=file_ids, priority=0)
server.set_files(torrent_hash=torrent_hash, file_ids=file_ids, priority=0)
# 开始任务
if settings.QB_FORCE_RESUME:
# 强制继续
self.qbittorrent.torrents_set_force_start(torrent_hash)
server.torrents_set_force_start(torrent_hash)
else:
self.qbittorrent.start_torrents(torrent_hash)
server.start_torrents(torrent_hash)
return torrent_hash, f"添加下载成功,已选择集数:{sucess_epidised}"
else:
if settings.QB_FORCE_RESUME:
self.qbittorrent.torrents_set_force_start(torrent_hash)
server.torrents_set_force_start(torrent_hash)
return torrent_hash, "添加下载成功"
def list_torrents(self, status: TorrentStatus = None,
@@ -181,12 +207,15 @@ class QbittorrentModule(_ModuleBase):
:param downloader: 下载器
:return: 下载器中符合状态的种子列表
"""
if downloader != "qbittorrent":
# 获取下载器
server = self.get_server(downloader)
if not server:
return None
ret_torrents = []
if hashs:
# 按Hash获取
torrents, _ = self.qbittorrent.get_torrents(ids=hashs, tags=settings.TORRENT_TAG)
torrents, _ = server.get_torrents(ids=hashs, tags=settings.TORRENT_TAG)
for torrent in torrents or []:
content_path = torrent.get("content_path")
if content_path:
@@ -202,7 +231,7 @@ class QbittorrentModule(_ModuleBase):
))
elif status == TorrentStatus.TRANSFER:
# 获取已完成且未整理的
torrents = self.qbittorrent.get_completed_torrents(tags=settings.TORRENT_TAG)
torrents = server.get_completed_torrents(tags=settings.TORRENT_TAG)
for torrent in torrents or []:
tags = torrent.get("tags") or []
if "已整理" in tags:
@@ -221,7 +250,7 @@ class QbittorrentModule(_ModuleBase):
))
elif status == TorrentStatus.DOWNLOADING:
# 获取正在下载的任务
torrents = self.qbittorrent.get_downloading_torrents(tags=settings.TORRENT_TAG)
torrents = server.get_downloading_torrents(tags=settings.TORRENT_TAG)
for torrent in torrents or []:
meta = MetaInfo(torrent.get('name'))
ret_torrents.append(DownloadingTorrent(
@@ -251,9 +280,10 @@ class QbittorrentModule(_ModuleBase):
:param path: 源目录
:param downloader: 下载器
"""
if downloader != "qbittorrent":
return
self.qbittorrent.set_torrents_tag(ids=hashs, tags=['已整理'])
server = self.get_server(downloader)
if not server:
return None
server.set_torrents_tag(ids=hashs, tags=['已整理'])
# 移动模式删除种子
if settings.TRANSFER_TYPE in ["move", "rclone_move"]:
if self.remove_torrents(hashs):
@@ -274,9 +304,10 @@ class QbittorrentModule(_ModuleBase):
:param downloader: 下载器
:return: bool
"""
if downloader != "qbittorrent":
server = self.get_server(downloader)
if not server:
return None
return self.qbittorrent.delete_torrents(delete_file=delete_file, ids=hashs)
return server.delete_torrents(delete_file=delete_file, ids=hashs)
def start_torrents(self, hashs: Union[list, str],
downloader: str = settings.DEFAULT_DOWNLOADER) -> Optional[bool]:
@@ -286,9 +317,10 @@ class QbittorrentModule(_ModuleBase):
:param downloader: 下载器
:return: bool
"""
if downloader != "qbittorrent":
server = self.get_server(downloader)
if not server:
return None
return self.qbittorrent.start_torrents(ids=hashs)
return server.start_torrents(ids=hashs)
def stop_torrents(self, hashs: Union[list, str], downloader: str = settings.DEFAULT_DOWNLOADER) -> Optional[bool]:
"""
@@ -297,29 +329,41 @@ class QbittorrentModule(_ModuleBase):
:param downloader: 下载器
:return: bool
"""
if downloader != "qbittorrent":
server = self.get_server(downloader)
if not server:
return None
return self.qbittorrent.stop_torrents(ids=hashs)
return server.stop_torrents(ids=hashs)
def torrent_files(self, tid: str, downloader: str = settings.DEFAULT_DOWNLOADER) -> Optional[TorrentFilesList]:
"""
获取种子文件列表
"""
if downloader != "qbittorrent":
server = self.get_server(downloader)
if not server:
return None
return self.qbittorrent.get_files(tid=tid)
return server.get_files(tid=tid)
def downloader_info(self) -> [schemas.DownloaderInfo]:
def downloader_info(self, downloader: str = None) -> Optional[List[schemas.DownloaderInfo]]:
"""
下载器信息
"""
if downloader:
server = self.get_server(downloader)
if not server:
return None
servers = [server]
else:
servers = self._servers.values()
# 调用Qbittorrent API查询实时信息
info = self.qbittorrent.transfer_info()
if not info:
return [schemas.DownloaderInfo()]
return [schemas.DownloaderInfo(
download_speed=info.get("dl_info_speed"),
upload_speed=info.get("up_info_speed"),
download_size=info.get("dl_info_data"),
upload_size=info.get("up_info_data")
)]
ret_info = []
for server in servers:
info = server.transfer_info()
if not info:
continue
ret_info.append(schemas.DownloaderInfo(
download_speed=info.get("dl_info_speed"),
upload_speed=info.get("up_info_speed"),
download_size=info.get("dl_info_data"),
upload_size=info.get("up_info_data")
))
return ret_info

View File

@@ -18,7 +18,7 @@ class Qbittorrent:
qbc: Client = None
def __init__(self, host: str = None, port: int = None, username: str = None, password: str = None):
def __init__(self, host: str = None, port: int = None, username: str = None, password: str = None, **kwargs):
"""
若不设置参数,则创建配置文件设置的下载器
"""

View File

@@ -24,7 +24,11 @@ class Slack:
_ds_url = f"http://127.0.0.1:{settings.PORT}/api/v1/message?token={settings.API_TOKEN}"
_channel = ""
def __init__(self, oauth_token: str, app_token: str, channel: str = ""):
def __init__(self, oauth_token: str, app_token: str, channel: str = "", **kwargs):
if not oauth_token or not app_token:
logger.error("Slack 配置不完整!")
return
try:
slack_app = App(token=oauth_token,

View File

@@ -1,10 +1,9 @@
import json
import re
from threading import Lock
from typing import Optional, List
from urllib.parse import quote
from threading import Lock
from app.core.config import settings
from app.core.context import MediaInfo, Context
from app.core.metainfo import MetaInfo
from app.log import logger
@@ -15,10 +14,13 @@ lock = Lock()
class SynologyChat:
def __init__(self):
def __init__(self, webhook: str, token: str, **kwargs):
if not webhook or not token:
logger.error("SynologyChat配置不完整")
return
self._req = RequestUtils(content_type="application/x-www-form-urlencoded")
self._webhook_url = settings.SYNOLOGYCHAT_WEBHOOK
self._token = settings.SYNOLOGYCHAT_TOKEN
self._webhook_url = webhook
self._token = token
if self._webhook_url:
self._domain = StringUtils.get_base_url(self._webhook_url)

View File

@@ -24,10 +24,13 @@ class Telegram:
_event = Event()
_bot: telebot.TeleBot = None
def __init__(self, token: str = None, chat_id: str = None):
def __init__(self, token: str, chat_id: str, **kwargs):
"""
初始化参数
"""
if not token or not chat_id:
logger.error("Telegram配置不完整")
return
# Token
self._telegram_token = token
# Chat Id

View File

@@ -1,6 +1,6 @@
import shutil
from pathlib import Path
from typing import Set, Tuple, Optional, Union, List
from typing import Set, Tuple, Optional, Union, List, Dict
from torrentool.torrent import Torrent
from transmission_rpc import File
@@ -8,6 +8,7 @@ from transmission_rpc import File
from app import schemas
from app.core.config import settings
from app.core.metainfo import MetaInfo
from app.helper.downloader import DownloaderHelper
from app.log import logger
from app.modules import _ModuleBase
from app.modules.transmission.transmission import Transmission
@@ -18,14 +19,32 @@ from app.utils.system import SystemUtils
class TransmissionModule(_ModuleBase):
transmission: Transmission = None
_servers: Dict[str, Transmission] = {}
_default_server: Transmission = None
def init_module(self) -> None:
self.transmission = Transmission()
# 读取下载器配置
self._servers = {}
downloaders = DownloaderHelper().get_downloaders()
if not downloaders:
return
for server in downloaders:
if server.type == "transmission":
self._servers[server.name] = Transmission(**server.config)
if server.default:
self._default_server = self._servers[server.name]
@staticmethod
def get_name() -> str:
return "Transmission"
def get_server(self, name: str = None) -> Optional[Transmission]:
"""
获取服务器name为空则返回默认服务器
"""
if name:
return self._servers.get(name)
return self._default_server
def stop(self):
pass
@@ -34,22 +53,27 @@ class TransmissionModule(_ModuleBase):
"""
测试模块连接性
"""
if self.transmission.is_inactive():
self.transmission.reconnect()
if not self.transmission.transfer_info():
return False, "无法获取Transmission状态请检查参数配置"
if not self._servers:
return False, "未配置Transmission下载器"
for name, server in self._servers.items():
if server.is_inactive():
server.reconnect()
if not server.transfer_info():
return False, f"无法连接Transmission下载器{name}"
return True, ""
def init_setting(self) -> Tuple[str, Union[str, bool]]:
return "DOWNLOADER", "transmission"
pass
def scheduler_job(self) -> None:
"""
定时任务每10分钟调用一次
"""
# 定时重连
if self.transmission.is_inactive():
self.transmission.reconnect()
for name, server in self._servers.items():
if server.is_inactive():
logger.info(f"Transmission下载器 {name} 连接断开,尝试重连 ...")
server.reconnect()
def download(self, content: Union[Path, str], download_dir: Path, cookie: str,
episodes: Set[int] = None, category: str = None,
@@ -79,15 +103,16 @@ class TransmissionModule(_ModuleBase):
logger.error(f"获取种子名称失败:{e}")
return "", 0
# 不是默认下载器不处理
if downloader != "transmission":
return None
if not content:
return None
return None, "下载内容为空"
if isinstance(content, Path) and not content.exists():
return None, f"种子文件不存在:{content}"
# 获取下载器
server = self.get_server(downloader)
if not server:
return None
# 如果要选择文件则先暂停
is_paused = True if episodes else False
# 标签
@@ -96,7 +121,7 @@ class TransmissionModule(_ModuleBase):
else:
labels = None
# 添加任务
torrent = self.transmission.add_torrent(
torrent = server.add_torrent(
content=content.read_bytes() if isinstance(content, Path) else content,
download_dir=str(download_dir),
is_paused=is_paused,
@@ -109,7 +134,7 @@ class TransmissionModule(_ModuleBase):
if not torrent_name:
return None, f"添加种子任务失败:无法读取种子文件"
# 查询所有下载器的种子
torrents, error = self.transmission.get_torrents()
torrents, error = server.get_torrents()
if error:
return None, "无法连接transmission下载器"
if torrents:
@@ -126,17 +151,17 @@ class TransmissionModule(_ModuleBase):
for tag in torrent.labels] if hasattr(torrent, "labels") else []
if "已整理" in labels:
labels.remove("已整理")
self.transmission.set_torrent_tag(ids=torrent_hash, tags=labels)
server.set_torrent_tag(ids=torrent_hash, tags=labels)
if settings.TORRENT_TAG and settings.TORRENT_TAG not in labels:
labels.append(settings.TORRENT_TAG)
self.transmission.set_torrent_tag(ids=torrent_hash, tags=labels)
server.set_torrent_tag(ids=torrent_hash, tags=labels)
return torrent_hash, f"下载任务已存在"
return None, f"添加种子任务失败:{content}"
else:
torrent_hash = torrent.hashString
if is_paused:
# 选择文件
torrent_files = self.transmission.get_files(torrent_hash)
torrent_files = server.get_files(torrent_hash)
if not torrent_files:
return torrent_hash, "获取种子文件失败,下载任务可能在暂停状态"
# 需要的文件信息
@@ -155,10 +180,10 @@ class TransmissionModule(_ModuleBase):
continue
file_ids.append(file_id)
# 选择文件
self.transmission.set_files(torrent_hash, file_ids)
self.transmission.set_unwanted_files(torrent_hash, unwanted_file_ids)
server.set_files(torrent_hash, file_ids)
server.set_unwanted_files(torrent_hash, unwanted_file_ids)
# 开始任务
self.transmission.start_torrents(torrent_hash)
server.start_torrents(torrent_hash)
return torrent_hash, "添加下载任务成功"
else:
return torrent_hash, "添加下载任务成功"
@@ -174,12 +199,14 @@ class TransmissionModule(_ModuleBase):
:param downloader: 下载器
:return: 下载器中符合状态的种子列表
"""
if downloader != "transmission":
# 获取下载器
server = self.get_server(downloader)
if not server:
return None
ret_torrents = []
if hashs:
# 按Hash获取
torrents, _ = self.transmission.get_torrents(ids=hashs, tags=settings.TORRENT_TAG)
torrents, _ = server.get_torrents(ids=hashs, tags=settings.TORRENT_TAG)
for torrent in torrents or []:
ret_torrents.append(TransferTorrent(
title=torrent.name,
@@ -190,7 +217,7 @@ class TransmissionModule(_ModuleBase):
))
elif status == TorrentStatus.TRANSFER:
# 获取已完成且未整理的
torrents = self.transmission.get_completed_torrents(tags=settings.TORRENT_TAG)
torrents = server.get_completed_torrents(tags=settings.TORRENT_TAG)
for torrent in torrents or []:
# 含"已整理"tag的不处理
if "已整理" in torrent.labels or []:
@@ -209,7 +236,7 @@ class TransmissionModule(_ModuleBase):
))
elif status == TorrentStatus.DOWNLOADING:
# 获取正在下载的任务
torrents = self.transmission.get_downloading_torrents(tags=settings.TORRENT_TAG)
torrents = server.get_downloading_torrents(tags=settings.TORRENT_TAG)
for torrent in torrents or []:
meta = MetaInfo(torrent.name)
dlspeed = torrent.rate_download if hasattr(torrent, "rate_download") else torrent.rateDownload
@@ -240,16 +267,18 @@ class TransmissionModule(_ModuleBase):
:param downloader: 下载器
:return: None
"""
if downloader != "transmission":
# 获取下载器
server = self.get_server(downloader)
if not server:
return None
# 获取原标签
org_tags = self.transmission.get_torrent_tags(ids=hashs)
org_tags = server.get_torrent_tags(ids=hashs)
# 种子打上已整理标签
if org_tags:
tags = org_tags + ['已整理']
else:
tags = ['已整理']
self.transmission.set_torrent_tag(ids=hashs, tags=tags)
server.set_torrent_tag(ids=hashs, tags=tags)
# 移动模式删除种子
if settings.TRANSFER_TYPE in ["move", "rclone_move"]:
if self.remove_torrents(hashs):
@@ -270,9 +299,11 @@ class TransmissionModule(_ModuleBase):
:param downloader: 下载器
:return: bool
"""
if downloader != "transmission":
# 获取下载器
server = self.get_server(downloader)
if not server:
return None
return self.transmission.delete_torrents(delete_file=delete_file, ids=hashs)
return server.delete_torrents(delete_file=delete_file, ids=hashs)
def start_torrents(self, hashs: Union[list, str],
downloader: str = settings.DEFAULT_DOWNLOADER) -> Optional[bool]:
@@ -282,9 +313,11 @@ class TransmissionModule(_ModuleBase):
:param downloader: 下载器
:return: bool
"""
if downloader != "transmission":
# 获取下载器
server = self.get_server(downloader)
if not server:
return None
return self.transmission.start_torrents(ids=hashs)
return server.start_torrents(ids=hashs)
def stop_torrents(self, hashs: Union[list, str],
downloader: str = settings.DEFAULT_DOWNLOADER) -> Optional[bool]:
@@ -294,28 +327,43 @@ class TransmissionModule(_ModuleBase):
:param downloader: 下载器
:return: bool
"""
if downloader != "transmission":
# 获取下载器
server = self.get_server(downloader)
if not server:
return None
return self.transmission.start_torrents(ids=hashs)
return server.start_torrents(ids=hashs)
def torrent_files(self, tid: str, downloader: str = settings.DEFAULT_DOWNLOADER) -> Optional[List[File]]:
"""
获取种子文件列表
"""
if downloader != "transmission":
# 获取下载器
server = self.get_server(downloader)
if not server:
return None
return self.transmission.get_files(tid=tid)
return server.get_files(tid=tid)
def downloader_info(self) -> [schemas.DownloaderInfo]:
def downloader_info(self, downloader: str = None) -> Optional[List[schemas.DownloaderInfo]]:
"""
下载器信息
"""
info = self.transmission.transfer_info()
if not info:
return [schemas.DownloaderInfo()]
return [schemas.DownloaderInfo(
download_speed=info.download_speed,
upload_speed=info.upload_speed,
download_size=info.current_stats.downloaded_bytes,
upload_size=info.current_stats.uploaded_bytes
)]
if downloader:
server = self.get_server(downloader)
if not server:
return None
servers = [server]
else:
servers = self._servers.values()
# 调用Qbittorrent API查询实时信息
ret_info = []
for server in servers:
info = server.transfer_info()
if not info:
continue
ret_info.append(schemas.DownloaderInfo(
download_speed=info.download_speed,
upload_speed=info.upload_speed,
download_size=info.current_stats.downloaded_bytes,
upload_size=info.current_stats.uploaded_bytes
))
return ret_info

View File

@@ -22,10 +22,13 @@ class Transmission:
"peersGettingFromUs", "peersSendingToUs", "uploadRatio", "uploadedEver", "downloadedEver", "downloadDir",
"error", "errorString", "doneDate", "queuePosition", "activityDate", "trackers"]
def __init__(self, host: str = None, port: int = None, username: str = None, password: str = None):
def __init__(self, host: str, port: int, username: str = None, password: str = None, **kwargs):
"""
若不设置参数,则创建配置文件设置的下载器
"""
if not host or not port:
logger.error("Transmission配置不完整")
return
self._host = host
self._port = port
self._username = username

View File

@@ -22,10 +22,13 @@ class VoceChat:
# 请求对象
_client = None
def __init__(self, host: str = None, apikey: str = None, channel_id: str = None):
def __init__(self, host: str, apikey: str, channel_id: str, **kwargs):
"""
初始化
"""
if not host or not apikey or not channel_id:
logger.error("VoceChat配置不完整")
return
self._host = host
if self._host:
if not self._host.endswith("/"):

View File

@@ -37,10 +37,13 @@ class WeChat:
# 企业微信创新菜单URL
_create_menu_url = "/cgi-bin/menu/create?access_token=%s&agentid=%s"
def __init__(self, corpid: str = None, appsecret: str = None, appid: str = None, proxy: str = None):
def __init__(self, corpid: str, appsecret: str, appid: str, proxy: str = None, **kwargs):
"""
初始化
"""
if not corpid or not appsecret or not appid:
logger.error("企业微信配置不完整!")
return
self._corpid = corpid
self._appsecret = appsecret
self._appid = appid

View File

@@ -13,5 +13,5 @@ from .mediaserver import *
from .message import *
from .tmdb import *
from .transfer import *
from .file import *
from .filetransfer import *
from .mediaserver import *

29
app/schemas/system.py Normal file
View File

@@ -0,0 +1,29 @@
from typing import Optional
from pydantic import BaseModel
class MediaServerConf(BaseModel):
"""
媒体服务器配置
"""
# 名称
name: Optional[str] = None
# 类型 emby/jellyfin/plex
type: Optional[str] = None
# 配置
config: Optional[dict] = {}
class DownloaderConf(BaseModel):
"""
下载器配置
"""
# 名称
name: Optional[str] = None
# 类型 qbittorrent/transmission
type: Optional[str] = None
# 是否默认
default: Optional[bool] = False
# 配置
config: Optional[dict] = {}