Merge pull request #154 from InfinityPacer/feature_dev

This commit is contained in:
jxxghp
2024-03-30 16:33:30 +08:00
committed by GitHub
2 changed files with 129 additions and 46 deletions

View File

@@ -226,7 +226,7 @@
"BrushFlow": {
"name": "站点刷流",
"description": "自动托管刷流,将会提高对应站点的访问频率。",
"version": "2.0",
"version": "2.1",
"icon": "brush.jpg",
"author": "jxxghp",
"level": 2

View File

@@ -1,16 +1,14 @@
import base64
import json
import random
import re
import threading
import time
import random
import json
import base64
from datetime import datetime, timedelta
from threading import Event
from typing import Any, List, Dict, Tuple, Optional, Union, Set
import pytz
from apscheduler.schedulers.background import BackgroundScheduler
from app import schemas
from app.chain.torrents import TorrentsChain
from app.core.config import settings
@@ -24,6 +22,7 @@ from app.plugins import _PluginBase
from app.schemas import NotificationType, TorrentInfo
from app.utils.http import RequestUtils
from app.utils.string import StringUtils
from apscheduler.schedulers.background import BackgroundScheduler
lock = threading.Lock()
@@ -67,6 +66,7 @@ class BrushConfig:
self.brush_sequential = config.get("brush_sequential", False)
self.proxy_download = config.get("proxy_download", True)
self.proxy_delete = config.get("proxy_delete", False)
self.log_more = config.get("log_more", False)
self.active_time_range = config.get("active_time_range")
self.enable_site_config = config.get("enable_site_config", False)
self.brush_tag = "刷流"
@@ -182,7 +182,7 @@ class BrushFlow(_PluginBase):
# 插件图标
plugin_icon = "brush.jpg"
# 插件版本
plugin_version = "2.0"
plugin_version = "2.1"
# 插件作者
plugin_author = "jxxghp,InfinityPacer"
# 作者主页
@@ -262,6 +262,12 @@ class BrushFlow(_PluginBase):
brush_config.archive_task = False
self.__update_config()
if brush_config.log_more:
if brush_config.enable_site_config:
logger.info(f"已开启站点独立配置,配置信息:{brush_config}")
else:
logger.info(f"没有开启站点独立配置,配置信息:{brush_config}")
# 停止现有任务
self.stop_service()
@@ -282,7 +288,7 @@ class BrushFlow(_PluginBase):
# 如果开启&存在站点时,才需要启用后台任务
self._task_brush_enable = brush_config.enabled and brush_config.brushsites
# brush_config.onlyonce = True
# brush_config.onlyonce = True
# 检查是否启用了一次性任务
if brush_config.onlyonce:
@@ -1030,6 +1036,27 @@ class BrushFlow(_PluginBase):
}
]
},
{
'component': 'VRow',
"content": [
{
'component': 'VCol',
'props': {
'cols': 12,
'md': 4
},
'content': [
{
'component': 'VSwitch',
'props': {
'model': 'log_more',
'label': '记录更多日志',
}
}
]
}
]
},
{
'component': 'VRow',
'content': [
@@ -1156,7 +1183,8 @@ class BrushFlow(_PluginBase):
"proxy_delete": False,
"freeleech": "free",
"hr": "yes",
"enable_site_config": False
"enable_site_config": False,
"log_more": False
}
def get_page(self) -> List[dict]:
@@ -1658,8 +1686,15 @@ class BrushFlow(_PluginBase):
torrent_tasks: Dict[str, dict] = self.get_data("torrents") or {}
torrents_size = self.__calculate_seeding_torrents_size(torrent_tasks=torrent_tasks)
# 判断能否通过保种体积前置条件
size_condition_passed, reason = self.__evaluate_size_condition_for_brush(torrents_size=torrents_size)
self.__log_brush_conditions(passed=size_condition_passed, reason=reason)
if not size_condition_passed:
return
# 判断能否通过刷流前置条件
pre_condition_passed, reason = self.__evaluate_pre_conditions_for_brush(torrents_size=torrents_size)
pre_condition_passed, reason = self.__evaluate_pre_conditions_for_brush()
self.__log_brush_conditions(passed=pre_condition_passed, reason=reason)
if not pre_condition_passed:
return
@@ -1695,7 +1730,9 @@ class BrushFlow(_PluginBase):
logger.info(f"刷流任务执行完成")
def __brush_site_torrents(self, siteid, torrent_tasks, statistic_info) -> bool:
"""
针对站点进行刷流
"""
siteinfo = self.siteoper.get(siteid)
if not siteinfo:
logger.warn(f"站点不存在:{siteid}")
@@ -1723,23 +1760,24 @@ class BrushFlow(_PluginBase):
# 过滤种子
for torrent in torrents:
# 判断能否通过刷流前置条件
seeding_size = torrents_size + torrent.size
pre_condition_passed, reason = self.__evaluate_pre_conditions_for_brush(torrents_size=seeding_size,
include_network_conditions=False)
pre_condition_passed, reason = self.__evaluate_pre_conditions_for_brush(include_network_conditions=False)
self.__log_brush_conditions(passed=pre_condition_passed, reason=reason, torrent=torrent)
if not pre_condition_passed:
# logger.info(f"种子没有通过刷流前置条件校验,原因:{reason} 种子:{torrent.title}|{torrent.description}")
return False
# else:
# logger.info(f"种子已通过刷流前置校验,种子:{torrent.title}|{torrent.description}")
# 判断能否通过保种体积刷流条件
size_condition_passed, reason = self.__evaluate_size_condition_for_brush(torrents_size=torrents_size,
brush_torrent_size=torrent.size)
self.__log_brush_conditions(passed=size_condition_passed, reason=reason, torrent=torrent)
if not size_condition_passed:
continue
# 判断能否通过刷流条件
condition_passed, reason = self.__evaluate_conditions_for_brush(torrent=torrent,
torrent_tasks=torrent_tasks)
self.__log_brush_conditions(passed=condition_passed, reason=reason, torrent=torrent)
if not condition_passed:
# logger.info(f"种子没有通过刷流条件校验,原因:{reason} 种子:{torrent.title}|{torrent.description}")
continue
# else:
# logger.info(f"种子已通过刷流条件校验,种子:{torrent.title}|{torrent.description}")
# 添加下载任务
hash_string = self.__download(torrent=torrent)
@@ -1790,14 +1828,43 @@ class BrushFlow(_PluginBase):
return True
def __evaluate_pre_conditions_for_brush(self, torrents_size: float,
include_network_conditions: bool = True) -> Tuple[bool, Optional[str]]:
def __evaluate_size_condition_for_brush(self, torrents_size: float,
brush_torrent_size: float = 0.0) -> Tuple[bool, Optional[str]]:
"""
过滤体积不符合条件的种子
"""
total_size = self.__bytes_to_gb(torrents_size + brush_torrent_size) # 预计总做种体积
def generate_message(config):
if brush_torrent_size > 0:
return (f"当前做种体积 {self.__bytes_to_gb(torrents_size):.1f} GB"
f"刷流种子 {self.__bytes_to_gb(brush_torrent_size):.1f} GB"
f"预计做种体积 {total_size:.1f} GB已超过做种体积 {config} GB")
else:
return f"当前做种体积 {self.__bytes_to_gb(torrents_size):.1f} GB已超过做种体积 {config} GB暂时停止新增任务"
reasons = [
("disksize",
lambda config: torrents_size + brush_torrent_size > float(config) * 1024 ** 3, generate_message)
]
brush_config = self.__get_brush_config()
for condition, check, message in reasons:
config_value = getattr(brush_config, condition, None)
if config_value and check(config_value):
reason = message(config_value)
return False, reason
return True, None
def __evaluate_pre_conditions_for_brush(self, include_network_conditions: bool = True) \
-> Tuple[bool, Optional[str]]:
"""
前置过滤不符合条件的种子
"""
reasons = [
("maxdlcount", lambda config: self.__get_downloading_count() >= int(config),
lambda config: f"当前同时下载任务数已达到最大值 {config},暂时停止新增任务"),
("disksize", lambda config: torrents_size > float(config) * 1024 ** 3,
lambda config: f"当前做种体积 {self.__bytes_to_gb(torrents_size):.1f} GB"
f"已超过保种体积 {config} GB暂时停止新增任务"),
lambda config: f"当前同时下载任务数已达到最大值 {config},暂时停止新增任务")
]
if include_network_conditions:
@@ -1819,7 +1886,6 @@ class BrushFlow(_PluginBase):
config_value = getattr(brush_config, condition, None)
if config_value and check(config_value):
reason = message(config_value)
logger.warn(reason)
return False, reason
return True, None
@@ -1894,6 +1960,18 @@ class BrushFlow(_PluginBase):
return True, None
def __log_brush_conditions(self, passed: bool, reason: str, torrent: Any = None):
"""
记录刷流日志
"""
if not passed:
if not torrent:
logger.warn(f"种子没有通过前置刷流条件校验,原因:{reason}")
else:
brush_config = self.__get_brush_config()
if brush_config.log_more:
logger.warn(f"种子没有通过刷流条件校验,原因:{reason} 种子:{torrent.title}|{torrent.description}")
# endregion
# region Check
@@ -1944,25 +2022,27 @@ class BrushFlow(_PluginBase):
# 先更新刷流任务的最新状态,上下传,分享率
self.__update_torrent_tasks_state(torrents=check_torrents, torrent_tasks=torrent_tasks)
# 先通过获取的全量种子,判断已经被删除,但是任务记录中还没有被标记删除的种子
undeleted_hashes = self.__get_undeleted_torrents_missing_in_downloader(torrent_tasks, torrent_check_hashes,
check_torrents) or []
# 排除MoviePilot种子
if check_torrents and brush_config.except_tags:
check_torrents = self.__filter_torrents_by_tag(torrents=check_torrents,
exclude_tag=settings.TORRENT_TAG)
need_delete_hashes = []
need_delete_hashes.extend(undeleted_hashes)
# 先通过获取的全量种子,判断已经被删除,但是任务记录中还没有被标记删除的种子
undeleted_hashes = self.__get_undeleted_torrents_missing_in_downloader(torrent_tasks, torrent_check_hashes,
check_torrents) or []
# 这里提前把已经被删除的种子进行标记,避免开启动态删除种子统计体积有时差
if undeleted_hashes:
for torrent_hash in undeleted_hashes:
torrent_tasks[torrent_hash]["deleted"] = True
need_delete_hashes = []
# 如果配置了删种阈值,则根据动态删种进行分组处理
if brush_config.proxy_delete and brush_config.delete_size_range:
logger.info("已开启动态删种,按系统默认动态删种条件开始检查任务")
proxy_delete_hashs = self.__delete_torrent_for_proxy(torrents=check_torrents,
torrent_tasks=torrent_tasks) or []
need_delete_hashes.extend(proxy_delete_hashs)
# 否则均认为是没有开启动态删种
# 否则均认为是没有开启动态删种
else:
logger.info("没有开启动态删种,按用户设置删种条件开始检查任务")
not_proxy_delete_hashs = self.__delete_torrent_for_evaluate_conditions(torrents=check_torrents,
@@ -2138,9 +2218,9 @@ class BrushFlow(_PluginBase):
def __delete_torrent_for_proxy(self, torrents: List[Any], torrent_tasks: Dict[str, dict]) -> List:
"""
支持动态删除种子,当设置了动态删种(全局)和删除阈值时,当种体积达到删除阈值时优先按设置规则进行删除若还没有达到阈值则排除HR种子后按加入时间倒序进行删除
删除阈值100种体积 > 100G 时,则开始删除种子,直至降低至 100G
删除阈值50-100种体积 > 100G 时,则开始删除种子,直至降至为 50G
支持动态删除种子,当设置了动态删种(全局)和删除阈值时,当种体积达到删除阈值时优先按设置规则进行删除若还没有达到阈值则排除HR种子后按加入时间倒序进行删除
删除阈值100种体积 > 100G 时,则开始删除种子,直至降低至 100G
删除阈值50-100种体积 > 100G 时,则开始删除种子,直至降至为 50G
"""
brush_config = self.__get_brush_config()
@@ -2150,26 +2230,27 @@ class BrushFlow(_PluginBase):
# 解析删除阈值范围
sizes = [float(size) * 1024 ** 3 for size in brush_config.delete_size_range.split("-")]
min_size = sizes[0] # 至少需要达到的种体积
max_size = sizes[1] if len(sizes) > 1 else sizes[0] # 触发删除操作的种体积上限
min_size = sizes[0] # 至少需要达到的种体积
max_size = sizes[1] if len(sizes) > 1 else sizes[0] # 触发删除操作的种体积上限
torrent_info_map = {self.__get_hash(torrent): self.__get_torrent_info(torrent=torrent) for torrent in torrents}
# 计算当前总种体积
total_torrent_size = sum(info.get("total_size", 0) for info in torrent_info_map.values())
# 计算当前总种体积
# total_torrent_size = sum(info.get("total_size", 0) for info in torrent_info_map.values())
total_torrent_size = self.__calculate_seeding_torrents_size(torrent_tasks=torrent_tasks)
# 当总体积未超过最大阈值时,不需要执行删除操作
if total_torrent_size < max_size:
logger.info(
f"当前种体积 {self.__bytes_to_gb(total_torrent_size):.1f} GB上限 {self.__bytes_to_gb(max_size):.1f} GB下限 {self.__bytes_to_gb(min_size):.1f} GB未触发动态删除")
f"当前种体积 {self.__bytes_to_gb(total_torrent_size):.1f} GB上限 {self.__bytes_to_gb(max_size):.1f} GB下限 {self.__bytes_to_gb(min_size):.1f} GB未触发动态删除")
return []
else:
logger.info(
f"当前种体积 {self.__bytes_to_gb(total_torrent_size):.1f} GB上限 {self.__bytes_to_gb(max_size):.1f} GB下限 {self.__bytes_to_gb(min_size):.1f} GB触发动态删除")
f"当前种体积 {self.__bytes_to_gb(total_torrent_size):.1f} GB上限 {self.__bytes_to_gb(max_size):.1f} GB下限 {self.__bytes_to_gb(min_size):.1f} GB触发动态删除")
need_delete_hashes = []
# 即使开了动态删除,但是也有可能部分站点单独设置了关闭,这里根据种子动态进行分组,先处理不需要动态的种子,按设置的规则进行删除
# 即使开了动态删除,但是也有可能部分站点单独设置了关闭,这里根据种子动态进行分组,先处理不需要动态的种子,按设置的规则进行删除
proxy_delete_torrents, not_proxy_delete_torrents = self.__group_torrents_by_proxy_delete(torrents=torrents,
torrent_tasks=torrent_tasks)
logger.info(f"托管种子数 {len(proxy_delete_torrents)},未托管种子数 {len(not_proxy_delete_torrents)}")
@@ -2227,7 +2308,7 @@ class BrushFlow(_PluginBase):
reason=reason)
logger.info(f"站点:{site_name}{reason},删除种子:{torrent_title}|{torrent_desc}")
msg = f"已完成 {len(need_delete_hashes)} 个种子删除,当前种体积 {self.__bytes_to_gb(total_torrent_size):.1f} GB"
msg = f"已完成 {len(need_delete_hashes)} 个种子删除,当前种体积 {self.__bytes_to_gb(total_torrent_size):.1f} GB"
self.post_message(mtype=NotificationType.SiteMessage, title="【刷流任务种子删除】", text=msg)
logger.info(msg)
@@ -2453,6 +2534,7 @@ class BrushFlow(_PluginBase):
"brush_sequential": brush_config.brush_sequential,
"proxy_download": brush_config.proxy_download,
"proxy_delete": brush_config.proxy_delete,
"log_more": brush_config.log_more,
"active_time_range": brush_config.active_time_range,
"enable_site_config": brush_config.enable_site_config,
"site_config": brush_config.site_config
@@ -2604,7 +2686,8 @@ class BrushFlow(_PluginBase):
# 获取种子Hash
torrent_hash = self.qb.get_torrent_id_by_tag(tags=tag)
if not torrent_hash:
logger.error(f"{brush_config.downloader} 获取种子Hash失败")
logger.error(f"{brush_config.downloader} 获取种子Hash失败"
f"{',请尝试开启代理下载种子' if not brush_config.proxy_download else ''}")
return None
return torrent_hash
return None