From c735ade0d0614323fbc6bf3354b8d36f19e0a2c9 Mon Sep 17 00:00:00 2001 From: x1ao4 Date: Fri, 25 Apr 2025 04:13:18 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=89=A7=E9=9B=86=E5=91=BD?= =?UTF-8?q?=E5=90=8D=E8=A1=A8=E8=BE=BE=E5=BC=8F=E7=9A=84=E5=BA=94=E7=94=A8?= =?UTF-8?q?=E6=95=88=E6=9E=9C=EF=BC=8C=E4=BC=98=E5=8C=96=E6=97=A5=E5=BF=97?= =?UTF-8?q?=E4=BF=A1=E6=81=AF=E7=9A=84=E6=89=93=E5=8D=B0=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/run.py | 58 +++++++++--- app/templates/index.html | 4 +- quark_auto_save.py | 189 ++++++++++++++++++++++++++------------- 3 files changed, 174 insertions(+), 77 deletions(-) diff --git a/app/run.py b/app/run.py index f4781f8..00eee06 100644 --- a/app/run.py +++ b/app/run.py @@ -329,7 +329,19 @@ def get_share_detail(): 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) if period_match: period_num = int(period_match.group(1)) @@ -342,7 +354,7 @@ def get_share_detail(): 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) if date_match: year = int(date_match.group(1)) @@ -358,19 +370,19 @@ def get_share_detail(): return base_value * 10 + 3 return base_value * 10 - # 3. 尝试提取任何数字 + # 5. 尝试提取任何数字 number_match = re.search(r'(\d+)', file_name) if number_match: return int(number_match.group(1)) - # 4. 默认使用原文件名 + # 6. 默认使用原文件名 return float('inf') # 过滤出非目录文件,并且排除已经符合命名规则的文件 files_to_process = [] for f in share_detail["list"]: if f["dir"]: - continue # 跳过文件夹 + continue # 跳过目录 # 检查文件是否已符合命名规则 if sequence_pattern == "{}": @@ -410,9 +422,10 @@ def get_share_detail(): # 对于单独的{},直接使用数字序号作为文件名 file["file_name_re"] = f"{current_sequence:02d}{file_ext}" else: + # 替换所有的{}为当前序号 file["file_name_re"] = sequence_pattern.replace("{}", f"{current_sequence:02d}") + file_ext current_sequence += 1 - + return share_detail elif regex.get("use_episode_naming") and regex.get("episode_naming"): # 剧集命名模式预览 @@ -444,7 +457,16 @@ def get_share_detail(): # 对于单独的[],使用特殊匹配 regex_pattern = "^(\\d+)$" # 匹配纯数字文件名 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: # 如果输入模式不包含[],则使用简单匹配模式,避免正则表达式错误 print(f"⚠️ 剧集命名模式中没有找到 [] 占位符,将使用简单匹配") @@ -469,10 +491,24 @@ def get_share_detail(): return 0 # 过滤出非目录文件,并且排除已经符合命名规则的文件 - files_to_process = [ - f for f in share_detail["list"] - if not f["dir"] and not re.match(regex_pattern, f["file_name"]) - ] + files_to_process = [] + for f in share_detail["list"]: + 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) diff --git a/app/templates/index.html b/app/templates/index.html index 996c830..b2117f9 100644 --- a/app/templates/index.html +++ b/app/templates/index.html @@ -479,10 +479,10 @@
- 顺序命名格式: + 顺序命名表达式:
- 剧集命名格式: + 剧集命名表达式:
diff --git a/quark_auto_save.py b/quark_auto_save.py index a3493dd..eeddbb1 100644 --- a/quark_auto_save.py +++ b/quark_auto_save.py @@ -789,18 +789,14 @@ class Quark: # 构建剧集命名的正则表达式 episode_pattern = task["episode_naming"] # 先检查是否包含合法的[]字符 - if episode_pattern == "[]": - # 对于单独的[],使用特殊匹配 - regex_pattern = "^(\\d+)$" # 匹配纯数字文件名 - elif "[]" in episode_pattern: - # 将[] 替换为 (\d+) - # 先将模式字符串进行转义,确保其他特殊字符不会干扰 - escaped_pattern = re.escape(episode_pattern) - # 然后将转义后的 \[ \] 替换为捕获组 (\d+) - regex_pattern = escaped_pattern.replace('\\[\\]', '(\\d+)') + if "[]" in episode_pattern: + # 对于所有包含[]的模式,使用完整的剧集号识别规则 + regex_pattern = "SPECIAL_EPISODE_PATTERN" # 这个标记后续用于特殊处理 + task["use_complex_episode_extraction"] = True # 添加一个标记 + # 保存原始模式,用于生成新文件名 + task["original_episode_pattern"] = episode_pattern else: # 如果输入模式不包含[],则使用简单匹配模式,避免正则表达式错误 - print(f"⚠️ 剧集命名模式中没有找到 [] 占位符,将使用简单匹配") regex_pattern = "^" + re.escape(episode_pattern) + "(\\d+)$" task["regex_pattern"] = regex_pattern @@ -1829,10 +1825,17 @@ class Quark: for dir_file in dir_file_list: if not dir_file["dir"] and regex_pattern: try: - matches = re.match(regex_pattern, dir_file["file_name"]) - if matches: - episode_num = int(matches.group(1)) - existing_episode_numbers.add(episode_num) + if regex_pattern == "SPECIAL_EPISODE_PATTERN": + # 对于特殊模式,使用extract_episode_number函数提取剧集号 + episode_num = extract_episode_number(dir_file["file_name"]) + 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: pass @@ -2166,19 +2169,21 @@ class Quark: # 检查是否需要重命名 episode_num = extract_episode_number(dir_file["file_name"]) if episode_num is not None: - # 检查文件名是否符合指定的剧集命名格式 + # 根据剧集命名模式生成目标文件名 + file_ext = os.path.splitext(dir_file["file_name"])[1] if episode_pattern == "[]": - # 对于单独的[]模式,检查文件名是否已经是纯数字格式 - file_name_without_ext = os.path.splitext(dir_file["file_name"])[0] - # 如果文件名不是纯数字格式,才进行重命名 - if not file_name_without_ext.isdigit() or len(file_name_without_ext) != 2: - file_ext = os.path.splitext(dir_file["file_name"])[1] - new_name = f"{episode_num:02d}{file_ext}" + # 使用完整的剧集号识别逻辑,而不是简单的纯数字判断 + # 生成新文件名 + new_name = f"{episode_num:02d}{file_ext}" + # 只有当当前文件名与目标文件名不同时才重命名 + if dir_file["file_name"] != new_name: rename_operations.append((dir_file, new_name, episode_num)) - elif not re.match(regex_pattern, dir_file["file_name"]): - file_ext = os.path.splitext(dir_file["file_name"])[1] + else: + # 生成目标文件名 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]) @@ -2200,10 +2205,46 @@ class Quark: break 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: # 收集错误日志但不打印 - 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 @@ -2474,44 +2515,63 @@ def do_save(account, tasklist=[]): # 执行重命名任务,但收集日志而不是立即打印 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): - # 当is_new_tree明确为False时,表示没有新文件,不处理 - if is_new_tree is not False and is_rename: - # 获取当前目录下的所有文件 - savepath = re.sub(r"/{2,}", "/", f"/{task['savepath']}") - if account.savepath_fid.get(savepath): - dir_file_list = account.ls_dir(account.savepath_fid[savepath]) - - # 如果有文件并且没有Tree对象,创建一个Tree对象 - if dir_file_list: # 去掉is_new_tree的检查,因为上面已经做了 - # 创建一个新的Tree对象 - new_tree = Tree() - # 创建根节点 - new_tree.create_node( - savepath, - "root", - data={ - "is_dir": True, - }, - ) - - # 添加文件节点 - for file in dir_file_list: - if not file["dir"]: # 只处理文件 - 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 + # 简化日志处理 - 只保留成功的重命名消息 + if rename_logs: + success_logs = [] + for log in rename_logs: + if "失败" not in log: + success_logs.append(log) + # 完全替换日志,只显示成功部分 + 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: + # 获取当前目录 + savepath = re.sub(r"/{2,}", "/", f"/{task['savepath']}") + if account.savepath_fid.get(savepath): + # 创建新的Tree对象 + new_tree = Tree() + # 创建根节点 + new_tree.create_node( + savepath, + "root", + data={ + "is_dir": True, + }, + ) + + # 从重命名日志中提取新文件名 + renamed_files = {} + for log in rename_logs: + # 格式:重命名: 旧名 → 新名 + match = re.search(r'重命名: (.*?) → (.*?)($|\s|,)', log) + if match: + old_name = match.group(1) + new_name = match.group(2) + renamed_files[old_name] = new_name + + # 获取文件列表,只添加重命名的文件 + fresh_dir_file_list = account.ls_dir(account.savepath_fid[savepath]) + + # 添加重命名后的文件到树中 + 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对象,则打印文件树 @@ -2788,6 +2848,7 @@ def do_save(account, tasklist=[]): add_notify(f"✅《{task['taskname']}》 添加追更:") add_notify(f"/{task['savepath']}") + # 创建episode_pattern函数用于排序 def extract_episode_number(filename): # 优先匹配SxxExx格式