61 lines
1.6 KiB
Python
61 lines
1.6 KiB
Python
"""Shared utilities for file naming and organization."""
|
|
|
|
import os
|
|
import re
|
|
from pathlib import Path
|
|
|
|
import mutagen
|
|
|
|
|
|
def sanitize_filename(name):
|
|
"""Remove characters prohibited in Windows filenames."""
|
|
name = re.sub(r'[<>:"/\\|?*]', '_', name)
|
|
return name.strip().strip('.')
|
|
|
|
|
|
def rename_from_metadata(file_path):
|
|
"""Rename an audio file to 'Title - Artist1, Artist2.ext' using embedded metadata.
|
|
|
|
Returns the new Path (or original if rename not possible).
|
|
"""
|
|
file_path = Path(file_path)
|
|
audio = mutagen.File(str(file_path), easy=True)
|
|
if audio is None:
|
|
return file_path
|
|
|
|
try:
|
|
title = audio.get("title", [None])[0]
|
|
artist = audio.get("artist", [None])[0]
|
|
except (IndexError, KeyError):
|
|
return file_path
|
|
|
|
if not title or not artist:
|
|
return file_path
|
|
|
|
new_name = sanitize_filename(f"{title} - {artist}{file_path.suffix}")
|
|
new_path = file_path.parent / new_name
|
|
|
|
if new_path == file_path:
|
|
return file_path
|
|
|
|
# Handle collisions
|
|
counter = 2
|
|
base_stem = new_path.stem
|
|
while new_path.exists():
|
|
new_path = new_path.with_stem(f"{base_stem} ({counter})")
|
|
counter += 1
|
|
|
|
file_path.rename(new_path)
|
|
return new_path
|
|
|
|
|
|
def cleanup_empty_dirs(directory):
|
|
"""Remove empty subdirectories (bottom-up)."""
|
|
directory = Path(directory)
|
|
for dirpath, dirnames, filenames in os.walk(str(directory), topdown=False):
|
|
dirpath = Path(dirpath)
|
|
if dirpath == directory:
|
|
continue
|
|
if not any(dirpath.iterdir()):
|
|
dirpath.rmdir()
|