优化剧集命名表达式的应用效果,优化日志信息的打印逻辑

This commit is contained in:
x1ao4 2025-04-25 04:13:18 +08:00
parent 50416ca514
commit c735ade0d0
3 changed files with 174 additions and 77 deletions

View File

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

View File

@ -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;">

View File

@ -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格式