From a75a976de71479ec17003389c954e37d728d9893 Mon Sep 17 00:00:00 2001 From: jxxghp Date: Sun, 12 May 2024 20:42:25 +0800 Subject: [PATCH 1/4] =?UTF-8?q?=E6=9B=B4=E6=96=B0=20package.json?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 5164bfd..e9f6425 100644 --- a/package.json +++ b/package.json @@ -686,8 +686,8 @@ } }, "MPServerStatus": { - "name": "MoviePilot服务器状态", - "description": "在仪表板中实时显示MoviePilot公共服务器状态(https://movie-pilot.org)", + "name": "MoviePilot服务器监控", + "description": "在仪表板中实时显示MoviePilot公共服务器状态。", "labels": "工具", "version": "1.0", "icon": "Duplicati_A.png", From 622722aef5c3eb227dee119f51940e65a88a58bf Mon Sep 17 00:00:00 2001 From: xuzhi Date: Mon, 13 May 2024 03:45:32 +0000 Subject: [PATCH 2/4] Update removelink to v2.1 --- package.json | 1 + plugins/removelink/__init__.py | 62 ++++++++++++++++++++++++++++------ 2 files changed, 53 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index 99c7cee..0c33c3f 100644 --- a/package.json +++ b/package.json @@ -485,6 +485,7 @@ "author": "DzAvril", "level": 1, "history": { + "v2.1": "联动删除历史记录", "v2.0": "联动删除种子,需安装插件[下载器助手]并打开监听源文件事件", "v1.9": "增加清理刮削文件功能(beta)", "v1.8": "增加清理空目录功能(beta)", diff --git a/plugins/removelink/__init__.py b/plugins/removelink/__init__.py index d3e4969..45fc27a 100644 --- a/plugins/removelink/__init__.py +++ b/plugins/removelink/__init__.py @@ -7,7 +7,7 @@ from typing import List, Tuple, Dict, Any from watchdog.events import FileSystemEventHandler from watchdog.observers import Observer - +from app.db.transferhistory_oper import TransferHistoryOper from app.log import logger from app.plugins import _PluginBase from app.schemas import NotificationType @@ -60,9 +60,16 @@ class FileMonitorHandler(FileSystemEventHandler): self.sync.state_set[str(file_path)] = file_path.stat().st_ino def on_deleted(self, event): - if event.is_directory: - return file_path = Path(event.src_path) + if event.is_directory: + # 单独处理文件夹删除触发删除种子 + if self.sync._delete_torrents: + # 发送事件 + logger.info(f"监测到删除文件夹:{file_path}") + eventmanager.send_event( + EventType.DownloadFileDeleted, {"src": str(file_path)} + ) + return if file_path.suffix in [".!qB", ".part", ".mp"]: return logger.info(f"监测到删除文件:{file_path}") @@ -108,7 +115,7 @@ class RemoveLink(_PluginBase): # 插件图标 plugin_icon = "Ombi_A.png" # 插件版本 - plugin_version = "2.0" + plugin_version = "2.1" # 插件作者 plugin_author = "DzAvril" # 作者主页 @@ -128,12 +135,15 @@ class RemoveLink(_PluginBase): _notify = False _delete_scrap_infos = False _delete_torrents = False + _delete_history = False + _transferhistory = None _observer = [] # 监控目录的文件列表 state_set: Dict[str, int] = {} def init_plugin(self, config: dict = None): logger.info(f"Hello, RemoveLink! config {config}") + self._transferhistory = TransferHistoryOper() if config: self._enabled = config.get("enabled") self._notify = config.get("notify") @@ -142,6 +152,7 @@ class RemoveLink(_PluginBase): self.exclude_keywords = config.get("exclude_keywords") or "" self._delete_scrap_infos = config.get("delete_scrap_infos") self._delete_torrents = config.get("delete_torrents") + self._delete_history = config.get("delete_history") # 停止现有任务 self.stop_service() @@ -206,7 +217,7 @@ class RemoveLink(_PluginBase): "content": [ { "component": "VCol", - "props": {"cols": 12, "md": 6}, + "props": {"cols": 12, "md": 4}, "content": [ { "component": "VSwitch", @@ -219,7 +230,7 @@ class RemoveLink(_PluginBase): }, { "component": "VCol", - "props": {"cols": 12, "md": 6}, + "props": {"cols": 12, "md": 4}, "content": [ { "component": "VSwitch", @@ -237,7 +248,7 @@ class RemoveLink(_PluginBase): "content": [ { "component": "VCol", - "props": {"cols": 12, "md": 6}, + "props": {"cols": 12, "md": 4}, "content": [ { "component": "VSwitch", @@ -250,7 +261,7 @@ class RemoveLink(_PluginBase): }, { "component": "VCol", - "props": {"cols": 12, "md": 6}, + "props": {"cols": 12, "md": 4}, "content": [ { "component": "VSwitch", @@ -261,6 +272,19 @@ class RemoveLink(_PluginBase): } ], }, + { + "component": "VCol", + "props": {"cols": 12, "md": 4}, + "content": [ + { + "component": "VSwitch", + "props": { + "model": "delete_history", + "label": "删除历史记录", + }, + } + ], + }, ], }, { @@ -441,7 +465,7 @@ class RemoveLink(_PluginBase): # 文件所在目录已被删除则退出 if not os.path.exists(path.parent): return - logger.info(f"清理刮削文件: {path}") + # logger.info(f"清理刮削文件: {path}") if not path.suffix.lower() in [ ".jpg", ".nfo", @@ -455,11 +479,25 @@ class RemoveLink(_PluginBase): # 清理空目录 self.delete_empty_folders(path) + def delete_history(self, path): + """ + 清理path相关的历史记录 + """ + if not self._delete_history: + return + # 查找历史记录 + transfer_history = self._transferhistory.get_by_src(path) + if transfer_history: + # 删除历史记录 + self._transferhistory.delete(transfer_history.id) + logger.info(f"删除历史记录:{transfer_history.id}") + + def delete_empty_folders(self, path): """ 从指定路径开始,逐级向上层目录检测并删除空目录,直到遇到非空目录或到达指定监控目录为止 """ - logger.info(f"清理空目录: {path}") + # logger.info(f"清理空目录: {path}") while True: parent_path = path.parent if self.__is_excluded(parent_path): @@ -505,6 +543,8 @@ class RemoveLink(_PluginBase): eventmanager.send_event( EventType.DownloadFileDeleted, {"src": str(file_path)} ) + # 删除历史记录 + self.delete_history(str(file_path)) # 删除的文件inode deleted_inode = self.state_set.get(str(file_path)) if not deleted_inode: @@ -530,6 +570,8 @@ class RemoveLink(_PluginBase): eventmanager.send_event( EventType.DownloadFileDeleted, {"src": str(file_path)} ) + # 删除历史记录 + self.delete_history(str(file_path)) if self._notify: self.post_message( mtype=NotificationType.SiteMessage, From 339da93b04453aba7a0713a027fe07e06503173b Mon Sep 17 00:00:00 2001 From: Allen Date: Mon, 13 May 2024 13:16:43 +0800 Subject: [PATCH 3/4] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BA=86=E8=A1=A8?= =?UTF-8?q?=E5=8D=95=E7=95=8C=E9=9D=A2=E5=92=8C=E4=B8=80=E4=BA=9B=E9=80=BB?= =?UTF-8?q?=E8=BE=91=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 3 +- plugins/downloaderhelper/__init__.py | 272 +++++++++++++-------------- plugins/downloaderhelper/module.py | 22 ++- 3 files changed, 143 insertions(+), 154 deletions(-) diff --git a/package.json b/package.json index 3658cda..fef90d9 100644 --- a/package.json +++ b/package.json @@ -611,11 +611,12 @@ "name": "下载器助手", "description": "自动做种、站点标签、自动删种。", "labels": "下载管理", - "version": "1.6", + "version": "1.7", "icon": "DownloaderHelper.png", "author": "hotlcc", "level": 2, "history": { + "v1.7": "优化了表单界面和一些逻辑。", "v1.6": "修复事件触发tr打标问题;表单界面优化。" } }, diff --git a/plugins/downloaderhelper/__init__.py b/plugins/downloaderhelper/__init__.py index c2f2b38..c802180 100644 --- a/plugins/downloaderhelper/__init__.py +++ b/plugins/downloaderhelper/__init__.py @@ -19,7 +19,7 @@ from app.log import logger from app.modules.qbittorrent.qbittorrent import Qbittorrent from app.modules.transmission.transmission import Transmission from app.plugins import _PluginBase -from app.plugins.downloaderhelper.module import TaskContext, TaskResult +from app.plugins.downloaderhelper.module import TaskContext, TaskResult, Downloader from app.schemas.types import EventType from app.utils.string import StringUtils @@ -32,7 +32,7 @@ class DownloaderHelper(_PluginBase): # 插件图标 plugin_icon = "DownloaderHelper.png" # 插件版本 - plugin_version = "1.6" + plugin_version = "1.7" # 插件作者 plugin_author = "hotlcc" # 作者主页 @@ -168,7 +168,82 @@ class DownloaderHelper(_PluginBase): } # 合并默认配置 config_suggest.update(self.__config_default) - + # 下载器tabs + downloader_tabs = [{ + 'component': 'VTab', + 'props': { + 'value': d.id + }, + 'text': d.name_ + } for d in Downloader if d] + # 下载器tab items + downloader_tab_items = [{ + 'component': 'VWindowItem', + 'props': { + 'value': d.id + }, + 'content': [{ + 'component': 'VRow', + 'content': [{ + 'component': 'VCol', + 'props': { + 'cols': 12, + 'xxl': 3, 'xl': 3, 'lg': 3, 'md': 3, 'sm': 6, 'xs': 12 + }, + 'content': [{ + 'component': 'VSwitch', + 'props': { + 'model': f'{d.short_id}_enable', + 'label': '任务开关', + 'hint': '该下载器子任务的开关' + } + }] + }, { + 'component': 'VCol', + 'props': { + 'cols': 12, + 'xxl': 3, 'xl': 3, 'lg': 3, 'md': 3, 'sm': 6, 'xs': 12 + }, + 'content': [{ + 'component': 'VSwitch', + 'props': { + 'model': f'{d.short_id}_enable_seeding', + 'label': '自动做种', + 'hint': '是否开启自动做种功能' + } + }] + }, { + 'component': 'VCol', + 'props': { + 'cols': 12, + 'xxl': 3, 'xl': 3, 'lg': 3, 'md': 3, 'sm': 6, 'xs': 12 + }, + 'content': [{ + 'component': 'VSwitch', + 'props': { + 'model': f'{d.short_id}_enable_tagging', + 'label': '站点标签', + 'hint': '是否开启站点标签功能' + } + }] + }, { + 'component': 'VCol', + 'props': { + 'cols': 12, + 'xxl': 3, 'xl': 3, 'lg': 3, 'md': 3, 'sm': 6, 'xs': 12 + }, + 'content': [{ + 'component': 'VSwitch', + 'props': { + 'model': f'{d.short_id}_enable_delete', + 'label': '自动删种', + 'hint': '是否开启自动删种功能' + } + }] + }] + }] + } for d in Downloader if d] + # 返回form return [{ 'component': 'VForm', 'content': [{ # 业务无关总控 @@ -313,174 +388,83 @@ class DownloaderHelper(_PluginBase): 'content': [{ 'component': 'VCol', 'props': { - 'cols': 12 + 'cols': 12, + 'xxl': 4, 'xl': 4, 'lg': 4, 'md': 4, 'sm': 6, 'xs': 12 }, 'content': [{ - 'component': 'VTextarea', + 'component': 'VSwitch', 'props': { - 'model': 'tracker_mappings', - 'label': 'Tracker映射', - 'placeholder': '格式:\n' - ':\n\n' - '例如:\n' - 'chdbits.xyz:ptchdbits.co', - 'hint': 'Tracker映射。用于在站点打标签时,指定tracker和站点域名不同的种子的域名对应关系;前面为tracker域名(完整域名或者主域名皆可),中间是英文冒号,后面是站点域名。' + 'model': '_config_tracker_mappings_dialog_closed', + 'label': '配置Tracker映射', + 'hint': '点击展开Tracker映射配置窗口。' } }] }] }, { - 'component': 'VTabs', + 'component': 'VDialog', 'props': { - 'model': '_tabs', - 'height': 72, - 'style': { - 'margin-top': '20px', - 'margin-bottom': '20px' - } + 'model': '_config_tracker_mappings_dialog_closed', + 'max-width': '60rem' }, 'content': [{ - 'component': 'VTab', + 'component': 'VCard', 'props': { - 'value': 'qbittorrent' - }, - 'text': 'qbittorrent' - }, { - 'component': 'VTab', - 'props': { - 'value': 'transmission' - }, - 'text': 'transmission' - }] - }, { - 'component': 'VWindow', - 'props': { - 'model': '_tabs' - }, - 'content': [{ - 'component': 'VWindowItem', - 'props': { - 'value': 'qbittorrent' + 'title': '配置Tracker映射', + 'style': { + 'padding': '0 20px 20px 20px' + } }, 'content': [{ + 'component': 'VDialogCloseBtn', + 'props': { + 'model': '_config_tracker_mappings_dialog_closed' + } + }, { 'component': 'VRow', 'content': [{ 'component': 'VCol', 'props': { - 'cols': 12, - 'xxl': 3, 'xl': 3, 'lg': 3, 'md': 3, 'sm': 6, 'xs': 12 + 'cols': 12 }, 'content': [{ - 'component': 'VSwitch', + 'component': 'VTextarea', 'props': { - 'model': 'qb_enable', - 'label': '任务开关', - 'hint': '该下载器子任务的开关' - } - }] - }, { - 'component': 'VCol', - 'props': { - 'cols': 12, - 'xxl': 3, 'xl': 3, 'lg': 3, 'md': 3, 'sm': 6, 'xs': 12 - }, - 'content': [{ - 'component': 'VSwitch', - 'props': { - 'model': 'qb_enable_seeding', - 'label': '自动做种', - 'hint': '是否开启自动做种功能' - } - }] - }, { - 'component': 'VCol', - 'props': { - 'cols': 12, - 'xxl': 3, 'xl': 3, 'lg': 3, 'md': 3, 'sm': 6, 'xs': 12 - }, - 'content': [{ - 'component': 'VSwitch', - 'props': { - 'model': 'qb_enable_tagging', - 'label': '站点标签', - 'hint': '是否开启站点标签功能' - } - }] - }, { - 'component': 'VCol', - 'props': { - 'cols': 12, - 'xxl': 3, 'xl': 3, 'lg': 3, 'md': 3, 'sm': 6, 'xs': 12 - }, - 'content': [{ - 'component': 'VSwitch', - 'props': { - 'model': 'qb_enable_delete', - 'label': '自动删种', - 'hint': '是否开启自动删种功能' + 'model': 'tracker_mappings', + 'label': 'Tracker映射', + 'placeholder': '格式:\n' + ':\n\n' + '例如:\n' + 'chdbits.xyz:ptchdbits.co', + 'hint': 'Tracker映射。用于在站点打标签时,指定tracker和站点域名不同的种子的域名对应关系;前面为tracker域名(完整域名或者主域名皆可),中间是英文冒号,后面是站点域名。' } }] }] }] - }, { - 'component': 'VWindowItem', + }] + }, { + 'component': 'VRow', + 'content': [{ + 'component': 'VCol', 'props': { - 'value': 'transmission' + 'cols': 12 }, 'content': [{ - 'component': 'VRow', - 'content': [{ - 'component': 'VCol', - 'props': { - 'cols': 12, - 'xxl': 3, 'xl': 3, 'lg': 3, 'md': 3, 'sm': 6, 'xs': 12 - }, - 'content': [{ - 'component': 'VSwitch', - 'props': { - 'model': 'tr_enable', - 'label': '任务开关' - } - }] - }, { - 'component': 'VCol', - 'props': { - 'cols': 12, - 'xxl': 3, 'xl': 3, 'lg': 3, 'md': 3, 'sm': 6, 'xs': 12 - }, - 'content': [{ - 'component': 'VSwitch', - 'props': { - 'model': 'tr_enable_seeding', - 'label': '自动做种' - } - }] - }, { - 'component': 'VCol', - 'props': { - 'cols': 12, - 'xxl': 3, 'xl': 3, 'lg': 3, 'md': 3, 'sm': 6, 'xs': 12 - }, - 'content': [{ - 'component': 'VSwitch', - 'props': { - 'model': 'tr_enable_tagging', - 'label': '站点标签' - } - }] - }, { - 'component': 'VCol', - 'props': { - 'cols': 12, - 'xxl': 3, 'xl': 3, 'lg': 3, 'md': 3, 'sm': 6, 'xs': 12 - }, - 'content': [{ - 'component': 'VSwitch', - 'props': { - 'model': 'tr_enable_delete', - 'label': '自动删种' - } - }] - }] + 'component': 'VTabs', + 'props': { + 'model': '_tabs', + 'height': 72, + 'style': { + 'margin-top-': '20px', + 'margin-bottom-': '20px' + } + }, + 'content': downloader_tabs + }, { + 'component': 'VWindow', + 'props': { + 'model': '_tabs' + }, + 'content': downloader_tab_items }] }] }, { diff --git a/plugins/downloaderhelper/module.py b/plugins/downloaderhelper/module.py index e94ad44..cab6158 100644 --- a/plugins/downloaderhelper/module.py +++ b/plugins/downloaderhelper/module.py @@ -1,15 +1,19 @@ from typing import Set, List, Optional +from enum import Enum -class Constants: +class Downloader(Enum): """ - 常量 + 下载器枚举 """ - # 下载器ID - # qb下载器id - qb_downloader_id: str = 'qbittorrent' - # tr下载器id - tr_downloader_id: str = 'transmission' + QB = ('qbittorrent', 'qBittorrent', 'qb', 'QB') + TR = ('transmission', 'Transmission', 'tr', 'TR') + + def __init__(self, id: str, name_: str, short_id: str, short_name: str): + self.id: str = id + self.name_: str = name_ + self.short_id: str = short_id + self.short_name: str = short_name class TaskResult: @@ -133,14 +137,14 @@ class TaskContext: 是否选择了qb下载器 :return: 是否选择了qb下载器 """ - return self.__is_selected_the_downloader(Constants.qb_downloader_id) + return self.__is_selected_the_downloader(Downloader.QB.id) def is_selected_tr_downloader(self) -> bool: """ 是否选择了tr下载器 :return: 是否选择了tr下载器 """ - return self.__is_selected_the_downloader(Constants.tr_downloader_id) + return self.__is_selected_the_downloader(Downloader.TR.id) def enable_seeding(self, enable_seeding: bool = True): """ From b7906f1c15b3a62c855eb555f51a5edd4da54f33 Mon Sep 17 00:00:00 2001 From: Allen Date: Mon, 13 May 2024 13:21:14 +0800 Subject: [PATCH 4/4] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- plugins/downloaderhelper/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/downloaderhelper/README.md b/plugins/downloaderhelper/README.md index c0a88d8..a41b700 100644 --- a/plugins/downloaderhelper/README.md +++ b/plugins/downloaderhelper/README.md @@ -43,6 +43,7 @@ |定时执行周期|插件定时服务的cron表达式,仅支持5位的,缺省时不注册定时服务。| |排除种子标签|多个标签通过英文逗号分割,具备配置的任意标签的种子不会进行自动做种、站点标签、自动删种操作。| |站点标签前缀|站点标签的前缀,缺省时不添加前缀。| +|配置Tracker映射|该开关无实际业务意义,仅用于触发展开配置Tracker映射窗口。| |Tracker映射|站点标签的原理是根据tracker的域名去匹配站点,但是有的PT站的tracker域名和站点域名不一致,导致匹配不到站点,因此需要对这些特殊站点的tracker做映射;每行一个映射,格式是 `tracker域名:站点域名`,tracker域名可以是完整域名或者主域名。| ##### 2.1.2、下载器子任务配置项