From 288ff6ba198e89ee74ab25e6db6a31b33c9a0134 Mon Sep 17 00:00:00 2001 From: Corinne Hoener Date: Mon, 26 Sep 2016 16:12:38 -0400 Subject: [PATCH 1/6] Add specificity option to lastgenre in order to prefer the most specific genres --- beetsplug/lastgenre/__init__.py | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/beetsplug/lastgenre/__init__.py b/beetsplug/lastgenre/__init__.py index cbaa972ca..373fea56c 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', ', + 'specificity': False, }) self.setup() @@ -158,6 +159,27 @@ 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. + """ + tags_by_depth = [] + for key, value in enumerate(tags): + depth = self._get_depth(value) + tags_by_depth.append({'tag': value, 'depth': depth}) + tags_by_depth = sorted(tags_by_depth, key=lambda k: k['depth'], reverse=True) + return [i['tag'] for i in tags_by_depth] + 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 +201,18 @@ class LastGenrePlugin(plugins.BeetsPlugin): parents = [find_parents(tag, self.c14n_branches)[-1]] tags_all += parents - if len(tags_all) >= count: + # Don't break here if we need to sort by specificity, which happens with + # the full tag list below + if not self.config['specificity'] and len(tags_all) >= count: break tags = tags_all tags = deduplicate(tags) + # sort the tags by specificity + if self.config['specificity']: + 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)] From be5ce3194f4f3ad74e0b9e38d47f78a895de58b6 Mon Sep 17 00:00:00 2001 From: Corinne Hoener Date: Mon, 26 Sep 2016 19:02:10 -0400 Subject: [PATCH 2/6] better syntax; better comments --- beetsplug/lastgenre/__init__.py | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/beetsplug/lastgenre/__init__.py b/beetsplug/lastgenre/__init__.py index 373fea56c..0faa587c2 100644 --- a/beetsplug/lastgenre/__init__.py +++ b/beetsplug/lastgenre/__init__.py @@ -173,12 +173,9 @@ class LastGenrePlugin(plugins.BeetsPlugin): """Given a list of tags, sort the tags by their depths in the genre tree. """ - tags_by_depth = [] - for key, value in enumerate(tags): - depth = self._get_depth(value) - tags_by_depth.append({'tag': value, 'depth': depth}) - tags_by_depth = sorted(tags_by_depth, key=lambda k: k['depth'], reverse=True) - return [i['tag'] for i in tags_by_depth] + 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 @@ -201,15 +198,15 @@ class LastGenrePlugin(plugins.BeetsPlugin): parents = [find_parents(tag, self.c14n_branches)[-1]] tags_all += parents - # Don't break here if we need to sort by specificity, which happens with - # the full tag list below + # 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['specificity'] and len(tags_all) >= count: break tags = tags_all tags = deduplicate(tags) - # sort the tags by specificity + # Sort the tags by specificity. if self.config['specificity']: tags = self._sort_by_depth(tags) From c10fe1e126b628945da0d0e2aafd1536e1ebb62a Mon Sep 17 00:00:00 2001 From: Corinne Hoener Date: Mon, 26 Sep 2016 19:06:34 -0400 Subject: [PATCH 3/6] change option name to prefer_specific --- beetsplug/lastgenre/__init__.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/beetsplug/lastgenre/__init__.py b/beetsplug/lastgenre/__init__.py index 0faa587c2..7c1cddf04 100644 --- a/beetsplug/lastgenre/__init__.py +++ b/beetsplug/lastgenre/__init__.py @@ -109,7 +109,7 @@ class LastGenrePlugin(plugins.BeetsPlugin): 'force': True, 'auto': True, 'separator': u', ', - 'specificity': False, + 'prefer_specific': False, }) self.setup() @@ -200,14 +200,15 @@ class LastGenrePlugin(plugins.BeetsPlugin): tags_all += parents # 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['specificity'] and len(tags_all) >= count: + 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['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 From a9ac2fa9c8e5b212160b07855fc2f012a0cea0d0 Mon Sep 17 00:00:00 2001 From: Corinne Hoener Date: Mon, 26 Sep 2016 19:21:58 -0400 Subject: [PATCH 4/6] add some docs --- docs/plugins/lastgenre.rst | 15 +++++++++++++++ 1 file changed, 15 insertions(+) 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``. From 1c9202cc8687c9bc23c580f9776c6aa9b85b19a4 Mon Sep 17 00:00:00 2001 From: Corinne Hoener Date: Mon, 26 Sep 2016 21:21:15 -0400 Subject: [PATCH 5/6] moar indents! --- beetsplug/lastgenre/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/beetsplug/lastgenre/__init__.py b/beetsplug/lastgenre/__init__.py index 7c1cddf04..728e6069c 100644 --- a/beetsplug/lastgenre/__init__.py +++ b/beetsplug/lastgenre/__init__.py @@ -200,8 +200,8 @@ class LastGenrePlugin(plugins.BeetsPlugin): tags_all += parents # 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): + if (not self.config['prefer_specific'] + and len(tags_all) >= count): break tags = tags_all From a7e3bf2d0b8cb4c2874a14471e2b8daaeb317970 Mon Sep 17 00:00:00 2001 From: Corinne Hoener Date: Tue, 27 Sep 2016 06:21:51 -0400 Subject: [PATCH 6/6] third time's a charm --- beetsplug/lastgenre/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/beetsplug/lastgenre/__init__.py b/beetsplug/lastgenre/__init__.py index 728e6069c..13f7a300d 100644 --- a/beetsplug/lastgenre/__init__.py +++ b/beetsplug/lastgenre/__init__.py @@ -200,8 +200,8 @@ class LastGenrePlugin(plugins.BeetsPlugin): tags_all += parents # 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): + if (not self.config['prefer_specific'] and + len(tags_all) >= count): break tags = tags_all