新增高级过滤功能

支持使用保留词和过滤词实现更为复杂的过滤逻辑
This commit is contained in:
x1ao4 2025-08-24 00:02:51 +08:00
parent 3249c18d17
commit 9e32ef7997
4 changed files with 247 additions and 165 deletions

View File

@ -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[]`)自动切换为 `剧集命名` 模式,该模式将从原始文件名中提取剧集编号,然后把提取的编号代入对应文件名的 `[]` 中,实现自动按剧集编号命名。
- **自动切换命名模式**:默认的命名模式依然为 `正则命名` 模式,现在会通过用户输入的 `匹配表达式` 自动实时判断和切换对应的模式。

View File

@ -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)mkvnfo" # 只有过滤词
- "期|加更,企划,超前,(1)mkvnfo" # 保留词|过滤词
- "2160P|加更,企划,超前,(1)mkvnfo" # 多个保留词(或关系)|过滤词
- "期|2160P|加更,企划,超前,(1)mkvnfo" # 多个保留词(并关系)|过滤词
- "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 = []

View File

@ -1044,11 +1044,11 @@
</div>
</div>
<div class="form-group row" title="名称包含过滤词汇的项目不会被转存,多个词用逗号分隔,支持通过文件名和扩展名过滤文件和文件夹">
<div class="form-group row" title="名称包含过滤词汇的项目不会被转存,多个词用逗号分隔,支持通过文件名和扩展名过滤文件和文件夹,支持使用保留词|过滤词的格式实现高级过滤查阅Wiki了解详情">
<label class="col-sm-2 col-form-label">过滤规则</label>
<div class="col-sm-10">
<div class="input-group">
<input type="text" name="filterwords[]" class="form-control" v-model="task.filterwords" placeholder="可选输入过滤词汇用逗号分隔纯享txt超前企划名称包含过滤词汇的项目不会被转存">
<input type="text" name="filterwords[]" class="form-control" v-model="task.filterwords" placeholder="可选输入过滤词汇用逗号分隔纯享txt超前企划名称包含过滤词汇的项目不会被转存,支持使用保留词|过滤词的格式实现高级过滤">
</div>
</div>
</div>
@ -1359,7 +1359,7 @@
@input="detectFileManagerNamingMode"
:title="fileManager.use_sequence_naming ? '输入带{}占位符的重命名格式,如:剧名 - S01E{}、剧名.S03E{}等,{}将被替换为集序号(按文件名和修改日期智能排序)' : (fileManager.use_episode_naming ? '输入带[]占位符的重命名格式,如:剧名 - S01E[]、剧名.S03E[]等,[]将被替换为从文件名中提取的集编号' : '只重命名匹配到文件名的文件,留空不会进行重命名')">
<input v-if="!fileManager.use_sequence_naming && !fileManager.use_episode_naming" type="text" class="form-control file-manager-input" v-model="fileManager.replace" placeholder="替换表达式" title="替换表达式">
<input type="text" class="form-control file-manager-input" v-model="fileManager.filterwords" placeholder="可选输入过滤词汇用逗号分隔纯享txt超前企划" title="名称包含过滤词汇的项目不会被重命名,多个词用逗号分隔,支持通过文件名和扩展名过滤文件和文件夹">
<input type="text" class="form-control file-manager-input" v-model="fileManager.filterwords" placeholder="可选输入过滤词汇用逗号分隔纯享txt超前企划" title="名称包含过滤词汇的项目不会被重命名,多个词用逗号分隔,支持通过文件名和扩展名过滤文件和文件夹,支持使用保留词|过滤词的格式实现高级过滤查阅Wiki了解详情">
<div class="input-group-append">
<div class="input-group-text" title="勾选后,重命名和过滤规则也将应用于文件夹">
<input type="checkbox" v-model="fileManager.include_folders">&nbsp;含文件夹
@ -1390,7 +1390,7 @@
<div class="input-group-prepend">
<span class="input-group-text">过滤规则</span>
</div>
<input type="text" class="form-control" v-model="fileManager.filterwords" placeholder="可选输入过滤词汇用逗号分隔纯享txt超前企划" title="名称包含过滤词汇的项目不会被重命名,多个词用逗号分隔,支持通过文件名和扩展名过滤文件和文件夹">
<input type="text" class="form-control" v-model="fileManager.filterwords" placeholder="可选输入过滤词汇用逗号分隔纯享txt超前企划" title="名称包含过滤词汇的项目不会被重命名,多个词用逗号分隔,支持通过文件名和扩展名过滤文件和文件夹,支持使用保留词|过滤词的格式实现高级过滤查阅Wiki了解详情">
<span class="input-group-text file-folder-rounded">
<input type="checkbox" v-model="fileManager.include_folders">&nbsp;含文件夹
</span>
@ -1971,11 +1971,11 @@
</div>
</div>
<div class="form-group row" title="名称包含过滤词汇的项目不会被转存,多个词用逗号分隔,支持通过文件名和扩展名过滤文件和文件夹">
<div class="form-group row" title="名称包含过滤词汇的项目不会被转存,多个词用逗号分隔,支持通过文件名和扩展名过滤文件和文件夹,支持使用保留词|过滤词的格式实现高级过滤查阅Wiki了解详情">
<label class="col-sm-2 col-form-label">过滤规则</label>
<div class="col-sm-10">
<div class="input-group">
<input type="text" name="filterwords[]" class="form-control" v-model="createTask.taskData.filterwords" placeholder="可选,输入过滤词汇,用逗号分隔,纯享txt超前企划名称包含过滤词汇的项目不会被转存">
<input type="text" name="filterwords[]" class="form-control" v-model="createTask.taskData.filterwords" placeholder="可选,输入过滤词汇,用逗号分隔,名称包含过滤词汇的项目不会被转存,支持使用保留词|过滤词的格式实现高级过滤">
</div>
</div>
</div>

View File

@ -36,6 +36,106 @@ except ImportError:
def close(self):
pass
def advanced_filter_files(file_list, filterwords):
"""
高级过滤函数支持保留词和过滤词
Args:
file_list: 文件列表
filterwords: 过滤规则字符串支持以下格式
- "加更,企划,超前,(1)mkvnfo" # 只有过滤词
- "期|加更,企划,超前,(1)mkvnfo" # 保留词|过滤词
- "2160P|加更,企划,超前,(1)mkvnfo" # 多个保留词(或关系)|过滤词
- "期|2160P|加更,企划,超前,(1)mkvnfo" # 多个保留词(并关系)|过滤词
- "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 = []