import os import re import argparse from datetime import datetime # ====================== 【配置区,不再写死目录】 ====================== LOG_FILE = "./rename_log.txt" # 清洗规则 RULES = [ (r"^[A-Za-z] ", ""), #1.移除开头单个字母+空格 (r"\[.*?\]", ""), #2.移除英文方括号及内部内容 (r"【.*?】", ""), #3.移除中文【】及内部内容 (r"丨", ""), #清除汉字竖线 丨 (r"\b(2160p|1080p|720p|HDR|DV|DoVi|HEVC|x265|x264|DDP|AAC|Atmos|TrueHD|中英双语|双语字幕)\b", "", re.IGNORECASE), (r"\.+", ""), #删除连续的点 (r"\s", ""), #删除全部空格 # 保留:汉字、半角英文、数字、半角 () . : + - (r"[^\u4e00-\u9fa5a-zA-Z0-9().:+-]", ""), ] ALLOW_EXT = {".mkv", ".mp4", ".mov", ".srt", ".ass", ".sub", ".strm",".flv",".avi",".wmv",".ts",".rmvb",".webm",".mp3",".flac",".aac",".wav",".ogg",".m4a",".wma",".alac",".rm",".iso"} # ==================================================================== def clean_filename(name: str) -> str: name_no_ext, ext = os.path.splitext(name) # 全角转半角 def full2half(s): result = [] for char in s: code = ord(char) if code == 12288: char = chr(32) elif 65281 <= code <= 65374: char = chr(code - 65248) result.append(char) return "".join(result) name_no_ext = full2half(name_no_ext) for item in RULES: if len(item) == 3: pat, repl, flag = item name_no_ext = re.sub(pat, repl, name_no_ext, flags=flag) else: pat, repl = item name_no_ext = re.sub(pat, repl, name_no_ext) name_no_ext = name_no_ext.strip() return name_no_ext + ext def write_log(msg: str): now = datetime.now().strftime("%Y-%m-%d %H:%M:%S") line = f"[{now}] {msg}" print(line) with open(LOG_FILE, "a", encoding="utf-8") as f: f.write(line + "\n") f.flush() def main(): parser = argparse.ArgumentParser(description="媒体文件名批量清洗重命名工具") parser.add_argument("--dir", required=True, help="目标根目录路径") parser.add_argument("--apply", action="store_true", help="加上此参数才执行真实重命名;默认仅预览DryRun") args = parser.parse_args() target_dir = args.dir DRY_RUN = not args.apply # 校验目录是否存在 if not os.path.isdir(target_dir): print(f"错误:目录不存在 -> {target_dir}") return # 每次运行前清空日志文件 with open(LOG_FILE, "w", encoding="utf-8") as f: f.flush() write_log("===== 文件名+文件夹清洗(移除开头A 中文片名前缀) =====") write_log(f"根目录:{target_dir}") write_log(f"预览模式 DRY_RUN = {DRY_RUN}") file_rename_list = [] dir_rename_list = [] total_scan_dirs = 0 total_all_files = 0 # 全部扫描到的文件(所有后缀) total_media_files = 0 # 媒体文件(ALLOW_EXT后缀) for root, dirs, files in os.walk(target_dir, topdown=False): total_scan_dirs += 1 for fname in files: total_all_files +=1 ext = os.path.splitext(fname)[1].lower() if ext in ALLOW_EXT: total_media_files += 1 new_name = clean_filename(fname) if new_name != fname: old_path = os.path.join(root, fname) new_path = os.path.join(root, new_name) file_rename_list.append((old_path, new_path)) write_log(f"待修改文件-> {fname} → {new_name}") # 处理文件夹 for dname in dirs: new_name = clean_filename(dname) if new_name != dname: old_path = os.path.join(root, dname) new_path = os.path.join(root, new_name) dir_rename_list.append((old_path, new_name)) write_log(f"待修改文件夹-> {dname} → {new_name}") # ========== 执行阶段 ========== if not DRY_RUN: # 先文件 for old, new in file_rename_list: try: os.rename(old, new) except Exception as e: write_log(f"【文件重命名失败】{old} -> {new} | 错误:{e}") # 后文件夹 for old, new in dir_rename_list: try: os.rename(old, new) except Exception as e: write_log(f"【文件夹重命名失败】{old} -> {new} | 错误:{e}") # 最终汇总输出 write_log(f"任务完成,总扫描目录:{total_scan_dirs},待修改文件夹:{len(dir_rename_list)}") write_log(f"总扫描文件:{total_all_files},媒体文件:{total_media_files},待修改文件:{len(file_rename_list)}") write_log("============================================================") if __name__ == "__main__": main()