import os import re from datetime import datetime # ====================== 【配置区】 ====================== MEDIA_FOLDER = "/Volumes/JoyDisk/myMedia/olstrms/电影/未分类/2024" DRY_RUN = True # True=预览不修改;False=实际重命名 SKIP_PROCESSED = False 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().:+-]", ""), ] # 加入strm ALLOW_EXT = {".mkv", ".mp4", ".mov", ".srt", ".ass", ".sub", ".strm"} # ======================================================= 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) # 增加flush,强制写入磁盘,解决日志文件不更新 with open(LOG_FILE, "a", encoding="utf-8") as f: f.write(line + "\n") f.flush() if __name__ == "__main__": # 每次运行前清空日志文件 with open(LOG_FILE, "w", encoding="utf-8") as f: f.flush() write_log("===== 文件名+文件夹清洗(移除开头A 中文片名前缀) =====") write_log(f"根目录:{MEDIA_FOLDER}") 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(MEDIA_FOLDER, 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 # 调试:打印所有扫描到的媒体文件(无论是否修改) write_log(f"[扫描媒体] {fname}") new_name = clean_filename(fname) if new_name != fname: if SKIP_PROCESSED and re.search(r"\(\d{4}\)", new_name): write_log(f"【跳过文件】{fname},检测年份括号") continue 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: if SKIP_PROCESSED and re.search(r"\(\d{4}\)", new_name): write_log(f"【跳过文件夹】{dname},检测年份括号") continue 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("============================================================")