diff --git a/package.json b/package.json index 650d7ee..c9b1f37 100644 --- a/package.json +++ b/package.json @@ -114,11 +114,12 @@ "name": "目录监控", "description": "监控目录文件发生变化时实时整理到媒体库。", "labels": "文件整理", - "version": "2.5", + "version": "2.5.1", "icon": "directory.png", "author": "jxxghp", "level": 1, "history": { + "v2.5.1": "过滤下载临时文件和不可整理文件,避免目录监控误触发整理", "v2.5": "目录监控改用watchfiles,移除旧监控依赖", "v2.4": "修复目录监控不使用ChatGPT辅助识别问题", "v2.3": "特殊场景下补充转移成功历史记录", @@ -372,11 +373,12 @@ "name": "整理VCB动漫压制组作品", "description": "一款辅助整理&提高识别VCB-Stuido动漫压制组作品的插件", "labels": "文件整理,识别", - "version": "1.8.2.3", + "version": "1.8.2.4", "icon": "vcbmonitor.png", "author": "pixel@qingwa", "level": 2, "history": { + "v1.8.2.4": "过滤下载临时文件和不可整理文件,避免目录监控误触发整理", "v1.8.2.3": "目录监控改用watchfiles,移除旧监控依赖", "v1.8.2.1": "修复日志输出&同步目录监控插件功能", "v1.8.2": "提高识别率", @@ -641,12 +643,13 @@ "name": "清理硬链接", "description": "监控目录内文件被删除时,同步删除监控目录内所有和它硬链接的文件", "labels": "文件整理", - "version": "2.3", + "version": "2.3.1", "icon": "Ombi_A.png", "author": "DzAvril", "level": 1, "v2": true, "history": { + "v2.3.1": "优化下载临时文件过滤,避免清理硬链接处理未完成文件", "v2.3": "目录监控改用watchfiles,移除旧监控依赖", "v2.2": "修复直接删除文件夹导致的插件崩溃的bug", "v2.1": "联动删除历史记录", @@ -661,12 +664,13 @@ "name": "实时硬链接", "description": "监控目录文件变化,实时硬链接。", "labels": "文件整理", - "version": "1.7", + "version": "1.7.1", "icon": "Linkace_C.png", "author": "jxxghp", "level": 1, "v2": true, "history": { + "v1.7.1": "过滤下载临时文件,避免实时硬链接处理未完成文件", "v1.7": "目录监控改用watchfiles,移除旧监控依赖", "v1.6": "增强API安全性" } diff --git a/plugins/dirmonitor/__init__.py b/plugins/dirmonitor/__init__.py index 330755b..2ac4823 100644 --- a/plugins/dirmonitor/__init__.py +++ b/plugins/dirmonitor/__init__.py @@ -31,6 +31,29 @@ from app.utils.system import SystemUtils lock = threading.Lock() +def _has_suffix_in(file_path: Path, extensions: List[str]) -> bool: + """ + 判断文件后缀是否命中给定扩展名列表。 + """ + if not file_path.suffix: + return False + return file_path.suffix.casefold() in {ext.casefold() for ext in extensions} + + +def _is_download_tmp_file(file_path: Path) -> bool: + """ + 判断文件是否为下载器尚未完成的临时文件。 + """ + return _has_suffix_in(file_path, settings.DOWNLOAD_TMPEXT) + + +def _is_monitor_media_file(file_path: Path) -> bool: + """ + 判断文件是否为目录监控可整理的媒体文件。 + """ + return not _is_download_tmp_file(file_path) and _has_suffix_in(file_path, settings.RMT_MEDIAEXT) + + class WatchfilesEvent: """ watchfiles 目录监控事件。 @@ -173,7 +196,10 @@ class FileMonitorHandler: path = Path(event_path) if not path.exists(): return - event = WatchfilesEvent(src_path=event_path, is_directory=path.is_dir()) + is_directory = path.is_dir() + if not is_directory and not _is_monitor_media_file(path): + return + event = WatchfilesEvent(src_path=event_path, is_directory=is_directory) text = "修改" if change_type == Change.modified else "创建" self.sync.event_handler(event=event, text=text, mon_path=self._watch_path, event_path=event_path) @@ -187,7 +213,7 @@ class DirMonitor(_PluginBase): # 插件图标 plugin_icon = "directory.png" # 插件版本 - plugin_version = "2.5" + plugin_version = "2.5.1" # 插件作者 plugin_author = "jxxghp" # 作者主页 @@ -424,6 +450,8 @@ class DirMonitor(_PluginBase): try: if not file_path.exists(): return + if not _is_monitor_media_file(file_path): + return # 全程加锁 with lock: transfer_history = self.transferhis.get_by_src(event_path) @@ -457,8 +485,7 @@ class DirMonitor(_PluginBase): return # 不是媒体文件不处理 - if file_path.suffix.casefold() not in map(str.casefold, settings.RMT_MEDIAEXT): - logger.debug(f"{event_path} 不是媒体文件") + if not _is_monitor_media_file(file_path): return # 判断是不是蓝光目录 diff --git a/plugins/linkmonitor/__init__.py b/plugins/linkmonitor/__init__.py index 7da29fa..e4f3530 100644 --- a/plugins/linkmonitor/__init__.py +++ b/plugins/linkmonitor/__init__.py @@ -22,6 +22,22 @@ from app.utils.system import SystemUtils lock = threading.Lock() +def _has_suffix_in(file_path: Path, extensions: List[str]) -> bool: + """ + 判断文件后缀是否命中给定扩展名列表。 + """ + if not file_path.suffix: + return False + return file_path.suffix.casefold() in {ext.casefold() for ext in extensions} + + +def _is_download_tmp_file(file_path: Path) -> bool: + """ + 判断文件是否为下载器尚未完成的临时文件。 + """ + return _has_suffix_in(file_path, settings.DOWNLOAD_TMPEXT) + + class WatchfilesEvent: """ watchfiles 目录监控事件。 @@ -164,7 +180,10 @@ class FileMonitorHandler: path = Path(event_path) if not path.exists(): return - event = WatchfilesEvent(src_path=event_path, is_directory=path.is_dir()) + is_directory = path.is_dir() + if not is_directory and _is_download_tmp_file(path): + return + event = WatchfilesEvent(src_path=event_path, is_directory=is_directory) text = "修改" if change_type == Change.modified else "创建" self.sync.event_handler(event=event, text=text, mon_path=self._watch_path, event_path=event_path) @@ -178,7 +197,7 @@ class LinkMonitor(_PluginBase): # 插件图标 plugin_icon = "Linkace_C.png" # 插件版本 - plugin_version = "1.7" + plugin_version = "1.7.1" # 插件作者 plugin_author = "jxxghp" # 作者主页 @@ -414,6 +433,8 @@ class LinkMonitor(_PluginBase): try: if not file_path.exists(): return + if _is_download_tmp_file(file_path): + return # 全程加锁 with lock: diff --git a/plugins/removelink/__init__.py b/plugins/removelink/__init__.py index ef86c49..fd4bef4 100644 --- a/plugins/removelink/__init__.py +++ b/plugins/removelink/__init__.py @@ -6,6 +6,7 @@ from pathlib import Path from typing import List, Tuple, Dict, Any, Optional from watchfiles import Change, watch +from app.core.config import settings from app.db.transferhistory_oper import TransferHistoryOper from app.log import logger from app.plugins import _PluginBase @@ -16,6 +17,22 @@ from app.schemas.types import EventType state_lock = threading.Lock() +def _has_suffix_in(file_path: Path, extensions: List[str]) -> bool: + """ + 判断文件后缀是否命中给定扩展名列表。 + """ + if not file_path.suffix: + return False + return file_path.suffix.casefold() in {ext.casefold() for ext in extensions} + + +def _is_download_tmp_file(file_path: Path) -> bool: + """ + 判断文件是否为下载器尚未完成的临时文件。 + """ + return _has_suffix_in(file_path, settings.DOWNLOAD_TMPEXT + [".mp"]) + + class WatchfilesEvent: """ watchfiles 目录监控事件。 @@ -204,7 +221,7 @@ class FileMonitorHandler: self.sync.dir_state_set.add(str(Path(event.src_path))) return file_path = Path(event.src_path) - if file_path.suffix in [".!qB", ".part", ".mp"]: + if _is_download_tmp_file(file_path): return logger.info(f"监测到新增文件:{file_path}") if self.sync.exclude_keywords: @@ -227,7 +244,7 @@ class FileMonitorHandler: if event.is_directory: return file_path = Path(event.dest_path) - if file_path.suffix in [".!qB", ".part", ".mp"]: + if _is_download_tmp_file(file_path): return logger.info(f"监测到新增文件:{file_path}") if self.sync.exclude_keywords: @@ -255,7 +272,7 @@ class FileMonitorHandler: EventType.DownloadFileDeleted, {"src": str(file_path)} ) return - if file_path.suffix in [".!qB", ".part", ".mp"]: + if _is_download_tmp_file(file_path): return logger.info(f"监测到删除文件:{file_path}") # 命中过滤关键字不处理 @@ -304,7 +321,7 @@ class RemoveLink(_PluginBase): # 插件图标 plugin_icon = "Ombi_A.png" # 插件版本 - plugin_version = "2.3" + plugin_version = "2.3.1" # 插件作者 plugin_author = "DzAvril" # 作者主页 diff --git a/plugins/vcbanimemonitor/__init__.py b/plugins/vcbanimemonitor/__init__.py index 2a68169..84f75bf 100644 --- a/plugins/vcbanimemonitor/__init__.py +++ b/plugins/vcbanimemonitor/__init__.py @@ -32,6 +32,36 @@ from app.utils.system import SystemUtils lock = threading.Lock() +def _has_suffix_in(file_path: Path, extensions: List[str]) -> bool: + """ + 判断文件后缀是否命中给定扩展名列表。 + """ + if not file_path.suffix: + return False + return file_path.suffix.casefold() in {ext.casefold() for ext in extensions} + + +def _is_download_tmp_file(file_path: Path) -> bool: + """ + 判断文件是否为下载器尚未完成的临时文件。 + """ + return _has_suffix_in(file_path, settings.DOWNLOAD_TMPEXT) + + +def _is_monitor_media_file(file_path: Path) -> bool: + """ + 判断文件是否为目录监控可整理的媒体文件。 + """ + return not _is_download_tmp_file(file_path) and _has_suffix_in(file_path, settings.RMT_MEDIAEXT) + + +def _is_torrent_file(file_path: Path) -> bool: + """ + 判断文件是否为已完成的种子文件。 + """ + return not _is_download_tmp_file(file_path) and file_path.suffix.casefold() == ".torrent" + + class WatchfilesEvent: """ watchfiles 目录监控事件。 @@ -174,7 +204,10 @@ class FileMonitorHandler: path = Path(event_path) if not path.exists(): return - event = WatchfilesEvent(src_path=event_path, is_directory=path.is_dir()) + is_directory = path.is_dir() + if not is_directory and not _is_monitor_media_file(path): + return + event = WatchfilesEvent(src_path=event_path, is_directory=is_directory) text = "修改" if change_type == Change.modified else "创建" self.sync.event_handler(event=event, text=text, mon_path=self._watch_path, event_path=event_path) @@ -205,7 +238,10 @@ class TorrentHandler: path = Path(event_path) if not path.exists(): return - event = WatchfilesEvent(src_path=event_path, is_directory=path.is_dir()) + is_directory = path.is_dir() + if is_directory or not _is_torrent_file(path): + return + event = WatchfilesEvent(src_path=event_path, is_directory=is_directory) text = "修改" if change_type == Change.modified else "创建" self.sync.torrent_event(event=event, text=text, mon_path=self._watch_path) @@ -218,7 +254,7 @@ class VCBAnimeMonitor(_PluginBase): # 插件图标 plugin_icon = "vcbmonitor.png" # 插件版本 - plugin_version = "1.8.2.3" + plugin_version = "1.8.2.4" # 插件作者 plugin_author = "pixel@qingwa" # 作者主页 @@ -483,6 +519,8 @@ class VCBAnimeMonitor(_PluginBase): try: if not file_path.exists(): return + if not _is_monitor_media_file(file_path): + return # 全程加锁 with lock: transfer_history = self.transferhis.get_by_src(event_path) @@ -516,8 +554,7 @@ class VCBAnimeMonitor(_PluginBase): return # 不是媒体文件不处理 - if file_path.suffix not in settings.RMT_MEDIAEXT: - logger.debug(f"{event_path} 不是媒体文件") + if not _is_monitor_media_file(file_path): return # 判断是不是蓝光目录 @@ -778,12 +815,10 @@ class VCBAnimeMonitor(_PluginBase): :param mon_path: 种子目录 """ evc_path = Path(event.src_path) - if not event.is_directory and (evc_path.suffix == ".torrent" or str(evc_path).split('.')[1] == "torrent"): + if not event.is_directory and _is_torrent_file(evc_path): # 文件发生变化 logger.debug("文件%s:%s" % (text, mon_path)) self.__handle_torrent(torrent_path=self._torrents_path) - else: - logger.debug("不是种子文件:%s" % mon_path) def __handle_torrent(self, torrent_path: str): torrent_path = Path(torrent_path)