import os import re import hashlib from datetime import datetime, timedelta import pywintypes import win32file import win32con # 预编译正则表达式提升自然排序性能 NATURAL_SORT_RE = re.compile(r'(\d+)') # 第一步:优化文件名处理(仅处理扩展名) def sanitize_filename(filename): base, ext = os.path.splitext(filename) # 仅处理扩展名,保留原始名称中的空格 if ext.lower() != '.mp3': ext = '.mp3' # 限制文件名长度防止溢出(取消下划线填充) return f"{base[:50]}{ext}" # 最大总长度54字符(50+4) # 第二步:单次遍历目录(减少IO开销) mp3_files = [] renamed_log = [] print("? 扫描中...", end='', flush=True) # 单次遍历同时筛选和计数 for filename in os.listdir(): if filename.lower().endswith('.mp3'): mp3_files.append(filename) total_files = len(mp3_files) print(f"共发现 {total_files} 个MP3文件") # 自然排序算法 def natural_sort_key(s): return [int(text) if text.isdigit() else text.lower() for text in NATURAL_SORT_RE.split(s)] mp3_files.sort(key=natural_sort_key) # 第三步:批量处理重命名(安全冲突处理) processed = 0 for idx in range(len(mp3_files)-1, -1, -1): # 逆向遍历避免冲突 filename = mp3_files[idx] clean_name = sanitize_filename(filename) if clean_name != filename: base, ext = os.path.splitext(clean_name) # 使用更安全的哈希处理冲突 unique_id = hashlib.md5(filename.encode()).hexdigest()[:8] temp_name = f"{base}_{unique_id}{ext}" try: os.rename(filename, temp_name) renamed_log.append((filename, temp_name)) mp3_files[idx] = temp_name except Exception as e: mp3_files.pop(idx) # 移除无法处理项 print(f"\n⚠️ 改名失败:{filename} → {temp_name} - {str(e)}") processed += 1 if processed % 10 == 0: # 降低进度刷新频率 print(f"\r? 扫描中... {processed}/{total_files}", end='', flush=True) print("\n\n? 扫描完成,开始时间设置...") # 第四步:预计算时间序列 start_dt = datetime.now() - timedelta(minutes=1) time_sequence = [start_dt + timedelta(seconds=i*2) for i in range(len(mp3_files))] # 第五步:安全设置文件时间 success_count = 0 for idx, (filename, target_dt) in enumerate(zip(mp3_files, time_sequence)): file_path = os.path.abspath(filename) try: # 创建时间对象 py_time = pywintypes.Time(target_dt) timestamp = target_dt.timestamp() # 显式文件句柄管理 h_file = win32file.CreateFile( file_path, win32con.GENERIC_WRITE, win32con.FILE_SHARE_READ | win32con.FILE_SHARE_WRITE, None, win32con.OPEN_EXISTING, win32con.FILE_ATTRIBUTE_NORMAL, None ) # 设置时间并立即关闭 win32file.SetFileTime(h_file, py_time, py_time, py_time) h_file.Close() # 同步Unix时间戳 os.utime(file_path, (timestamp, timestamp)) success_count += 1 if idx % 10 == 0: # 降低进度刷新频率 print(f"\r✅ 已处理 {idx+1}/{len(mp3_files)}", end='', flush=True) except Exception as e: print(f"\n? 失败({idx+1}): {filename} - {str(e)}") finally: # 确保句柄关闭 if 'h_file' in locals(): try: h_file.Close() except: pass # 结果输出(保持原样) print(f"\n\n? 操作完成!共处理{len(mp3_files)}个文件") print(f" ✅ 成功:{success_count}个 | ❌ 失败:{len(mp3_files)-success_count}个") if renamed_log: print("\n? 文件名优化示例:") print(f" 原文件名 → 新文件名(前3条)") for old, new in renamed_log[:3]: print(f" {old} → {new}") if len(renamed_log) > 3: print(f" ...(共{len(renamed_log)}条,完整记录见同目录rename.log)") with open('rename.log', 'w', encoding='utf-8') as f: f.write('\n'.join([f"{old} → {new}" for old, new in renamed_log])) print("\n? 三重验证方法:") print(" 1. 资源管理器地址栏输入:?sort=name → 应与时间排序一致") print(" 2. 按住Shift右键→属性:创建/修改时间均为设定值(精确到秒)") print(" 3. 播放测试:车载/音箱按时间排序应与文件名顺序完全一致") print("\n? 提示:若处理超500个文件,建议将time_step调至5秒以上")
步里
评论时间:2025-04-03 19:35:10
import os
import re
import hashlib
from datetime import datetime, timedelta
import pywintypes
import win32file
import win32con
import eyed3 # 需要安装:pip install eyed3
# 预编译正则表达式提升自然排序性能
NATURAL_SORT_RE = re.compile(r'(\d+)')
# 第一步:优化文件名处理(仅处理扩展名)
def sanitize_filename(filename):
base, ext = os.path.splitext(filename)
# 仅处理扩展名,保留原始名称中的空格
if ext.lower() != '.mp3':
ext = '.mp3'
# 限制文件名长度防止溢出(取消下划线填充)
return f"{base[:50]}{ext}" # 最大总长度54字符(50+4)
# 第二步:单次遍历目录(减少IO开销)
mp3_files = []
renamed_log = []
print("? 扫描中...", end='', flush=True)
# 单次遍历同时筛选和计数
for filename in os.listdir():
if filename.lower().endswith('.mp3'):
mp3_files.append(filename)
total_files = len(mp3_files)
print(f"共发现 {total_files} 个MP3文件")
# 自然排序算法
def natural_sort_key(s):
return [int(text) if text.isdigit() else text.lower()
for text in NATURAL_SORT_RE.split(s)]
mp3_files.sort(key=natural_sort_key)
# 第三步:批量处理重命名(安全冲突处理)
processed = 0
for idx in range(len(mp3_files)-1, -1, -1): # 逆向遍历避免冲突
filename = mp3_files[idx]
clean_name = sanitize_filename(filename)
if clean_name != filename:
base, ext = os.path.splitext(clean_name)
# 使用更安全的哈希处理冲突
unique_id = hashlib.md5(filename.encode()).hexdigest()[:8]
temp_name = f"{base}_{unique_id}{ext}"
try:
os.rename(filename, temp_name)
renamed_log.append((filename, temp_name))
mp3_files[idx] = temp_name
except Exception as e:
mp3_files.pop(idx) # 移除无法处理项
print(f"\n⚠️ 改名失败:{filename} → {temp_name} - {str(e)}")
processed += 1
if processed % 10 == 0: # 降低进度刷新频率
print(f"\r? 扫描中... {processed}/{total_files}", end='', flush=True)
print("\n\n? 扫描完成,开始时间设置...")
# 第四步:预计算时间序列
start_dt = datetime.now() - timedelta(minutes=1)
time_sequence = [start_dt + timedelta(seconds=i*2) for i in range(len(mp3_files))]
# 第五步:安全设置文件时间并更新ID3标签
success_count = 0
for idx, (filename, target_dt) in enumerate(zip(mp3_files, time_sequence)):
file_path = os.path.abspath(filename)
try:
# 创建时间对象
py_time = pywintypes.Time(target_dt)
timestamp = target_dt.timestamp()
# 显式文件句柄管理
h_file = win32file.CreateFile(
file_path,
win32con.GENERIC_WRITE,
win32con.FILE_SHARE_READ | win32con.FILE_SHARE_WRITE,
None,
win32con.OPEN_EXISTING,
win32con.FILE_ATTRIBUTE_NORMAL,
None
)
# 设置时间并立即关闭
win32file.SetFileTime(h_file, py_time, py_time, py_time)
h_file.Close()
# 同步Unix时间戳
os.utime(file_path, (timestamp, timestamp))
# 自动修复损坏的ID3标签并更新Track Number字段
audiofile = eyed3.load(file_path)
if audiofile.tag is None:
audiofile.initTag()
audiofile.tag.track_num = (idx + 1, len(mp3_files))
audiofile.tag.save()
success_count += 1
if idx % 10 == 0: # 降低进度刷新频率
print(f"\r✅ 已处理 {idx+1}/{len(mp3_files)}", end='', flush=True)
except Exception as e:
print(f"\n? 失败({idx+1}): {filename} - {str(e)}")
finally:
# 确保句柄关闭
if 'h_file' in locals():
try: h_file.Close()
except: pass
# 结果输出(保持原样)
print(f"\n\n? 操作完成!共处理{len(mp3_files)}个文件")
print(f" ✅ 成功:{success_count}个 | ❌ 失败:{len(mp3_files)-success_count}个")
if renamed_log:
print("\n? 文件名优化示例:")
print(f" 原文件名 → 新文件名(前3条)")
for old, new in renamed_log[:3]:
print(f" {old} → {new}")
if len(renamed_log) > 3:
print(f" ...(共{len(renamed_log)}条,完整记录见同目录rename.log)")
with open('rename.log', 'w', encoding='utf-8') as f:
f.write('\n'.join([f"{old} → {new}" for old, new in renamed_log]))
print("\n? 三重验证方法:")
print(" 1. 资源管理器地址栏输入:?sort=name → 应与时间排序一致")
print(" 2. 按住Shift右键→属性:创建/修改时间均为设定值(精确到秒)")
print(" 3. 播放测试:车载/音箱按时间排序应与文件名顺序完全一致")
print("\n? 提示:若处理超500个文件,建议将time_step调至5秒以上")