diff --git a/beetsplug/lastgenre/__init__.py b/beetsplug/lastgenre/__init__.py index 9523f57f5..2756f452c 100644 --- a/beetsplug/lastgenre/__init__.py +++ b/beetsplug/lastgenre/__init__.py @@ -109,6 +109,7 @@ class LastGenrePlugin(plugins.BeetsPlugin): 'force': True, 'auto': True, 'separator': u', ', + 'prefer_specific': False, }) self.setup() @@ -158,6 +159,24 @@ class LastGenrePlugin(plugins.BeetsPlugin): elif source == 'artist': return 'artist', + def _get_depth(self, tag): + """Find the depth of a tag in the genres tree. + """ + depth = None + for key, value in enumerate(self.c14n_branches): + if tag in value: + depth = value.index(tag) + break + return depth + + def _sort_by_depth(self, tags): + """Given a list of tags, sort the tags by their depths in the + genre tree. + """ + depth_tag_pairs = [(self._get_depth(t), t) for t in tags] + depth_tag_pairs.sort(reverse=True) + return [p[1] for p in depth_tag_pairs] + def _resolve_genres(self, tags): """Given a list of strings, return a genre by joining them into a single string and (optionally) canonicalizing each. @@ -179,12 +198,19 @@ class LastGenrePlugin(plugins.BeetsPlugin): parents = [find_parents(tag, self.c14n_branches)[-1]] tags_all += parents - if len(tags_all) >= count: + # Stop if we have enough tags already, unless we need to find + # the most specific tag (instead of the most popular). + if (not self.config['prefer_specific'] and + len(tags_all) >= count): break tags = tags_all tags = deduplicate(tags) + # Sort the tags by specificity. + if self.config['prefer_specific']: + tags = self._sort_by_depth(tags) + # c14n only adds allowed genres but we may have had forbidden genres in # the original tags list tags = [x.title() for x in tags if self._is_allowed(x)] diff --git a/docs/plugins/lastgenre.rst b/docs/plugins/lastgenre.rst index 125b1073d..5e3235bd7 100644 --- a/docs/plugins/lastgenre.rst +++ b/docs/plugins/lastgenre.rst @@ -103,6 +103,19 @@ The plugin uses this weight to discard unpopular tags. The default is to ignore tags with a weight less then 10. You can change this by setting the ``min_weight`` config option. +Specific vs. Popular Genres +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +By default, the plugin sorts genres by popularity. However, you can use the +``prefer_specific`` option to override this behavior and instead sort genres +by specificity, as determined by your whitelist and canonicalization tree. + +For instance, say you have both ``folk`` and ``americana`` in your whitelist +and canonicalization tree and ``americana`` is a leaf within ``folk``. If +Last.fm returns both of those tags, lastgenre is going to use the most +popular, which is often the most generic (in this case ``folk``). By setting +``prefer_specific`` to true, lastgenre would use ``americana`` instead. + Configuration ------------- @@ -126,6 +139,8 @@ configuration file. The available options are: Default: ``yes``. - **min_weight**: Minimum popularity factor below which genres are discarded. Default: 10. +- **prefer_specific**: Sort genres by the most to least specific, rather than + most to least popular. Default: ``no``. - **source**: Which entity to look up in Last.fm. Can be either ``artist``, ``album`` or ``track``. Default: ``album``.