fix: filter watchfiles temporary files

This commit is contained in:
jxxghp
2026-05-25 06:14:02 +08:00
parent 50b4d2558c
commit 21ebda74b1
5 changed files with 126 additions and 22 deletions

View File

@@ -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安全性"
}

View File

@@ -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
# 判断是不是蓝光目录

View File

@@ -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:

View File

@@ -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"
# 作者主页

View File

@@ -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)