修复转存记录和文件树未显示实际保存文件名的问题

- 修复顺序命名模式文件树创建条件判断逻辑
- 新增实际文件名映射机制,确保文件树和转存记录显示实际文件
- 修复文件排序、扩展名和图标显示错误的问题
- 正确处理重命名失败的情况
This commit is contained in:
x1ao4 2025-07-02 00:31:56 +08:00
parent 09d99a052d
commit 9cd952799d
2 changed files with 201 additions and 89 deletions

View File

@ -813,7 +813,7 @@
</div> </div>
</div> </div>
</div> </div>
<div class="form-group row" title="匹配成功的文件夹的所有嵌套目录都会被更新,并且会应用与根目录相同的命名和过滤规则。注意:原理是逐级索引,深层嵌套目录的场景下效率非常低,慎用.*"> <div class="form-group row" title="匹配成功的文件夹的所有嵌套目录都会被更新,并且会应用与根目录相同的正则命名和过滤规则。注意:原理是逐级索引,深层嵌套目录的场景下效率非常低,慎用.*">
<label class="col-sm-2 col-form-label">更新目录</label> <label class="col-sm-2 col-form-label">更新目录</label>
<div class="col-sm-10"> <div class="col-sm-10">
<input type="text" name="update_subdir[]" class="form-control" v-model="task.update_subdir" placeholder="可选输入需要更新的子目录的文件夹名称或正则表达式多个项目用竖线分隔4K|1080P"> <input type="text" name="update_subdir[]" class="form-control" v-model="task.update_subdir" placeholder="可选输入需要更新的子目录的文件夹名称或正则表达式多个项目用竖线分隔4K|1080P">

View File

@ -1416,70 +1416,76 @@ class Quark:
return False return False
# 添加一个专门从重命名日志更新记录的方法 # 添加一个专门从重命名日志更新记录的方法
def update_transfer_record_from_log(self, task, rename_log): def update_transfer_record_from_log(self, task, rename_log, actual_file_names=None):
"""从重命名日志中提取信息并更新记录 """从重命名日志中提取信息并更新记录
Args: Args:
task: 任务信息 task: 任务信息
rename_log: 重命名日志格式为 "重命名: 旧名 → 新名" rename_log: 重命名日志格式为 "重命名: 旧名 → 新名"
actual_file_names: 实际文件名映射字典用于修正显示的文件名
""" """
try: try:
# 使用字符串分割方法提取文件名,更可靠地获取完整文件名 # 使用字符串分割方法提取文件名,更可靠地获取完整文件名
if "重命名:" not in rename_log or "" not in rename_log: if "重命名:" not in rename_log or "" not in rename_log:
return False return False
# 先分割出"重命名:"后面的部分 # 先分割出"重命名:"后面的部分
parts = rename_log.split("重命名:", 1)[1].strip() parts = rename_log.split("重命名:", 1)[1].strip()
# 再按箭头分割 # 再按箭头分割
if "" not in parts: if "" not in parts:
return False return False
old_name, new_name = parts.split("", 1) old_name, expected_new_name = parts.split("", 1)
# 如果新名称包含"失败",则是失败的重命名,跳过 # 如果新名称包含"失败",则是失败的重命名,跳过
if "失败" in new_name: if "失败" in expected_new_name:
return False return False
# 处理可能的截断标记,只保留实际文件名部分 # 处理可能的截断标记,只保留实际文件名部分
# 注意:只有明确是失败消息才应该截断 # 注意:只有明确是失败消息才应该截断
if " 失败," in new_name: if " 失败," in expected_new_name:
new_name = new_name.split(" 失败,")[0] expected_new_name = expected_new_name.split(" 失败,")[0]
# 去除首尾空格 # 去除首尾空格
old_name = old_name.strip() old_name = old_name.strip()
new_name = new_name.strip() expected_new_name = expected_new_name.strip()
# 确保提取到的是完整文件名 # 确保提取到的是完整文件名
if not old_name or not new_name: if not old_name or not expected_new_name:
return False return False
# 获取实际的文件名(如果提供了映射)
actual_new_name = expected_new_name
if actual_file_names and old_name in actual_file_names:
actual_new_name = actual_file_names[old_name]
# 初始化数据库 # 初始化数据库
db = RecordDB() db = RecordDB()
# 使用原文件名和任务名查找记录 # 使用原文件名和任务名查找记录
task_name = task.get("taskname", "") task_name = task.get("taskname", "")
# 获取保存路径 # 获取保存路径
save_path = task.get("savepath", "") save_path = task.get("savepath", "")
# 注意:从日志中无法获取子目录信息,只能使用任务的主保存路径 # 注意:从日志中无法获取子目录信息,只能使用任务的主保存路径
# 检查文件是否已存在于记录中 # 检查文件是否已存在于记录中
# 先查询是否有匹配的记录 # 先查询是否有匹配的记录
cursor = db.conn.cursor() cursor = db.conn.cursor()
query = "SELECT file_id FROM transfer_records WHERE original_name = ? AND task_name = ? AND save_path = ?" query = "SELECT file_id FROM transfer_records WHERE original_name = ? AND task_name = ? AND save_path = ?"
cursor.execute(query, (old_name, task_name, save_path)) cursor.execute(query, (old_name, task_name, save_path))
result = cursor.fetchone() result = cursor.fetchone()
# 如果找到了匹配的记录使用file_id进行更新 # 如果找到了匹配的记录使用file_id进行更新
file_id = result[0] if result else "" file_id = result[0] if result else ""
# 更新记录 # 更新记录,使用实际的文件名
if file_id: if file_id:
# 使用file_id更新 # 使用file_id更新
updated = db.update_renamed_to( updated = db.update_renamed_to(
file_id=file_id, file_id=file_id,
original_name="", # 不使用原文件名因为已有file_id original_name="", # 不使用原文件名因为已有file_id
renamed_to=new_name, renamed_to=actual_new_name, # 使用实际的文件名
task_name=task_name, task_name=task_name,
save_path=save_path save_path=save_path
) )
@ -1488,14 +1494,14 @@ class Quark:
updated = db.update_renamed_to( updated = db.update_renamed_to(
file_id="", # 不使用file_id查询因为在日志中无法获取 file_id="", # 不使用file_id查询因为在日志中无法获取
original_name=old_name, original_name=old_name,
renamed_to=new_name, renamed_to=actual_new_name, # 使用实际的文件名
task_name=task_name, task_name=task_name,
save_path=save_path save_path=save_path
) )
# 关闭数据库连接 # 关闭数据库连接
db.close() db.close()
return updated > 0 return updated > 0
except Exception as e: except Exception as e:
print(f"根据日志更新转存记录失败: {e}") print(f"根据日志更新转存记录失败: {e}")
@ -1504,14 +1510,84 @@ class Quark:
# 批量处理重命名日志 # 批量处理重命名日志
def process_rename_logs(self, task, rename_logs): def process_rename_logs(self, task, rename_logs):
"""处理重命名日志列表,更新数据库记录 """处理重命名日志列表,更新数据库记录
Args: Args:
task: 任务信息 task: 任务信息
rename_logs: 重命名日志列表 rename_logs: 重命名日志列表
""" """
# 获取实际的文件名映射
actual_file_names = self.get_actual_file_names_from_directory(task, rename_logs)
for log in rename_logs: for log in rename_logs:
if "重命名:" in log and "" in log and "失败" not in log: if "重命名:" in log and "" in log and "失败" not in log:
self.update_transfer_record_from_log(task, log) self.update_transfer_record_from_log(task, log, actual_file_names)
def get_actual_file_names_from_directory(self, task, rename_logs):
"""从目录中获取实际的文件名,用于修正转存记录和日志显示
Args:
task: 任务信息
rename_logs: 重命名日志列表
Returns:
dict: 原文件名到实际文件名的映射
"""
try:
# 获取保存路径
savepath = re.sub(r"/{2,}", "/", f"/{task['savepath']}")
# 获取当前目录的文件列表
if hasattr(self, 'savepath_fid') and self.savepath_fid.get(savepath):
dir_file_list = self.ls_dir(self.savepath_fid[savepath])
else:
return {}
# 从重命名日志中提取预期的重命名映射(包括失败的重命名)
expected_renames = {}
for log in rename_logs:
if "重命名:" in log and "" in log:
parts = log.split("重命名:", 1)[1].strip()
if "" in parts:
old_name, new_name = parts.split("", 1)
# 处理可能的失败信息
if " 失败," in new_name:
new_name = new_name.split(" 失败,")[0]
old_name = old_name.strip()
new_name = new_name.strip()
expected_renames[old_name] = new_name
# 获取目录中实际存在的文件名
actual_files = [f["file_name"] for f in dir_file_list if not f["dir"]]
# 创建实际文件名映射
actual_renames = {}
# 对于每个预期的重命名,检查实际文件是否存在
for old_name, expected_new_name in expected_renames.items():
if expected_new_name in actual_files:
# 预期的重命名成功了
actual_renames[old_name] = expected_new_name
elif old_name in actual_files:
# 重命名失败,文件保持原名
actual_renames[old_name] = old_name
else:
# 尝试模糊匹配,可能文件名有细微差异
for actual_file in actual_files:
# 简单的相似度检查:如果文件名包含预期名称的主要部分
if (len(expected_new_name) > 10 and
expected_new_name[:10] in actual_file and
actual_file not in actual_renames.values()):
actual_renames[old_name] = actual_file
break
else:
# 如果找不到匹配,使用预期名称(可能文件已被删除或移动)
actual_renames[old_name] = expected_new_name
return actual_renames
except Exception as e:
print(f"获取实际文件名失败: {e}")
return {}
def check_file_exists_in_records(self, file_id, task=None): def check_file_exists_in_records(self, file_id, task=None):
"""检查文件ID是否存在于转存记录中 """检查文件ID是否存在于转存记录中
@ -3535,9 +3611,9 @@ def do_save(account, tasklist=[]):
# 完全替换日志,只显示成功部分 # 完全替换日志,只显示成功部分
rename_logs = success_logs rename_logs = success_logs
# 只有当is_new_tree为False且有成功的重命名日志时才需要创建新的Tree对象 # 对于顺序命名模式,需要重新创建文件树以显示实际的文件名
# 这确保只显示当次转存的文件,而不是目录中的所有文件 # 这确保显示的是实际存在的文件名,而不是预期的文件名
if task.get("shareurl") and (not is_new_tree or is_new_tree is False) and rename_logs and is_rename: if task.get("shareurl") and rename_logs and is_rename and (task.get("use_sequence_naming") or task.get("use_episode_naming")):
# 获取当前目录 # 获取当前目录
savepath = re.sub(r"/{2,}", "/", f"/{task['savepath']}") savepath = re.sub(r"/{2,}", "/", f"/{task['savepath']}")
if account.savepath_fid.get(savepath): if account.savepath_fid.get(savepath):
@ -3552,26 +3628,43 @@ def do_save(account, tasklist=[]):
}, },
) )
# 从重命名日志中提取新文件名 # 获取实际的文件名映射
renamed_files = {} actual_file_names = account.get_actual_file_names_from_directory(task, rename_logs)
# 从重命名日志中提取预期的文件名映射
expected_renamed_files = {}
for log in rename_logs: for log in rename_logs:
# 格式:重命名: 旧名 → 新名 # 格式:重命名: 旧名 → 新名
match = re.search(r'重命名: (.*?) → (.+?)($|\s||失败)', log) match = re.search(r'重命名: (.*?) → (.+?)($|\s||失败)', log)
if match: if match:
old_name = match.group(1).strip() old_name = match.group(1).strip()
new_name = match.group(2).strip() expected_new_name = match.group(2).strip()
renamed_files[old_name] = new_name expected_renamed_files[old_name] = expected_new_name
# 获取文件列表,只添加重命名的文件 # 获取文件列表,只添加重命名的文件
fresh_dir_file_list = account.ls_dir(account.savepath_fid[savepath]) fresh_dir_file_list = account.ls_dir(account.savepath_fid[savepath])
# 添加重命名后的文件到树中 # 添加实际存在的文件到树中
for file in fresh_dir_file_list: for file in fresh_dir_file_list:
if not file["dir"]: # 只处理文件 if not file["dir"]: # 只处理文件
# 只添加重命名后的文件(当次转存的文件) # 检查这个文件是否是当次转存的文件
if file["file_name"] in renamed_files.values(): is_transferred_file = False
# 方法1检查文件名是否在实际重命名结果中
if file["file_name"] in actual_file_names.values():
is_transferred_file = True
# 方法2检查文件名是否在预期重命名结果中用于重命名失败的情况
elif file["file_name"] in expected_renamed_files.values():
is_transferred_file = True
# 方法3检查文件名是否是原始文件名重命名失败保持原名
elif file["file_name"] in expected_renamed_files.keys():
is_transferred_file = True
if is_transferred_file:
new_tree.create_node( new_tree.create_node(
file["file_name"], file["file_name"], # 使用实际存在的文件名
file["fid"], file["fid"],
parent="root", parent="root",
data={ data={
@ -3621,30 +3714,33 @@ def do_save(account, tasklist=[]):
# 按文件名排序 # 按文件名排序
if is_special_sequence: if is_special_sequence:
# 对于顺序命名模式,使用重命名日志来获取新增的文件 # 对于顺序命名模式,直接使用文件树中的实际文件名
if rename_logs: if rename_logs:
# 从重命名日志提取新旧文件名 # 直接显示文件树中的实际文件名(已经是重命名后的结果)
renamed_files = {} # 按照文件名中的序号进行排序
for log in rename_logs: def extract_sequence_number(node):
# 格式:重命名: 旧名 → 新名 filename = remove_file_icons(node.tag)
match = re.search(r'重命名: (.*?) → (.+?)($|\s||失败)', log) # 尝试从文件名中提取序号,如 "乘风2025 - S06E01.flac" -> 1
import re
match = re.search(r'E(\d+)', filename)
if match: if match:
old_name = match.group(1).strip() return int(match.group(1))
new_name = match.group(2).strip() # 如果没有找到E序号尝试其他模式
renamed_files[old_name] = new_name match = re.search(r'(\d+)', filename)
if match:
# 只显示重命名的文件 return int(match.group(1))
for node in file_nodes: return 0
# 获取原始文件名(去除已有图标)
orig_filename = remove_file_icons(node.tag) # 按序号排序
# 检查此文件是否在重命名日志中 sorted_nodes = sorted(file_nodes, key=extract_sequence_number)
if orig_filename in renamed_files:
# 使用重命名后的文件名 for node in sorted_nodes:
new_filename = renamed_files[orig_filename] # 获取实际文件名(去除已有图标)
# 获取适当的图标 actual_filename = remove_file_icons(node.tag)
icon = get_file_icon(new_filename, is_dir=node.data.get("is_dir", False)) # 获取适当的图标
# 添加到显示列表 icon = get_file_icon(actual_filename, is_dir=node.data.get("is_dir", False))
display_files.append((f"{icon} {new_filename}", node)) # 添加到显示列表
display_files.append((f"{icon} {actual_filename}", node))
else: else:
# 如果没有重命名日志,使用原来的顺序命名逻辑 # 如果没有重命名日志,使用原来的顺序命名逻辑
if task.get("use_sequence_naming") and task.get("sequence_naming"): if task.get("use_sequence_naming") and task.get("sequence_naming"):
@ -4307,12 +4403,15 @@ def do_save(account, tasklist=[]):
# 重新获取文件列表 # 重新获取文件列表
savepath = re.sub(r"/{2,}", "/", f"/{task['savepath']}") savepath = re.sub(r"/{2,}", "/", f"/{task['savepath']}")
dir_file_list = account.ls_dir(account.savepath_fid[savepath]) dir_file_list = account.ls_dir(account.savepath_fid[savepath])
# 过滤出非目录的文件 # 过滤出非目录的文件
file_nodes = [f for f in dir_file_list if not f["dir"]] file_nodes = [f for f in dir_file_list if not f["dir"]]
# 从重命名日志提取新旧文件名 # 获取实际的文件名映射
renamed_files = {} actual_file_names = account.get_actual_file_names_from_directory(task, rename_logs)
# 从重命名日志提取预期的新旧文件名映射
expected_renamed_files = {}
for log in rename_logs: for log in rename_logs:
# 格式:重命名: 旧名 → 新名 # 格式:重命名: 旧名 → 新名
if "重命名:" in log and "" in log: if "重命名:" in log and "" in log:
@ -4320,22 +4419,27 @@ def do_save(account, tasklist=[]):
parts = log.split("重命名:", 1)[1].strip() parts = log.split("重命名:", 1)[1].strip()
# 再按箭头分割 # 再按箭头分割
if "" in parts: if "" in parts:
old_name, new_name = parts.split("", 1) old_name, expected_new_name = parts.split("", 1)
# 只处理失败信息,不截断正常文件名 # 只处理失败信息,不截断正常文件名
if " 失败," in new_name: if " 失败," in expected_new_name:
new_name = new_name.split(" 失败,")[0] expected_new_name = expected_new_name.split(" 失败,")[0]
# 去除首尾空格 # 去除首尾空格
old_name = old_name.strip() old_name = old_name.strip()
new_name = new_name.strip() expected_new_name = expected_new_name.strip()
renamed_files[old_name] = new_name expected_renamed_files[old_name] = expected_new_name
# 确保至少显示重命名后的文件 # 确保至少显示实际存在的文件
display_files = [] display_files = []
# 添加所有重命名的目标文件 # 添加所有实际存在的转存文件
for old_name, new_name in renamed_files.items(): for old_name in expected_renamed_files.keys():
if new_name not in display_files: # 获取实际的文件名
display_files.append(new_name) actual_name = actual_file_names.get(old_name, expected_renamed_files[old_name])
# 检查文件是否实际存在于目录中
if any(f["file_name"] == actual_name for f in file_nodes):
if actual_name not in display_files:
display_files.append(actual_name)
# 此外,检查是否有新的文件节点(比较节点时间) # 此外,检查是否有新的文件节点(比较节点时间)
if not display_files and is_new_tree and hasattr(is_new_tree, 'nodes'): if not display_files and is_new_tree and hasattr(is_new_tree, 'nodes'):
@ -4502,25 +4606,33 @@ def do_save(account, tasklist=[]):
# 重新获取文件列表 # 重新获取文件列表
savepath = re.sub(r"/{2,}", "/", f"/{task['savepath']}") savepath = re.sub(r"/{2,}", "/", f"/{task['savepath']}")
dir_file_list = account.ls_dir(account.savepath_fid[savepath]) dir_file_list = account.ls_dir(account.savepath_fid[savepath])
# 过滤出非目录的文件 # 过滤出非目录的文件
file_nodes = [f for f in dir_file_list if not f["dir"]] file_nodes = [f for f in dir_file_list if not f["dir"]]
# 从重命名日志提取新旧文件名 # 获取实际的文件名映射
renamed_files = {} actual_file_names = account.get_actual_file_names_from_directory(task, rename_logs)
# 从重命名日志提取预期的新旧文件名映射
expected_renamed_files = {}
for log in rename_logs: for log in rename_logs:
# 格式:重命名: 旧名 → 新名 # 格式:重命名: 旧名 → 新名
match = re.search(r'重命名: (.*?) → (.+?)($|\s||失败)', log) match = re.search(r'重命名: (.*?) → (.+?)($|\s||失败)', log)
if match: if match:
old_name = match.group(1).strip() old_name = match.group(1).strip()
new_name = match.group(2).strip() expected_new_name = match.group(2).strip()
renamed_files[old_name] = new_name expected_renamed_files[old_name] = expected_new_name
# 只显示重命名的文件 # 只显示实际存在的转存文件
display_files = [] display_files = []
for file in file_nodes: for old_name in expected_renamed_files.keys():
if file["file_name"] in renamed_files: # 获取实际的文件名
display_files.append(renamed_files[file["file_name"]]) actual_name = actual_file_names.get(old_name, expected_renamed_files[old_name])
# 检查文件是否实际存在于目录中
if any(f["file_name"] == actual_name for f in file_nodes):
if actual_name not in display_files:
display_files.append(actual_name)
# 如果没有找到任何文件要显示,使用原始逻辑 # 如果没有找到任何文件要显示,使用原始逻辑
if not display_files: if not display_files: