Merge pull request #240 from DzAvril/main

This commit is contained in:
jxxghp
2024-04-26 20:54:55 +08:00
committed by GitHub
2 changed files with 231 additions and 28 deletions

View File

@@ -486,10 +486,13 @@
"QbCommand": {
"name": "QB远程操作",
"description": "通过定时任务或交互命令远程操作QB暂停/开始/限速等。",
"version": "1.3",
"version": "1.4",
"icon": "Qbittorrent_A.png",
"author": "DzAvril",
"level": 1
"level": 1,
"history": {
"v1.4": "可选某些站点不再做种(暂停做种后不会被恢复)"
}
},
"TrCommand": {
"name": "TR远程操作",

View File

@@ -1,5 +1,7 @@
from typing import List, Tuple, Dict, Any
from enum import Enum
from urllib.parse import urlparse
import urllib
from app.log import logger
from app.modules.qbittorrent import Qbittorrent
from app.plugins import _PluginBase
@@ -7,6 +9,13 @@ from app.schemas import NotificationType
from app.schemas.types import EventType
from apscheduler.triggers.cron import CronTrigger
from app.core.event import eventmanager, Event
from apscheduler.schedulers.background import BackgroundScheduler
from app.core.config import settings
from app.helper.sites import SitesHelper
from app.db.site_oper import SiteOper
from app.utils.string import StringUtils
from datetime import datetime, timedelta
import pytz
import time
@@ -18,7 +27,7 @@ class QbCommand(_PluginBase):
# 插件图标
plugin_icon = "Qbittorrent_A.png"
# 插件版本
plugin_version = "1.3"
plugin_version = "1.4"
# 插件作者
plugin_author = "DzAvril"
# 作者主页
@@ -31,6 +40,8 @@ class QbCommand(_PluginBase):
auth_level = 1
# 私有属性
_sites = None
_siteoper = None
_qb = None
_enabled: bool = False
_notify: bool = False
@@ -45,8 +56,14 @@ class QbCommand(_PluginBase):
_enable_upload_limit = False
_download_limit = 0
_enable_download_limit = False
_op_site_ids = []
_op_sites = []
_multi_level_root_domain = ["edu.cn", "com.cn", "net.cn", "org.cn"]
_scheduler = None
def init_plugin(self, config: dict = None):
self._sites = SitesHelper()
self._siteoper = SiteOper()
# 停止现有任务
self.stop_service()
# 读取配置
@@ -65,14 +82,33 @@ class QbCommand(_PluginBase):
self._enable_download_limit = config.get("enable_download_limit")
self._enable_upload_limit = config.get("enable_upload_limit")
self._qb = Qbittorrent()
self._op_site_ids = config.get("op_site_ids") or []
# 查询所有站点
all_sites = [site for site in self._sites.get_indexers() if not site.get("public")] + self.__custom_sites()
# 过滤掉没有选中的站点
self._op_sites = [site for site in all_sites if site.get("id") in self._op_site_ids]
if self._only_pause_once or self._only_resume_once:
if self._only_pause_once and self._only_resume_once:
logger.warning("只能选择一个: 立即暂停或立即开始所有任务")
elif self._only_pause_once:
self.pause_torrent()
self._scheduler = BackgroundScheduler(timezone=settings.TZ)
logger.info(f"立即运行一次暂停所有任务")
self._scheduler.add_job(
self.pause_torrent,
"date",
run_date=datetime.now(tz=pytz.timezone(settings.TZ))
+ timedelta(seconds=3),
)
elif self._only_resume_once:
self.resume_torrent()
self._scheduler = BackgroundScheduler(timezone=settings.TZ)
logger.info(f"立即运行一次开始所有任务")
self._scheduler.add_job(
self.resume_torrent,
"date",
run_date=datetime.now(tz=pytz.timezone(settings.TZ))
+ timedelta(seconds=3),
)
self._only_resume_once = False
self._only_pause_once = False
@@ -84,16 +120,56 @@ class QbCommand(_PluginBase):
"notify": self._notify,
"pause_cron": self._pause_cron,
"resume_cron": self._resume_cron,
"op_site_ids": self._op_site_ids,
}
)
if self._only_pause_upload or self._only_pause_download or self._only_pause_checking:
# 启动任务
if self._scheduler.get_jobs():
self._scheduler.print_jobs()
self._scheduler.start()
if (
self._only_pause_upload
or self._only_pause_download
or self._only_pause_checking
):
if self._only_pause_upload:
self.pause_torrent(self.TorrentType.UPLOADING)
self._scheduler = BackgroundScheduler(timezone=settings.TZ)
logger.info(f"立即运行一次暂停所有上传任务")
self._scheduler.add_job(
self.pause_torrent,
"date",
run_date=datetime.now(tz=pytz.timezone(settings.TZ))
+ timedelta(seconds=3),
kwargs={
'type': self.TorrentType.UPLOADING
}
)
if self._only_pause_download:
self.pause_torrent(self.TorrentType.DOWNLOADING)
self._scheduler = BackgroundScheduler(timezone=settings.TZ)
logger.info(f"立即运行一次暂停所有下载任务")
self._scheduler.add_job(
self.pause_torrent,
"date",
run_date=datetime.now(tz=pytz.timezone(settings.TZ))
+ timedelta(seconds=3),
kwargs={
'type': self.TorrentType.DOWNLOADING
}
)
if self._only_pause_checking:
self.pause_torrent(self.TorrentType.CHECKING)
self._scheduler = BackgroundScheduler(timezone=settings.TZ)
logger.info(f"立即运行一次暂停所有检查任务")
self._scheduler.add_job(
self.pause_torrent,
"date",
run_date=datetime.now(tz=pytz.timezone(settings.TZ))
+ timedelta(seconds=3),
kwargs={
'type': self.TorrentType.CHECKING
}
)
self._only_pause_upload = False
self._only_pause_download = False
@@ -107,9 +183,15 @@ class QbCommand(_PluginBase):
"notify": self._notify,
"pause_cron": self._pause_cron,
"resume_cron": self._resume_cron,
"op_site_ids": self._op_site_ids,
}
)
# 启动任务
if self._scheduler.get_jobs():
self._scheduler.print_jobs()
self._scheduler.start()
self.set_limit(self._upload_limit, self._download_limit)
def get_state(self) -> bool:
@@ -186,6 +268,13 @@ class QbCommand(_PluginBase):
},
]
def __custom_sites(self) -> List[Any]:
custom_sites = []
custom_sites_config = self.get_config("CustomSites")
if custom_sites_config and custom_sites_config.get("enabled"):
custom_sites = custom_sites_config.get("sites")
return custom_sites
def get_api(self) -> List[Dict[str, Any]]:
pass
@@ -427,6 +516,7 @@ class QbCommand(_PluginBase):
return
all_torrents = self.get_all_torrents()
all_torrents = self.filter_torrents(all_torrents)
hash_downloading, hash_uploading, hash_paused, hash_checking, hash_error = (
self.get_torrents_status(all_torrents)
)
@@ -489,6 +579,41 @@ class QbCommand(_PluginBase):
f"错误数量: {len(hash_error)}\n",
)
def filter_torrents(self, all_torrents):
"""
过滤掉不参与保种的种子
"""
if len(self._op_sites) == 0:
return all_torrents
urls = [site.get("url") for site in self._op_sites]
op_sites_main_domains = []
for url in urls:
domain = StringUtils.get_url_netloc(url)
main_domain = self.get_main_domain(domain[1])
op_sites_main_domains.append(main_domain)
torrents = []
for torrent in all_torrents:
if torrent.get("state") == "pausedUP":
tracker_url = self.get_torrent_tracker(torrent)
if not tracker_url:
logger.info(f"获取种子 {torrent.name} Tracker失败不过滤该种子")
torrents.append(torrent)
_, tracker_domain = StringUtils.get_url_netloc(tracker_url)
if not tracker_domain:
logger.info(f"获取种子 {torrent.name} Tracker失败不过滤该种子")
torrents.append(torrent)
tracker_main_domain = self.get_main_domain(domain=tracker_domain)
if tracker_main_domain in op_sites_main_domains:
logger.info(
f"种子 {torrent.name} 属于站点{tracker_main_domain},不执行操作"
)
continue
torrents.append(torrent)
return torrents
@eventmanager.register(EventType.PluginAction)
def handle_qb_status(self, event: Event):
if not self._enabled:
@@ -508,14 +633,13 @@ class QbCommand(_PluginBase):
self.get_torrents_status(all_torrents)
)
logger.info(
f"QB开始任务启动 \n"
f"QB任务状态 \n"
f"种子总数: {len(all_torrents)} \n"
f"做种数量: {len(hash_uploading)}\n"
f"下载数量: {len(hash_downloading)}\n"
f"检查数量: {len(hash_checking)}\n"
f"暂停数量: {len(hash_paused)}\n"
f"错误数量: {len(hash_error)}\n"
f"开始操作中请稍等...\n",
)
if self._notify:
self.post_message(
@@ -648,7 +772,67 @@ class QbCommand(_PluginBase):
text=f"设置QB限速失败",
)
def get_torrent_tracker(self, torrent):
"""
qb解析 tracker
:return: tracker url
"""
if not torrent:
return None
tracker = torrent.get("tracker")
if tracker and len(tracker) > 0:
return tracker
magnet_uri = torrent.get("magnet_uri")
if not magnet_uri or len(magnet_uri) <= 0:
return None
magnet_uri_obj = urlparse(magnet_uri)
query = urllib.parse.parse_qs(magnet_uri_obj.query)
tr = query["tr"]
if not tr or len(tr) <= 0:
return None
return tr[0]
def get_main_domain(self, domain):
"""
获取域名的主域名
:param domain: 原域名
:return: 主域名
"""
if not domain:
return None
domain_arr = domain.split(".")
domain_len = len(domain_arr)
if domain_len < 2:
return None
root_domain, root_domain_len = self.match_multi_level_root_domain(domain=domain)
if root_domain:
return f"{domain_arr[-root_domain_len - 1]}.{root_domain}"
else:
return f"{domain_arr[-2]}.{domain_arr[-1]}"
def match_multi_level_root_domain(self, domain):
"""
匹配多级根域名
:param domain: 被匹配的域名
:return: 匹配的根域名, 匹配的根域名长度
"""
if not domain or not self._multi_level_root_domain:
return None, 0
for root_domain in self._multi_level_root_domain:
if domain.endswith("." + root_domain):
root_domain_len = len(root_domain.split("."))
return root_domain, root_domain_len
return None, 0
def get_form(self) -> Tuple[List[dict], Dict[str, Any]]:
customSites = self.__custom_sites()
site_options = [
{"title": site.name, "value": site.id}
for site in self._siteoper.list_order_by_pri()
] + [
{"title": site.get("name"), "value": site.get("id")} for site in customSites
]
return [
{
"component": "VForm",
@@ -849,6 +1033,27 @@ class QbCommand(_PluginBase):
},
],
},
{
"component": "VRow",
"content": [
{
"component": "VCol",
"props": {"cols": 12},
"content": [
{
"component": "VSelect",
"props": {
"chips": True,
"multiple": True,
"model": "op_site_ids",
"label": "停止保种站点(暂停保种后不会被恢复)",
"items": site_options,
},
}
],
}
],
},
{
"component": "VRow",
"content": [
@@ -884,22 +1089,6 @@ class QbCommand(_PluginBase):
}
],
},
{
"component": "VCol",
"props": {
"cols": 12,
},
"content": [
{
"component": "VAlert",
"props": {
"type": "info",
"variant": "tonal",
"text": "PT精神重在分享请勿恶意限速因此导致账号被封禁作者概不负责",
},
}
],
},
],
},
],
@@ -916,10 +1105,21 @@ class QbCommand(_PluginBase):
"download_limit": 0,
"enable_upload_limit": False,
"enable_download_limit": False,
"op_site_ids": [],
}
def get_page(self) -> List[dict]:
pass
def stop_service(self):
pass
"""
退出插件
"""
try:
if self._scheduler:
self._scheduler.remove_all_jobs()
if self._scheduler.running:
self._scheduler.shutdown()
self._scheduler = None
except Exception as e:
logger.error("退出插件失败:%s" % str(e))