Compare commits

...

3 Commits

Author SHA1 Message Date
fb031fcaf6
Implement move instruction generation 2025-06-01 12:11:35 +02:00
86a870d429
Avoid shutil where we can 2025-06-01 11:30:49 +02:00
3d9f8d46ee
Implement move instruction 2025-06-01 10:58:40 +02:00
2 changed files with 48 additions and 2 deletions

View File

@ -15,4 +15,7 @@ class MoveInstruction(Instruction):
new_path: Path new_path: Path
def __call__(self) -> None: def __call__(self) -> None:
raise NotImplementedError() # TODO if self.new_path.exists():
raise FileExistsError("New path already exists, not moving anything")
self.old_path.rename(self.new_path)

View File

@ -1,13 +1,56 @@
import re
from collections.abc import Sequence from collections.abc import Sequence
from pathlib import Path from pathlib import Path
from karaokatalog.instructions.MoveInstruction import MoveInstruction from karaokatalog.instructions.MoveInstruction import MoveInstruction
from karaokatalog.Song import Song from karaokatalog.Song import Song
FORBIDDEN_CHARACTERS = re.compile(r'[/<>:"\\|?*]')
def _get_canonical_song_dir(song: Song, variant: int = 0) -> Path:
"""
Get the (relative) canonical directory in which this song should be located, that is
the directory f"{song.artist}/{song.title}".
Illegal characters are replaced. If variant > 0, f" ({variant})" is appended to the result.
"""
variant_suffix = f" ({variant})" if variant > 0 else ""
return Path(FORBIDDEN_CHARACTERS.sub("", song.artist)) / Path(
FORBIDDEN_CHARACTERS.sub("", song.title) + variant_suffix
)
def move(songs: Sequence[Song], base_dir: Path) -> Sequence[MoveInstruction]: def move(songs: Sequence[Song], base_dir: Path) -> Sequence[MoveInstruction]:
""" """
Create move instructions to move every song into the proper song directory Create move instructions to move every song into the proper song directory
within the given base_dir. within the given base_dir.
""" """
raise NotImplementedError() # TODO song_dirs = set(song.dir.relative_to(base_dir) for song in songs)
move_instructions = []
for song in songs:
absolute_song_dir = song.dir
song_dir = absolute_song_dir.relative_to(base_dir)
canonical_song_dir = _get_canonical_song_dir(song)
if song_dir not in song_dirs:
# A move instruction has already been generated for the dir this song is in
# (which is possible, because some dirs contain multiple (variants of) songs)
continue
if song_dir != canonical_song_dir:
# song_dir is not a good name, we want to replace it, so this path will soon be free
song_dirs.remove(song_dir)
# Find a canonical song dir variant that does not yet exist
variant = 1
while canonical_song_dir in song_dirs:
canonical_song_dir = _get_canonical_song_dir(song, variant)
song_dirs.add(canonical_song_dir)
move_instructions.append(
MoveInstruction(absolute_song_dir, base_dir / canonical_song_dir)
)
return tuple(move_instructions)