def infer_album_fields(self): """Make the some album fields equal across `self.items` """ changes = {} if self.choice_flag == action.ASIS: # Taking metadata "as-is". Guess whether this album is VA. plur_albumartist, freq = util.plurality([i.albumartist or i.artist for i in self.items]) if freq == len(self.items) or (freq > 1 and float(freq) / len(self.items) >= SINGLE_ARTIST_THRESH): # Single-artist album. changes["albumartist"] = plur_albumartist changes["comp"] = False else: # VA. changes["albumartist"] = VARIOUS_ARTISTS changes["comp"] = True elif self.choice_flag == action.APPLY: # Applying autotagged metadata. Just get AA from the first # item. if not self.items[0].albumartist: changes["albumartist"] = self.items[0].artist if not self.items[0].mb_albumartistid: changes["mb_albumartistid"] = self.items[0].mb_artistid # Apply new metadata. for item in self.items: item.update(changes)
def align_album_level_fields(self): """Make the some album fields equal across `self.items` """ changes = {} if self.choice_flag == action.ASIS: # Taking metadata "as-is". Guess whether this album is VA. plur_albumartist, freq = util.plurality( [i.albumartist or i.artist for i in self.items]) if freq == len(self.items) or \ (freq > 1 and float(freq) / len(self.items) >= SINGLE_ARTIST_THRESH): # Single-artist album. changes['albumartist'] = plur_albumartist changes['comp'] = False else: # VA. changes['albumartist'] = VARIOUS_ARTISTS changes['comp'] = True elif self.choice_flag == action.APPLY: # Applying autotagged metadata. Just get AA from the first # item. if not self.items[0].albumartist: changes['albumartist'] = self.items[0].artist if not self.items[0].mb_albumartistid: changes['mb_albumartistid'] = self.items[0].mb_artistid # Apply new metadata. for item in self.items: item.update(changes)
def current_metadata(items): """Extract the likely current metadata for an album given a list of its items. Return two dictionaries: - The most common value for each field. - Whether each field's value was unanimous (values are booleans). """ assert items # Must be nonempty. likelies = {} consensus = {} fields = [ "artist", "album", "albumartist", "year", "disctotal", "mb_albumid", "label", "catalognum", "country", "media", "albumdisambig", ] for field in fields: values = [item[field] for item in items if item] likelies[field], freq = plurality(values) consensus[field] = freq == len(values) # If there's an album artist consensus, use this for the artist. if consensus["albumartist"] and likelies["albumartist"]: likelies["artist"] = likelies["albumartist"] return likelies, consensus
def _infer_album_fields(task): """Given an album and an associated import task, massage the album-level metadata. This ensures that the album artist is set and that the "compilation" flag is set automatically. """ assert task.is_album assert task.items changes = {} if task.choice_flag == action.ASIS: # Taking metadata "as-is". Guess whether this album is VA. # Check for albumartist field: for item in task.items: if item is not None: first_item = item break else: assert False, "all items are None" if first_item.mb_albumartistid: changes['mb_albumartistid'] = first_item.mb_albumartistid if first_item.albumartist: changes['albumartist'] = first_item.albumartist else: # Guess if the album is not VA: plur_artist, freq = plurality([i.artist for i in task.items]) if freq == len(task.items) or (freq > 1 and float(freq) / len(task.items) >= SINGLE_ARTIST_THRESH): # Single-artist album. changes['albumartist'] = plur_artist changes['comp'] = False else: # VA. changes['albumartist'] = VARIOUS_ARTISTS changes['comp'] = True elif task.choice_flag == action.APPLY: # Applying autotagged metadata. Just get AA from the first # item. for item in task.items: if item is not None: first_item = item break else: assert False, "all items are None" if not first_item.albumartist: changes['albumartist'] = first_item.artist if not first_item.mb_albumartistid: changes['mb_albumartistid'] = first_item.mb_artistid else: assert False # Apply new metadata. for item in task.items: if item is not None: for k, v in changes.iteritems(): setattr(item, k, v)
def get_cur_artist(items): """Given a sequence of items, returns the current artist and artist ID that is most popular among the fingerprinted metadata for the tracks. """ # Get "fingerprinted" artists for each track. artists = [] artist_ids = [] for item in items: last_data = match(item.path) if last_data: artists.append(last_data["artist"]) if last_data["artist_mbid"]: artist_ids.append(last_data["artist_mbid"]) # Vote on the most popular artist. artist, _ = plurality(artists) artist_id, _ = plurality(artist_ids) return artist, artist_id
def get_cur_artist(items): """Given a sequence of items, returns the current artist and artist ID that is most popular among the fingerprinted metadata for the tracks. """ # Get "fingerprinted" artists for each track. artists = [] artist_ids = [] for item in items: last_data = last_match(item.path) if last_data: artists.append(last_data['artist']) if last_data['artist_mbid']: artist_ids.append(last_data['artist_mbid']) # Vote on the most popular artist. artist, _ = plurality(artists) artist_id, _ = plurality(artist_ids) return artist, artist_id
def current_metadata(items): """Returns the most likely artist and album for a set of Items. Each is determined by tag reflected by the plurality of the Items. """ keys = 'artist', 'album' likelies = {} consensus = {} for key in keys: values = [getattr(item, key) for item in items if item] likelies[key], freq = plurality(values) consensus[key] = (freq == len(values)) return likelies['artist'], likelies['album'], consensus['artist']
def _infer_album_fields(task): """Given an album and an associated import task, massage the album-level metadata. This ensures that the album artist is set and that the "compilation" flag is set automatically. """ assert task.is_album assert task.items changes = {} if task.choice_flag == action.ASIS: # Taking metadata "as-is". Guess whether this album is VA. plur_albumartist, freq = util.plurality( [i.albumartist or i.artist for i in task.items]) if freq == len(task.items) or ( freq > 1 and float(freq) / len(task.items) >= SINGLE_ARTIST_THRESH): # Single-artist album. changes['albumartist'] = plur_albumartist changes['comp'] = False else: # VA. changes['albumartist'] = VARIOUS_ARTISTS changes['comp'] = True elif task.choice_flag == action.APPLY: # Applying autotagged metadata. Just get AA from the first # item. for item in task.items: if item is not None: first_item = item break else: assert False, "all items are None" if not first_item.albumartist: changes['albumartist'] = first_item.artist if not first_item.mb_albumartistid: changes['mb_albumartistid'] = first_item.mb_artistid else: assert False # Apply new metadata. for item in task.items: if item is not None: for k, v in changes.iteritems(): setattr(item, k, v)
def current_metadata(items): """Returns the most likely artist and album for a set of Items. Each is determined by tag reflected by the plurality of the Items. """ likelies = {} consensus = {} for key in "artist", "album", "albumartist": values = [getattr(item, key) for item in items if item] likelies[key], freq = plurality(values) consensus[key] = freq == len(values) if consensus["albumartist"] and likelies["albumartist"]: artist = likelies["albumartist"] else: artist = likelies["artist"] return artist, likelies["album"], consensus["artist"]
def current_metadata(items): """Extract the likely current metadata for an album given a list of its items. Return two dictionaries: - The most common value for each field. - Whether each field's value was unanimous (values are booleans). """ assert items # Must be nonempty. likelies = {} consensus = {} fields = ['artist', 'album', 'albumartist', 'year', 'disctotal', 'mb_albumid', 'label', 'catalognum', 'country', 'media', 'albumdisambig'] for key in fields: values = [getattr(item, key) for item in items if item] likelies[key], freq = plurality(values) consensus[key] = (freq == len(values)) # If there's an album artist consensus, use this for the artist. if consensus['albumartist'] and likelies['albumartist']: likelies['artist'] = likelies['albumartist'] return likelies, consensus
def test_plurality_near_consensus(self): objs = [1, 1, 2, 1] obj, freq = plurality(objs) self.assertEqual(obj, 1) self.assertEqual(freq, 3)
def _get_genre(self, obj): """Get the genre string for an Album or Item object based on self.sources. Return a `(genre, source)` pair. The prioritization order is: - track (for Items only) - album - artist - original - fallback - None """ # Shortcut to existing genre if not forcing. if not self.config['force'] and self._is_allowed(obj.genre): return obj.genre, 'keep' # Track genre (for Items only). if isinstance(obj, library.Item): if 'track' in self.sources: result = self.fetch_track_genre(obj) if result: return result, 'track' # Album genre. if 'album' in self.sources: result = self.fetch_album_genre(obj) if result: return result, 'album' # Artist (or album artist) genre. if 'artist' in self.sources: result = None if isinstance(obj, library.Item): result = self.fetch_artist_genre(obj) elif obj.albumartist != 'Various Artists': result = self.fetch_album_artist_genre(obj) else: # For "Various Artists", pick the most popular track genre. item_genres = [] for item in obj.items(): item_genre = None if 'track' in self.sources: item_genre = self.fetch_track_genre(item) if not item_genre: item_genre = self.fetch_artist_genre(item) if item_genre: item_genres.append(item_genre) if item_genres: result, _ = plurality(item_genres) if result: return result, 'artist' # Filter the existing genre. if obj.genre: result = self._resolve_genres([obj.genre]) if result: return result, 'original' # Fallback string. fallback = self.config['fallback'].get() if fallback: return fallback, 'fallback' return None, None
def _get_genre(self, obj): """Get the genre string for an Album or Item object based on self.sources. Return a `(genre, source)` pair. The prioritization order is: - track (for Items only) - album - artist - original - fallback - None """ # Shortcut to existing genre if not forcing. if not self.config["force"] and self._is_allowed(obj.genre): return obj.genre, "keep" # Track genre (for Items only). if isinstance(obj, library.Item): if "track" in self.sources: result = self.fetch_track_genre(obj) if result: return result, "track" # Album genre. if "album" in self.sources: result = self.fetch_album_genre(obj) if result: return result, "album" # Artist (or album artist) genre. if "artist" in self.sources: result = None if isinstance(obj, library.Item): result = self.fetch_artist_genre(obj) elif obj.albumartist != config["va_name"].as_str(): result = self.fetch_album_artist_genre(obj) else: # For "Various Artists", pick the most popular track genre. item_genres = [] for item in obj.items(): item_genre = None if "track" in self.sources: item_genre = self.fetch_track_genre(item) if not item_genre: item_genre = self.fetch_artist_genre(item) if item_genre: item_genres.append(item_genre) if item_genres: result, _ = plurality(item_genres) if result: return result, "artist" # Filter the existing genre. if obj.genre: result = self._resolve_genres([obj.genre]) if result: return result, "original" # Fallback string. fallback = self.config["fallback"].get() if fallback: return fallback, "fallback" return None, None
def test_plurality_conflict(self): objs = [1, 1, 2, 2, 3] obj, freq = plurality(objs) self.assertTrue(obj in (1, 2)) self.assertEqual(freq, 2)
def test_plurality_empty_sequence_raises_error(self): with self.assertRaises(ValueError): plurality([])
def _get_genre(self, obj): """Get the genre string for an Album or Item object based on self.sources. Return a `(genre, source)` pair. The prioritization order is: - track (for Items only) - album - artist - original - fallback - None """ # Shortcut to existing genre if not forcing. if not self.config['force'] and _is_allowed(obj.genre): return obj.genre, 'keep' # Track genre (for Items only). if isinstance(obj, library.Item): if 'track' in self.sources: result = fetch_track_genre(obj) if result: return result, 'track' # Album genre. if 'album' in self.sources: result = fetch_album_genre(obj) if result: return result, 'album' # Artist (or album artist) genre. if 'artist' in self.sources: result = None if isinstance(obj, library.Item): result = fetch_artist_genre(obj) elif obj.albumartist != 'Various Artists': result = fetch_album_artist_genre(obj) else: # For "Various Artists", pick the most popular track genre. item_genres = [] for item in obj.items(): item_genre = None if 'track' in self.sources: item_genre = fetch_track_genre(item) if not item_genre: item_genre = fetch_artist_genre(item) if item_genre: item_genres.append(item_genre) if item_genres: result, _ = plurality(item_genres) if result: return result, 'artist' # Filter the existing genre. if obj.genre: result = _strings_to_genre([obj.genre]) if result: return result, 'original' # Fallback string. fallback = self.config['fallback'].get() if fallback: return fallback, 'fallback' return None, None