Merge pull request #417 from InfinityPacer/main

This commit is contained in:
jxxghp
2024-07-12 14:48:55 +08:00
committed by GitHub
2 changed files with 109 additions and 88 deletions

View File

@@ -355,11 +355,12 @@
"name": "站点刷流",
"description": "自动托管刷流,将会提高对应站点的访问频率。",
"labels": "刷流,仪表板",
"version": "3.3",
"version": "3.4",
"icon": "brush.jpg",
"author": "jxxghp,InfinityPacer",
"level": 2,
"history": {
"v3.4": "移除「记录更多日志」配置项并调整为DEBUG日志支持「删除排除标签」配置项增加刷流任务时支持触发插件事件",
"v3.3": "支持QB删除种子时强制汇报Tracker站点独立配置增加「站点全局H&R」配置项",
"v3.2": "支持推送QB种子时启用「先下载首尾文件块」选项",
"v3.1": "支持仪表板显示站点刷流数据需要主程序升级v1.8.7+版本",

View File

@@ -25,6 +25,7 @@ from app.modules.qbittorrent import Qbittorrent
from app.modules.transmission import Transmission
from app.plugins import _PluginBase
from app.schemas import NotificationType, TorrentInfo, MediaType
from app.schemas.types import EventType
from app.utils.http import RequestUtils
from app.utils.string import StringUtils
@@ -67,11 +68,11 @@ class BrushConfig:
self.clear_task = config.get("clear_task", False)
self.archive_task = config.get("archive_task", False)
self.except_tags = config.get("except_tags", True)
self.delete_except_tags = config.get("delete_except_tags")
self.except_subscribe = config.get("except_subscribe", True)
self.brush_sequential = config.get("brush_sequential", False)
self.proxy_download = config.get("proxy_download", False)
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.downloader_monitor = config.get("downloader_monitor")
self.qb_category = config.get("qb_category")
@@ -257,7 +258,7 @@ class BrushFlow(_PluginBase):
# 插件图标
plugin_icon = "brush.jpg"
# 插件版本
plugin_version = "3.3"
plugin_version = "3.4"
# 插件作者
plugin_author = "jxxghp,InfinityPacer"
# 作者主页
@@ -295,7 +296,6 @@ class BrushFlow(_PluginBase):
# endregion
def init_plugin(self, config: dict = None):
logger.info(f"站点刷流服务初始化")
self.siteshelper = SitesHelper()
self.siteoper = SiteOper()
self.torrents = TorrentsChain()
@@ -340,11 +340,10 @@ 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}")
if brush_config.enable_site_config:
logger.debug(f"已开启站点独立配置,配置信息:{brush_config}")
else:
logger.debug(f"没有开启站点独立配置,配置信息:{brush_config}")
# 停止现有任务
self.stop_service()
@@ -366,8 +365,6 @@ class BrushFlow(_PluginBase):
# 如果开启&存在站点时,才需要启用后台任务
self._task_brush_enable = brush_config.enabled and brush_config.brushsites
# brush_config.onlyonce = True
# 检查是否启用了一次性任务
if brush_config.onlyonce:
self._scheduler = BackgroundScheduler(timezone=settings.TZ)
@@ -974,11 +971,6 @@ class BrushFlow(_PluginBase):
'component': 'VWindow',
'props': {
'model': '_tabs'
# VWindow设置paddnig会导致切换Tab时页面高度变动调整为修改VRow的方案
# 'style': {
# 'padding-top': '24px',
# 'padding-bottom': '24px',
# },
},
'content': [
{
@@ -1426,11 +1418,28 @@ class BrushFlow(_PluginBase):
'component': 'VTextField',
'props': {
'model': 'seed_inactivetime',
'label': '未活动时间(分钟) ',
'label': '未活动时间(分钟)',
'placeholder': '超过时删除任务'
}
}
]
},
{
'component': 'VCol',
'props': {
"cols": 12,
"md": 4
},
'content': [
{
'component': 'VTextField',
'props': {
'model': 'delete_except_tags',
'label': '删除排除标签',
'placeholder': 'MOVIEPILOT,H&R'
}
}
]
}
]
}
@@ -1658,27 +1667,6 @@ class BrushFlow(_PluginBase):
]
}
]
},
{
'component': 'VRow',
"content": [
{
'component': 'VCol',
'props': {
'cols': 12,
'md': 4
},
'content': [
{
'component': 'VSwitch',
'props': {
'model': 'log_more',
'label': '记录更多日志',
}
}
]
}
]
}
]
}
@@ -1742,7 +1730,7 @@ class BrushFlow(_PluginBase):
'props': {
'type': 'error',
'variant': 'tonal',
'text': '注意排除H&R并不保证能完全适配所有站点部分站点在列表页不显示H&R标志但实际上是有H&R的请注意核对使用'
'text': '注意排除H&R并不保证能完全适配所有站点部分站点在列表页不显示H&R标志但实际上是有H&R的请注意核对使用'
}
}
]
@@ -1850,6 +1838,7 @@ class BrushFlow(_PluginBase):
"clear_task": False,
"archive_task": False,
"except_tags": True,
"delete_except_tags": f"{settings.TORRENT_TAG},H&R" if settings.TORRENT_TAG else "H&R",
"except_subscribe": True,
"brush_sequential": False,
"proxy_download": False,
@@ -1857,7 +1846,6 @@ class BrushFlow(_PluginBase):
"freeleech": "free",
"hr": "yes",
"enable_site_config": False,
"log_more": False,
"downloader_monitor": False,
"auto_qb_category": False,
"qb_first_last_piece": False,
@@ -2055,9 +2043,6 @@ class BrushFlow(_PluginBase):
if brush_config.site_hr_active:
logger.info(f"站点 {siteinfo.name} 已开启全站H&R选项所有种子设置为H&R种子")
# 由于缓存原因这里不能直接改torrents在后续加入任务中调整
# for torrent in torrents:
# torrent.hit_and_run = True
# 排除包含订阅的种子
if brush_config.except_subscribe:
@@ -2068,7 +2053,7 @@ class BrushFlow(_PluginBase):
torrents_size = self.__calculate_seeding_torrents_size(torrent_tasks=torrent_tasks)
logger.info(f"正在准备种子刷流,数量{len(torrents)}")
logger.info(f"正在准备种子刷流,数量 {len(torrents)}")
# 过滤种子
for torrent in torrents:
@@ -2078,6 +2063,8 @@ class BrushFlow(_PluginBase):
if not pre_condition_passed:
return False
logger.debug(f"种子详情:{torrent}")
# 判断能否通过保种体积刷流条件
size_condition_passed, reason = self.__evaluate_size_condition_for_brush(torrents_size=torrents_size,
add_torrent_size=torrent.size)
@@ -2098,8 +2085,8 @@ class BrushFlow(_PluginBase):
logger.warn(f"{torrent.title} 添加刷流任务失败!")
continue
# 保存任务信息
torrent_tasks[hash_string] = {
# 触发刷流下载时间并保存任务信息
torrent_task = {
"site": siteinfo.id,
"site_name": siteinfo.name,
"title": torrent.title,
@@ -2134,6 +2121,12 @@ class BrushFlow(_PluginBase):
"time": time.time()
}
self.eventmanager.send_event(etype=EventType.PluginAction, data={
"action": "brushflow_download_added",
"data": torrent_task
})
torrent_tasks[hash_string] = torrent_task
# 统计数据
torrents_size += torrent.size
statistic_info["count"] += 1
@@ -2306,7 +2299,8 @@ class BrushFlow(_PluginBase):
return True, None
def __log_brush_conditions(self, passed: bool, reason: str, torrent: Any = None):
@staticmethod
def __log_brush_conditions(passed: bool, reason: str, torrent: Any = None):
"""
记录刷流日志
"""
@@ -2314,9 +2308,7 @@ class BrushFlow(_PluginBase):
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}")
logger.debug(f"种子没有通过刷流条件校验,原因:{reason} 种子:{torrent.title}|{torrent.description}")
# endregion
@@ -2372,34 +2364,57 @@ class BrushFlow(_PluginBase):
# 更新刷流任务列表中在下载器中删除的种子为删除状态
self.__update_undeleted_torrents_missing_in_downloader(torrent_tasks, torrent_check_hashes, check_torrents)
# 排除MoviePilot种子
if check_torrents and brush_config.except_tags:
check_torrents = self.__filter_torrents_by_tag(torrents=check_torrents,
exclude_tag=settings.TORRENT_TAG)
# 根据配置的标签进行种子排除
if check_torrents:
logger.info(f"当前刷流任务共 {len(check_torrents)} 个有效种子,正在准备按设定的种子标签进行排除")
# 初始化一个空的列表来存储需要排除的标签
tags_to_exclude = set()
# 如果 except_tags 配置为 True将 settings.TORRENT_TAG 添加到排除列表中(前提是它不为空且不是纯空白)
if brush_config.except_tags and settings.TORRENT_TAG.strip():
tags_to_exclude.add(settings.TORRENT_TAG.strip())
# 如果 delete_except_tags 非空且不是纯空白,则添加到排除列表中
if brush_config.delete_except_tags and brush_config.delete_except_tags.strip():
tags_to_exclude.update(tag.strip() for tag in brush_config.delete_except_tags.split(','))
# 将所有需要排除的标签组合成一个字符串,每个标签之间用逗号分隔
combined_tags = ",".join(tags_to_exclude)
if combined_tags: # 确保有标签需要排除
pre_filter_count = len(check_torrents) # 获取过滤前的任务数量
check_torrents = self.__filter_torrents_by_tag(torrents=check_torrents, exclude_tag=combined_tags)
post_filter_count = len(check_torrents) # 获取过滤后的任务数量
excluded_count = pre_filter_count - post_filter_count # 计算被排除的任务数量
logger.info(
f"有效种子数 {pre_filter_count},排除标签 '{combined_tags}' 后,"
f"剩余种子数 {post_filter_count},排除种子数 {excluded_count}")
else:
logger.info("没有配置有效的排除标签,所有种子均参与后续处理")
need_delete_hashes = []
# 如果配置了动态删除以及删种阈值,则根据动态删种进行分组处理
if brush_config.proxy_delete and brush_config.delete_size_range:
logger.info("已开启动态删种,按系统默认动态删种条件开始检查任务")
proxy_delete_hashes = self.__delete_torrent_for_proxy(torrents=check_torrents,
torrent_tasks=torrent_tasks) or []
need_delete_hashes.extend(proxy_delete_hashes)
# 否则均认为是没有开启动态删种
# 种子删除检查
if not check_torrents:
logger.info("没有需要检查的任务,跳过")
else:
logger.info("没有开启动态删种,按用户设置删种条件开始检查任务")
not_proxy_delete_hashes = self.__delete_torrent_for_evaluate_conditions(torrents=check_torrents,
torrent_tasks=torrent_tasks) or []
need_delete_hashes.extend(not_proxy_delete_hashes)
need_delete_hashes = []
if need_delete_hashes:
# 如果是QB则重新汇报Tracker
if brush_config.downloader == "qbittorrent":
self.__qb_torrents_reannounce(torrent_hashes=need_delete_hashes)
# 删除种子
if downloader.delete_torrents(ids=need_delete_hashes, delete_file=True):
for torrent_hash in need_delete_hashes:
torrent_tasks[torrent_hash]["deleted"] = True
# 如果配置了动态删除以及删种阈值,则根据动态删种进行分组处理
if brush_config.proxy_delete and brush_config.delete_size_range:
logger.info("已开启动态删种,按系统默认动态删种条件开始检查任务")
proxy_delete_hashes = self.__delete_torrent_for_proxy(torrents=check_torrents,
torrent_tasks=torrent_tasks) or []
need_delete_hashes.extend(proxy_delete_hashes)
# 否则均认为是没有开启动态删种
else:
logger.info("没有开启动态删种,按用户设置删种条件开始检查任务")
not_proxy_delete_hashes = self.__delete_torrent_for_evaluate_conditions(torrents=check_torrents,
torrent_tasks=torrent_tasks) or []
need_delete_hashes.extend(not_proxy_delete_hashes)
if need_delete_hashes:
# 如果是QB则重新汇报Tracker
if brush_config.downloader == "qbittorrent":
self.__qb_torrents_reannounce(torrent_hashes=need_delete_hashes)
# 删除种子
if downloader.delete_torrents(ids=need_delete_hashes, delete_file=True):
for torrent_hash in need_delete_hashes:
torrent_tasks[torrent_hash]["deleted"] = True
self.__update_and_save_statistic_info(torrent_tasks)
@@ -2618,8 +2633,7 @@ class BrushFlow(_PluginBase):
reason=reason)
logger.info(f"站点:{site_name}{reason},删除种子:{torrent_title}|{torrent_desc}")
else:
if brush_config.log_more:
logger.info(f"站点:{site_name}{reason},不删除种子:{torrent_title}|{torrent_desc}")
logger.debug(f"站点:{site_name}{reason},不删除种子:{torrent_title}|{torrent_desc}")
return delete_hashes
@@ -2657,8 +2671,7 @@ class BrushFlow(_PluginBase):
reason=reason)
logger.info(f"站点:{site_name}{reason},删除种子:{torrent_title}|{torrent_desc}")
else:
if brush_config.log_more:
logger.info(f"站点:{site_name}{reason},不删除种子:{torrent_title}|{torrent_desc}")
logger.debug(f"站点:{site_name}{reason},不删除种子:{torrent_title}|{torrent_desc}")
return delete_hashes
@@ -2914,7 +2927,7 @@ class BrushFlow(_PluginBase):
"active_downloaded": active_downloaded
})
logger.info(f"刷流任务统计数据总任务数:{total_count},活跃任务数:{active_count},已删除:{total_deleted}"
logger.info(f"刷流任务统计数据总任务数:{total_count},活跃任务数:{active_count},已删除:{total_deleted}"
f"待归档:{total_unarchived}"
f"活跃上传量:{StringUtils.str_filesize(active_uploaded)}"
f"活跃下载量:{StringUtils.str_filesize(active_downloaded)}"
@@ -3030,11 +3043,11 @@ class BrushFlow(_PluginBase):
"clear_task": brush_config.clear_task,
"archive_task": brush_config.archive_task,
"except_tags": brush_config.except_tags,
"delete_except_tags": brush_config.delete_except_tags,
"except_subscribe": brush_config.except_subscribe,
"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,
"downloader_monitor": brush_config.downloader_monitor,
"qb_category": brush_config.qb_category,
@@ -3131,7 +3144,7 @@ class BrushFlow(_PluginBase):
data = data.get(key)
if not data:
return None
logger.info(f"获取到下载地址:{data}")
logger.debug(f"获取到下载地址:{data}")
return data
return None
@@ -3201,8 +3214,7 @@ 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失败"
f"{',请尝试启用「代理下载种子」配置项' if not brush_config.proxy_download else ''}")
logger.error(f"{brush_config.downloader} 获取种子Hash失败,详细信息请查看 README")
return None
return torrent_hash
return None
@@ -3705,14 +3717,21 @@ class BrushFlow(_PluginBase):
def __filter_torrents_by_tag(self, torrents: List[Any], exclude_tag: str) -> List[Any]:
"""
根据标签过滤torrents
根据标签过滤torrents,排除标签格式为逗号分隔的字符串,例如 "MOVIEPILOT, H&R"
"""
# 如果排除标签字符串为空,则返回原始列表
if not exclude_tag:
return torrents
# 将 exclude_tag 字符串分割成一个集合,并去除每个标签两端的空白,忽略空白标签并自动去重
exclude_tags = set(tag.strip() for tag in exclude_tag.split(',') if tag.strip())
filter_torrents = []
for torrent in torrents:
# 使用 __get_label 方法获取每个 torrent 的标签列表
labels = self.__get_label(torrent)
# 如果排除标签不在这个列表中,则添加到过滤后的列表
if exclude_tag not in labels:
# 检查是否有任何一个排除标签存在于标签列表中
if not any(exclude in labels for exclude in exclude_tags):
filter_torrents.append(torrent)
return filter_torrents
@@ -3766,7 +3785,8 @@ class BrushFlow(_PluginBase):
for key in set(self._subscribe_infos) - current_keys:
del self._subscribe_infos[key]
logger.info(f"订阅标题匹配完成,当前订阅的标题集合为:{self._subscribe_infos}")
logger.info("订阅标题匹配完成")
logger.debug(f"当前订阅的标题集合为:{self._subscribe_infos}")
unique_titles = {title for titles in self._subscribe_infos.values() for title in titles}
return unique_titles