From 6edc6841bf1dce98c8c6c37e48a5749530529701 Mon Sep 17 00:00:00 2001 From: Colin Date: Wed, 29 Nov 2023 12:52:35 +0000 Subject: [PATCH] sane-tag-music: support directory/tree operations --- .../sane-scripts/src/sane-tag-music | 60 +++++++++++++++---- 1 file changed, 48 insertions(+), 12 deletions(-) diff --git a/pkgs/additional/sane-scripts/src/sane-tag-music b/pkgs/additional/sane-scripts/src/sane-tag-music index e0a384cae..b07dedeb5 100755 --- a/pkgs/additional/sane-scripts/src/sane-tag-music +++ b/pkgs/additional/sane-scripts/src/sane-tag-music @@ -1,5 +1,8 @@ #!/usr/bin/env nix-shell #!nix-shell -i python3 -p "python3.withPackages (ps: [ ps.mutagen ])" +# +# mutagen docs: +# - from dataclasses import dataclass @@ -41,16 +44,28 @@ class Tags: return f"artist:{self.artist}/{self.albumartist}, album:{self.album}, title:{self.title}, trackno:{self.tracknumber}" def union(self, fallback: 'Tags') -> 'Tags': - def merge_field(first: list[str], second: list[str]) -> list[str]: - first_lower = [i.lower() for i in first] - return first + [i for i in second if i.lower() not in first_lower] + def merge_field(primary: list[str], secondary: list[str]) -> list[str]: + # primary_lower = [i.lower() for i in primary] + # return primary + [i for i in secondary if i.lower() not in primary_lower] + return primary or secondary + + + artist=merge_field(self.artist, fallback.artist) + album=merge_field(self.album, fallback.album) + title=merge_field(self.title, fallback.title) + albumartist=merge_field(self.albumartist, fallback.albumartist) + tracknumber=merge_field(self.tracknumber, fallback.tracknumber) + + if artist == albumartist: + # if extraneous, then keep the album artist whatever it originally was + albumartist = self.albumartist return Tags( - artist=merge_field(self.artist, fallback.artist), - album=merge_field(self.album, fallback.album), - title=merge_field(self.title, fallback.title), - albumartist=merge_field(self.albumartist, fallback.albumartist), - tracknumber=merge_field(self.tracknumber, fallback.tracknumber), + artist=artist, + album=album, + title=title, + albumartist=albumartist, + tracknumber=tracknumber, ) def promote_albumartist(self) -> None: @@ -75,7 +90,7 @@ class Tags: comps = p.split('/') tags = Tags() if len(comps) == 3: - tags.albumartist = comps[0] + tags.albumartist = [comps[0]] album_part = comps[1].split('-') if len(album_part) == 2: # artist/artist-album/track @@ -85,7 +100,7 @@ class Tags: tags.album = [comps[1]] track_part = comps[2].split('-') if len(track_part) == 1: - tags.title = [comps[2]] + tags.title = [os.path.splitext(comps[2])[0]] # TODO: handle the else case return tags @@ -104,6 +119,12 @@ class AudioFile: else: self.muta = None + @staticmethod + def new(path_: str) -> 'AudioFile': + f = AudioFile(path_) + if f.muta is not None: + return f + def tags_on_disk(self) -> Tags: return Tags( artist=self.muta.get('artist', []) if self.muta else [], @@ -131,7 +152,11 @@ class Tagger: self.dry_run = dry_run def tag_file(self, path_: str) -> None: - file_ = AudioFile(path_) + file_ = AudioFile.new(path_) + if not file_: + logger.debug(f"skipping unsupported file: {path_}") + return + old_tags = file_.tags_on_disk() path_tags = Tags.from_path(path_) @@ -147,6 +172,17 @@ class Tagger: if self.guard_dry_run("writing tags"): file_.write_tags(new_tags) + def tag_file_tree(self, root: str) -> None: + for dir_, subdirs, files_ in os.walk(root): + for f in files_: + self.tag_file(os.path.join(dir_, f)) + + def tag_file_or_tree(self, path_: str) -> None: + if os.path.isdir(path_): + self.tag_file_tree(path_) + else: + self.tag_file(path_) + def show_tagdif(self, path_: str, old_tags: Tags, new_tags: Tags): logger.info(f"updating tags for {path_}") logger.info(f" {old_tags}") @@ -183,7 +219,7 @@ def main(): logging.getLogger().setLevel(logging.DEBUG) tagger = Tagger(dry_run=args.dry_run) - tagger.tag_file(args.path) + tagger.tag_file_or_tree(args.path) if __name__ == '__main__': main()