From 00d644ef07a166ea46321b2aa5ccdf3b07ff535e Mon Sep 17 00:00:00 2001 From: Colin Date: Thu, 11 Jul 2024 03:30:42 +0000 Subject: [PATCH] sane-tag-music: support "artist.png" artist images --- .../sane-scripts/src/sane-tag-music | 39 +++++++++++++++++-- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/pkgs/additional/sane-scripts/src/sane-tag-music b/pkgs/additional/sane-scripts/src/sane-tag-music index cc3d00767..ad1a6f53d 100755 --- a/pkgs/additional/sane-scripts/src/sane-tag-music +++ b/pkgs/additional/sane-scripts/src/sane-tag-music @@ -94,6 +94,12 @@ logger = logging.getLogger(__name__) kks = pykakasi.kakasi() +IMAGE_EXTENSIONS = [ + "jpg", + "jpeg", + "png", +] + class MediaType(Enum): Audio = "audio" Image = "image" @@ -333,9 +339,24 @@ class Tags: if loose_compare_str(self.album[0], "Singles"): self.tracknumber = [] + def is_artist_item(self, ext: str) -> bool: + """ + some items are associated with the artist instead of with an album. + for example, `artist.png`. + it's tough to distinguish such an item from an incomplete tag, + so we special-case them. + """ + title = self.title[0].lower() if len(self.title) == 1 else "" + return ext.lower() in IMAGE_EXTENSIONS \ + and not self.producer \ + and (self.albumartist or self.artist) \ + and not self.album \ + and title == "artist" + def to_path(self, ext: str) -> str | None: + is_artist_item = self.is_artist_item(ext) artist = self.albumartist or self.artist - if not (artist and self.album and self.title and ext): + if not (artist and self.album and self.title and ext or is_artist_item): return None artist = clean_fields_for_fs(artist, single_fields=False) @@ -345,6 +366,9 @@ class Tags: title = clean_fields_for_fs(self.title) filename = clean_fields_for_fs(trackno + [ f"{title}.{ext}" ]) + if is_artist_item: + return os.path.join(artist, filename) + if not (artist and album and title): return None @@ -459,13 +483,17 @@ class MediaFile: def __repr__(self) -> str: return self.path_ + @property + def ext(self): + return os.path.splitext(self.path_)[1][1:] + @staticmethod def new(f: 'str | MediaFile | None') -> 'MediaFile | None': if f is None: return None if isinstance(f, str): lower = f.lower() - if lower.endswith(".png") or lower.endswith(".jpg") or lower.endswith("jpeg"): + if any(lower.endswith(f".{e}") for e in IMAGE_EXTENSIONS): f = ImageFile(f) else: f = AudioFile(f) @@ -630,7 +658,12 @@ class Tagger: def is_sufficiently_tagged(self, file_: MediaFile) -> bool: tags = file_.tags_on_disk() # N.B.: track number isn't wholly necessary; just a nice-to-have. - return (tags.artist or tags.albumartist) and tags.album and tags.title + if (tags.artist or tags.albumartist) and tags.album and tags.title: + return True + if tags.is_artist_item(file_.ext): + # artist image, not part of any album + return True + return False def tag_file(self, file_: MediaFile) -> None: old_tags = file_.tags_on_disk()