From 4e26168ab518685e45fa0a0c9c4f5a1724cda805 Mon Sep 17 00:00:00 2001 From: jxxghp Date: Thu, 7 Sep 2023 17:40:09 +0800 Subject: [PATCH] fix plugin --- app/plugins/brushflow/__init__.py | 562 +++++++++++++++++++++++++++++- app/utils/string.py | 13 + 2 files changed, 571 insertions(+), 4 deletions(-) diff --git a/app/plugins/brushflow/__init__.py b/app/plugins/brushflow/__init__.py index bc963cb4..efbafd61 100644 --- a/app/plugins/brushflow/__init__.py +++ b/app/plugins/brushflow/__init__.py @@ -1,6 +1,14 @@ +from datetime import datetime, timedelta from typing import Any, List, Dict, Tuple +import pytz +from apscheduler.schedulers.background import BackgroundScheduler + +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): @@ -26,18 +34,179 @@ class BrushFlow(_PluginBase): auth_level = 3 # 私有属性 + sites = None + _cron = 10 + _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() 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.print_jobs() + self._scheduler.start() def get_state(self) -> bool: - return self._enabled + return True if self._enabled and self._brushsites and self._downloader else False @staticmethod def get_command() -> List[Dict[str, Any]]: @@ -50,7 +219,9 @@ class BrushFlow(_PluginBase): """ 拼装插件配置页面,需要返回两块数据:1、页面配置;2、数据结构 """ - request_options = ["POST", "GET"] + # 站点的可选项 + site_options = [{"title": site.get("name"), "value": site.get("id")} + for site in self.sites.get_indexers()] return [ { 'component': 'VForm', @@ -84,7 +255,7 @@ class BrushFlow(_PluginBase): { 'component': 'VSwitch', 'props': { - 'model': 'enabled', + 'model': 'notify', 'label': '发送通知', } } @@ -111,7 +282,383 @@ class BrushFlow(_PluginBase): { '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': '留空自动' + } + } + ] + } ] }, ] @@ -120,6 +667,7 @@ class BrushFlow(_PluginBase): "enabled": False, "notify": True, "onlyonce": False, + "freeleech": "free" } def get_page(self) -> List[dict]: @@ -130,3 +678,9 @@ class BrushFlow(_PluginBase): 退出插件 """ pass + + def brush(self): + """ + 执行刷流动作 + """ + pass diff --git a/app/utils/string.py b/app/utils/string.py index 2e58073c..6434d29e 100644 --- a/app/utils/string.py +++ b/app/utils/string.py @@ -641,3 +641,16 @@ class StringUtils: formatted_string = "、".join(formatted_ranges) return formatted_string + + @staticmethod + def is_number(text: str) -> bool: + """ + 判断字符是否为可以转换为整数或者浮点数 + """ + if not text: + return False + try: + float(text) + return True + except ValueError: + return False