From 9e32ef79975e99843813942cb2644c95a7cae6f6 Mon Sep 17 00:00:00 2001 From: x1ao4 Date: Sun, 24 Aug 2025 00:02:51 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E9=AB=98=E7=BA=A7=E8=BF=87?= =?UTF-8?q?=E6=BB=A4=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 支持使用保留词和过滤词实现更为复杂的过滤逻辑 --- README.md | 2 +- app/run.py | 158 ++++++++++++++++++++------ app/templates/index.html | 12 +- quark_auto_save.py | 240 +++++++++++++++++++-------------------- 4 files changed, 247 insertions(+), 165 deletions(-) diff --git a/README.md b/README.md index 8c8e547..ee5b7e9 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # 夸克自动转存 本项目是在 [Cp0204/quark-auto-save:0.5.3.1](https://github.com/Cp0204/quark-auto-save) 的基础上修改而来的(感谢 [Cp0204](https://github.com/Cp0204)),我对整个 WebUI 进行了重塑,增加了更多实用功能,新增功能的代码都是通过 AI 完成的,不保证功能的稳定性。主要的新增功能如下([详见](https://github.com/x1ao4/quark-auto-save-x/wiki)): -- **过滤项目**:通过在 `过滤规则` 里设置过滤词来过滤不需要转存的文件或文件夹。 +- **过滤项目**:通过在 `过滤规则` 里设置过滤词来过滤不需要转存的文件或文件夹。支持高级过滤功能,使用保留词和过滤词可实现复杂的过滤逻辑。 - **顺序命名**:通过使用包含 `{}` 的表达式(如 `乘风2025 - S06E{}`)自动切换为 `顺序命名` 模式,该模式将通过文件名与上传时间等信息对文件进行智能排序,然后按顺序对每个文件的 `{}` 赋予序号,实现顺序命名。 - **剧集命名**:通过使用包含 `[]` 的表达式(如 `黑镜 - S06E[]`)自动切换为 `剧集命名` 模式,该模式将从原始文件名中提取剧集编号,然后把提取的编号代入对应文件名的 `[]` 中,实现自动按剧集编号命名。 - **自动切换命名模式**:默认的命名模式依然为 `正则命名` 模式,现在会通过用户输入的 `匹配表达式` 自动实时判断和切换对应的模式。 diff --git a/app/run.py b/app/run.py index 2c28a65..abb33d5 100644 --- a/app/run.py +++ b/app/run.py @@ -43,6 +43,106 @@ from quark_auto_save import extract_episode_number, sort_file_by_name, chinese_t # 导入豆瓣服务 from sdk.douban_service import douban_service +def advanced_filter_files(file_list, filterwords): + """ + 高级过滤函数,支持保留词和过滤词 + + Args: + file_list: 文件列表 + filterwords: 过滤规则字符串,支持以下格式: + - "加更,企划,超前,(1),mkv,nfo" # 只有过滤词 + - "期|加更,企划,超前,(1),mkv,nfo" # 保留词|过滤词 + - "期,2160P|加更,企划,超前,(1),mkv,nfo" # 多个保留词(或关系)|过滤词 + - "期|2160P|加更,企划,超前,(1),mkv,nfo" # 多个保留词(并关系)|过滤词 + - "期,2160P|" # 只有保留词,无过滤词 + + Returns: + 过滤后的文件列表 + """ + if not filterwords or not filterwords.strip(): + return file_list + + # 检查是否包含分隔符 | + if '|' not in filterwords: + # 只有过滤词的情况 + filterwords = filterwords.replace(",", ",") + filterwords_list = [word.strip().lower() for word in filterwords.split(',') if word.strip()] + + filtered_files = [] + for file in file_list: + file_name = file['file_name'].lower() + file_ext = os.path.splitext(file_name)[1].lower().lstrip('.') + + # 检查过滤词是否存在于文件名中,或者过滤词等于扩展名 + if not any(word in file_name for word in filterwords_list) and not any(word == file_ext for word in filterwords_list): + filtered_files.append(file) + + return filtered_files + + # 包含分隔符的情况,需要解析保留词和过滤词 + parts = filterwords.split('|') + if len(parts) < 2: + # 格式错误,返回原列表 + return file_list + + # 最后一个|后面的是过滤词 + filter_part = parts[-1].strip() + # 前面的都是保留词 + keep_parts = [part.strip() for part in parts[:-1] if part.strip()] + + # 解析过滤词 + filterwords_list = [] + if filter_part: + filter_part = filter_part.replace(",", ",") + filterwords_list = [word.strip().lower() for word in filter_part.split(',') if word.strip()] + + # 解析保留词:每个|分隔的部分都是一个独立的筛选条件 + # 这些条件需要按顺序依次应用,形成链式筛选 + keep_conditions = [] + for part in keep_parts: + if part.strip(): + if ',' in part or ',' in part: + # 包含逗号,表示或关系 + part = part.replace(",", ",") + or_words = [word.strip().lower() for word in part.split(',') if word.strip()] + keep_conditions.append(("or", or_words)) + else: + # 不包含逗号,表示单个词 + keep_conditions.append(("single", [part.strip().lower()])) + + # 第一步:应用保留词筛选(链式筛选) + if keep_conditions: + for condition_type, words in keep_conditions: + filtered_by_keep = [] + for file in file_list: + file_name = file['file_name'].lower() + + if condition_type == "or": + # 或关系:包含任意一个词即可 + if any(word in file_name for word in words): + filtered_by_keep.append(file) + elif condition_type == "single": + # 单个词:必须包含 + if words[0] in file_name: + filtered_by_keep.append(file) + + file_list = filtered_by_keep + + # 第二步:应用过滤词过滤 + if filterwords_list: + filtered_files = [] + for file in file_list: + file_name = file['file_name'].lower() + file_ext = os.path.splitext(file_name)[1].lower().lstrip('.') + + # 检查过滤词是否存在于文件名中,或者过滤词等于扩展名 + if not any(word in file_name for word in filterwords_list) and not any(word == file_ext for word in filterwords_list): + filtered_files.append(file) + + return filtered_files + + return file_list + def process_season_episode_info(filename, task_name=None): """ @@ -924,15 +1024,14 @@ def get_share_detail(): # 根据提取的排序值进行排序 sorted_files = sorted(files_to_process, key=extract_sort_value) - # 应用过滤词过滤 + # 应用高级过滤词过滤 filterwords = regex.get("filterwords", "") if filterwords: - # 同时支持中英文逗号分隔 - filterwords = filterwords.replace(",", ",") - filterwords_list = [word.strip() for word in filterwords.split(',')] + # 使用高级过滤函数 + filtered_files = advanced_filter_files(sorted_files, filterwords) + # 标记被过滤的文件 for item in sorted_files: - # 被过滤的文件不会有file_name_re,与不匹配正则的文件显示一致 - if any(word in item['file_name'] for word in filterwords_list): + if item not in filtered_files: item["filtered"] = True # 为每个文件分配序号 @@ -982,15 +1081,14 @@ def get_share_detail(): ] episode_patterns.extend(chinese_patterns) - # 应用过滤词过滤 + # 应用高级过滤词过滤 filterwords = regex.get("filterwords", "") if filterwords: - # 同时支持中英文逗号分隔 - filterwords = filterwords.replace(",", ",") - filterwords_list = [word.strip() for word in filterwords.split(',')] + # 使用高级过滤函数 + filtered_files = advanced_filter_files(share_detail["list"], filterwords) + # 标记被过滤的文件 for item in share_detail["list"]: - # 被过滤的文件显示一个 × - if any(word in item['file_name'] for word in filterwords_list): + if item not in filtered_files: item["filtered"] = True item["file_name_re"] = "×" @@ -1019,15 +1117,14 @@ def get_share_detail(): regex.get("magic_regex", {}), ) - # 应用过滤词过滤 + # 应用高级过滤词过滤 filterwords = regex.get("filterwords", "") if filterwords: - # 同时支持中英文逗号分隔 - filterwords = filterwords.replace(",", ",") - filterwords_list = [word.strip() for word in filterwords.split(',')] + # 使用高级过滤函数 + filtered_files = advanced_filter_files(share_detail["list"], filterwords) + # 标记被过滤的文件 for item in share_detail["list"]: - # 被过滤的文件不会有file_name_re,与不匹配正则的文件显示一致 - if any(word in item['file_name'] for word in filterwords_list): + if item not in filtered_files: item["filtered"] = True # 应用正则命名 @@ -2066,25 +2163,12 @@ def preview_rename(): if isinstance(files, dict) and files.get("error"): return jsonify({"success": False, "message": f"获取文件列表失败: {files.get('error', '未知错误')}"}) - # 过滤要排除的文件 - # 替换中文逗号为英文逗号 - filterwords = filterwords.replace(",", ",") - filter_list = [keyword.strip() for keyword in filterwords.split(",") if keyword.strip()] - filtered_files = [] - for file in files: - # 如果不包含文件夹且当前项是文件夹,跳过 - if not include_folders and file["dir"]: - continue - - # 检查是否包含过滤关键词 - should_filter = False - for keyword in filter_list: - if keyword and keyword in file["file_name"]: - should_filter = True - break - - if not should_filter: - filtered_files.append(file) + # 使用高级过滤函数过滤文件 + filtered_files = advanced_filter_files(files, filterwords) + + # 如果不包含文件夹,进一步过滤掉文件夹 + if not include_folders: + filtered_files = [file for file in filtered_files if not file["dir"]] # 按不同命名模式处理 preview_results = [] diff --git a/app/templates/index.html b/app/templates/index.html index 27c0d02..317a9b1 100644 --- a/app/templates/index.html +++ b/app/templates/index.html @@ -1044,11 +1044,11 @@ -
+
- +
@@ -1359,7 +1359,7 @@ @input="detectFileManagerNamingMode" :title="fileManager.use_sequence_naming ? '输入带{}占位符的重命名格式,如:剧名 - S01E{}、剧名.S03E{}等,{}将被替换为集序号(按文件名和修改日期智能排序)' : (fileManager.use_episode_naming ? '输入带[]占位符的重命名格式,如:剧名 - S01E[]、剧名.S03E[]等,[]将被替换为从文件名中提取的集编号' : '只重命名匹配到文件名的文件,留空不会进行重命名')"> - +
 含文件夹 @@ -1390,7 +1390,7 @@
过滤规则
- +  含文件夹 @@ -1971,11 +1971,11 @@
-
+
- +
diff --git a/quark_auto_save.py b/quark_auto_save.py index 109193f..0f94ed4 100644 --- a/quark_auto_save.py +++ b/quark_auto_save.py @@ -36,6 +36,106 @@ except ImportError: def close(self): pass +def advanced_filter_files(file_list, filterwords): + """ + 高级过滤函数,支持保留词和过滤词 + + Args: + file_list: 文件列表 + filterwords: 过滤规则字符串,支持以下格式: + - "加更,企划,超前,(1),mkv,nfo" # 只有过滤词 + - "期|加更,企划,超前,(1),mkv,nfo" # 保留词|过滤词 + - "期,2160P|加更,企划,超前,(1),mkv,nfo" # 多个保留词(或关系)|过滤词 + - "期|2160P|加更,企划,超前,(1),mkv,nfo" # 多个保留词(并关系)|过滤词 + - "期,2160P|" # 只有保留词,无过滤词 + + Returns: + 过滤后的文件列表 + """ + if not filterwords or not filterwords.strip(): + return file_list + + # 检查是否包含分隔符 | + if '|' not in filterwords: + # 只有过滤词的情况 + filterwords = filterwords.replace(",", ",") + filterwords_list = [word.strip().lower() for word in filterwords.split(',') if word.strip()] + + filtered_files = [] + for file in file_list: + file_name = file['file_name'].lower() + file_ext = os.path.splitext(file_name)[1].lower().lstrip('.') + + # 检查过滤词是否存在于文件名中,或者过滤词等于扩展名 + if not any(word in file_name for word in filterwords_list) and not any(word == file_ext for word in filterwords_list): + filtered_files.append(file) + + return filtered_files + + # 包含分隔符的情况,需要解析保留词和过滤词 + parts = filterwords.split('|') + if len(parts) < 2: + # 格式错误,返回原列表 + return file_list + + # 最后一个|后面的是过滤词 + filter_part = parts[-1].strip() + # 前面的都是保留词 + keep_parts = [part.strip() for part in parts[:-1] if part.strip()] + + # 解析过滤词 + filterwords_list = [] + if filter_part: + filter_part = filter_part.replace(",", ",") + filterwords_list = [word.strip().lower() for word in filter_part.split(',') if word.strip()] + + # 解析保留词:每个|分隔的部分都是一个独立的筛选条件 + # 这些条件需要按顺序依次应用,形成链式筛选 + keep_conditions = [] + for part in keep_parts: + if part.strip(): + if ',' in part or ',' in part: + # 包含逗号,表示或关系 + part = part.replace(",", ",") + or_words = [word.strip().lower() for word in part.split(',') if word.strip()] + keep_conditions.append(("or", or_words)) + else: + # 不包含逗号,表示单个词 + keep_conditions.append(("single", [part.strip().lower()])) + + # 第一步:应用保留词筛选(链式筛选) + if keep_conditions: + for condition_type, words in keep_conditions: + filtered_by_keep = [] + for file in file_list: + file_name = file['file_name'].lower() + + if condition_type == "or": + # 或关系:包含任意一个词即可 + if any(word in file_name for word in words): + filtered_by_keep.append(file) + elif condition_type == "single": + # 单个词:必须包含 + if words[0] in file_name: + filtered_by_keep.append(file) + + file_list = filtered_by_keep + + # 第二步:应用过滤词过滤 + if filterwords_list: + filtered_files = [] + for file in file_list: + file_name = file['file_name'].lower() + file_ext = os.path.splitext(file_name)[1].lower().lstrip('.') + + # 检查过滤词是否存在于文件名中,或者过滤词等于扩展名 + if not any(word in file_name for word in filterwords_list) and not any(word == file_ext for word in filterwords_list): + filtered_files.append(file) + + return filtered_files + + return file_list + # 全局的文件排序函数 def sort_file_by_name(file): """ @@ -1978,22 +2078,8 @@ class Quark: # 记录过滤前的文件总数(包括文件夹) original_total_count = len(share_file_list) - # 同时支持中英文逗号分隔 - filterwords = task["filterwords"].replace(",", ",") - filterwords_list = [word.strip().lower() for word in filterwords.split(',')] - - # 改进过滤逻辑,同时检查文件名和扩展名 - filtered_files = [] - for file in share_file_list: - file_name = file['file_name'].lower() - # 提取文件扩展名(不带点) - file_ext = os.path.splitext(file_name)[1].lower().lstrip('.') - - # 检查过滤词是否存在于文件名中,或者过滤词等于扩展名 - if not any(word in file_name for word in filterwords_list) and not any(word == file_ext for word in filterwords_list): - filtered_files.append(file) - - share_file_list = filtered_files + # 使用高级过滤函数处理保留词和过滤词 + share_file_list = advanced_filter_files(share_file_list, task["filterwords"]) # 打印过滤信息(格式保持不变) # 计算剩余文件数 @@ -3008,31 +3094,8 @@ class Quark: # 记录过滤前的文件总数 original_total_count = len(dir_file_list) - # 同时支持中英文逗号分隔 - filterwords = task["filterwords"].replace(",", ",") - filterwords_list = [word.strip().lower() for word in filterwords.split(',')] - - # 过滤掉包含过滤词的文件 - filtered_files = [] - for file in dir_file_list: - if file.get("dir", False): - # 文件夹也需要检查过滤词 - file_name = file['file_name'].lower() - file_ext = os.path.splitext(file_name)[1].lower().lstrip('.') - - # 检查过滤词是否存在于文件名中,或者过滤词等于扩展名 - if not any(word in file_name for word in filterwords_list) and not any(word == file_ext for word in filterwords_list): - filtered_files.append(file) - else: - # 文件检查过滤词 - file_name = file['file_name'].lower() - file_ext = os.path.splitext(file_name)[1].lower().lstrip('.') - - # 检查过滤词是否存在于文件名中,或者过滤词等于扩展名 - if not any(word in file_name for word in filterwords_list) and not any(word == file_ext for word in filterwords_list): - filtered_files.append(file) - - dir_file_list = filtered_files + # 使用高级过滤函数处理保留词和过滤词 + dir_file_list = advanced_filter_files(dir_file_list, task["filterwords"]) dir_file_name_list = [item["file_name"] for item in dir_file_list] # 找出当前最大序号 @@ -3435,23 +3498,14 @@ class Quark: # 检查过滤词 should_filter = False if task.get("filterwords"): - # 同时支持中英文逗号分隔 - filterwords = task["filterwords"].replace(",", ",") - filterwords_list = [word.strip().lower() for word in filterwords.split(',')] - - # 检查原始文件名 - original_name_lower = share_file["file_name"].lower() - if any(word in original_name_lower for word in filterwords_list): - should_filter = True - - # 检查目标文件名 - save_name_lower = save_name.lower() - if any(word in save_name_lower for word in filterwords_list): - should_filter = True - - # 检查文件扩展名 - file_ext_lower = file_ext.lower().lstrip('.') - if any(word == file_ext_lower for word in filterwords_list): + # 使用高级过滤函数检查文件名 + temp_file_list = [{"file_name": share_file["file_name"]}] + if advanced_filter_files(temp_file_list, task["filterwords"]): + # 检查目标文件名 + temp_save_list = [{"file_name": save_name}] + if not advanced_filter_files(temp_save_list, task["filterwords"]): + should_filter = True + else: should_filter = True # 只处理不需要过滤的文件 @@ -3465,19 +3519,9 @@ class Quark: # 检查过滤词 should_filter = False if task.get("filterwords"): - # 同时支持中英文逗号分隔 - filterwords = task["filterwords"].replace(",", ",") - filterwords_list = [word.strip().lower() for word in filterwords.split(',')] - - # 检查原始文件名 - original_name_lower = share_file["file_name"].lower() - if any(word in original_name_lower for word in filterwords_list): - should_filter = True - - # 检查文件扩展名 - file_ext = os.path.splitext(share_file["file_name"])[1].lower() - file_ext_lower = file_ext.lstrip('.') - if any(word == file_ext_lower for word in filterwords_list): + # 使用高级过滤函数检查文件名 + temp_file_list = [{"file_name": share_file["file_name"]}] + if not advanced_filter_files(temp_file_list, task["filterwords"]): should_filter = True # 只处理不需要过滤的文件 @@ -3644,31 +3688,8 @@ class Quark: # 记录过滤前的文件总数 original_total_count = len(dir_file_list) - # 同时支持中英文逗号分隔 - filterwords = task["filterwords"].replace(",", ",") - filterwords_list = [word.strip().lower() for word in filterwords.split(',')] - - # 过滤掉包含过滤词的文件 - filtered_files = [] - for file in dir_file_list: - if file.get("dir", False): - # 文件夹也需要检查过滤词 - file_name = file['file_name'].lower() - file_ext = os.path.splitext(file_name)[1].lower().lstrip('.') - - # 检查过滤词是否存在于文件名中,或者过滤词等于扩展名 - if not any(word in file_name for word in filterwords_list) and not any(word == file_ext for word in filterwords_list): - filtered_files.append(file) - else: - # 文件检查过滤词 - file_name = file['file_name'].lower() - file_ext = os.path.splitext(file_name)[1].lower().lstrip('.') - - # 检查过滤词是否存在于文件名中,或者过滤词等于扩展名 - if not any(word in file_name for word in filterwords_list) and not any(word == file_ext for word in filterwords_list): - filtered_files.append(file) - - dir_file_list = filtered_files + # 使用高级过滤函数处理保留词和过滤词 + dir_file_list = advanced_filter_files(dir_file_list, task["filterwords"]) # 使用一个列表收集所有需要重命名的操作 rename_operations = [] @@ -3820,31 +3841,8 @@ class Quark: # 记录过滤前的文件总数 original_total_count = len(dir_file_list) - # 同时支持中英文逗号分隔 - filterwords = task["filterwords"].replace(",", ",") - filterwords_list = [word.strip().lower() for word in filterwords.split(',')] - - # 过滤掉包含过滤词的文件 - filtered_files = [] - for file in dir_file_list: - if file.get("dir", False): - # 文件夹也需要检查过滤词 - file_name = file['file_name'].lower() - file_ext = os.path.splitext(file_name)[1].lower().lstrip('.') - - # 检查过滤词是否存在于文件名中,或者过滤词等于扩展名 - if not any(word in file_name for word in filterwords_list) and not any(word == file_ext for word in filterwords_list): - filtered_files.append(file) - else: - # 文件检查过滤词 - file_name = file['file_name'].lower() - file_ext = os.path.splitext(file_name)[1].lower().lstrip('.') - - # 检查过滤词是否存在于文件名中,或者过滤词等于扩展名 - if not any(word in file_name for word in filterwords_list) and not any(word == file_ext for word in filterwords_list): - filtered_files.append(file) - - dir_file_list = filtered_files + # 使用高级过滤函数处理保留词和过滤词 + dir_file_list = advanced_filter_files(dir_file_list, task["filterwords"]) # 使用一个列表收集所有需要重命名的操作 rename_operations = []