#!/usr/bin/env python3 """ Добавляет название аниме (из директории) в конец названия файла перед расширением и удаляет "[AniTousen]" из начала названия. Пример: Директория: Kuroshitsuji Файл: [AniTousen] TV-1 ED01 - I'm ALIVE! (BECCA).mp3 Результат: TV-1 ED01 - I'm ALIVE! (BECCA) [Kuroshitsuji].mp3 Использование: python3 rename_anime.py test python3 rename_anime.py run --dry-run /path/to/music python3 rename_anime.py run /path/to/music """ import os import sys import argparse from pathlib import Path AUDIO_EXTENSIONS = {".flac", ".mp3", ".ogg", ".opus", ".m4a"} def remove_ani_tousen(stem: str) -> str: """ Удаляет "[AniTousen]" из начала названия файла. Было: [AniTousen] TV-1 ED01 - I'm ALIVE! (BECCA) Стало: TV-1 ED01 - I'm ALIVE! (BECCA) """ # Проверяем начинается ли с [AniTousen] if stem.startswith("[AniTousen] "): return stem[len("[AniTousen] "):] elif stem.startswith("[AniTousen]"): return stem[len("[AniTousen]"):].lstrip() return stem def rename_file(filepath: Path, anime_source: str, dry_run: bool = False) -> bool: """ Переименовать файл: добавить название аниме в конец и удалить [AniTousen] из начала. Было: [AniTousen] TV-1 ED01 - I'm ALIVE! (BECCA).mp3 Стало: TV-1 ED01 - I'm ALIVE! (BECCA) [Kuroshitsuji].mp3 """ if dry_run: return True old_name = filepath.name stem = filepath.stem suffix = filepath.suffix # Удаляем [AniTousen] из начала new_stem = remove_ani_tousen(stem) # Добавляем название аниме в конец new_stem = f"{new_stem} [{anime_source}]" new_name = f"{new_stem}{suffix}" new_path = filepath.parent / new_name # Проверяем, не существует ли уже файл с таким именем if new_path.exists(): print(f" ⚠ Файл уже существует: {new_name}") return False try: filepath.rename(new_path) return True except Exception as e: print(f" ✗ Ошибка переименования: {e}") return False def process_directory(root_path: str, dry_run: bool = False): """Обработать директорию.""" root = Path(root_path) if not root.is_dir(): print(f"Директория не найдена: {root_path}") sys.exit(1) prefix = "[DRY] " if dry_run else "" renamed = 0 errors = 0 skipped = 0 for anime_dir in sorted(root.iterdir()): if not anime_dir.is_dir(): continue # Пропускаем скрытые директории if anime_dir.name.startswith('.'): continue anime_source = anime_dir.name # Находим все аудиофайлы рекурсивно audio_files = sorted([ f for f in anime_dir.rglob("*") if f.is_file() and f.suffix.lower() in AUDIO_EXTENSIONS ]) if not audio_files: continue print(f"\n{prefix}📁 {anime_source} ({len(audio_files)} файлов)") for filepath in audio_files: # Проверяем, не добавлено ли уже название аниме stem = filepath.stem if stem.endswith(f" [{anime_source}]"): skipped += 1 print(f" ⊘ {filepath.name} (уже переименован)") continue success = rename_file(filepath, anime_source, dry_run) if success: renamed += 1 old_name = filepath.name new_name = f"{filepath.stem} [{anime_source}]{filepath.suffix}" print(f" ✓ {old_name}") print(f" → {new_name}") else: errors += 1 print(f"\n{'=' * 60}") print(f" {'Будет переименовано' if dry_run else 'Переименовано'}: {renamed}") print(f" Пропущено (уже переименовано): {skipped}") print(f" Ошибок: {errors}") print(f"{'=' * 60}") def test(): """Тест логики переименования.""" examples = [ # (старое_имя, название_папки, ожидаемое_новое_имя) ( "[AniTousen] TV-1 ED01 - I'm ALIVE! (BECCA).mp3", "Kuroshitsuji", "TV-1 ED01 - I'm ALIVE! (BECCA) [Kuroshitsuji].mp3" ), ( "[AniTousen] TV-2 ED EP12 - Get up! (Asano Masumi).flac", "Ikkitousen", "TV-2 ED EP12 - Get up! (Asano Masumi) [Ikkitousen].flac" ), ( "[AniTousen] Movie ED - Song Name.wav", "Your Name", "Movie ED - Song Name [Your Name].wav" ), # Файл без [AniTousen] - просто добавляем аниме ( "TV OP - Song.mp3", "Test Anime", "TV OP - Song [Test Anime].mp3" ), ] print("Тест переименования:\n") ok = 0 for old_name, anime, expected_new in examples: stem = Path(old_name).stem suffix = Path(old_name).suffix new_stem = f"{stem} [{anime}]" new_name = f"{new_stem}{suffix}" match = new_name == expected_new status = "✓" if match else "✗" if match: ok += 1 print(f" {status} {old_name}") print(f" → {new_name}") if not match: print(f" ОЖИДАЛОСЬ: {expected_new}") print() print(f"Пройдено: {ok}/{len(examples)}") def main(): parser = argparse.ArgumentParser(description="Переименование аниме саундтреков") sub = parser.add_subparsers(dest="command") p_run = sub.add_parser("run", help="Переименовать файлы") p_run.add_argument("directory", help="Директория с музыкой") p_run.add_argument("--dry-run", action="store_true", help="Показать что будет сделано без изменений") sub.add_parser("test", help="Тест логики") args = parser.parse_args() if args.command == "test": test() elif args.command == "run": process_directory(args.directory, args.dry_run) else: parser.print_help() if __name__ == "__main__": main()