2023-09-07 20:19:03 +08:00

721 lines
32 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from datetime import datetime, timedelta
from threading import Event
from typing import Any, List, Dict, Tuple
import pytz
from apscheduler.schedulers.background import BackgroundScheduler
from app.chain.torrents import TorrentsChain
from app.core.config import settings
from app.helper.sites import SitesHelper
from app.log import logger
from app.plugins import _PluginBase
from app.utils.string import StringUtils
class BrushFlow(_PluginBase):
# 插件名称
plugin_name = "站点刷流"
# 插件描述
plugin_desc = "自动托管刷流,将会默认提高对应站点的种子刷新频率。"
# 插件图标
plugin_icon = "fileupload.png"
# 主题色
plugin_color = "#EC5665"
# 插件版本
plugin_version = "1.0"
# 插件作者
plugin_author = "jxxghp"
# 作者主页
author_url = "https://github.com/jxxghp"
# 插件配置项ID前缀
plugin_config_prefix = "brushflow_"
# 加载顺序
plugin_order = 21
# 可使用的用户级别
auth_level = 3
# 私有属性
sites = None
torrents = None
# 添加种子定时
_cron = 10
# 检查种子定时
_check_interval = 5
# 退出事件
_event = Event()
_scheduler = None
_enabled = False
_notify = True
_onlyonce = False
_brushsites = []
_downloader = "qbittorrent"
_disksize = 0
_freeleech = "free"
_maxupspeed = 0
_maxdlspeed = 0
_maxdlcount = 0
_include = ""
_exclude = ""
_size = 0
_seeder = 0
_pubtime = 0
_seed_time = 0
_seed_ratio = 0
_seed_size = 0
_download_time = 0
_seed_avgspeed = 0
_seed_inactivetime = 0
_up_speed = 0
_dl_speed = 0
_save_path = ""
def init_plugin(self, config: dict = None):
self.sites = SitesHelper()
self.torrents = TorrentsChain()
if config:
self._enabled = config.get("enabled")
self._notify = config.get("notify")
self._onlyonce = config.get("onlyonce")
self._brushsites = config.get("brushsites")
self._downloader = config.get("downloader")
self._disksize = config.get("disksize")
self._freeleech = config.get("freeleech")
self._maxupspeed = config.get("maxupspeed")
self._maxdlspeed = config.get("maxdlspeed")
self._maxdlcount = config.get("maxdlcount")
self._include = config.get("include")
self._exclude = config.get("exclude")
self._size = config.get("size")
self._seeder = config.get("seeder")
self._pubtime = config.get("pubtime")
self._seed_time = config.get("seed_time")
self._seed_ratio = config.get("seed_ratio")
self._seed_size = config.get("seed_size")
self._download_time = config.get("download_time")
self._seed_avgspeed = config.get("seed_avgspeed")
self._seed_inactivetime = config.get("seed_inactivetime")
self._up_speed = config.get("up_speed")
self._dl_speed = config.get("dl_speed")
self._save_path = config.get("save_path")
# 停止现有任务
self.stop_service()
# 启动定时任务 & 立即运行一次
if self.get_state() or self._onlyonce:
# 检查配置
if self._disksize and not StringUtils.is_number(self._disksize):
logger.error(f"保种体积设置错误:{self._disksize}")
self.systemmessage.put(f"保种体积设置错误:{self._disksize}")
return
if self._maxupspeed and not StringUtils.is_number(self._maxupspeed):
logger.error(f"总上传带宽设置错误:{self._maxupspeed}")
self.systemmessage.put(f"总上传带宽设置错误:{self._maxupspeed}")
return
if self._maxdlspeed and not StringUtils.is_number(self._maxdlspeed):
logger.error(f"总下载带宽设置错误:{self._maxdlspeed}")
self.systemmessage.put(f"总下载带宽设置错误:{self._maxdlspeed}")
return
if self._maxdlcount and not StringUtils.is_number(self._maxdlcount):
logger.error(f"同时下载任务数设置错误:{self._maxdlcount}")
self.systemmessage.put(f"同时下载任务数设置错误:{self._maxdlcount}")
return
if self._size and not StringUtils.is_number(self._size):
logger.error(f"种子大小设置错误:{self._size}")
self.systemmessage.put(f"种子大小设置错误:{self._size}")
return
if self._seeder and not StringUtils.is_number(self._seeder):
logger.error(f"做种人数设置错误:{self._seeder}")
self.systemmessage.put(f"做种人数设置错误:{self._seeder}")
return
if self._seed_time and not StringUtils.is_number(self._seed_time):
logger.error(f"做种时间设置错误:{self._seed_time}")
self.systemmessage.put(f"做种时间设置错误:{self._seed_time}")
return
if self._seed_ratio and not StringUtils.is_number(self._seed_ratio):
logger.error(f"分享率设置错误:{self._seed_ratio}")
self.systemmessage.put(f"分享率设置错误:{self._seed_ratio}")
return
if self._seed_size and not StringUtils.is_number(self._seed_size):
logger.error(f"上传量设置错误:{self._seed_size}")
self.systemmessage.put(f"上传量设置错误:{self._seed_size}")
return
if self._download_time and not StringUtils.is_number(self._download_time):
logger.error(f"下载超时时间设置错误:{self._download_time}")
self.systemmessage.put(f"下载超时时间设置错误:{self._download_time}")
return
if self._seed_avgspeed and not StringUtils.is_number(self._seed_avgspeed):
logger.error(f"平均上传速度设置错误:{self._seed_avgspeed}")
self.systemmessage.put(f"平均上传速度设置错误:{self._seed_avgspeed}")
return
if self._seed_inactivetime and not StringUtils.is_number(self._seed_inactivetime):
logger.error(f"未活动时间设置错误:{self._seed_inactivetime}")
self.systemmessage.put(f"未活动时间设置错误:{self._seed_inactivetime}")
return
if self._up_speed and not StringUtils.is_number(self._up_speed):
logger.error(f"单任务上传限速设置错误:{self._up_speed}")
self.systemmessage.put(f"单任务上传限速设置错误:{self._up_speed}")
return
if self._dl_speed and not StringUtils.is_number(self._dl_speed):
logger.error(f"单任务下载限速设置错误:{self._dl_speed}")
self.systemmessage.put(f"单任务下载限速设置错误:{self._dl_speed}")
return
# 检查必要条件
if not self._brushsites or not self._downloader:
return
# 启动任务
self._scheduler = BackgroundScheduler(timezone=settings.TZ)
logger.info(f"站点刷流服务启动,周期:{self._cron}分钟")
try:
self._scheduler.add_job(self.brush, 'interval', minutes=self._cron)
except Exception as e:
logger.error(f"站点刷流服务启动失败:{e}")
self.systemmessage(f"站点刷流服务启动失败:{e}")
return
if self._onlyonce:
logger.info(f"站点刷流服务启动,立即运行一次")
self._scheduler.add_job(self.brush, 'date',
run_date=datetime.now(
tz=pytz.timezone(settings.TZ)
) + timedelta(seconds=3))
# 关闭一次性开关
self._onlyonce = False
self.update_config({
"onlyonce": False,
"enabled": self._enabled,
"notify": self._notify,
"brushsites": self._brushsites,
"downloader": self._downloader,
"disksize": self._disksize,
"freeleech": self._freeleech,
"maxupspeed": self._maxupspeed,
"maxdlspeed": self._maxdlspeed,
"maxdlcount": self._maxdlcount,
"include": self._include,
"exclude": self._exclude,
"size": self._size,
"seeder": self._seeder,
"pubtime": self._pubtime,
"seed_time": self._seed_time,
"seed_ratio": self._seed_ratio,
"seed_size": self._seed_size,
"download_time": self._download_time,
"seed_avgspeed": self._seed_avgspeed,
"seed_inactivetime": self._seed_inactivetime,
"up_speed": self._up_speed,
"dl_speed": self._dl_speed,
"save_path": self._save_path
})
if self._scheduler.get_jobs():
# 增加检查任务
self._scheduler.add_job(self.check, 'interval', minutes=self._check_interval)
# 启动服务
self._scheduler.print_jobs()
self._scheduler.start()
def get_state(self) -> bool:
return True if self._enabled and self._brushsites and self._downloader else False
@staticmethod
def get_command() -> List[Dict[str, Any]]:
pass
def get_api(self) -> List[Dict[str, Any]]:
pass
def get_form(self) -> Tuple[List[dict], Dict[str, Any]]:
"""
拼装插件配置页面需要返回两块数据1、页面配置2、数据结构
"""
# 站点的可选项
site_options = [{"title": site.get("name"), "value": site.get("id")}
for site in self.sites.get_indexers()]
return [
{
'component': 'VForm',
'content': [
{
'component': 'VRow',
'content': [
{
'component': 'VCol',
'props': {
'cols': 12,
'md': 4
},
'content': [
{
'component': 'VSwitch',
'props': {
'model': 'enabled',
'label': '启用插件',
}
}
]
},
{
'component': 'VCol',
'props': {
'cols': 12,
'md': 4
},
'content': [
{
'component': 'VSwitch',
'props': {
'model': 'notify',
'label': '发送通知',
}
}
]
},
{
'component': 'VCol',
'props': {
'cols': 12,
'md': 4
},
'content': [
{
'component': 'VSwitch',
'props': {
'model': 'onlyonce',
'label': '立即运行一次',
}
}
]
}
]
},
{
'component': 'VRow',
'content': [
{
'component': 'VCol',
'content': [
{
'component': 'VSelect',
'props': {
'chips': True,
'multiple': True,
'model': 'brushsites',
'label': '刷流站点',
'items': site_options
}
}
]
}
]
},
{
'component': 'VRow',
'content': [
{
'component': 'VCol',
'props': {
"cols": 12,
"md": 4
},
'content': [
{
'component': 'VSelect',
'props': {
'model': 'downloader',
'label': '下载器',
'items': [
{'title': 'Qbittorrent', 'value': 'qbittorrent'},
{'title': 'Transmission', 'value': 'transmission'}
]
}
}
]
},
{
'component': 'VCol',
'props': {
"cols": 12,
"md": 4
},
'content': [
{
'component': 'VTextField',
'props': {
'model': 'disksize',
'label': '保种体积GB',
'placeholder': '达到后停止新增任务'
}
}
]
},
{
'component': 'VCol',
'props': {
"cols": 12,
"md": 4
},
'content': [
{
'component': 'VSelect',
'props': {
'model': 'freeleech',
'label': '促销',
'items': [
{'title': '全部(包括普通)', 'value': ''},
{'title': '免费', 'value': 'free'},
{'title': '2X免费', 'value': '2xfree'},
]
}
}
]
},
{
'component': 'VCol',
'props': {
"cols": 12,
"md": 4
},
'content': [
{
'component': 'VTextField',
'props': {
'model': 'maxupspeed',
'label': '总上传带宽KB/s',
'placeholder': '达到后停止新增任务'
}
}
]
},
{
'component': 'VCol',
'props': {
"cols": 12,
"md": 4
},
'content': [
{
'component': 'VTextField',
'props': {
'model': 'maxdlspeed',
'label': '总下载带宽KB/s',
'placeholder': '达到后停止新增任务'
}
}
]
},
{
'component': 'VCol',
'props': {
"cols": 12,
"md": 4
},
'content': [
{
'component': 'VTextField',
'props': {
'model': 'maxdlcount',
'label': '同时下载任务数',
'placeholder': '达到后停止新增任务'
}
}
]
},
{
'component': 'VCol',
'props': {
"cols": 12,
"md": 4
},
'content': [
{
'component': 'VTextField',
'props': {
'model': 'include',
'label': '包含规则',
'placeholder': '支持正式表达式'
}
}
]
},
{
'component': 'VCol',
'props': {
"cols": 12,
"md": 4
},
'content': [
{
'component': 'VTextField',
'props': {
'model': 'exclude',
'label': '排除规则',
'placeholder': '支持正式表达式'
}
}
]
},
{
'component': 'VCol',
'props': {
"cols": 12,
"md": 4
},
'content': [
{
'component': 'VTextField',
'props': {
'model': 'size',
'label': '种子大小GB',
'placeholder': '5 或 5-10'
}
}
]
},
{
'component': 'VCol',
'props': {
"cols": 12,
"md": 4
},
'content': [
{
'component': 'VTextField',
'props': {
'model': 'seeder',
'label': '做种人数',
'placeholder': '5 或 5-10'
}
}
]
},
{
'component': 'VCol',
'props': {
"cols": 12,
"md": 4
},
'content': [
{
'component': 'VTextField',
'props': {
'model': 'pubtime',
'label': '发布时间(分钟)',
'placeholder': '5 或 5-10'
}
}
]
}
]
},
{
'component': 'VRow',
'content': [
{
'component': 'VCol',
'props': {
"cols": 12,
"md": 4
},
'content': [
{
'component': 'VTextField',
'props': {
'model': 'seed_time',
'label': '做种时间(小时)',
'placeholder': '达到后删除任务'
}
}
]
},
{
'component': 'VCol',
'props': {
"cols": 12,
"md": 4
},
'content': [
{
'component': 'VTextField',
'props': {
'model': 'seed_ratio',
'label': '分享率',
'placeholder': '达到后删除任务'
}
}
]
},
{
'component': 'VCol',
'props': {
"cols": 12,
"md": 4
},
'content': [
{
'component': 'VTextField',
'props': {
'model': 'seed_size',
'label': '上传量GB',
'placeholder': '达到后删除任务'
}
}
]
},
{
'component': 'VCol',
'props': {
"cols": 12,
"md": 4
},
'content': [
{
'component': 'VTextField',
'props': {
'model': 'download_time',
'label': '下载超时时间(小时)',
'placeholder': '达到后删除任务'
}
}
]
},
{
'component': 'VCol',
'props': {
"cols": 12,
"md": 4
},
'content': [
{
'component': 'VTextField',
'props': {
'model': 'seed_avgspeed',
'label': '平均上传速度KB/s',
'placeholder': '低于时删除任务'
}
}
]
},
{
'component': 'VCol',
'props': {
"cols": 12,
"md": 4
},
'content': [
{
'component': 'VTextField',
'props': {
'model': 'seed_inactivetime',
'label': '未活动时间(分钟) ',
'placeholder': '超过时删除任务'
}
}
]
}
]
},
{
'component': 'VRow',
'content': [
{
'component': 'VCol',
'props': {
"cols": 12,
"md": 4
},
'content': [
{
'component': 'VTextField',
'props': {
'model': 'up_speed',
'label': '单任务上传限速KB/s',
'placeholder': '种子上传限速'
}
}
]
},
{
'component': 'VCol',
'props': {
"cols": 12,
"md": 4
},
'content': [
{
'component': 'VTextField',
'props': {
'model': 'dl_speed',
'label': '单任务下载限速KB/s',
'placeholder': '种子下载限速'
}
}
]
},
{
'component': 'VCol',
'props': {
"cols": 12,
"md": 4
},
'content': [
{
'component': 'VTextField',
'props': {
'model': 'save_path',
'label': '保存目录',
'placeholder': '留空自动'
}
}
]
}
]
},
]
}
], {
"enabled": False,
"notify": True,
"onlyonce": False,
"freeleech": "free"
}
def get_page(self) -> List[dict]:
pass
def stop_service(self):
"""
退出插件
"""
try:
if self._scheduler:
self._scheduler.remove_all_jobs()
if self._scheduler.running:
self._event.set()
self._scheduler.shutdown()
self._event.clear()
self._scheduler = None
except Exception as e:
print(str(e))
def brush(self):
"""
执行刷流动作,添加下载任务
"""
if not self._brushsites or not self._downloader:
return
logger.info(f"开始执行刷流任务 ...")
logger.info(f"刷流任务执行完成")
def check(self):
"""
定时检查,删除下载任务
"""
if not self._downloader:
return
logger.info(f"开始检查刷流下载任务 ...")
logger.info(f"刷流下载任务检查完成")