75 lines
2.1 KiB
Python

from __future__ import annotations
import filecmp
from dataclasses import dataclass
from pathlib import Path
from typing import Self
from karaokatalog.parse_song_txt import parse_song_txt
type Title = str
type Artist = str
@dataclass(frozen=True)
class Song:
"""
A song, represented by a txt file and accompanying files (most importantly, an audio file).
See https://usdx.eu/format/ for the specification.
"""
title: Title
artist: Artist
audio: Path | None
video: Path | None
cover: Path | None
song_txt: Path
@property
def dir(self) -> Path:
return self.song_txt.parent
def has_identic_files(self, other: Song) -> bool:
"""
Return if the directory for this song and the directory for the other song contain identic files.
"""
comparison = filecmp.dircmp(self.dir, other.dir, shallow=False)
# Two directories are identic if they don't have any differing files, and if there were also no
# errors during comparison.
#
# See also:
# - https://docs.python.org/3/library/filecmp.html#filecmp.dircmp.diff_files
return not comparison.diff_files and not comparison.funny_files
@classmethod
def from_song_txt(cls, song_txt: Path) -> Self | None:
tags = parse_song_txt(song_txt)
title = tags.get("TITLE")
artist = tags.get("ARTIST")
audio_name = tags.get("AUDIO", tags.get("MP3"))
video_name = tags.get("VIDEO")
cover_name = tags.get("COVER")
if not title or not artist:
# Both are mandatory according to the specification
return None
return cls(
title=title,
artist=artist,
audio=song_txt / audio_name if audio_name else None,
video=song_txt / video_name if video_name else None,
cover=song_txt / cover_name if cover_name else None,
song_txt=song_txt,
)
def as_dict(self) -> dict[str, str]:
return {
"title": self.title,
"artist": self.artist,
# TODO More fields
}