mirror of
https://github.com/Cp0204/quark-auto-save.git
synced 2026-01-16 01:10:44 +08:00
优化剧集命名表达式的应用效果,优化日志信息的打印逻辑
This commit is contained in:
parent
50416ca514
commit
c735ade0d0
56
app/run.py
56
app/run.py
@ -329,7 +329,19 @@ def get_share_detail():
|
|||||||
|
|
||||||
file_name = file["file_name"]
|
file_name = file["file_name"]
|
||||||
|
|
||||||
# 1. 首先尝试提取期数(第X期)
|
# 1. 首先尝试提取SxxExx格式(如S01E01)
|
||||||
|
match_s_e = re.search(r'[Ss](\d+)[Ee](\d+)', file_name)
|
||||||
|
if match_s_e:
|
||||||
|
season = int(match_s_e.group(1))
|
||||||
|
episode = int(match_s_e.group(2))
|
||||||
|
return season * 1000 + episode
|
||||||
|
|
||||||
|
# 2. 尝试提取E01/EP01格式
|
||||||
|
match_e = re.search(r'[Ee][Pp]?(\d+)', file_name)
|
||||||
|
if match_e:
|
||||||
|
return int(match_e.group(1))
|
||||||
|
|
||||||
|
# 3. 首先尝试提取期数(第X期)
|
||||||
period_match = re.search(r'第(\d+)期[上中下]', file_name)
|
period_match = re.search(r'第(\d+)期[上中下]', file_name)
|
||||||
if period_match:
|
if period_match:
|
||||||
period_num = int(period_match.group(1))
|
period_num = int(period_match.group(1))
|
||||||
@ -342,7 +354,7 @@ def get_share_detail():
|
|||||||
return period_num * 3
|
return period_num * 3
|
||||||
return period_num * 3
|
return period_num * 3
|
||||||
|
|
||||||
# 2. 尝试提取日期格式(YYYY-MM-DD)
|
# 4. 尝试提取日期格式(YYYY-MM-DD)
|
||||||
date_match = re.search(r'(\d{4})-(\d{2})-(\d{2})', file_name)
|
date_match = re.search(r'(\d{4})-(\d{2})-(\d{2})', file_name)
|
||||||
if date_match:
|
if date_match:
|
||||||
year = int(date_match.group(1))
|
year = int(date_match.group(1))
|
||||||
@ -358,19 +370,19 @@ def get_share_detail():
|
|||||||
return base_value * 10 + 3
|
return base_value * 10 + 3
|
||||||
return base_value * 10
|
return base_value * 10
|
||||||
|
|
||||||
# 3. 尝试提取任何数字
|
# 5. 尝试提取任何数字
|
||||||
number_match = re.search(r'(\d+)', file_name)
|
number_match = re.search(r'(\d+)', file_name)
|
||||||
if number_match:
|
if number_match:
|
||||||
return int(number_match.group(1))
|
return int(number_match.group(1))
|
||||||
|
|
||||||
# 4. 默认使用原文件名
|
# 6. 默认使用原文件名
|
||||||
return float('inf')
|
return float('inf')
|
||||||
|
|
||||||
# 过滤出非目录文件,并且排除已经符合命名规则的文件
|
# 过滤出非目录文件,并且排除已经符合命名规则的文件
|
||||||
files_to_process = []
|
files_to_process = []
|
||||||
for f in share_detail["list"]:
|
for f in share_detail["list"]:
|
||||||
if f["dir"]:
|
if f["dir"]:
|
||||||
continue # 跳过文件夹
|
continue # 跳过目录
|
||||||
|
|
||||||
# 检查文件是否已符合命名规则
|
# 检查文件是否已符合命名规则
|
||||||
if sequence_pattern == "{}":
|
if sequence_pattern == "{}":
|
||||||
@ -410,6 +422,7 @@ def get_share_detail():
|
|||||||
# 对于单独的{},直接使用数字序号作为文件名
|
# 对于单独的{},直接使用数字序号作为文件名
|
||||||
file["file_name_re"] = f"{current_sequence:02d}{file_ext}"
|
file["file_name_re"] = f"{current_sequence:02d}{file_ext}"
|
||||||
else:
|
else:
|
||||||
|
# 替换所有的{}为当前序号
|
||||||
file["file_name_re"] = sequence_pattern.replace("{}", f"{current_sequence:02d}") + file_ext
|
file["file_name_re"] = sequence_pattern.replace("{}", f"{current_sequence:02d}") + file_ext
|
||||||
current_sequence += 1
|
current_sequence += 1
|
||||||
|
|
||||||
@ -444,7 +457,16 @@ def get_share_detail():
|
|||||||
# 对于单独的[],使用特殊匹配
|
# 对于单独的[],使用特殊匹配
|
||||||
regex_pattern = "^(\\d+)$" # 匹配纯数字文件名
|
regex_pattern = "^(\\d+)$" # 匹配纯数字文件名
|
||||||
elif "[]" in episode_pattern:
|
elif "[]" in episode_pattern:
|
||||||
regex_pattern = re.escape(episode_pattern).replace('\\[\\]', '(\\d+)')
|
# 特殊处理E[]、EP[]等常见格式,使用更宽松的匹配方式
|
||||||
|
if episode_pattern == "E[]":
|
||||||
|
# 对于E[]格式,只检查文件名中是否包含形如E01的部分
|
||||||
|
regex_pattern = "^E(\\d+)$" # 只匹配纯E+数字的文件名格式
|
||||||
|
elif episode_pattern == "EP[]":
|
||||||
|
# 对于EP[]格式,只检查文件名中是否包含形如EP01的部分
|
||||||
|
regex_pattern = "^EP(\\d+)$" # 只匹配纯EP+数字的文件名格式
|
||||||
|
else:
|
||||||
|
# 对于其他带[]的格式,使用常规转义和替换
|
||||||
|
regex_pattern = re.escape(episode_pattern).replace('\\[\\]', '(\\d+)')
|
||||||
else:
|
else:
|
||||||
# 如果输入模式不包含[],则使用简单匹配模式,避免正则表达式错误
|
# 如果输入模式不包含[],则使用简单匹配模式,避免正则表达式错误
|
||||||
print(f"⚠️ 剧集命名模式中没有找到 [] 占位符,将使用简单匹配")
|
print(f"⚠️ 剧集命名模式中没有找到 [] 占位符,将使用简单匹配")
|
||||||
@ -469,10 +491,24 @@ def get_share_detail():
|
|||||||
return 0
|
return 0
|
||||||
|
|
||||||
# 过滤出非目录文件,并且排除已经符合命名规则的文件
|
# 过滤出非目录文件,并且排除已经符合命名规则的文件
|
||||||
files_to_process = [
|
files_to_process = []
|
||||||
f for f in share_detail["list"]
|
for f in share_detail["list"]:
|
||||||
if not f["dir"] and not re.match(regex_pattern, f["file_name"])
|
if f["dir"]:
|
||||||
]
|
continue # 跳过目录
|
||||||
|
|
||||||
|
# 检查文件是否已符合命名规则
|
||||||
|
if episode_pattern == "[]":
|
||||||
|
# 对于单独的[],检查文件名是否为纯数字
|
||||||
|
file_name_without_ext = os.path.splitext(f["file_name"])[0]
|
||||||
|
if file_name_without_ext.isdigit():
|
||||||
|
# 增加判断:如果是日期格式的纯数字,不视为已命名
|
||||||
|
if not is_date_format(file_name_without_ext):
|
||||||
|
continue # 跳过已符合命名规则的文件
|
||||||
|
elif re.match(regex_pattern, f["file_name"]):
|
||||||
|
continue # 跳过已符合命名规则的文件
|
||||||
|
|
||||||
|
# 添加到待处理文件列表
|
||||||
|
files_to_process.append(f)
|
||||||
|
|
||||||
# 根据提取的排序值进行排序
|
# 根据提取的排序值进行排序
|
||||||
sorted_files = sorted(files_to_process, key=extract_sorting_value)
|
sorted_files = sorted(files_to_process, key=extract_sorting_value)
|
||||||
|
|||||||
@ -479,10 +479,10 @@
|
|||||||
<!-- 文件列表 -->
|
<!-- 文件列表 -->
|
||||||
<div class="mb-3" v-if="fileSelect.previewRegex">
|
<div class="mb-3" v-if="fileSelect.previewRegex">
|
||||||
<div v-if="formData.tasklist[fileSelect.index].use_sequence_naming">
|
<div v-if="formData.tasklist[fileSelect.index].use_sequence_naming">
|
||||||
<b>顺序命名格式:</b><span class="badge badge-info" v-html="formData.tasklist[fileSelect.index].pattern"></span>
|
<b>顺序命名表达式:</b><span class="badge badge-info" v-html="formData.tasklist[fileSelect.index].pattern"></span>
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="formData.tasklist[fileSelect.index].use_episode_naming">
|
<div v-else-if="formData.tasklist[fileSelect.index].use_episode_naming">
|
||||||
<b>剧集命名格式:</b><span class="badge badge-info" v-html="formData.tasklist[fileSelect.index].pattern"></span>
|
<b>剧集命名表达式:</b><span class="badge badge-info" v-html="formData.tasklist[fileSelect.index].pattern"></span>
|
||||||
</div>
|
</div>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<div style="margin-bottom: 0;">
|
<div style="margin-bottom: 0;">
|
||||||
|
|||||||
@ -789,18 +789,14 @@ class Quark:
|
|||||||
# 构建剧集命名的正则表达式
|
# 构建剧集命名的正则表达式
|
||||||
episode_pattern = task["episode_naming"]
|
episode_pattern = task["episode_naming"]
|
||||||
# 先检查是否包含合法的[]字符
|
# 先检查是否包含合法的[]字符
|
||||||
if episode_pattern == "[]":
|
if "[]" in episode_pattern:
|
||||||
# 对于单独的[],使用特殊匹配
|
# 对于所有包含[]的模式,使用完整的剧集号识别规则
|
||||||
regex_pattern = "^(\\d+)$" # 匹配纯数字文件名
|
regex_pattern = "SPECIAL_EPISODE_PATTERN" # 这个标记后续用于特殊处理
|
||||||
elif "[]" in episode_pattern:
|
task["use_complex_episode_extraction"] = True # 添加一个标记
|
||||||
# 将[] 替换为 (\d+)
|
# 保存原始模式,用于生成新文件名
|
||||||
# 先将模式字符串进行转义,确保其他特殊字符不会干扰
|
task["original_episode_pattern"] = episode_pattern
|
||||||
escaped_pattern = re.escape(episode_pattern)
|
|
||||||
# 然后将转义后的 \[ \] 替换为捕获组 (\d+)
|
|
||||||
regex_pattern = escaped_pattern.replace('\\[\\]', '(\\d+)')
|
|
||||||
else:
|
else:
|
||||||
# 如果输入模式不包含[],则使用简单匹配模式,避免正则表达式错误
|
# 如果输入模式不包含[],则使用简单匹配模式,避免正则表达式错误
|
||||||
print(f"⚠️ 剧集命名模式中没有找到 [] 占位符,将使用简单匹配")
|
|
||||||
regex_pattern = "^" + re.escape(episode_pattern) + "(\\d+)$"
|
regex_pattern = "^" + re.escape(episode_pattern) + "(\\d+)$"
|
||||||
|
|
||||||
task["regex_pattern"] = regex_pattern
|
task["regex_pattern"] = regex_pattern
|
||||||
@ -1829,10 +1825,17 @@ class Quark:
|
|||||||
for dir_file in dir_file_list:
|
for dir_file in dir_file_list:
|
||||||
if not dir_file["dir"] and regex_pattern:
|
if not dir_file["dir"] and regex_pattern:
|
||||||
try:
|
try:
|
||||||
matches = re.match(regex_pattern, dir_file["file_name"])
|
if regex_pattern == "SPECIAL_EPISODE_PATTERN":
|
||||||
if matches:
|
# 对于特殊模式,使用extract_episode_number函数提取剧集号
|
||||||
episode_num = int(matches.group(1))
|
episode_num = extract_episode_number(dir_file["file_name"])
|
||||||
existing_episode_numbers.add(episode_num)
|
if episode_num is not None:
|
||||||
|
existing_episode_numbers.add(episode_num)
|
||||||
|
else:
|
||||||
|
# 使用常规正则表达式匹配
|
||||||
|
matches = re.match(regex_pattern, dir_file["file_name"])
|
||||||
|
if matches:
|
||||||
|
episode_num = int(matches.group(1))
|
||||||
|
existing_episode_numbers.add(episode_num)
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@ -2166,19 +2169,21 @@ class Quark:
|
|||||||
# 检查是否需要重命名
|
# 检查是否需要重命名
|
||||||
episode_num = extract_episode_number(dir_file["file_name"])
|
episode_num = extract_episode_number(dir_file["file_name"])
|
||||||
if episode_num is not None:
|
if episode_num is not None:
|
||||||
# 检查文件名是否符合指定的剧集命名格式
|
# 根据剧集命名模式生成目标文件名
|
||||||
|
file_ext = os.path.splitext(dir_file["file_name"])[1]
|
||||||
if episode_pattern == "[]":
|
if episode_pattern == "[]":
|
||||||
# 对于单独的[]模式,检查文件名是否已经是纯数字格式
|
# 使用完整的剧集号识别逻辑,而不是简单的纯数字判断
|
||||||
file_name_without_ext = os.path.splitext(dir_file["file_name"])[0]
|
# 生成新文件名
|
||||||
# 如果文件名不是纯数字格式,才进行重命名
|
new_name = f"{episode_num:02d}{file_ext}"
|
||||||
if not file_name_without_ext.isdigit() or len(file_name_without_ext) != 2:
|
# 只有当当前文件名与目标文件名不同时才重命名
|
||||||
file_ext = os.path.splitext(dir_file["file_name"])[1]
|
if dir_file["file_name"] != new_name:
|
||||||
new_name = f"{episode_num:02d}{file_ext}"
|
|
||||||
rename_operations.append((dir_file, new_name, episode_num))
|
rename_operations.append((dir_file, new_name, episode_num))
|
||||||
elif not re.match(regex_pattern, dir_file["file_name"]):
|
else:
|
||||||
file_ext = os.path.splitext(dir_file["file_name"])[1]
|
# 生成目标文件名
|
||||||
new_name = episode_pattern.replace("[]", f"{episode_num:02d}") + file_ext
|
new_name = episode_pattern.replace("[]", f"{episode_num:02d}") + file_ext
|
||||||
rename_operations.append((dir_file, new_name, episode_num))
|
# 检查文件名是否已经符合目标格式
|
||||||
|
if dir_file["file_name"] != new_name:
|
||||||
|
rename_operations.append((dir_file, new_name, episode_num))
|
||||||
|
|
||||||
# 按剧集号排序
|
# 按剧集号排序
|
||||||
rename_operations.sort(key=lambda x: x[2])
|
rename_operations.sort(key=lambda x: x[2])
|
||||||
@ -2200,10 +2205,46 @@ class Quark:
|
|||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
# 收集错误日志但不打印
|
# 收集错误日志但不打印
|
||||||
rename_logs.append(f"重命名: {dir_file['file_name']} → {new_name} 失败,{rename_return['message']}")
|
error_msg = rename_return.get("message", "未知错误")
|
||||||
|
|
||||||
|
# 刷新目录列表,检查文件是否实际已重命名成功
|
||||||
|
fresh_dir_file_list = self.ls_dir(self.savepath_fid[savepath])
|
||||||
|
target_exists = any(df["file_name"] == new_name for df in fresh_dir_file_list)
|
||||||
|
|
||||||
|
# 如果目标文件已存在,说明重命名已经成功或有同名文件
|
||||||
|
if target_exists:
|
||||||
|
# 对于已经成功的情况,我们仍然记录成功
|
||||||
|
rename_logs.append(f"重命名: {dir_file['file_name']} → {new_name}")
|
||||||
|
is_rename_count += 1
|
||||||
|
# 更新dir_file_list中的文件名
|
||||||
|
for df in dir_file_list:
|
||||||
|
if df["fid"] == dir_file["fid"]:
|
||||||
|
df["file_name"] = new_name
|
||||||
|
break
|
||||||
|
# 记录已重命名的文件
|
||||||
|
already_renamed_files.add(new_name)
|
||||||
|
else:
|
||||||
|
# 真正的错误情况
|
||||||
|
# 注释掉错误消息记录
|
||||||
|
# rename_logs.append(f"重命名: {dir_file['file_name']} → {new_name} 失败,{error_msg}")
|
||||||
|
pass
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
# 收集错误日志但不打印
|
# 收集错误日志但不打印
|
||||||
rename_logs.append(f"重命名出错: {dir_file['file_name']} → {new_name},错误:{str(e)}")
|
# 注释掉异常信息记录
|
||||||
|
# rename_logs.append(f"重命名出错: {dir_file['file_name']} → {new_name},错误:{str(e)}")
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
# 检查目标文件是否已经存在且是我们想要重命名的结果
|
||||||
|
# 这可能是因为之前的操作已经成功,但API返回了错误
|
||||||
|
fresh_dir_file_list = self.ls_dir(self.savepath_fid[savepath])
|
||||||
|
if any(df["file_name"] == new_name and df["fid"] != dir_file["fid"] for df in fresh_dir_file_list):
|
||||||
|
# 真正存在同名文件
|
||||||
|
# 注释掉同名文件警告信息记录
|
||||||
|
# rename_logs.append(f"重命名: {dir_file['file_name']} → {new_name} 失败,目标文件名已存在")
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
# 目标文件可能是之前操作的结果,不显示错误
|
||||||
|
pass
|
||||||
|
|
||||||
# 返回重命名日志和成功标志
|
# 返回重命名日志和成功标志
|
||||||
return (is_rename_count > 0), rename_logs
|
return (is_rename_count > 0), rename_logs
|
||||||
@ -2474,44 +2515,63 @@ def do_save(account, tasklist=[]):
|
|||||||
# 执行重命名任务,但收集日志而不是立即打印
|
# 执行重命名任务,但收集日志而不是立即打印
|
||||||
is_rename, rename_logs = account.do_rename_task(task)
|
is_rename, rename_logs = account.do_rename_task(task)
|
||||||
|
|
||||||
# 如果是正则命名模式,且没有Tree对象(即通过转存得到的Tree对象),则需要手动创建一个Tree视图
|
# 简化日志处理 - 只保留成功的重命名消息
|
||||||
if is_regex_mode and not (is_new_tree and isinstance(is_new_tree, Tree) and is_new_tree.size() > 1):
|
if rename_logs:
|
||||||
# 当is_new_tree明确为False时,表示没有新文件,不处理
|
success_logs = []
|
||||||
if is_new_tree is not False and is_rename:
|
for log in rename_logs:
|
||||||
# 获取当前目录下的所有文件
|
if "失败" not in log:
|
||||||
savepath = re.sub(r"/{2,}", "/", f"/{task['savepath']}")
|
success_logs.append(log)
|
||||||
if account.savepath_fid.get(savepath):
|
# 完全替换日志,只显示成功部分
|
||||||
dir_file_list = account.ls_dir(account.savepath_fid[savepath])
|
rename_logs = success_logs
|
||||||
|
|
||||||
# 如果有文件并且没有Tree对象,创建一个Tree对象
|
# 只有当is_new_tree为False且有成功的重命名日志时,才需要创建新的Tree对象
|
||||||
if dir_file_list: # 去掉is_new_tree的检查,因为上面已经做了
|
# 这确保只显示当次转存的文件,而不是目录中的所有文件
|
||||||
# 创建一个新的Tree对象
|
if task.get("shareurl") and (not is_new_tree or is_new_tree is False) and rename_logs and is_rename:
|
||||||
new_tree = Tree()
|
# 获取当前目录
|
||||||
# 创建根节点
|
savepath = re.sub(r"/{2,}", "/", f"/{task['savepath']}")
|
||||||
new_tree.create_node(
|
if account.savepath_fid.get(savepath):
|
||||||
savepath,
|
# 创建新的Tree对象
|
||||||
"root",
|
new_tree = Tree()
|
||||||
data={
|
# 创建根节点
|
||||||
"is_dir": True,
|
new_tree.create_node(
|
||||||
},
|
savepath,
|
||||||
)
|
"root",
|
||||||
|
data={
|
||||||
|
"is_dir": True,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
# 添加文件节点
|
# 从重命名日志中提取新文件名
|
||||||
for file in dir_file_list:
|
renamed_files = {}
|
||||||
if not file["dir"]: # 只处理文件
|
for log in rename_logs:
|
||||||
new_tree.create_node(
|
# 格式:重命名: 旧名 → 新名
|
||||||
file["file_name"],
|
match = re.search(r'重命名: (.*?) → (.*?)($|\s|,)', log)
|
||||||
file["fid"],
|
if match:
|
||||||
parent="root",
|
old_name = match.group(1)
|
||||||
data={
|
new_name = match.group(2)
|
||||||
"is_dir": False,
|
renamed_files[old_name] = new_name
|
||||||
"path": f"{savepath}/{file['file_name']}",
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
# 如果树的大小大于1(有文件),则设置为新的Tree对象
|
# 获取文件列表,只添加重命名的文件
|
||||||
if new_tree.size() > 1:
|
fresh_dir_file_list = account.ls_dir(account.savepath_fid[savepath])
|
||||||
is_new_tree = new_tree
|
|
||||||
|
# 添加重命名后的文件到树中
|
||||||
|
for file in fresh_dir_file_list:
|
||||||
|
if not file["dir"]: # 只处理文件
|
||||||
|
# 只添加重命名后的文件(当次转存的文件)
|
||||||
|
if file["file_name"] in renamed_files.values():
|
||||||
|
new_tree.create_node(
|
||||||
|
file["file_name"],
|
||||||
|
file["fid"],
|
||||||
|
parent="root",
|
||||||
|
data={
|
||||||
|
"is_dir": False,
|
||||||
|
"path": f"{savepath}/{file['file_name']}",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
# 如果树的大小大于1(有文件),则设置为新的Tree对象
|
||||||
|
if new_tree.size() > 1:
|
||||||
|
is_new_tree = new_tree
|
||||||
|
|
||||||
# 添加生成文件树的功能(无论是否是顺序命名模式)
|
# 添加生成文件树的功能(无论是否是顺序命名模式)
|
||||||
# 如果is_new_tree返回了Tree对象,则打印文件树
|
# 如果is_new_tree返回了Tree对象,则打印文件树
|
||||||
@ -2788,6 +2848,7 @@ def do_save(account, tasklist=[]):
|
|||||||
add_notify(f"✅《{task['taskname']}》 添加追更:")
|
add_notify(f"✅《{task['taskname']}》 添加追更:")
|
||||||
add_notify(f"/{task['savepath']}")
|
add_notify(f"/{task['savepath']}")
|
||||||
|
|
||||||
|
|
||||||
# 创建episode_pattern函数用于排序
|
# 创建episode_pattern函数用于排序
|
||||||
def extract_episode_number(filename):
|
def extract_episode_number(filename):
|
||||||
# 优先匹配SxxExx格式
|
# 优先匹配SxxExx格式
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user