diff --git a/package.json b/package.json index af2326c..c095132 100644 --- a/package.json +++ b/package.json @@ -387,11 +387,12 @@ "RemoveLink": { "name": "清理硬链接", "description": "监控目录内文件被删除时,同步删除监控目录内所有和它硬链接的文件", - "version": "1.7", + "version": "1.8", "icon": "Ombi_A.png", "author": "DzAvril", "level": 1, "history": { + "v1.8": "增加清理空目录功能(beta)", "v1.7": "修复因未监测重命名事件导致的清理硬链接失败的问题", "v1.6": "提升插件性能" } diff --git a/plugins/removelink/__init__.py b/plugins/removelink/__init__.py index 604cdd4..c55c614 100644 --- a/plugins/removelink/__init__.py +++ b/plugins/removelink/__init__.py @@ -40,7 +40,7 @@ class FileMonitorHandler(FileSystemEventHandler): # 新增文件记录 with state_lock: self.sync.state_set[str(file_path)] = file_path.stat().st_ino - + def on_moved(self, event): if event.is_directory: return @@ -106,7 +106,7 @@ class RemoveLink(_PluginBase): # 插件图标 plugin_icon = "Ombi_A.png" # 插件版本 - plugin_version = "1.7" + plugin_version = "1.8" # 插件作者 plugin_author = "DzAvril" # 作者主页 @@ -124,6 +124,7 @@ class RemoveLink(_PluginBase): exclude_keywords = "" _enabled = False _notify = False + _delete_empty = False _observer = [] # 监控目录的文件列表 state_set: Dict[str, int] = {} @@ -136,6 +137,7 @@ class RemoveLink(_PluginBase): self.monitor_dirs = config.get("monitor_dirs") self.exclude_dirs = config.get("exclude_dirs") or "" self.exclude_keywords = config.get("exclude_keywords") or "" + self._delete_empty = config.get("delete_empty") # 停止现有任务 self.stop_service() @@ -224,6 +226,19 @@ class RemoveLink(_PluginBase): } ], }, + { + "component": "VCol", + "props": {"cols": 12, "md": 4}, + "content": [ + { + "component": "VSwitch", + "props": { + "model": "delete_empty", + "label": "清理空目录(beta)", + }, + } + ], + }, ], }, { @@ -289,32 +304,47 @@ class RemoveLink(_PluginBase): ], }, { - 'component': 'VRow', - 'content': [ + "component": "VRow", + "content": [ { - 'component': 'VCol', - 'props': { - 'cols': 12, + "component": "VCol", + "props": { + "cols": 12, }, - 'content': [ + "content": [ { - 'component': 'VAlert', - 'props': { - 'type': 'info', - 'variant': 'tonal', - 'text': '监控目录如有多个需换行,源目录和硬链接目录都需要添加到监控目录中;如需实现删除硬链接时不删除源文件,可把源文件目录配置到不删除目录中。' - } + "component": "VAlert", + "props": { + "type": "info", + "variant": "tonal", + "text": "监控目录如有多个需换行,源目录和硬链接目录都需要添加到监控目录中;如需实现删除硬链接时不删除源文件,可把源文件目录配置到不删除目录中。", + }, } - ] - } - ] - } + ], + }, + { + "component": "VCol", + "props": { + "cols": 12, + }, + "content": [ + { + "component": "VAlert", + "props": { + "type": "info", + "variant": "tonal", + "text": "清理空目录为测试功能,请谨慎开启。", + }, + } + ], + }, + ], + }, ], } ], { "enabled": False, "notify": False, - "onlyonce": False, "monitor_dirs": "", "exclude_keywords": "", } @@ -344,13 +374,42 @@ class RemoveLink(_PluginBase): if exclude_dir and exclude_dir in str(file_path): return True return False - + def delete_empty_folders(self, path): + """ + 从指定路径开始,逐级向上层目录检测并删除空目录,直到遇到非空目录或到达指定监控目录为止 + """ + if not self._delete_empty: + return + while True: + parent_path = os.path.dirname(path) + # parent_path如已被删除则退出检查 + if not os.path.exists(parent_path): + break + # 如果当前路径等于监控目录之一,停止向上检查 + if parent_path in self.monitor_dirs.split("\n"): + break + # 检查当前目录是否为空且不在排除列表内 + if not os.listdir(parent_path) and not self.__is_excluded(parent_path): + os.rmdir(parent_path) + logger.info(f"清理空目录:{parent_path}") + if self._notify: + self.post_message( + mtype=NotificationType.SiteMessage, + title=f"【清理硬链接】", + text=f"清理空文件夹:[{parent_path}]\n", + ) + else: + break + # 更新路径为父目录,准备下一轮检查 + path = parent_path def handle_deleted(self, file_path: Path): """ 处理删除事件 """ # 删除的文件对应的监控信息 with state_lock: + # 清理空目录 + self.delete_empty_folders(file_path) # 删除的文件inode deleted_inode = self.state_set.get(str(file_path)) if not deleted_inode: @@ -369,12 +428,16 @@ class RemoveLink(_PluginBase): # 删除硬链接文件 logger.info(f"删除硬链接文件:{path}, inode: {inode}") file.unlink() + # 清理空目录 + self.delete_empty_folders(file) if self._notify: self.post_message( mtype=NotificationType.SiteMessage, title=f"【清理硬链接】", text=f"监控到删除源文件:[{file_path}]\n" - f"同步删除硬链接文件:[{path}]", + f"同步删除硬链接文件:[{path}]", ) except Exception as e: - logger.error("删除硬链接文件发生错误:%s - %s" % (str(e), traceback.format_exc())) + logger.error( + "删除硬链接文件发生错误:%s - %s" % (str(e), traceback.format_exc()) + )