utility-scripts/rename_anime.py
shu 70814ddc1f import: anime utility scripts from /storage/scripts/
Перенос из vespaserver:/storage/scripts/ перед удалением оригиналов.
Скрипты разовые — клонируются по мере необходимости.
2026-04-29 18:24:58 +03:00

209 lines
6.8 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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()