import: anime utility scripts from /storage/scripts/
Перенос из vespaserver:/storage/scripts/ перед удалением оригиналов. Скрипты разовые — клонируются по мере необходимости.
This commit is contained in:
parent
67b460d40c
commit
70814ddc1f
5 changed files with 1918 additions and 2 deletions
209
rename_anime.py
Normal file
209
rename_anime.py
Normal file
|
|
@ -0,0 +1,209 @@
|
|||
#!/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()
|
||||
Loading…
Add table
Add a link
Reference in a new issue