131 lines
5.0 KiB
Python
131 lines
5.0 KiB
Python
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() |