From 981d4dc829b91c8bbc3d1821a93a6c68cedc9e86 Mon Sep 17 00:00:00 2001 From: soergeld Date: Sat, 25 Apr 2020 19:50:59 +0200 Subject: [PATCH 01/29] First try adding new albuminfo and trackinfo class --- beets/autotag/hooks.py | 171 ++++++++++++----------------------------- 1 file changed, 51 insertions(+), 120 deletions(-) diff --git a/beets/autotag/hooks.py b/beets/autotag/hooks.py index d7c701db6..2555ed69f 100644 --- a/beets/autotag/hooks.py +++ b/beets/autotag/hooks.py @@ -39,8 +39,51 @@ except AttributeError: # Classes used to represent candidate options. +class Map(dict): + """ + Example: + m = Map({'first_name': 'Eduardo'}, last_name='Pool', age=24, sports=['Soccer']) + """ + def __init__(self, *args, **kwargs): + super(Map, self).__init__(*args, **kwargs) + for arg in args: + if isinstance(arg, dict): + for k, v in arg.iteritems(): + self[k] = v + + if kwargs: + for k, v in kwargs.iteritems(): + self[k] = v + + def __getattr__(self, attr): + return self.get(attr) + + def __setattr__(self, key, value): + self.__setitem__(key, value) + + def __setitem__(self, key, value): + super(Map, self).__setitem__(key, value) + self.__dict__.update({key: value}) + + def __delattr__(self, item): + self.__delitem__(item) + + def __delitem__(self, key): + super(Map, self).__delitem__(key) + del self.__dict__[key] + + def __getstate__(self): + return self.__dict__ + + def __setstate__(self,state): + for key in state: + self.__setattr__(key,state[key]) + def __hash__(self): + return hash(tuple(sorted(self.items()))) -class AlbumInfo(object): + + +class AlbumInfo(Map): """Describes a canonical release that may be used to match a release in the library. Consists of these data members: @@ -49,77 +92,18 @@ class AlbumInfo(object): - ``artist``: name of the release's primary artist - ``artist_id`` - ``tracks``: list of TrackInfo objects making up the release - - ``asin``: Amazon ASIN - - ``albumtype``: string describing the kind of release - - ``va``: boolean: whether the release has "various artists" - - ``year``: release year - - ``month``: release month - - ``day``: release day - - ``label``: music label responsible for the release - - ``mediums``: the number of discs in this release - - ``artist_sort``: name of the release's artist for sorting - - ``releasegroup_id``: MBID for the album's release group - - ``catalognum``: the label's catalog number for the release - - ``script``: character set used for metadata - - ``language``: human language of the metadata - - ``country``: the release country - - ``albumstatus``: MusicBrainz release status (Official, etc.) - - ``media``: delivery mechanism (Vinyl, etc.) - - ``albumdisambig``: MusicBrainz release disambiguation comment - - ``releasegroupdisambig``: MusicBrainz release group - disambiguation comment. - - ``artist_credit``: Release-specific artist name - - ``data_source``: The original data source (MusicBrainz, Discogs, etc.) - - ``data_url``: The data source release URL. ``mediums`` along with the fields up through ``tracks`` are required. The others are optional and may be None. """ - def __init__(self, album, album_id, artist, artist_id, tracks, asin=None, - albumtype=None, va=False, year=None, month=None, day=None, - label=None, mediums=None, artist_sort=None, - releasegroup_id=None, catalognum=None, script=None, - language=None, country=None, style=None, genre=None, - albumstatus=None, media=None, albumdisambig=None, - releasegroupdisambig=None, artist_credit=None, - original_year=None, original_month=None, - original_day=None, data_source=None, data_url=None, - discogs_albumid=None, discogs_labelid=None, - discogs_artistid=None): + def __init__(self, album, album_id, artist, artist_id, tracks, **kwargs): self.album = album self.album_id = album_id self.artist = artist self.artist_id = artist_id self.tracks = tracks - self.asin = asin - self.albumtype = albumtype - self.va = va - self.year = year - self.month = month - self.day = day - self.label = label - self.mediums = mediums - self.artist_sort = artist_sort - self.releasegroup_id = releasegroup_id - self.catalognum = catalognum - self.script = script - self.language = language - self.country = country - self.style = style - self.genre = genre - self.albumstatus = albumstatus - self.media = media - self.albumdisambig = albumdisambig - self.releasegroupdisambig = releasegroupdisambig - self.artist_credit = artist_credit - self.original_year = original_year - self.original_month = original_month - self.original_day = original_day - self.data_source = data_source - self.data_url = data_url - self.discogs_albumid = discogs_albumid - self.discogs_labelid = discogs_labelid - self.discogs_artistid = discogs_artistid + for arg in kwargs: + self.__setattr__(arg,kwargs[arg]) # Work around a bug in python-musicbrainz-ngs that causes some # strings to be bytes rather than Unicode. @@ -143,75 +127,22 @@ class AlbumInfo(object): track.decode(codec) -class TrackInfo(object): +class TrackInfo(Map): """Describes a canonical track present on a release. Appears as part of an AlbumInfo's ``tracks`` list. Consists of these data members: - ``title``: name of the track - ``track_id``: MusicBrainz ID; UUID fragment only - - ``release_track_id``: MusicBrainz ID respective to a track on a - particular release; UUID fragment only - - ``artist``: individual track artist name - - ``artist_id`` - - ``length``: float: duration of the track in seconds - - ``index``: position on the entire release - - ``media``: delivery mechanism (Vinyl, etc.) - - ``medium``: the disc number this track appears on in the album - - ``medium_index``: the track's position on the disc - - ``medium_total``: the number of tracks on the item's disc - - ``artist_sort``: name of the track artist for sorting - - ``disctitle``: name of the individual medium (subtitle) - - ``artist_credit``: Recording-specific artist name - - ``data_source``: The original data source (MusicBrainz, Discogs, etc.) - - ``data_url``: The data source release URL. - - ``lyricist``: individual track lyricist name - - ``composer``: individual track composer name - - ``composer_sort``: individual track composer sort name - - ``arranger`: individual track arranger name - - ``track_alt``: alternative track number (tape, vinyl, etc.) - - ``work`: individual track work title - - ``mb_workid`: individual track work id - - ``work_disambig`: individual track work diambiguation Only ``title`` and ``track_id`` are required. The rest of the fields may be None. The indices ``index``, ``medium``, and ``medium_index`` are all 1-based. """ - def __init__(self, title, track_id, release_track_id=None, artist=None, - artist_id=None, length=None, index=None, medium=None, - medium_index=None, medium_total=None, artist_sort=None, - disctitle=None, artist_credit=None, data_source=None, - data_url=None, media=None, lyricist=None, composer=None, - composer_sort=None, arranger=None, track_alt=None, - work=None, mb_workid=None, work_disambig=None, bpm=None, - initial_key=None, genre=None): + def __init__(self, title, track_id,**kwargs): self.title = title self.track_id = track_id - self.release_track_id = release_track_id - self.artist = artist - self.artist_id = artist_id - self.length = length - self.index = index - self.media = media - self.medium = medium - self.medium_index = medium_index - self.medium_total = medium_total - self.artist_sort = artist_sort - self.disctitle = disctitle - self.artist_credit = artist_credit - self.data_source = data_source - self.data_url = data_url - self.lyricist = lyricist - self.composer = composer - self.composer_sort = composer_sort - self.arranger = arranger - self.track_alt = track_alt - self.work = work - self.mb_workid = mb_workid - self.work_disambig = work_disambig - self.bpm = bpm - self.initial_key = initial_key - self.genre = genre + for arg in kwargs: + self.__setattr__(arg,kwargs[arg]) # As above, work around a bug in python-musicbrainz-ngs. def decode(self, codec='utf-8'): From da43ff9c188276548c30237e6d93c6c45106bebe Mon Sep 17 00:00:00 2001 From: soergeld Date: Sat, 25 Apr 2020 20:43:30 +0200 Subject: [PATCH 02/29] arrange __getattr__ to behave normally --- beets/autotag/hooks.py | 94 ++++++++++++++++++++++-------------------- 1 file changed, 49 insertions(+), 45 deletions(-) diff --git a/beets/autotag/hooks.py b/beets/autotag/hooks.py index 2555ed69f..cc8e6a68a 100644 --- a/beets/autotag/hooks.py +++ b/beets/autotag/hooks.py @@ -39,49 +39,53 @@ except AttributeError: # Classes used to represent candidate options. -class Map(dict): - """ - Example: - m = Map({'first_name': 'Eduardo'}, last_name='Pool', age=24, sports=['Soccer']) - """ - def __init__(self, *args, **kwargs): - super(Map, self).__init__(*args, **kwargs) - for arg in args: - if isinstance(arg, dict): - for k, v in arg.iteritems(): - self[k] = v - - if kwargs: - for k, v in kwargs.iteritems(): - self[k] = v - - def __getattr__(self, attr): - return self.get(attr) - - def __setattr__(self, key, value): - self.__setitem__(key, value) - - def __setitem__(self, key, value): - super(Map, self).__setitem__(key, value) - self.__dict__.update({key: value}) - - def __delattr__(self, item): - self.__delitem__(item) - - def __delitem__(self, key): - super(Map, self).__delitem__(key) - del self.__dict__[key] - - def __getstate__(self): - return self.__dict__ - - def __setstate__(self,state): - for key in state: - self.__setattr__(key,state[key]) - def __hash__(self): - return hash(tuple(sorted(self.items()))) +class Map(dict): + """ + Example: + m = Map({'first_name': 'Eduardo'}, last_name='Pool', age=24, + sports=['Soccer']) + """ + def __init__(self, *args, **kwargs): + super(Map, self).__init__(*args, **kwargs) + for arg in args: + if isinstance(arg, dict): + for k, v in arg.iteritems(): + self[k] = v + + if kwargs: + for k, v in kwargs.iteritems(): + self[k] = v + + def __getattr__(self, attr): + if attr in self: + return self.get(attr) + else: + raise AttributeError + + def __setattr__(self, key, value): + self.__setitem__(key, value) + + def __setitem__(self, key, value): + super(Map, self).__setitem__(key, value) + self.__dict__.update({key: value}) + + def __delattr__(self, item): + self.__delitem__(item) + + def __delitem__(self, key): + super(Map, self).__delitem__(key) + del self.__dict__[key] + + def __getstate__(self): + return self.__dict__ + + def __setstate__(self, state): + for key in state: + self.__setattr__(key, state[key]) + + def __hash__(self): + return hash(tuple(sorted(self.items()))) - class AlbumInfo(Map): """Describes a canonical release that may be used to match a release @@ -103,7 +107,7 @@ class AlbumInfo(Map): self.artist_id = artist_id self.tracks = tracks for arg in kwargs: - self.__setattr__(arg,kwargs[arg]) + self.__setattr__(arg, kwargs[arg]) # Work around a bug in python-musicbrainz-ngs that causes some # strings to be bytes rather than Unicode. @@ -138,11 +142,11 @@ class TrackInfo(Map): may be None. The indices ``index``, ``medium``, and ``medium_index`` are all 1-based. """ - def __init__(self, title, track_id,**kwargs): + def __init__(self, title, track_id, **kwargs): self.title = title self.track_id = track_id for arg in kwargs: - self.__setattr__(arg,kwargs[arg]) + self.__setattr__(arg, kwargs[arg]) # As above, work around a bug in python-musicbrainz-ngs. def decode(self, codec='utf-8'): From fea6ffc0386eed693fdeef27556635eef4d44bbd Mon Sep 17 00:00:00 2001 From: soergeld Date: Sat, 25 Apr 2020 22:24:24 +0200 Subject: [PATCH 03/29] arrange decode, set all attributes to be flexible --- beets/autotag/hooks.py | 39 ++++++++++++--------------------------- 1 file changed, 12 insertions(+), 27 deletions(-) diff --git a/beets/autotag/hooks.py b/beets/autotag/hooks.py index cc8e6a68a..dd9726b84 100644 --- a/beets/autotag/hooks.py +++ b/beets/autotag/hooks.py @@ -100,14 +100,9 @@ class AlbumInfo(Map): ``mediums`` along with the fields up through ``tracks`` are required. The others are optional and may be None. """ - def __init__(self, album, album_id, artist, artist_id, tracks, **kwargs): - self.album = album - self.album_id = album_id - self.artist = artist - self.artist_id = artist_id - self.tracks = tracks + def __init__(self, **kwargs): for arg in kwargs: - self.__setattr__(arg, kwargs[arg]) + self.__setattr__(arg, kwargs[arg]) # Work around a bug in python-musicbrainz-ngs that causes some # strings to be bytes rather than Unicode. @@ -116,19 +111,11 @@ class AlbumInfo(Map): """Ensure that all string attributes on this object, and the constituent `TrackInfo` objects, are decoded to Unicode. """ - for fld in ['album', 'artist', 'albumtype', 'label', 'artist_sort', - 'catalognum', 'script', 'language', 'country', 'style', - 'genre', 'albumstatus', 'albumdisambig', - 'releasegroupdisambig', 'artist_credit', - 'media', 'discogs_albumid', 'discogs_labelid', - 'discogs_artistid']: + for fld in self: value = getattr(self, fld) - if isinstance(value, bytes): - setattr(self, fld, value.decode(codec, 'ignore')) - - if self.tracks: - for track in self.tracks: - track.decode(codec) + if type(value) == str: + if isinstance(value, bytes): + setattr(self, fld, value.decode(codec, 'ignore')) class TrackInfo(Map): @@ -142,22 +129,20 @@ class TrackInfo(Map): may be None. The indices ``index``, ``medium``, and ``medium_index`` are all 1-based. """ - def __init__(self, title, track_id, **kwargs): - self.title = title - self.track_id = track_id + def __init__(self, **kwargs): for arg in kwargs: - self.__setattr__(arg, kwargs[arg]) + self.__setattr__(arg, kwargs[arg]) # As above, work around a bug in python-musicbrainz-ngs. def decode(self, codec='utf-8'): """Ensure that all string attributes on this object are decoded to Unicode. """ - for fld in ['title', 'artist', 'medium', 'artist_sort', 'disctitle', - 'artist_credit', 'media']: + for fld in self: value = getattr(self, fld) - if isinstance(value, bytes): - setattr(self, fld, value.decode(codec, 'ignore')) + if type(value) == str: + if isinstance(value, bytes): + setattr(self, fld, value.decode(codec, 'ignore')) # Candidate distance scoring. From 53ce6f8b3d7d62c4ca54966042530f6c6e7d7c45 Mon Sep 17 00:00:00 2001 From: soergeld Date: Sat, 25 Apr 2020 22:32:03 +0200 Subject: [PATCH 04/29] all attributes are flexible, so no positional arguments when initiating the class --- beets/autotag/mb.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/beets/autotag/mb.py b/beets/autotag/mb.py index f86d3be71..ea8ef24da 100644 --- a/beets/autotag/mb.py +++ b/beets/autotag/mb.py @@ -193,8 +193,8 @@ def track_info(recording, index=None, medium=None, medium_index=None, the number of tracks on the medium. Each number is a 1-based index. """ info = beets.autotag.hooks.TrackInfo( - recording['title'], - recording['id'], + title=recording['title'], + track_id=recording['id'], index=index, medium=medium, medium_index=medium_index, @@ -341,11 +341,11 @@ def album_info(release): track_infos.append(ti) info = beets.autotag.hooks.AlbumInfo( - release['title'], - release['id'], - artist_name, - release['artist-credit'][0]['artist']['id'], - track_infos, + album=release['title'], + album_id=release['id'], + artist=artist_name, + artist_id=release['artist-credit'][0]['artist']['id'], + tracks=track_infos, mediums=len(release['medium-list']), artist_sort=artist_sort_name, artist_credit=artist_credit_name, From 1b2c8398b11ade93621583e06c0aea24b83c39b5 Mon Sep 17 00:00:00 2001 From: soergeld Date: Sat, 25 Apr 2020 23:13:08 +0200 Subject: [PATCH 05/29] cleaning up beets/autotag/__init__.py --- beets/autotag/__init__.py | 154 ++++++++++++++++++++++---------------- 1 file changed, 89 insertions(+), 65 deletions(-) diff --git a/beets/autotag/__init__.py b/beets/autotag/__init__.py index f9e38413e..2dcf93c52 100644 --- a/beets/autotag/__init__.py +++ b/beets/autotag/__init__.py @@ -41,31 +41,10 @@ log = logging.getLogger('beets') def apply_item_metadata(item, track_info): """Set an item's metadata from its matched TrackInfo object. """ - item.artist = track_info.artist - item.artist_sort = track_info.artist_sort - item.artist_credit = track_info.artist_credit - item.title = track_info.title - item.mb_trackid = track_info.track_id - item.mb_releasetrackid = track_info.release_track_id - if track_info.artist_id: - item.mb_artistid = track_info.artist_id - if track_info.data_source: - item.data_source = track_info.data_source - - if track_info.lyricist is not None: - item.lyricist = track_info.lyricist - if track_info.composer is not None: - item.composer = track_info.composer - if track_info.composer_sort is not None: - item.composer_sort = track_info.composer_sort - if track_info.arranger is not None: - item.arranger = track_info.arranger - if track_info.work is not None: - item.work = track_info.work - if track_info.mb_workid is not None: - item.mb_workid = track_info.mb_workid - if track_info.work_disambig is not None: - item.work_disambig = track_info.work_disambig + print('zer' in track_info) + for attr in track_info: + print(attr in track_info) + item.__setattr__(attr, getattr(track_info, attr)) # At the moment, the other metadata is left intact (including album # and track number). Perhaps these should be emptied? @@ -78,25 +57,50 @@ def apply_metadata(album_info, mapping): for item, track_info in mapping.items(): # Artist or artist credit. if config['artist_credit']: - item.artist = (track_info.artist_credit or - track_info.artist or - album_info.artist_credit or - album_info.artist) - item.albumartist = (album_info.artist_credit or - album_info.artist) + + if 'artist_credit' in track_info: + item.artist = track_info.artist_credit + elif 'artist' in track_info: + item.artist = track_info.artist + elif 'artist_credit' in album_info: + item.artist = album_info.artist_credit + elif 'artist' in album_info: + item.artist = album_info.artist + + if 'artist_credit' in album_info: + item.albumartist = album_info.artist_credit + elif 'artist' in album_info: + item.albumartist = album_info.artist + else: - item.artist = (track_info.artist or album_info.artist) - item.albumartist = album_info.artist + if 'artist' in track_info: + item.artist = track_info.artist + elif 'artist' in album_info: + item.artist = album_info.artist + + if 'artist' in album_info: + item.albumartist = album_info.artist # Album. - item.album = album_info.album + if 'album' in album_info: + item.album = album_info.album # Artist sort and credit names. - item.artist_sort = track_info.artist_sort or album_info.artist_sort - item.artist_credit = (track_info.artist_credit or - album_info.artist_credit) - item.albumartist_sort = album_info.artist_sort - item.albumartist_credit = album_info.artist_credit + if 'artist_sort' in track_info: + item.artist_sort = track_info.artist_sort + elif 'artist_sort' in album_info: + item.artist_sort = album_info.artist_sort + + if 'artist_credit' in track_info: + item.artist_credit = track_info.artist_credit + elif 'artist_credit' in album_info: + item.artist_credit = album_info.artist_credit + + if 'albumartist_sort' in album_info: + item.albumartist_sort = album_info.artist_sort + + if 'albumartist_credit' in album_info: + item.albumartist_credit = album_info.artist_credit # Release date. for prefix in '', 'original_': @@ -106,7 +110,10 @@ def apply_metadata(album_info, mapping): for suffix in 'year', 'month', 'day': key = prefix + suffix - value = getattr(album_info, key) or 0 + if key in album_info: + value = getattr(album_info, key) + else: + value = 0 # If we don't even have a year, apply nothing. if suffix == 'year' and not value: @@ -122,40 +129,55 @@ def apply_metadata(album_info, mapping): item[suffix] = value # Title. - item.title = track_info.title + if 'title' in track_info: + item.title = track_info.title if config['per_disc_numbering']: # We want to let the track number be zero, but if the medium index # is not provided we need to fall back to the overall index. - if track_info.medium_index is not None: + if 'medium_index' in track_info: item.track = track_info.medium_index - else: + elif 'index' in track_info: item.track = track_info.index - item.tracktotal = track_info.medium_total or len(album_info.tracks) + if 'medium_total' in track_info: + item.tracktotal = track_info.medium_total + elif 'tracks' in album_info: + item.tracktotal = len(album_info.tracks) else: - item.track = track_info.index - item.tracktotal = len(album_info.tracks) + if 'index' in track_info: + item.track = track_info.index + if 'tracks' in album_info: + item.tracktotal = len(album_info.tracks) # Disc and disc count. - item.disc = track_info.medium - item.disctotal = album_info.mediums + if 'medium' in track_info: + item.disc = track_info.medium + if 'mediums' in album_info: + item.disctotal = album_info.mediums # MusicBrainz IDs. - item.mb_trackid = track_info.track_id - item.mb_releasetrackid = track_info.release_track_id - item.mb_albumid = album_info.album_id - if track_info.artist_id: + if 'track_id' in track_info: + item.mb_trackid = track_info.track_id + if 'release_track_id' in track_info: + item.mb_releasetrackid = track_info.release_track_id + if 'album_id' in album_info: + item.mb_albumid = album_info.album_id + if 'artist_id' in track_info: item.mb_artistid = track_info.artist_id - else: + elif 'artist_id' in album_info: item.mb_artistid = album_info.artist_id - item.mb_albumartistid = album_info.artist_id - item.mb_releasegroupid = album_info.releasegroup_id + if 'artist_id' in album_info: + item.mb_albumartistid = album_info.artist_id + if 'releasegroup_id' in album_info: + item.mb_releasegroupid = album_info.releasegroup_id # Compilation flag. - item.comp = album_info.va + if 'va' in album_info: + item.comp = album_info.va # Track alt. - item.track_alt = track_info.track_alt + if 'track_alt' in track_info: + item.track_alt = track_info.track_alt # Miscellaneous/nullable metadata. misc_fields = { @@ -197,14 +219,16 @@ def apply_metadata(album_info, mapping): # field is explicitly allowed to be overwritten for field in misc_fields['album']: clobber = field in config['overwrite_null']['album'].as_str_seq() - value = getattr(album_info, field) - if value is None and not clobber: - continue - item[field] = value + if field in album_info: + value = getattr(album_info, field) + if value is None and not clobber: + continue + item[field] = value for field in misc_fields['track']: clobber = field in config['overwrite_null']['track'].as_str_seq() - value = getattr(track_info, field) - if value is None and not clobber: - continue - item[field] = value + if field in track_info: + value = getattr(track_info, field) + if value is None and not clobber: + continue + item[field] = value From 62566ee61ddde9c6de8d6e527820fa32acaf579f Mon Sep 17 00:00:00 2001 From: soergeld Date: Sat, 25 Apr 2020 23:13:38 +0200 Subject: [PATCH 06/29] remove prints for testing --- beets/autotag/__init__.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/beets/autotag/__init__.py b/beets/autotag/__init__.py index 2dcf93c52..530dcaf6e 100644 --- a/beets/autotag/__init__.py +++ b/beets/autotag/__init__.py @@ -41,9 +41,7 @@ log = logging.getLogger('beets') def apply_item_metadata(item, track_info): """Set an item's metadata from its matched TrackInfo object. """ - print('zer' in track_info) for attr in track_info: - print(attr in track_info) item.__setattr__(attr, getattr(track_info, attr)) # At the moment, the other metadata is left intact (including album From f507f0463906adfaae0aec6f0c0acbe3b1cb2814 Mon Sep 17 00:00:00 2001 From: soergeld Date: Mon, 27 Apr 2020 11:21:13 +0200 Subject: [PATCH 07/29] reintroduce default arguments, adapt all occurences of TrackInfo and AlbumInfo to the absence of positional arguments --- beets/autotag/hooks.py | 84 +++++++++++++++++++++++++++++++++++++++++- beetsplug/cue.py | 3 +- beetsplug/discogs.py | 7 ++-- test/test_autotag.py | 47 +++++++++++------------ test/test_ui.py | 6 ++- 5 files changed, 116 insertions(+), 31 deletions(-) diff --git a/beets/autotag/hooks.py b/beets/autotag/hooks.py index dd9726b84..fcb4ba161 100644 --- a/beets/autotag/hooks.py +++ b/beets/autotag/hooks.py @@ -100,7 +100,51 @@ class AlbumInfo(Map): ``mediums`` along with the fields up through ``tracks`` are required. The others are optional and may be None. """ - def __init__(self, **kwargs): + def __init__(self, album=None, album_id=None, artist=None, artist_id=None, + tracks, asin=None, albumtype=None, va=False, year=None, + month=None, day=None, label=None, mediums=None, + artist_sort=None, releasegroup_id=None, catalognum=None, + script=None, language=None, country=None, style=None, + genre=None, albumstatus=None, media=None, albumdisambig=None, + releasegroupdisambig=None, artist_credit=None, + original_year=None, original_month=None, + original_day=None, data_source=None, data_url=None, + discogs_albumid=None, discogs_labelid=None, + discogs_artistid=None, **kwargs): + self.album = album + self.album_id = album_id + self.artist = artist + self.artist_id = artist_id + self.tracks = tracks + self.asin = asin + self.albumtype = albumtype + self.va = va + self.year = year + self.month = month + self.day = day + self.label = label + self.mediums = mediums + self.artist_sort = artist_sort + self.releasegroup_id = releasegroup_id + self.catalognum = catalognum + self.script = script + self.language = language + self.country = country + self.style = style + self.genre = genre + self.albumstatus = albumstatus + self.media = media + self.albumdisambig = albumdisambig + self.releasegroupdisambig = releasegroupdisambig + self.artist_credit = artist_credit + self.original_year = original_year + self.original_month = original_month + self.original_day = original_day + self.data_source = data_source + self.data_url = data_url + self.discogs_albumid = discogs_albumid + self.discogs_labelid = discogs_labelid + self.discogs_artistid = discogs_artistid for arg in kwargs: self.__setattr__(arg, kwargs[arg]) @@ -129,7 +173,43 @@ class TrackInfo(Map): may be None. The indices ``index``, ``medium``, and ``medium_index`` are all 1-based. """ - def __init__(self, **kwargs): + def __init__(self, title=None, track_id=None, release_track_id=None, + artist=None, artist_id=None, length=None, index=None, + medium=None, medium_index=None, medium_total=None, + artist_sort=None, disctitle=None, artist_credit=None, + data_source=None, data_url=None, media=None, lyricist=None, + composer=None, composer_sort=None, arranger=None, + performer=None, track_alt=None, work=None, mb_workid=None, + work_disambig=None, bpm=None, initial_key=None, genre=None, + **kwargs): + self.title = title + self.track_id = track_id + self.release_track_id = release_track_id + self.artist = artist + self.artist_id = artist_id + self.length = length + self.index = index + self.media = media + self.medium = medium + self.medium_index = medium_index + self.medium_total = medium_total + self.artist_sort = artist_sort + self.disctitle = disctitle + self.artist_credit = artist_credit + self.data_source = data_source + self.data_url = data_url + self.lyricist = lyricist + self.composer = composer + self.composer_sort = composer_sort + self.arranger = arranger + self.performer = performer + self.track_alt = track_alt + self.work = work + self.mb_workid = mb_workid + self.work_disambig = work_disambig + self.bpm = bpm + self.initial_key = initial_key + self.genre = genre for arg in kwargs: self.__setattr__(arg, kwargs[arg]) diff --git a/beetsplug/cue.py b/beetsplug/cue.py index 92ca8784a..1ff817b2b 100644 --- a/beetsplug/cue.py +++ b/beetsplug/cue.py @@ -53,5 +53,6 @@ class CuePlugin(BeetsPlugin): title = "dunno lol" track_id = "wtf" index = int(path.basename(t)[len("split-track"):-len(".wav")]) - yield TrackInfo(title, track_id, index=index, artist=artist) + yield TrackInfo(title=title, track_id=track_id, index=index, + artist=artist) # generate TrackInfo instances diff --git a/beetsplug/discogs.py b/beetsplug/discogs.py index 86ace9aa8..a0a6ea654 100644 --- a/beetsplug/discogs.py +++ b/beetsplug/discogs.py @@ -356,7 +356,8 @@ class DiscogsPlugin(BeetsPlugin): # a master release, otherwise fetch the master release. original_year = self.get_master_year(master_id) if master_id else year - return AlbumInfo(album, album_id, artist, artist_id, tracks, asin=None, + return AlbumInfo(album=album, album_id=album_id, artist=artist, + artist_id=artist_id, tracks=tracks, asin=None, albumtype=albumtype, va=va, year=year, month=None, day=None, label=label, mediums=len(set(mediums)), artist_sort=None, releasegroup_id=master_id, @@ -567,8 +568,8 @@ class DiscogsPlugin(BeetsPlugin): track.get('artists', []) ) length = self.get_track_length(track['duration']) - return TrackInfo(title, track_id, artist=artist, artist_id=artist_id, - length=length, index=index, + return TrackInfo(title=title, track_id=track_id, artist=artist, + artist_id=artist_id, length=length, index=index, medium=medium, medium_index=medium_index, artist_sort=None, disctitle=None, artist_credit=None) diff --git a/test/test_autotag.py b/test/test_autotag.py index a11bc8fac..3b9ff3e67 100644 --- a/test/test_autotag.py +++ b/test/test_autotag.py @@ -106,9 +106,9 @@ def _make_item(title, track, artist=u'some artist'): def _make_trackinfo(): return [ - TrackInfo(u'one', None, artist=u'some artist', length=1, index=1), - TrackInfo(u'two', None, artist=u'some artist', length=1, index=2), - TrackInfo(u'three', None, artist=u'some artist', length=1, index=3), + TrackInfo(title=u'one', track_id=one, artist=u'some artist', length=1, index=1), + TrackInfo(title=u'two', track_id=None, artist=u'some artist', length=1, index=2), + TrackInfo(title=u'three', track_id=None, artist=u'some artist', length=1, index=3), ] @@ -503,9 +503,9 @@ class AssignmentTest(unittest.TestCase): items.append(self.item(u'three', 2)) items.append(self.item(u'two', 3)) trackinfo = [] - trackinfo.append(TrackInfo(u'one', None)) - trackinfo.append(TrackInfo(u'two', None)) - trackinfo.append(TrackInfo(u'three', None)) + trackinfo.append(TrackInfo(title=u'one', track_id=None)) + trackinfo.append(TrackInfo(title=u'two', track_id=None)) + trackinfo.append(TrackInfo(title=u'three', track_id=None)) mapping, extra_items, extra_tracks = \ match.assign_items(items, trackinfo) self.assertEqual(extra_items, []) @@ -522,9 +522,9 @@ class AssignmentTest(unittest.TestCase): items.append(self.item(u'three', 1)) items.append(self.item(u'two', 1)) trackinfo = [] - trackinfo.append(TrackInfo(u'one', None)) - trackinfo.append(TrackInfo(u'two', None)) - trackinfo.append(TrackInfo(u'three', None)) + trackinfo.append(TrackInfo(title=u'one', track_id=None)) + trackinfo.append(TrackInfo(title=u'two', track_id=None)) + trackinfo.append(TrackInfo(title=u'three', track_id=None)) mapping, extra_items, extra_tracks = \ match.assign_items(items, trackinfo) self.assertEqual(extra_items, []) @@ -540,9 +540,9 @@ class AssignmentTest(unittest.TestCase): items.append(self.item(u'one', 1)) items.append(self.item(u'three', 3)) trackinfo = [] - trackinfo.append(TrackInfo(u'one', None)) - trackinfo.append(TrackInfo(u'two', None)) - trackinfo.append(TrackInfo(u'three', None)) + trackinfo.append(TrackInfo(title=u'one', track_id=None)) + trackinfo.append(TrackInfo(title=u'two', track_id=None)) + trackinfo.append(TrackInfo(title=u'three', track_id=None)) mapping, extra_items, extra_tracks = \ match.assign_items(items, trackinfo) self.assertEqual(extra_items, []) @@ -558,8 +558,8 @@ class AssignmentTest(unittest.TestCase): items.append(self.item(u'two', 2)) items.append(self.item(u'three', 3)) trackinfo = [] - trackinfo.append(TrackInfo(u'one', None)) - trackinfo.append(TrackInfo(u'three', None)) + trackinfo.append(TrackInfo(title=u'one', track_id=None)) + trackinfo.append(TrackInfo(title=u'three', track_id=None)) mapping, extra_items, extra_tracks = \ match.assign_items(items, trackinfo) self.assertEqual(extra_items, [items[1]]) @@ -595,7 +595,8 @@ class AssignmentTest(unittest.TestCase): items.append(item(12, 186.45916150485752)) def info(index, title, length): - return TrackInfo(title, None, length=length, index=index) + return TrackInfo(title=title, track_id=None, length=length, + index=index) trackinfo = [] trackinfo.append(info(1, u'Alone', 238.893)) trackinfo.append(info(2, u'The Woman in You', 341.44)) @@ -638,8 +639,8 @@ class ApplyTest(_common.TestCase, ApplyTestUtil): self.items.append(Item({})) trackinfo = [] trackinfo.append(TrackInfo( - u'oneNew', - u'dfa939ec-118c-4d0f-84a0-60f3d1e6522c', + title=u'oneNew', + track_id=u'dfa939ec-118c-4d0f-84a0-60f3d1e6522c', medium=1, medium_index=1, medium_total=1, @@ -648,8 +649,8 @@ class ApplyTest(_common.TestCase, ApplyTestUtil): artist_sort='trackArtistSort', )) trackinfo.append(TrackInfo( - u'twoNew', - u'40130ed1-a27c-42fd-a328-1ebefb6caef4', + title=u'twoNew', + track_id=u'40130ed1-a27c-42fd-a328-1ebefb6caef4', medium=2, medium_index=1, index=2, @@ -828,15 +829,15 @@ class ApplyCompilationTest(_common.TestCase, ApplyTestUtil): self.items.append(Item({})) trackinfo = [] trackinfo.append(TrackInfo( - u'oneNew', - u'dfa939ec-118c-4d0f-84a0-60f3d1e6522c', + title=u'oneNew', + track_id=u'dfa939ec-118c-4d0f-84a0-60f3d1e6522c', artist=u'artistOneNew', artist_id=u'a05686fc-9db2-4c23-b99e-77f5db3e5282', index=1, )) trackinfo.append(TrackInfo( - u'twoNew', - u'40130ed1-a27c-42fd-a328-1ebefb6caef4', + title=u'twoNew', + track_id=u'40130ed1-a27c-42fd-a328-1ebefb6caef4', artist=u'artistTwoNew', artist_id=u'80b3cf5e-18fe-4c59-98c7-e5bb87210710', index=2, diff --git a/test/test_ui.py b/test/test_ui.py index 110e80782..c4d502c73 100644 --- a/test/test_ui.py +++ b/test/test_ui.py @@ -1051,8 +1051,10 @@ class ShowChangeTest(_common.TestCase): self.items[0].track = 1 self.items[0].path = b'/path/to/file.mp3' self.info = autotag.AlbumInfo( - u'the album', u'album id', u'the artist', u'artist id', [ - autotag.TrackInfo(u'the title', u'track id', index=1) + album=u'the album', album_id=u'album id', artist=u'the artist', + artist_id=u'artist id', tracks=[ + autotag.TrackInfo(title=u'the title', track_id=u'track id', + index=1) ] ) From 32d81d801bd7d9eea9a6887cdf443e0675ce6215 Mon Sep 17 00:00:00 2001 From: soergeld Date: Mon, 27 Apr 2020 11:25:42 +0200 Subject: [PATCH 08/29] forgot a positional argument --- beets/autotag/hooks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/beets/autotag/hooks.py b/beets/autotag/hooks.py index fcb4ba161..0017f9dd6 100644 --- a/beets/autotag/hooks.py +++ b/beets/autotag/hooks.py @@ -101,7 +101,7 @@ class AlbumInfo(Map): The others are optional and may be None. """ def __init__(self, album=None, album_id=None, artist=None, artist_id=None, - tracks, asin=None, albumtype=None, va=False, year=None, + tracks=None, asin=None, albumtype=None, va=False, year=None, month=None, day=None, label=None, mediums=None, artist_sort=None, releasegroup_id=None, catalognum=None, script=None, language=None, country=None, style=None, From 805f4e801f36cf73be8735daf601647b45b55770 Mon Sep 17 00:00:00 2001 From: soergeld Date: Mon, 27 Apr 2020 11:31:39 +0200 Subject: [PATCH 09/29] typo in tests --- test/test_autotag.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_autotag.py b/test/test_autotag.py index 3b9ff3e67..b894a65f3 100644 --- a/test/test_autotag.py +++ b/test/test_autotag.py @@ -106,7 +106,7 @@ def _make_item(title, track, artist=u'some artist'): def _make_trackinfo(): return [ - TrackInfo(title=u'one', track_id=one, artist=u'some artist', length=1, index=1), + TrackInfo(title=u'one', track_id=None, artist=u'some artist', length=1, index=1), TrackInfo(title=u'two', track_id=None, artist=u'some artist', length=1, index=2), TrackInfo(title=u'three', track_id=None, artist=u'some artist', length=1, index=3), ] From bd543136d66037e13400793d518ee74593d7f5f5 Mon Sep 17 00:00:00 2001 From: soergeld Date: Mon, 27 Apr 2020 11:37:19 +0200 Subject: [PATCH 10/29] scale back some changes in __init__.py and hooks.py --- beets/autotag/__init__.py | 155 +++++++++++++++++--------------------- beets/autotag/hooks.py | 10 ++- 2 files changed, 76 insertions(+), 89 deletions(-) diff --git a/beets/autotag/__init__.py b/beets/autotag/__init__.py index 530dcaf6e..05b341a2d 100644 --- a/beets/autotag/__init__.py +++ b/beets/autotag/__init__.py @@ -41,8 +41,33 @@ log = logging.getLogger('beets') def apply_item_metadata(item, track_info): """Set an item's metadata from its matched TrackInfo object. """ - for attr in track_info: - item.__setattr__(attr, getattr(track_info, attr)) + item.artist = track_info.artist + item.artist_sort = track_info.artist_sort + item.artist_credit = track_info.artist_credit + item.title = track_info.title + item.mb_trackid = track_info.track_id + item.mb_releasetrackid = track_info.release_track_id + if track_info.artist_id: + item.mb_artistid = track_info.artist_id + if track_info.data_source: + item.data_source = track_info.data_source + + if track_info.lyricist is not None: + item.lyricist = track_info.lyricist + if track_info.composer is not None: + item.composer = track_info.composer + if track_info.composer_sort is not None: + item.composer_sort = track_info.composer_sort + if track_info.arranger is not None: + item.arranger = track_info.arranger + if track_info.performer is not None: + item.performer = track_info.performer + if track_info.work is not None: + item.work = track_info.work + if track_info.mb_workid is not None: + item.mb_workid = track_info.mb_workid + if track_info.work_disambig is not None: + item.work_disambig = track_info.work_disambig # At the moment, the other metadata is left intact (including album # and track number). Perhaps these should be emptied? @@ -55,50 +80,25 @@ def apply_metadata(album_info, mapping): for item, track_info in mapping.items(): # Artist or artist credit. if config['artist_credit']: - - if 'artist_credit' in track_info: - item.artist = track_info.artist_credit - elif 'artist' in track_info: - item.artist = track_info.artist - elif 'artist_credit' in album_info: - item.artist = album_info.artist_credit - elif 'artist' in album_info: - item.artist = album_info.artist - - if 'artist_credit' in album_info: - item.albumartist = album_info.artist_credit - elif 'artist' in album_info: - item.albumartist = album_info.artist - + item.artist = (track_info.artist_credit or + track_info.artist or + album_info.artist_credit or + album_info.artist) + item.albumartist = (album_info.artist_credit or + album_info.artist) else: - if 'artist' in track_info: - item.artist = track_info.artist - elif 'artist' in album_info: - item.artist = album_info.artist - - if 'artist' in album_info: - item.albumartist = album_info.artist + item.artist = (track_info.artist or album_info.artist) + item.albumartist = album_info.artist # Album. - if 'album' in album_info: - item.album = album_info.album + item.album = album_info.album # Artist sort and credit names. - if 'artist_sort' in track_info: - item.artist_sort = track_info.artist_sort - elif 'artist_sort' in album_info: - item.artist_sort = album_info.artist_sort - - if 'artist_credit' in track_info: - item.artist_credit = track_info.artist_credit - elif 'artist_credit' in album_info: - item.artist_credit = album_info.artist_credit - - if 'albumartist_sort' in album_info: - item.albumartist_sort = album_info.artist_sort - - if 'albumartist_credit' in album_info: - item.albumartist_credit = album_info.artist_credit + item.artist_sort = track_info.artist_sort or album_info.artist_sort + item.artist_credit = (track_info.artist_credit or + album_info.artist_credit) + item.albumartist_sort = album_info.artist_sort + item.albumartist_credit = album_info.artist_credit # Release date. for prefix in '', 'original_': @@ -108,10 +108,7 @@ def apply_metadata(album_info, mapping): for suffix in 'year', 'month', 'day': key = prefix + suffix - if key in album_info: - value = getattr(album_info, key) - else: - value = 0 + value = getattr(album_info, key) or 0 # If we don't even have a year, apply nothing. if suffix == 'year' and not value: @@ -127,55 +124,40 @@ def apply_metadata(album_info, mapping): item[suffix] = value # Title. - if 'title' in track_info: - item.title = track_info.title + item.title = track_info.title if config['per_disc_numbering']: # We want to let the track number be zero, but if the medium index # is not provided we need to fall back to the overall index. - if 'medium_index' in track_info: + if track_info.medium_index is not None: item.track = track_info.medium_index - elif 'index' in track_info: + else: item.track = track_info.index - if 'medium_total' in track_info: - item.tracktotal = track_info.medium_total - elif 'tracks' in album_info: - item.tracktotal = len(album_info.tracks) + item.tracktotal = track_info.medium_total or len(album_info.tracks) else: - if 'index' in track_info: - item.track = track_info.index - if 'tracks' in album_info: - item.tracktotal = len(album_info.tracks) + item.track = track_info.index + item.tracktotal = len(album_info.tracks) # Disc and disc count. - if 'medium' in track_info: - item.disc = track_info.medium - if 'mediums' in album_info: - item.disctotal = album_info.mediums + item.disc = track_info.medium + item.disctotal = album_info.mediums # MusicBrainz IDs. - if 'track_id' in track_info: - item.mb_trackid = track_info.track_id - if 'release_track_id' in track_info: - item.mb_releasetrackid = track_info.release_track_id - if 'album_id' in album_info: - item.mb_albumid = album_info.album_id - if 'artist_id' in track_info: + item.mb_trackid = track_info.track_id + item.mb_releasetrackid = track_info.release_track_id + item.mb_albumid = album_info.album_id + if track_info.artist_id: item.mb_artistid = track_info.artist_id - elif 'artist_id' in album_info: + else: item.mb_artistid = album_info.artist_id - if 'artist_id' in album_info: - item.mb_albumartistid = album_info.artist_id - if 'releasegroup_id' in album_info: - item.mb_releasegroupid = album_info.releasegroup_id + item.mb_albumartistid = album_info.artist_id + item.mb_releasegroupid = album_info.releasegroup_id # Compilation flag. - if 'va' in album_info: - item.comp = album_info.va + item.comp = album_info.va # Track alt. - if 'track_alt' in track_info: - item.track_alt = track_info.track_alt + item.track_alt = track_info.track_alt # Miscellaneous/nullable metadata. misc_fields = { @@ -204,6 +186,7 @@ def apply_metadata(album_info, mapping): 'composer', 'composer_sort', 'arranger', + 'performer', 'work', 'mb_workid', 'work_disambig', @@ -217,16 +200,14 @@ def apply_metadata(album_info, mapping): # field is explicitly allowed to be overwritten for field in misc_fields['album']: clobber = field in config['overwrite_null']['album'].as_str_seq() - if field in album_info: - value = getattr(album_info, field) - if value is None and not clobber: - continue - item[field] = value + value = getattr(album_info, field) + if value is None and not clobber: + continue + item[field] = value for field in misc_fields['track']: clobber = field in config['overwrite_null']['track'].as_str_seq() - if field in track_info: - value = getattr(track_info, field) - if value is None and not clobber: - continue - item[field] = value + value = getattr(track_info, field) + if value is None and not clobber: + continue + item[field] = value diff --git a/beets/autotag/hooks.py b/beets/autotag/hooks.py index 0017f9dd6..1e856ca0a 100644 --- a/beets/autotag/hooks.py +++ b/beets/autotag/hooks.py @@ -155,7 +155,12 @@ class AlbumInfo(Map): """Ensure that all string attributes on this object, and the constituent `TrackInfo` objects, are decoded to Unicode. """ - for fld in self: + for fld in ['album', 'artist', 'albumtype', 'label', 'artist_sort', + 'catalognum', 'script', 'language', 'country', 'style', + 'genre', 'albumstatus', 'albumdisambig', + 'releasegroupdisambig', 'artist_credit', + 'media', 'discogs_albumid', 'discogs_labelid', + 'discogs_artistid']: value = getattr(self, fld) if type(value) == str: if isinstance(value, bytes): @@ -218,7 +223,8 @@ class TrackInfo(Map): """Ensure that all string attributes on this object are decoded to Unicode. """ - for fld in self: + for fld in ['title', 'artist', 'medium', 'artist_sort', 'disctitle', + 'artist_credit', 'media']: value = getattr(self, fld) if type(value) == str: if isinstance(value, bytes): From 363cbf8147940f471558e3545cc58ee2f491fb3e Mon Sep 17 00:00:00 2001 From: soergeld Date: Mon, 27 Apr 2020 11:39:20 +0200 Subject: [PATCH 11/29] mixed two PR --- beets/autotag/__init__.py | 3 --- beets/autotag/hooks.py | 3 +-- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/beets/autotag/__init__.py b/beets/autotag/__init__.py index 05b341a2d..f9e38413e 100644 --- a/beets/autotag/__init__.py +++ b/beets/autotag/__init__.py @@ -60,8 +60,6 @@ def apply_item_metadata(item, track_info): item.composer_sort = track_info.composer_sort if track_info.arranger is not None: item.arranger = track_info.arranger - if track_info.performer is not None: - item.performer = track_info.performer if track_info.work is not None: item.work = track_info.work if track_info.mb_workid is not None: @@ -186,7 +184,6 @@ def apply_metadata(album_info, mapping): 'composer', 'composer_sort', 'arranger', - 'performer', 'work', 'mb_workid', 'work_disambig', diff --git a/beets/autotag/hooks.py b/beets/autotag/hooks.py index 1e856ca0a..48d1cae52 100644 --- a/beets/autotag/hooks.py +++ b/beets/autotag/hooks.py @@ -184,7 +184,7 @@ class TrackInfo(Map): artist_sort=None, disctitle=None, artist_credit=None, data_source=None, data_url=None, media=None, lyricist=None, composer=None, composer_sort=None, arranger=None, - performer=None, track_alt=None, work=None, mb_workid=None, + track_alt=None, work=None, mb_workid=None, work_disambig=None, bpm=None, initial_key=None, genre=None, **kwargs): self.title = title @@ -207,7 +207,6 @@ class TrackInfo(Map): self.composer = composer self.composer_sort = composer_sort self.arranger = arranger - self.performer = performer self.track_alt = track_alt self.work = work self.mb_workid = mb_workid From 14e1b33839afbf1c8fb1c4f94caf77bb99d82b96 Mon Sep 17 00:00:00 2001 From: soergeld Date: Mon, 27 Apr 2020 11:44:51 +0200 Subject: [PATCH 12/29] lines too long --- test/test_autotag.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/test/test_autotag.py b/test/test_autotag.py index b894a65f3..fcf2ee5b1 100644 --- a/test/test_autotag.py +++ b/test/test_autotag.py @@ -106,9 +106,12 @@ def _make_item(title, track, artist=u'some artist'): def _make_trackinfo(): return [ - TrackInfo(title=u'one', track_id=None, artist=u'some artist', length=1, index=1), - TrackInfo(title=u'two', track_id=None, artist=u'some artist', length=1, index=2), - TrackInfo(title=u'three', track_id=None, artist=u'some artist', length=1, index=3), + TrackInfo(title=u'one', track_id=None, artist=u'some artist', + length=1, index=1), + TrackInfo(title=u'two', track_id=None, artist=u'some artist', + length=1, index=2), + TrackInfo(title=u'three', track_id=None, artist=u'some artist', + length=1, index=3), ] From 8df2e5b8f5affc9a5472886e819a31789c8fa280 Mon Sep 17 00:00:00 2001 From: soergeld Date: Mon, 27 Apr 2020 12:01:35 +0200 Subject: [PATCH 13/29] arrange decoder --- beets/autotag/hooks.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/beets/autotag/hooks.py b/beets/autotag/hooks.py index 48d1cae52..95533d11d 100644 --- a/beets/autotag/hooks.py +++ b/beets/autotag/hooks.py @@ -162,7 +162,7 @@ class AlbumInfo(Map): 'media', 'discogs_albumid', 'discogs_labelid', 'discogs_artistid']: value = getattr(self, fld) - if type(value) == str: + if isinstance(value, bytes): if isinstance(value, bytes): setattr(self, fld, value.decode(codec, 'ignore')) @@ -225,7 +225,7 @@ class TrackInfo(Map): for fld in ['title', 'artist', 'medium', 'artist_sort', 'disctitle', 'artist_credit', 'media']: value = getattr(self, fld) - if type(value) == str: + if isinstance(value, bytes): if isinstance(value, bytes): setattr(self, fld, value.decode(codec, 'ignore')) From 98389b6161bcdad8cbbd8638eeedd01d877b25a3 Mon Sep 17 00:00:00 2001 From: soergeld Date: Mon, 27 Apr 2020 12:02:27 +0200 Subject: [PATCH 14/29] dupe test --- beets/autotag/hooks.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/beets/autotag/hooks.py b/beets/autotag/hooks.py index 95533d11d..83abd8736 100644 --- a/beets/autotag/hooks.py +++ b/beets/autotag/hooks.py @@ -163,8 +163,7 @@ class AlbumInfo(Map): 'discogs_artistid']: value = getattr(self, fld) if isinstance(value, bytes): - if isinstance(value, bytes): - setattr(self, fld, value.decode(codec, 'ignore')) + setattr(self, fld, value.decode(codec, 'ignore')) class TrackInfo(Map): @@ -226,8 +225,7 @@ class TrackInfo(Map): 'artist_credit', 'media']: value = getattr(self, fld) if isinstance(value, bytes): - if isinstance(value, bytes): - setattr(self, fld, value.decode(codec, 'ignore')) + setattr(self, fld, value.decode(codec, 'ignore')) # Candidate distance scoring. From 63df7cf3edf9fc45fad4186d4332f30afe66a952 Mon Sep 17 00:00:00 2001 From: soergeld Date: Mon, 27 Apr 2020 15:03:55 +0200 Subject: [PATCH 15/29] forgot to decode all tracks of an album --- beets/autotag/hooks.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/beets/autotag/hooks.py b/beets/autotag/hooks.py index 83abd8736..a5d1ae428 100644 --- a/beets/autotag/hooks.py +++ b/beets/autotag/hooks.py @@ -165,6 +165,10 @@ class AlbumInfo(Map): if isinstance(value, bytes): setattr(self, fld, value.decode(codec, 'ignore')) + if 'tracks' in self: + for track in self.tracks: + track.decode(codec) + class TrackInfo(Map): """Describes a canonical track present on a release. Appears as part From 048b5c21517b88089723ecbca6282e337c976244 Mon Sep 17 00:00:00 2001 From: soergeld Date: Mon, 27 Apr 2020 15:55:12 +0200 Subject: [PATCH 16/29] remove need for deepcopy, simplify __init__ --- beets/autotag/hooks.py | 18 +--- test/test_autotag.py | 199 +++++++++++++++++++++++++++++++++++++++-- test/test_ui.py | 7 +- 3 files changed, 202 insertions(+), 22 deletions(-) diff --git a/beets/autotag/hooks.py b/beets/autotag/hooks.py index a5d1ae428..0c6e1eb80 100644 --- a/beets/autotag/hooks.py +++ b/beets/autotag/hooks.py @@ -45,13 +45,8 @@ class Map(dict): m = Map({'first_name': 'Eduardo'}, last_name='Pool', age=24, sports=['Soccer']) """ - def __init__(self, *args, **kwargs): - super(Map, self).__init__(*args, **kwargs) - for arg in args: - if isinstance(arg, dict): - for k, v in arg.iteritems(): - self[k] = v - + def __init__(self, **kwargs): + super(Map, self).__init__(**kwargs) if kwargs: for k, v in kwargs.iteritems(): self[k] = v @@ -76,15 +71,8 @@ class Map(dict): super(Map, self).__delitem__(key) del self.__dict__[key] - def __getstate__(self): - return self.__dict__ - - def __setstate__(self, state): - for key in state: - self.__setattr__(key, state[key]) - def __hash__(self): - return hash(tuple(sorted(self.items()))) + return self.id class AlbumInfo(Map): diff --git a/test/test_autotag.py b/test/test_autotag.py index fcf2ee5b1..97030785b 100644 --- a/test/test_autotag.py +++ b/test/test_autotag.py @@ -753,13 +753,75 @@ class ApplyTest(_common.TestCase, ApplyTestUtil): self.assertEqual(self.items[1].albumtype, 'album') def test_album_artist_overrides_empty_track_artist(self): - my_info = copy.deepcopy(self.info) + # make a deepcopy of self.info + trackinfo = [] + trackinfo.append(TrackInfo( + title=u'oneNew', + track_id=u'dfa939ec-118c-4d0f-84a0-60f3d1e6522c', + medium=1, + medium_index=1, + medium_total=1, + index=1, + artist_credit='trackArtistCredit', + artist_sort='trackArtistSort', + )) + trackinfo.append(TrackInfo( + title=u'twoNew', + track_id=u'40130ed1-a27c-42fd-a328-1ebefb6caef4', + medium=2, + medium_index=1, + index=2, + medium_total=1, + )) + my_info = AlbumInfo( + tracks=trackinfo, + artist=u'artistNew', + album=u'albumNew', + album_id='7edb51cb-77d6-4416-a23c-3a8c2994a2c7', + artist_id='a6623d39-2d8e-4f70-8242-0a9553b91e50', + artist_credit=u'albumArtistCredit', + artist_sort=u'albumArtistSort', + albumtype=u'album', + va=False, + mediums=2, + ) self._apply(info=my_info) self.assertEqual(self.items[0].artist, 'artistNew') self.assertEqual(self.items[1].artist, 'artistNew') def test_album_artist_overridden_by_nonempty_track_artist(self): - my_info = copy.deepcopy(self.info) + # make a deepcopy of self.info + trackinfo = [] + trackinfo.append(TrackInfo( + title=u'oneNew', + track_id=u'dfa939ec-118c-4d0f-84a0-60f3d1e6522c', + medium=1, + medium_index=1, + medium_total=1, + index=1, + artist_credit='trackArtistCredit', + artist_sort='trackArtistSort', + )) + trackinfo.append(TrackInfo( + title=u'twoNew', + track_id=u'40130ed1-a27c-42fd-a328-1ebefb6caef4', + medium=2, + medium_index=1, + index=2, + medium_total=1, + )) + my_info = AlbumInfo( + tracks=trackinfo, + artist=u'artistNew', + album=u'albumNew', + album_id='7edb51cb-77d6-4416-a23c-3a8c2994a2c7', + artist_id='a6623d39-2d8e-4f70-8242-0a9553b91e50', + artist_credit=u'albumArtistCredit', + artist_sort=u'albumArtistSort', + albumtype=u'album', + va=False, + mediums=2, + ) my_info.tracks[0].artist = 'artist1!' my_info.tracks[1].artist = 'artist2!' self._apply(info=my_info) @@ -781,7 +843,38 @@ class ApplyTest(_common.TestCase, ApplyTestUtil): self.assertEqual(self.items[1].artist_sort, 'albumArtistSort') def test_full_date_applied(self): - my_info = copy.deepcopy(self.info) + # make a deepcopy of self.info + trackinfo = [] + trackinfo.append(TrackInfo( + title=u'oneNew', + track_id=u'dfa939ec-118c-4d0f-84a0-60f3d1e6522c', + medium=1, + medium_index=1, + medium_total=1, + index=1, + artist_credit='trackArtistCredit', + artist_sort='trackArtistSort', + )) + trackinfo.append(TrackInfo( + title=u'twoNew', + track_id=u'40130ed1-a27c-42fd-a328-1ebefb6caef4', + medium=2, + medium_index=1, + index=2, + medium_total=1, + )) + my_info = AlbumInfo( + tracks=trackinfo, + artist=u'artistNew', + album=u'albumNew', + album_id='7edb51cb-77d6-4416-a23c-3a8c2994a2c7', + artist_id='a6623d39-2d8e-4f70-8242-0a9553b91e50', + artist_credit=u'albumArtistCredit', + artist_sort=u'albumArtistSort', + albumtype=u'album', + va=False, + mediums=2, + ) my_info.year = 2013 my_info.month = 12 my_info.day = 18 @@ -796,7 +889,38 @@ class ApplyTest(_common.TestCase, ApplyTestUtil): self.items.append(Item(year=1, month=2, day=3)) self.items.append(Item(year=4, month=5, day=6)) - my_info = copy.deepcopy(self.info) + # make a deepcopy of self.info + trackinfo = [] + trackinfo.append(TrackInfo( + title=u'oneNew', + track_id=u'dfa939ec-118c-4d0f-84a0-60f3d1e6522c', + medium=1, + medium_index=1, + medium_total=1, + index=1, + artist_credit='trackArtistCredit', + artist_sort='trackArtistSort', + )) + trackinfo.append(TrackInfo( + title=u'twoNew', + track_id=u'40130ed1-a27c-42fd-a328-1ebefb6caef4', + medium=2, + medium_index=1, + index=2, + medium_total=1, + )) + my_info = AlbumInfo( + tracks=trackinfo, + artist=u'artistNew', + album=u'albumNew', + album_id='7edb51cb-77d6-4416-a23c-3a8c2994a2c7', + artist_id='a6623d39-2d8e-4f70-8242-0a9553b91e50', + artist_credit=u'albumArtistCredit', + artist_sort=u'albumArtistSort', + albumtype=u'album', + va=False, + mediums=2, + ) my_info.year = 2013 self._apply(info=my_info) @@ -816,7 +940,38 @@ class ApplyTest(_common.TestCase, ApplyTestUtil): self.assertEqual(self.items[0].day, 3) def test_data_source_applied(self): - my_info = copy.deepcopy(self.info) + # make a deepcopy of self.info + trackinfo = [] + trackinfo.append(TrackInfo( + title=u'oneNew', + track_id=u'dfa939ec-118c-4d0f-84a0-60f3d1e6522c', + medium=1, + medium_index=1, + medium_total=1, + index=1, + artist_credit='trackArtistCredit', + artist_sort='trackArtistSort', + )) + trackinfo.append(TrackInfo( + title=u'twoNew', + track_id=u'40130ed1-a27c-42fd-a328-1ebefb6caef4', + medium=2, + medium_index=1, + index=2, + medium_total=1, + )) + my_info = AlbumInfo( + tracks=trackinfo, + artist=u'artistNew', + album=u'albumNew', + album_id='7edb51cb-77d6-4416-a23c-3a8c2994a2c7', + artist_id='a6623d39-2d8e-4f70-8242-0a9553b91e50', + artist_credit=u'albumArtistCredit', + artist_sort=u'albumArtistSort', + albumtype=u'album', + va=False, + mediums=2, + ) my_info.data_source = 'MusicBrainz' self._apply(info=my_info) @@ -827,6 +982,7 @@ class ApplyCompilationTest(_common.TestCase, ApplyTestUtil): def setUp(self): super(ApplyCompilationTest, self).setUp() + # make a deepcopy of self.info self.items = [] self.items.append(Item({})) self.items.append(Item({})) @@ -878,7 +1034,38 @@ class ApplyCompilationTest(_common.TestCase, ApplyTestUtil): self.assertFalse(self.items[1].comp) def test_va_flag_sets_comp(self): - va_info = copy.deepcopy(self.info) + # make a deepcopy of self.info + trackinfo = [] + trackinfo.append(TrackInfo( + title=u'oneNew', + track_id=u'dfa939ec-118c-4d0f-84a0-60f3d1e6522c', + medium=1, + medium_index=1, + medium_total=1, + index=1, + artist_credit='trackArtistCredit', + artist_sort='trackArtistSort', + )) + trackinfo.append(TrackInfo( + title=u'twoNew', + track_id=u'40130ed1-a27c-42fd-a328-1ebefb6caef4', + medium=2, + medium_index=1, + index=2, + medium_total=1, + )) + va_info = AlbumInfo( + tracks=trackinfo, + artist=u'artistNew', + album=u'albumNew', + album_id='7edb51cb-77d6-4416-a23c-3a8c2994a2c7', + artist_id='a6623d39-2d8e-4f70-8242-0a9553b91e50', + artist_credit=u'albumArtistCredit', + artist_sort=u'albumArtistSort', + albumtype=u'album', + va=False, + mediums=2, + ) va_info.va = True self._apply(info=va_info) self.assertTrue(self.items[0].comp) diff --git a/test/test_ui.py b/test/test_ui.py index c4d502c73..608c1d7fb 100644 --- a/test/test_ui.py +++ b/test/test_ui.py @@ -1138,7 +1138,12 @@ class SummarizeItemsTest(_common.TestCase): summary = commands.summarize_items([self.item], False) self.assertEqual(summary, u"1 items, F, 4kbps, 10:54, 987.0 B") - i2 = deepcopy(self.item) + # make a copy of self.item + i2 = library.Item() + i2.bitrate = 4321 + i2.length = 10 * 60 + 54 + i2.format = "F" + summary = commands.summarize_items([self.item, i2], False) self.assertEqual(summary, u"2 items, F, 4kbps, 21:48, 1.9 KiB") From a51ef113d3fd1642f5c7298895697dae76fc9b1c Mon Sep 17 00:00:00 2001 From: soergeld Date: Mon, 27 Apr 2020 15:59:21 +0200 Subject: [PATCH 17/29] arranged hash --- beets/autotag/hooks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/beets/autotag/hooks.py b/beets/autotag/hooks.py index 0c6e1eb80..350ca065c 100644 --- a/beets/autotag/hooks.py +++ b/beets/autotag/hooks.py @@ -72,7 +72,7 @@ class Map(dict): del self.__dict__[key] def __hash__(self): - return self.id + return id(self) class AlbumInfo(Map): From 2bc0027adf9824d7a312ae65ba0c045965eb4e60 Mon Sep 17 00:00:00 2001 From: soergeld Date: Mon, 27 Apr 2020 16:08:47 +0200 Subject: [PATCH 18/29] remove some prints, unused libraries, __setitem__ method --- beets/autotag/hooks.py | 4 ---- test/test_autotag.py | 1 - test/test_ui.py | 1 - 3 files changed, 6 deletions(-) diff --git a/beets/autotag/hooks.py b/beets/autotag/hooks.py index 350ca065c..74145e78d 100644 --- a/beets/autotag/hooks.py +++ b/beets/autotag/hooks.py @@ -60,10 +60,6 @@ class Map(dict): def __setattr__(self, key, value): self.__setitem__(key, value) - def __setitem__(self, key, value): - super(Map, self).__setitem__(key, value) - self.__dict__.update({key: value}) - def __delattr__(self, item): self.__delitem__(item) diff --git a/test/test_autotag.py b/test/test_autotag.py index 97030785b..385b793ee 100644 --- a/test/test_autotag.py +++ b/test/test_autotag.py @@ -18,7 +18,6 @@ from __future__ import division, absolute_import, print_function import re -import copy import unittest from test import _common diff --git a/test/test_ui.py b/test/test_ui.py index 608c1d7fb..d0be10060 100644 --- a/test/test_ui.py +++ b/test/test_ui.py @@ -22,7 +22,6 @@ import shutil import re import subprocess import platform -from copy import deepcopy import six import unittest From eec7994dbe4cc3e12495de359989c1f6884f312c Mon Sep 17 00:00:00 2001 From: soergeld Date: Mon, 27 Apr 2020 16:16:37 +0200 Subject: [PATCH 19/29] corrected on deepcopy replacement --- test/test_autotag.py | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/test/test_autotag.py b/test/test_autotag.py index 385b793ee..af74b5c8f 100644 --- a/test/test_autotag.py +++ b/test/test_autotag.py @@ -1035,35 +1035,28 @@ class ApplyCompilationTest(_common.TestCase, ApplyTestUtil): def test_va_flag_sets_comp(self): # make a deepcopy of self.info trackinfo = [] + trackinfo = [] trackinfo.append(TrackInfo( title=u'oneNew', track_id=u'dfa939ec-118c-4d0f-84a0-60f3d1e6522c', - medium=1, - medium_index=1, - medium_total=1, + artist=u'artistOneNew', + artist_id=u'a05686fc-9db2-4c23-b99e-77f5db3e5282', index=1, - artist_credit='trackArtistCredit', - artist_sort='trackArtistSort', )) trackinfo.append(TrackInfo( title=u'twoNew', track_id=u'40130ed1-a27c-42fd-a328-1ebefb6caef4', - medium=2, - medium_index=1, + artist=u'artistTwoNew', + artist_id=u'80b3cf5e-18fe-4c59-98c7-e5bb87210710', index=2, - medium_total=1, )) va_info = AlbumInfo( tracks=trackinfo, - artist=u'artistNew', + artist=u'variousNew', album=u'albumNew', - album_id='7edb51cb-77d6-4416-a23c-3a8c2994a2c7', - artist_id='a6623d39-2d8e-4f70-8242-0a9553b91e50', - artist_credit=u'albumArtistCredit', - artist_sort=u'albumArtistSort', - albumtype=u'album', - va=False, - mediums=2, + album_id='3b69ea40-39b8-487f-8818-04b6eff8c21a', + artist_id='89ad4ac3-39f7-470e-963a-56509c546377', + albumtype=u'compilation', ) va_info.va = True self._apply(info=va_info) From d07a3e36970aa785a8085afd56eaa4062be93787 Mon Sep 17 00:00:00 2001 From: soergeld Date: Mon, 27 Apr 2020 16:17:11 +0200 Subject: [PATCH 20/29] remove __delitem__ and __delattr__ methods --- beets/autotag/hooks.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/beets/autotag/hooks.py b/beets/autotag/hooks.py index 74145e78d..36ea8db1e 100644 --- a/beets/autotag/hooks.py +++ b/beets/autotag/hooks.py @@ -60,13 +60,6 @@ class Map(dict): def __setattr__(self, key, value): self.__setitem__(key, value) - def __delattr__(self, item): - self.__delitem__(item) - - def __delitem__(self, key): - super(Map, self).__delitem__(key) - del self.__dict__[key] - def __hash__(self): return id(self) From 13db4063fa0d5515f8a479e90c1a2235c6e4cd06 Mon Sep 17 00:00:00 2001 From: soergeld Date: Mon, 27 Apr 2020 16:31:38 +0200 Subject: [PATCH 21/29] actually, I don't need __init__ --- beets/autotag/hooks.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/beets/autotag/hooks.py b/beets/autotag/hooks.py index 36ea8db1e..1500538d3 100644 --- a/beets/autotag/hooks.py +++ b/beets/autotag/hooks.py @@ -45,11 +45,6 @@ class Map(dict): m = Map({'first_name': 'Eduardo'}, last_name='Pool', age=24, sports=['Soccer']) """ - def __init__(self, **kwargs): - super(Map, self).__init__(**kwargs) - if kwargs: - for k, v in kwargs.iteritems(): - self[k] = v def __getattr__(self, attr): if attr in self: From b39ef0b8f949ea41d0d9e40674b20d9201c370a3 Mon Sep 17 00:00:00 2001 From: soergeld Date: Tue, 28 Apr 2020 12:16:19 +0200 Subject: [PATCH 22/29] better deepcopy, docstring, minor improvements --- beets/autotag/hooks.py | 26 +++--- test/test_autotag.py | 198 +++++------------------------------------ 2 files changed, 40 insertions(+), 184 deletions(-) diff --git a/beets/autotag/hooks.py b/beets/autotag/hooks.py index 1500538d3..81c2fadb1 100644 --- a/beets/autotag/hooks.py +++ b/beets/autotag/hooks.py @@ -39,11 +39,19 @@ except AttributeError: # Classes used to represent candidate options. -class Map(dict): +class AttrDict(dict): """ - Example: - m = Map({'first_name': 'Eduardo'}, last_name='Pool', age=24, - sports=['Soccer']) + Dictionary with flexible attributes + to get an tag value: + value = info.tag + or value = info[tag] + or value = info.get(tag) + or value = getattr(info, tag) + all raise AttributeError when info doesn't have tag + to set a tag value: + info.tag = value + or info[tag] = value + or setattr(info, tag, value) """ def __getattr__(self, attr): @@ -59,7 +67,7 @@ class Map(dict): return id(self) -class AlbumInfo(Map): +class AlbumInfo(AttrDict): """Describes a canonical release that may be used to match a release in the library. Consists of these data members: @@ -117,8 +125,7 @@ class AlbumInfo(Map): self.discogs_albumid = discogs_albumid self.discogs_labelid = discogs_labelid self.discogs_artistid = discogs_artistid - for arg in kwargs: - self.__setattr__(arg, kwargs[arg]) + self.update(kwargs) # Work around a bug in python-musicbrainz-ngs that causes some # strings to be bytes rather than Unicode. @@ -142,7 +149,7 @@ class AlbumInfo(Map): track.decode(codec) -class TrackInfo(Map): +class TrackInfo(AttrDict): """Describes a canonical track present on a release. Appears as part of an AlbumInfo's ``tracks`` list. Consists of these data members: @@ -189,8 +196,7 @@ class TrackInfo(Map): self.bpm = bpm self.initial_key = initial_key self.genre = genre - for arg in kwargs: - self.__setattr__(arg, kwargs[arg]) + self.update(kwargs) # As above, work around a bug in python-musicbrainz-ngs. def decode(self, codec='utf-8'): diff --git a/test/test_autotag.py b/test/test_autotag.py index af74b5c8f..3532a80ec 100644 --- a/test/test_autotag.py +++ b/test/test_autotag.py @@ -754,36 +754,10 @@ class ApplyTest(_common.TestCase, ApplyTestUtil): def test_album_artist_overrides_empty_track_artist(self): # make a deepcopy of self.info trackinfo = [] - trackinfo.append(TrackInfo( - title=u'oneNew', - track_id=u'dfa939ec-118c-4d0f-84a0-60f3d1e6522c', - medium=1, - medium_index=1, - medium_total=1, - index=1, - artist_credit='trackArtistCredit', - artist_sort='trackArtistSort', - )) - trackinfo.append(TrackInfo( - title=u'twoNew', - track_id=u'40130ed1-a27c-42fd-a328-1ebefb6caef4', - medium=2, - medium_index=1, - index=2, - medium_total=1, - )) - my_info = AlbumInfo( - tracks=trackinfo, - artist=u'artistNew', - album=u'albumNew', - album_id='7edb51cb-77d6-4416-a23c-3a8c2994a2c7', - artist_id='a6623d39-2d8e-4f70-8242-0a9553b91e50', - artist_credit=u'albumArtistCredit', - artist_sort=u'albumArtistSort', - albumtype=u'album', - va=False, - mediums=2, - ) + trackinfo.append(TrackInfo(self.info.tracks[0])) + trackinfo.append(TrackInfo(self.info.tracks[1])) + my_info = AlbumInfo(self.info) + my_info.tracks = trackinfo self._apply(info=my_info) self.assertEqual(self.items[0].artist, 'artistNew') self.assertEqual(self.items[1].artist, 'artistNew') @@ -791,36 +765,10 @@ class ApplyTest(_common.TestCase, ApplyTestUtil): def test_album_artist_overridden_by_nonempty_track_artist(self): # make a deepcopy of self.info trackinfo = [] - trackinfo.append(TrackInfo( - title=u'oneNew', - track_id=u'dfa939ec-118c-4d0f-84a0-60f3d1e6522c', - medium=1, - medium_index=1, - medium_total=1, - index=1, - artist_credit='trackArtistCredit', - artist_sort='trackArtistSort', - )) - trackinfo.append(TrackInfo( - title=u'twoNew', - track_id=u'40130ed1-a27c-42fd-a328-1ebefb6caef4', - medium=2, - medium_index=1, - index=2, - medium_total=1, - )) - my_info = AlbumInfo( - tracks=trackinfo, - artist=u'artistNew', - album=u'albumNew', - album_id='7edb51cb-77d6-4416-a23c-3a8c2994a2c7', - artist_id='a6623d39-2d8e-4f70-8242-0a9553b91e50', - artist_credit=u'albumArtistCredit', - artist_sort=u'albumArtistSort', - albumtype=u'album', - va=False, - mediums=2, - ) + trackinfo.append(TrackInfo(self.info.tracks[0])) + trackinfo.append(TrackInfo(self.info.tracks[1])) + my_info = AlbumInfo(self.info) + my_info.tracks = trackinfo my_info.tracks[0].artist = 'artist1!' my_info.tracks[1].artist = 'artist2!' self._apply(info=my_info) @@ -844,36 +792,10 @@ class ApplyTest(_common.TestCase, ApplyTestUtil): def test_full_date_applied(self): # make a deepcopy of self.info trackinfo = [] - trackinfo.append(TrackInfo( - title=u'oneNew', - track_id=u'dfa939ec-118c-4d0f-84a0-60f3d1e6522c', - medium=1, - medium_index=1, - medium_total=1, - index=1, - artist_credit='trackArtistCredit', - artist_sort='trackArtistSort', - )) - trackinfo.append(TrackInfo( - title=u'twoNew', - track_id=u'40130ed1-a27c-42fd-a328-1ebefb6caef4', - medium=2, - medium_index=1, - index=2, - medium_total=1, - )) - my_info = AlbumInfo( - tracks=trackinfo, - artist=u'artistNew', - album=u'albumNew', - album_id='7edb51cb-77d6-4416-a23c-3a8c2994a2c7', - artist_id='a6623d39-2d8e-4f70-8242-0a9553b91e50', - artist_credit=u'albumArtistCredit', - artist_sort=u'albumArtistSort', - albumtype=u'album', - va=False, - mediums=2, - ) + trackinfo.append(TrackInfo(self.info.tracks[0])) + trackinfo.append(TrackInfo(self.info.tracks[1])) + my_info = AlbumInfo(self.info) + my_info.tracks = trackinfo my_info.year = 2013 my_info.month = 12 my_info.day = 18 @@ -890,36 +812,10 @@ class ApplyTest(_common.TestCase, ApplyTestUtil): # make a deepcopy of self.info trackinfo = [] - trackinfo.append(TrackInfo( - title=u'oneNew', - track_id=u'dfa939ec-118c-4d0f-84a0-60f3d1e6522c', - medium=1, - medium_index=1, - medium_total=1, - index=1, - artist_credit='trackArtistCredit', - artist_sort='trackArtistSort', - )) - trackinfo.append(TrackInfo( - title=u'twoNew', - track_id=u'40130ed1-a27c-42fd-a328-1ebefb6caef4', - medium=2, - medium_index=1, - index=2, - medium_total=1, - )) - my_info = AlbumInfo( - tracks=trackinfo, - artist=u'artistNew', - album=u'albumNew', - album_id='7edb51cb-77d6-4416-a23c-3a8c2994a2c7', - artist_id='a6623d39-2d8e-4f70-8242-0a9553b91e50', - artist_credit=u'albumArtistCredit', - artist_sort=u'albumArtistSort', - albumtype=u'album', - va=False, - mediums=2, - ) + trackinfo.append(TrackInfo(self.info.tracks[0])) + trackinfo.append(TrackInfo(self.info.tracks[1])) + my_info = AlbumInfo(self.info) + my_info.tracks = trackinfo my_info.year = 2013 self._apply(info=my_info) @@ -941,36 +837,10 @@ class ApplyTest(_common.TestCase, ApplyTestUtil): def test_data_source_applied(self): # make a deepcopy of self.info trackinfo = [] - trackinfo.append(TrackInfo( - title=u'oneNew', - track_id=u'dfa939ec-118c-4d0f-84a0-60f3d1e6522c', - medium=1, - medium_index=1, - medium_total=1, - index=1, - artist_credit='trackArtistCredit', - artist_sort='trackArtistSort', - )) - trackinfo.append(TrackInfo( - title=u'twoNew', - track_id=u'40130ed1-a27c-42fd-a328-1ebefb6caef4', - medium=2, - medium_index=1, - index=2, - medium_total=1, - )) - my_info = AlbumInfo( - tracks=trackinfo, - artist=u'artistNew', - album=u'albumNew', - album_id='7edb51cb-77d6-4416-a23c-3a8c2994a2c7', - artist_id='a6623d39-2d8e-4f70-8242-0a9553b91e50', - artist_credit=u'albumArtistCredit', - artist_sort=u'albumArtistSort', - albumtype=u'album', - va=False, - mediums=2, - ) + trackinfo.append(TrackInfo(self.info.tracks[0])) + trackinfo.append(TrackInfo(self.info.tracks[1])) + my_info = AlbumInfo(self.info) + my_info.tracks = trackinfo my_info.data_source = 'MusicBrainz' self._apply(info=my_info) @@ -981,7 +851,6 @@ class ApplyCompilationTest(_common.TestCase, ApplyTestUtil): def setUp(self): super(ApplyCompilationTest, self).setUp() - # make a deepcopy of self.info self.items = [] self.items.append(Item({})) self.items.append(Item({})) @@ -1035,29 +904,10 @@ class ApplyCompilationTest(_common.TestCase, ApplyTestUtil): def test_va_flag_sets_comp(self): # make a deepcopy of self.info trackinfo = [] - trackinfo = [] - trackinfo.append(TrackInfo( - title=u'oneNew', - track_id=u'dfa939ec-118c-4d0f-84a0-60f3d1e6522c', - artist=u'artistOneNew', - artist_id=u'a05686fc-9db2-4c23-b99e-77f5db3e5282', - index=1, - )) - trackinfo.append(TrackInfo( - title=u'twoNew', - track_id=u'40130ed1-a27c-42fd-a328-1ebefb6caef4', - artist=u'artistTwoNew', - artist_id=u'80b3cf5e-18fe-4c59-98c7-e5bb87210710', - index=2, - )) - va_info = AlbumInfo( - tracks=trackinfo, - artist=u'variousNew', - album=u'albumNew', - album_id='3b69ea40-39b8-487f-8818-04b6eff8c21a', - artist_id='89ad4ac3-39f7-470e-963a-56509c546377', - albumtype=u'compilation', - ) + trackinfo.append(TrackInfo(self.info.tracks[0])) + trackinfo.append(TrackInfo(self.info.tracks[1])) + va_info = AlbumInfo(self.info) + va_info.tracks = trackinfo va_info.va = True self._apply(info=va_info) self.assertTrue(self.items[0].comp) From ba2b22cac581d4cb304457b57fe5e1555bc18e45 Mon Sep 17 00:00:00 2001 From: soergeld Date: Tue, 28 Apr 2020 12:59:52 +0200 Subject: [PATCH 23/29] try to correct deepcopy --- test/test_autotag.py | 66 ++++++++++++++++++++++++++++++++------------ 1 file changed, 48 insertions(+), 18 deletions(-) diff --git a/test/test_autotag.py b/test/test_autotag.py index 3532a80ec..06e6b7e72 100644 --- a/test/test_autotag.py +++ b/test/test_autotag.py @@ -754,9 +754,14 @@ class ApplyTest(_common.TestCase, ApplyTestUtil): def test_album_artist_overrides_empty_track_artist(self): # make a deepcopy of self.info trackinfo = [] - trackinfo.append(TrackInfo(self.info.tracks[0])) - trackinfo.append(TrackInfo(self.info.tracks[1])) - my_info = AlbumInfo(self.info) + track = TrackInfo() + track.update(self.info.tracks[0]) + trackinfo.append(track) + track = TrackInfo() + track.update(self.info.tracks[1]) + trackinfo.append(track) + my_info = AlbumInfo() + my_info.update(self.info) my_info.tracks = trackinfo self._apply(info=my_info) self.assertEqual(self.items[0].artist, 'artistNew') @@ -765,9 +770,14 @@ class ApplyTest(_common.TestCase, ApplyTestUtil): def test_album_artist_overridden_by_nonempty_track_artist(self): # make a deepcopy of self.info trackinfo = [] - trackinfo.append(TrackInfo(self.info.tracks[0])) - trackinfo.append(TrackInfo(self.info.tracks[1])) - my_info = AlbumInfo(self.info) + track = TrackInfo() + track.update(self.info.tracks[0]) + trackinfo.append(track) + track = TrackInfo() + track.update(self.info.tracks[1]) + trackinfo.append(track) + my_info = AlbumInfo() + my_info.update(self.info) my_info.tracks = trackinfo my_info.tracks[0].artist = 'artist1!' my_info.tracks[1].artist = 'artist2!' @@ -792,9 +802,14 @@ class ApplyTest(_common.TestCase, ApplyTestUtil): def test_full_date_applied(self): # make a deepcopy of self.info trackinfo = [] - trackinfo.append(TrackInfo(self.info.tracks[0])) - trackinfo.append(TrackInfo(self.info.tracks[1])) - my_info = AlbumInfo(self.info) + track = TrackInfo() + track.update(self.info.tracks[0]) + trackinfo.append(track) + track = TrackInfo() + track.update(self.info.tracks[1]) + trackinfo.append(track) + my_info = AlbumInfo() + my_info.update(self.info) my_info.tracks = trackinfo my_info.year = 2013 my_info.month = 12 @@ -812,9 +827,14 @@ class ApplyTest(_common.TestCase, ApplyTestUtil): # make a deepcopy of self.info trackinfo = [] - trackinfo.append(TrackInfo(self.info.tracks[0])) - trackinfo.append(TrackInfo(self.info.tracks[1])) - my_info = AlbumInfo(self.info) + track = TrackInfo() + track.update(self.info.tracks[0]) + trackinfo.append(track) + track = TrackInfo() + track.update(self.info.tracks[1]) + trackinfo.append(track) + my_info = AlbumInfo() + my_info.update(self.info) my_info.tracks = trackinfo my_info.year = 2013 self._apply(info=my_info) @@ -837,9 +857,14 @@ class ApplyTest(_common.TestCase, ApplyTestUtil): def test_data_source_applied(self): # make a deepcopy of self.info trackinfo = [] - trackinfo.append(TrackInfo(self.info.tracks[0])) - trackinfo.append(TrackInfo(self.info.tracks[1])) - my_info = AlbumInfo(self.info) + track = TrackInfo() + track.update(self.info.tracks[0]) + trackinfo.append(track) + track = TrackInfo() + track.update(self.info.tracks[1]) + trackinfo.append(track) + my_info = AlbumInfo() + my_info.update(self.info) my_info.tracks = trackinfo my_info.data_source = 'MusicBrainz' self._apply(info=my_info) @@ -904,9 +929,14 @@ class ApplyCompilationTest(_common.TestCase, ApplyTestUtil): def test_va_flag_sets_comp(self): # make a deepcopy of self.info trackinfo = [] - trackinfo.append(TrackInfo(self.info.tracks[0])) - trackinfo.append(TrackInfo(self.info.tracks[1])) - va_info = AlbumInfo(self.info) + track = TrackInfo() + track.update(self.info.tracks[0]) + trackinfo.append(track) + track = TrackInfo() + track.update(self.info.tracks[1]) + trackinfo.append(track) + va_info = AlbumInfo() + va_info.update(self.info) va_info.tracks = trackinfo va_info.va = True self._apply(info=va_info) From 84bbd14c767d3a720e57b5572f9c8f443c18896a Mon Sep 17 00:00:00 2001 From: soergeld Date: Tue, 28 Apr 2020 13:09:04 +0200 Subject: [PATCH 24/29] trailing whitespace --- beets/autotag/hooks.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/beets/autotag/hooks.py b/beets/autotag/hooks.py index 81c2fadb1..2c78749e6 100644 --- a/beets/autotag/hooks.py +++ b/beets/autotag/hooks.py @@ -42,13 +42,13 @@ except AttributeError: class AttrDict(dict): """ Dictionary with flexible attributes - to get an tag value: + to get an tag value: value = info.tag or value = info[tag] or value = info.get(tag) or value = getattr(info, tag) all raise AttributeError when info doesn't have tag - to set a tag value: + to set a tag value: info.tag = value or info[tag] = value or setattr(info, tag, value) From d7ed84646ef2b5a91cda234c0284433ff6c67bd5 Mon Sep 17 00:00:00 2001 From: soergeld Date: Tue, 28 Apr 2020 14:18:27 +0200 Subject: [PATCH 25/29] create method for deepcopy --- beets/autotag/hooks.py | 14 +++++++++ test/test_autotag.py | 66 ++++-------------------------------------- 2 files changed, 20 insertions(+), 60 deletions(-) diff --git a/beets/autotag/hooks.py b/beets/autotag/hooks.py index 2c78749e6..444ae7b59 100644 --- a/beets/autotag/hooks.py +++ b/beets/autotag/hooks.py @@ -148,6 +148,16 @@ class AlbumInfo(AttrDict): for track in self.tracks: track.decode(codec) + def dup_albuminfo(self): + dupe = AlbumInfo() + dupe.update(self) + if 'tracks' in self: + tracks = [] + for track in self.tracks: + tracks.append(track.dup_trackinfo()) + dupe.tracks = tracks + return dupe + class TrackInfo(AttrDict): """Describes a canonical track present on a release. Appears as part @@ -209,6 +219,10 @@ class TrackInfo(AttrDict): if isinstance(value, bytes): setattr(self, fld, value.decode(codec, 'ignore')) + def dup_trackinfo(self): + dupe = TrackInfo() + dupe.update(self) + # Candidate distance scoring. diff --git a/test/test_autotag.py b/test/test_autotag.py index 06e6b7e72..cef6364d0 100644 --- a/test/test_autotag.py +++ b/test/test_autotag.py @@ -753,32 +753,14 @@ class ApplyTest(_common.TestCase, ApplyTestUtil): def test_album_artist_overrides_empty_track_artist(self): # make a deepcopy of self.info - trackinfo = [] - track = TrackInfo() - track.update(self.info.tracks[0]) - trackinfo.append(track) - track = TrackInfo() - track.update(self.info.tracks[1]) - trackinfo.append(track) - my_info = AlbumInfo() - my_info.update(self.info) - my_info.tracks = trackinfo + my_info = self.info.dup_albuminfo() self._apply(info=my_info) self.assertEqual(self.items[0].artist, 'artistNew') self.assertEqual(self.items[1].artist, 'artistNew') def test_album_artist_overridden_by_nonempty_track_artist(self): # make a deepcopy of self.info - trackinfo = [] - track = TrackInfo() - track.update(self.info.tracks[0]) - trackinfo.append(track) - track = TrackInfo() - track.update(self.info.tracks[1]) - trackinfo.append(track) - my_info = AlbumInfo() - my_info.update(self.info) - my_info.tracks = trackinfo + my_info = self.info.dup_albuminfo() my_info.tracks[0].artist = 'artist1!' my_info.tracks[1].artist = 'artist2!' self._apply(info=my_info) @@ -801,16 +783,7 @@ class ApplyTest(_common.TestCase, ApplyTestUtil): def test_full_date_applied(self): # make a deepcopy of self.info - trackinfo = [] - track = TrackInfo() - track.update(self.info.tracks[0]) - trackinfo.append(track) - track = TrackInfo() - track.update(self.info.tracks[1]) - trackinfo.append(track) - my_info = AlbumInfo() - my_info.update(self.info) - my_info.tracks = trackinfo + my_info = self.info.dup_albuminfo() my_info.year = 2013 my_info.month = 12 my_info.day = 18 @@ -826,16 +799,7 @@ class ApplyTest(_common.TestCase, ApplyTestUtil): self.items.append(Item(year=4, month=5, day=6)) # make a deepcopy of self.info - trackinfo = [] - track = TrackInfo() - track.update(self.info.tracks[0]) - trackinfo.append(track) - track = TrackInfo() - track.update(self.info.tracks[1]) - trackinfo.append(track) - my_info = AlbumInfo() - my_info.update(self.info) - my_info.tracks = trackinfo + my_info = self.info.dup_albuminfo() my_info.year = 2013 self._apply(info=my_info) @@ -856,16 +820,7 @@ class ApplyTest(_common.TestCase, ApplyTestUtil): def test_data_source_applied(self): # make a deepcopy of self.info - trackinfo = [] - track = TrackInfo() - track.update(self.info.tracks[0]) - trackinfo.append(track) - track = TrackInfo() - track.update(self.info.tracks[1]) - trackinfo.append(track) - my_info = AlbumInfo() - my_info.update(self.info) - my_info.tracks = trackinfo + my_info = self.info.dup_albuminfo() my_info.data_source = 'MusicBrainz' self._apply(info=my_info) @@ -928,16 +883,7 @@ class ApplyCompilationTest(_common.TestCase, ApplyTestUtil): def test_va_flag_sets_comp(self): # make a deepcopy of self.info - trackinfo = [] - track = TrackInfo() - track.update(self.info.tracks[0]) - trackinfo.append(track) - track = TrackInfo() - track.update(self.info.tracks[1]) - trackinfo.append(track) - va_info = AlbumInfo() - va_info.update(self.info) - va_info.tracks = trackinfo + va_info = self.info.dup_albuminfo() va_info.va = True self._apply(info=va_info) self.assertTrue(self.items[0].comp) From 370df6253d4c4da48183a018332c8c2778877979 Mon Sep 17 00:00:00 2001 From: soergeld Date: Tue, 28 Apr 2020 14:19:59 +0200 Subject: [PATCH 26/29] forgot a return --- beets/autotag/hooks.py | 1 + 1 file changed, 1 insertion(+) diff --git a/beets/autotag/hooks.py b/beets/autotag/hooks.py index 444ae7b59..c2775bf32 100644 --- a/beets/autotag/hooks.py +++ b/beets/autotag/hooks.py @@ -222,6 +222,7 @@ class TrackInfo(AttrDict): def dup_trackinfo(self): dupe = TrackInfo() dupe.update(self) + return dupe # Candidate distance scoring. From 7c71bb87a2f549c063af979b50e2160cd32058ed Mon Sep 17 00:00:00 2001 From: soergeld Date: Fri, 8 May 2020 16:32:12 +0200 Subject: [PATCH 27/29] cleaning up, renaming dup_XXInfo() to copy() --- beets/autotag/hooks.py | 23 +++++-------- beetsplug/discogs.py | 19 ++++------ test/test_autotag.py | 78 +++++++++++++++--------------------------- test/test_ui.py | 5 +-- 4 files changed, 44 insertions(+), 81 deletions(-) diff --git a/beets/autotag/hooks.py b/beets/autotag/hooks.py index c2775bf32..6e0cada53 100644 --- a/beets/autotag/hooks.py +++ b/beets/autotag/hooks.py @@ -80,9 +80,9 @@ class AlbumInfo(AttrDict): ``mediums`` along with the fields up through ``tracks`` are required. The others are optional and may be None. """ - def __init__(self, album=None, album_id=None, artist=None, artist_id=None, - tracks=None, asin=None, albumtype=None, va=False, year=None, - month=None, day=None, label=None, mediums=None, + def __init__(self, tracks, album=None, album_id=None, artist=None, + artist_id=None, asin=None, albumtype=None, va=False, + year=None, month=None, day=None, label=None, mediums=None, artist_sort=None, releasegroup_id=None, catalognum=None, script=None, language=None, country=None, style=None, genre=None, albumstatus=None, media=None, albumdisambig=None, @@ -144,18 +144,13 @@ class AlbumInfo(AttrDict): if isinstance(value, bytes): setattr(self, fld, value.decode(codec, 'ignore')) - if 'tracks' in self: - for track in self.tracks: - track.decode(codec) + for track in self.tracks: + track.decode(codec) - def dup_albuminfo(self): - dupe = AlbumInfo() + def copy(self): + dupe = AlbumInfo([]) dupe.update(self) - if 'tracks' in self: - tracks = [] - for track in self.tracks: - tracks.append(track.dup_trackinfo()) - dupe.tracks = tracks + dupe.tracks = [track.copy() for track in self.tracks] return dupe @@ -219,7 +214,7 @@ class TrackInfo(AttrDict): if isinstance(value, bytes): setattr(self, fld, value.decode(codec, 'ignore')) - def dup_trackinfo(self): + def copy(self): dupe = TrackInfo() dupe.update(self) return dupe diff --git a/beetsplug/discogs.py b/beetsplug/discogs.py index a0a6ea654..038fa809a 100644 --- a/beetsplug/discogs.py +++ b/beetsplug/discogs.py @@ -357,17 +357,13 @@ class DiscogsPlugin(BeetsPlugin): original_year = self.get_master_year(master_id) if master_id else year return AlbumInfo(album=album, album_id=album_id, artist=artist, - artist_id=artist_id, tracks=tracks, asin=None, - albumtype=albumtype, va=va, year=year, month=None, - day=None, label=label, mediums=len(set(mediums)), - artist_sort=None, releasegroup_id=master_id, - catalognum=catalogno, script=None, language=None, + artist_id=artist_id, tracks=tracks, + albumtype=albumtype, va=va, year=year, + label=label, mediums=len(set(mediums)), + releasegroup_id=master_id, catalognum=catalogno, country=country, style=style, genre=genre, - albumstatus=None, media=media, - albumdisambig=None, artist_credit=None, - original_year=original_year, original_month=None, - original_day=None, data_source='Discogs', - data_url=data_url, + media=media, original_year=original_year, + data_source='Discogs', data_url=data_url, discogs_albumid=discogs_albumid, discogs_labelid=labelid, discogs_artistid=artist_id) @@ -570,8 +566,7 @@ class DiscogsPlugin(BeetsPlugin): length = self.get_track_length(track['duration']) return TrackInfo(title=title, track_id=track_id, artist=artist, artist_id=artist_id, length=length, index=index, - medium=medium, medium_index=medium_index, - artist_sort=None, disctitle=None, artist_credit=None) + medium=medium, medium_index=medium_index) def get_track_index(self, position): """Returns the medium, medium index and subtrack index for a discogs diff --git a/test/test_autotag.py b/test/test_autotag.py index cef6364d0..febd1641d 100644 --- a/test/test_autotag.py +++ b/test/test_autotag.py @@ -350,9 +350,7 @@ class AlbumDistanceTest(_common.TestCase): artist=u'some artist', album=u'some album', tracks=_make_trackinfo(), - va=False, - album_id=None, - artist_id=None, + va=False ) self.assertEqual(self._dist(items, info), 0) @@ -364,9 +362,7 @@ class AlbumDistanceTest(_common.TestCase): artist=u'some artist', album=u'some album', tracks=_make_trackinfo(), - va=False, - album_id=None, - artist_id=None, + va=False ) dist = self._dist(items, info) self.assertNotEqual(dist, 0) @@ -382,9 +378,7 @@ class AlbumDistanceTest(_common.TestCase): artist=u'someone else', album=u'some album', tracks=_make_trackinfo(), - va=False, - album_id=None, - artist_id=None, + va=False ) self.assertNotEqual(self._dist(items, info), 0) @@ -397,9 +391,7 @@ class AlbumDistanceTest(_common.TestCase): artist=u'should be ignored', album=u'some album', tracks=_make_trackinfo(), - va=True, - album_id=None, - artist_id=None, + va=True ) self.assertEqual(self._dist(items, info), 0) @@ -413,9 +405,7 @@ class AlbumDistanceTest(_common.TestCase): artist=u'should be ignored', album=u'some album', tracks=_make_trackinfo(), - va=True, - album_id=None, - artist_id=None, + va=True ) info.tracks[0].artist = None info.tracks[1].artist = None @@ -431,9 +421,7 @@ class AlbumDistanceTest(_common.TestCase): artist=u'some artist', album=u'some album', tracks=_make_trackinfo(), - va=True, - album_id=None, - artist_id=None, + va=True ) self.assertNotEqual(self._dist(items, info), 0) @@ -446,9 +434,7 @@ class AlbumDistanceTest(_common.TestCase): artist=u'some artist', album=u'some album', tracks=_make_trackinfo(), - va=False, - album_id=None, - artist_id=None, + va=False ) dist = self._dist(items, info) self.assertTrue(0 < dist < 0.2) @@ -462,9 +448,7 @@ class AlbumDistanceTest(_common.TestCase): artist=u'some artist', album=u'some album', tracks=_make_trackinfo(), - va=False, - album_id=None, - artist_id=None, + va=False ) info.tracks[0].medium_index = 1 info.tracks[1].medium_index = 2 @@ -481,9 +465,7 @@ class AlbumDistanceTest(_common.TestCase): artist=u'some artist', album=u'some album', tracks=_make_trackinfo(), - va=False, - album_id=None, - artist_id=None, + va=False ) info.tracks[0].medium_index = 1 info.tracks[1].medium_index = 2 @@ -505,9 +487,9 @@ class AssignmentTest(unittest.TestCase): items.append(self.item(u'three', 2)) items.append(self.item(u'two', 3)) trackinfo = [] - trackinfo.append(TrackInfo(title=u'one', track_id=None)) - trackinfo.append(TrackInfo(title=u'two', track_id=None)) - trackinfo.append(TrackInfo(title=u'three', track_id=None)) + trackinfo.append(TrackInfo(title=u'one')) + trackinfo.append(TrackInfo(title=u'two')) + trackinfo.append(TrackInfo(title=u'three')) mapping, extra_items, extra_tracks = \ match.assign_items(items, trackinfo) self.assertEqual(extra_items, []) @@ -524,9 +506,9 @@ class AssignmentTest(unittest.TestCase): items.append(self.item(u'three', 1)) items.append(self.item(u'two', 1)) trackinfo = [] - trackinfo.append(TrackInfo(title=u'one', track_id=None)) - trackinfo.append(TrackInfo(title=u'two', track_id=None)) - trackinfo.append(TrackInfo(title=u'three', track_id=None)) + trackinfo.append(TrackInfo(title=u'one')) + trackinfo.append(TrackInfo(title=u'two')) + trackinfo.append(TrackInfo(title=u'three')) mapping, extra_items, extra_tracks = \ match.assign_items(items, trackinfo) self.assertEqual(extra_items, []) @@ -542,9 +524,9 @@ class AssignmentTest(unittest.TestCase): items.append(self.item(u'one', 1)) items.append(self.item(u'three', 3)) trackinfo = [] - trackinfo.append(TrackInfo(title=u'one', track_id=None)) - trackinfo.append(TrackInfo(title=u'two', track_id=None)) - trackinfo.append(TrackInfo(title=u'three', track_id=None)) + trackinfo.append(TrackInfo(title=u'one')) + trackinfo.append(TrackInfo(title=u'two')) + trackinfo.append(TrackInfo(title=u'three')) mapping, extra_items, extra_tracks = \ match.assign_items(items, trackinfo) self.assertEqual(extra_items, []) @@ -560,8 +542,8 @@ class AssignmentTest(unittest.TestCase): items.append(self.item(u'two', 2)) items.append(self.item(u'three', 3)) trackinfo = [] - trackinfo.append(TrackInfo(title=u'one', track_id=None)) - trackinfo.append(TrackInfo(title=u'three', track_id=None)) + trackinfo.append(TrackInfo(title=u'one')) + trackinfo.append(TrackInfo(title=u'three')) mapping, extra_items, extra_tracks = \ match.assign_items(items, trackinfo) self.assertEqual(extra_items, [items[1]]) @@ -597,7 +579,7 @@ class AssignmentTest(unittest.TestCase): items.append(item(12, 186.45916150485752)) def info(index, title, length): - return TrackInfo(title=title, track_id=None, length=length, + return TrackInfo(title=title, length=length, index=index) trackinfo = [] trackinfo.append(info(1, u'Alone', 238.893)) @@ -752,15 +734,13 @@ class ApplyTest(_common.TestCase, ApplyTestUtil): self.assertEqual(self.items[1].albumtype, 'album') def test_album_artist_overrides_empty_track_artist(self): - # make a deepcopy of self.info - my_info = self.info.dup_albuminfo() + my_info = self.info.copy() self._apply(info=my_info) self.assertEqual(self.items[0].artist, 'artistNew') self.assertEqual(self.items[1].artist, 'artistNew') def test_album_artist_overridden_by_nonempty_track_artist(self): - # make a deepcopy of self.info - my_info = self.info.dup_albuminfo() + my_info = self.info.copy() my_info.tracks[0].artist = 'artist1!' my_info.tracks[1].artist = 'artist2!' self._apply(info=my_info) @@ -782,8 +762,7 @@ class ApplyTest(_common.TestCase, ApplyTestUtil): self.assertEqual(self.items[1].artist_sort, 'albumArtistSort') def test_full_date_applied(self): - # make a deepcopy of self.info - my_info = self.info.dup_albuminfo() + my_info = self.info.copy() my_info.year = 2013 my_info.month = 12 my_info.day = 18 @@ -798,8 +777,7 @@ class ApplyTest(_common.TestCase, ApplyTestUtil): self.items.append(Item(year=1, month=2, day=3)) self.items.append(Item(year=4, month=5, day=6)) - # make a deepcopy of self.info - my_info = self.info.dup_albuminfo() + my_info = self.info.copy() my_info.year = 2013 self._apply(info=my_info) @@ -819,8 +797,7 @@ class ApplyTest(_common.TestCase, ApplyTestUtil): self.assertEqual(self.items[0].day, 3) def test_data_source_applied(self): - # make a deepcopy of self.info - my_info = self.info.dup_albuminfo() + my_info = self.info.copy() my_info.data_source = 'MusicBrainz' self._apply(info=my_info) @@ -882,8 +859,7 @@ class ApplyCompilationTest(_common.TestCase, ApplyTestUtil): self.assertFalse(self.items[1].comp) def test_va_flag_sets_comp(self): - # make a deepcopy of self.info - va_info = self.info.dup_albuminfo() + va_info = self.info.copy() va_info.va = True self._apply(info=va_info) self.assertTrue(self.items[0].comp) diff --git a/test/test_ui.py b/test/test_ui.py index d0be10060..b1e7e8fad 100644 --- a/test/test_ui.py +++ b/test/test_ui.py @@ -1138,10 +1138,7 @@ class SummarizeItemsTest(_common.TestCase): self.assertEqual(summary, u"1 items, F, 4kbps, 10:54, 987.0 B") # make a copy of self.item - i2 = library.Item() - i2.bitrate = 4321 - i2.length = 10 * 60 + 54 - i2.format = "F" + i2 = self.item.copy() summary = commands.summarize_items([self.item, i2], False) self.assertEqual(summary, u"2 items, F, 4kbps, 21:48, 1.9 KiB") From 4b7f42d21458166ef5eefb9b3068d8ff0447016c Mon Sep 17 00:00:00 2001 From: Dorian Soergel Date: Sat, 9 May 2020 12:29:52 +0200 Subject: [PATCH 28/29] Update AttrDict docstring Co-authored-by: Adrian Sampson --- beets/autotag/hooks.py | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/beets/autotag/hooks.py b/beets/autotag/hooks.py index 6e0cada53..9bdf6b001 100644 --- a/beets/autotag/hooks.py +++ b/beets/autotag/hooks.py @@ -40,18 +40,8 @@ except AttributeError: # Classes used to represent candidate options. class AttrDict(dict): - """ - Dictionary with flexible attributes - to get an tag value: - value = info.tag - or value = info[tag] - or value = info.get(tag) - or value = getattr(info, tag) - all raise AttributeError when info doesn't have tag - to set a tag value: - info.tag = value - or info[tag] = value - or setattr(info, tag, value) + """A dictionary that supports attribute ("dot") access, so `d.field` + is equivalent to `d['field']`. """ def __getattr__(self, attr): From 8fa103e0de4de0ebb01685345f44e229cf4dab21 Mon Sep 17 00:00:00 2001 From: soergeld Date: Sat, 9 May 2020 12:44:36 +0200 Subject: [PATCH 29/29] changelog entry --- docs/changelog.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/changelog.rst b/docs/changelog.rst index e2f1521eb..5434e8b13 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -115,6 +115,9 @@ New features: :bug:`3459` * :doc:`/plugins/fetchart`: Album art can now be fetched from `last.fm`_. :bug:`3530` +* The classes ``AlbumInfo`` and ``TrackInfo`` now have flexible attributes, + allowing to solve :bug:`1547`. + Thanks to :user:`dosoe`. Fixes: