From eafceaa0ed870a154572f9b7d79d6dd3073181c2 Mon Sep 17 00:00:00 2001 From: Peter Schnebel Date: Mon, 14 Oct 2013 12:36:39 +0200 Subject: [PATCH 1/8] Added min_weight option to lastgenre, to provide for filtering out unpopulare genres. --- beetsplug/lastgenre/__init__.py | 10 ++++++++++ docs/plugins/lastgenre.rst | 9 +++++++++ 2 files changed, 19 insertions(+) diff --git a/beetsplug/lastgenre/__init__.py b/beetsplug/lastgenre/__init__.py index 33fad7628..17f89b041 100644 --- a/beetsplug/lastgenre/__init__.py +++ b/beetsplug/lastgenre/__init__.py @@ -61,11 +61,20 @@ def _tags_for(obj): return [] tags = [] + discarded_tags = [] + min_weight = config['lastgenre']['min_weight'].get(int) for el in res: if isinstance(el, pylast.TopItem): + if min_weight > -1: + if len(tags) > 0 and min_weight > int(el.weight): + discarded_tags.append(el.item.get_name()) + continue el = el.item tags.append(el.get_name()) log.debug(u'last.fm tags: %s' % unicode(tags)) + if min_weight > -1: + log.debug(u'last.fm tags (weight < {0}): {1}'.format( + min_weight, unicode(discarded_tags))) return tags def _is_allowed(genre): @@ -205,6 +214,7 @@ class LastGenrePlugin(plugins.BeetsPlugin): self.config.add({ 'whitelist': os.path.join(os.path.dirname(__file__), 'genres.txt'), 'multiple': False, + 'min_weight': -1, 'fallback': None, 'canonical': None, 'source': 'album', diff --git a/docs/plugins/lastgenre.rst b/docs/plugins/lastgenre.rst index 37497fa40..51e47aacd 100644 --- a/docs/plugins/lastgenre.rst +++ b/docs/plugins/lastgenre.rst @@ -94,6 +94,15 @@ you prefer to use a list of *all available* genre tags, turn on the Comma-separated lists of genres will then be used instead of single genres. +If you want to filter out less popular tags, you can set the ``min_weight`` +config option:: + + lastgenre: + min_weight: 50 + +Only tags with a weight greater then ``min_weight`` will be used. The weight +ranges from 0 (unpopular) to 100 (most popular). + Running Manually ---------------- From c5896684f35f7eecfb1815bd46bf5c549ff610c7 Mon Sep 17 00:00:00 2001 From: Peter Schnebel Date: Tue, 15 Oct 2013 13:20:45 +0200 Subject: [PATCH 2/8] The following only applies when ```multiple``` is activated: Return one valid genre even if its weight is lower then ```min_weight```. Default for ```min_weight``` is now *10*. Added new config option ```max_genres``` to limit the amount of genres returned. Default is *3*. --- beetsplug/lastgenre/__init__.py | 66 ++++++++++++++++----------------- docs/plugins/lastgenre.rst | 23 ++++++++---- 2 files changed, 48 insertions(+), 41 deletions(-) diff --git a/beetsplug/lastgenre/__init__.py b/beetsplug/lastgenre/__init__.py index 17f89b041..12b26adc3 100644 --- a/beetsplug/lastgenre/__init__.py +++ b/beetsplug/lastgenre/__init__.py @@ -61,20 +61,31 @@ def _tags_for(obj): return [] tags = [] - discarded_tags = [] - min_weight = config['lastgenre']['min_weight'].get(int) + multiple = config['lastgenre']['multiple'].get(bool) + if multiple: + min_weight = config['lastgenre']['min_weight'].get(int) + max_genres = config['lastgenre']['max_genres'].get(int) + else: + min_weight = -1 + max_genres = 1 + for el in res: + # pylast 0.5.x does not use Album.getTopTags, so we don't have a + # weight for album tags. However: Album.getInfo (they use that) + # returns only ~5 tags, so we use maximum weight for them if isinstance(el, pylast.TopItem): - if min_weight > -1: - if len(tags) > 0 and min_weight > int(el.weight): - discarded_tags.append(el.item.get_name()) - continue - el = el.item - tags.append(el.get_name()) - log.debug(u'last.fm tags: %s' % unicode(tags)) - if min_weight > -1: - log.debug(u'last.fm tags (weight < {0}): {1}'.format( - min_weight, unicode(discarded_tags))) + weight = int(el.weight) + tag = el.item.get_name().lower() + else: + weight = 100 + tag = el.get_name().lower() + if _is_allowed(tag): + if min_weight > -1 and min_weight > weight and len(tags) > 0: + return tags + log.debug(u'lastfm.tag (min. {}): {} [{}]'.format(min_weight, tag, weight)) + tags.append(tag) + if len(tags) == max_genres: + return tags return tags def _is_allowed(genre): @@ -83,24 +94,10 @@ def _is_allowed(genre): """ if genre is None: return False - if genre.lower() in options['whitelist']: + if not options['whitelist'] or genre in options['whitelist']: return True return False -def _find_allowed(genres): - """Given a list of candidate genres (strings), return an allowed - genre string. If `multiple` is on, then this may be a - comma-separated list; otherwise, it is one of the elements of - `genres`. - """ - allowed_genres = [g.title() for g in genres if _is_allowed(g)] - if not allowed_genres: - return - if config['lastgenre']['multiple']: - return u', '.join(allowed_genres) - else: - return allowed_genres[0] - def _strings_to_genre(tags): """Given a list of strings, return a genre. Returns the first string that is present in the genre whitelist (or the canonicalization @@ -108,16 +105,16 @@ def _strings_to_genre(tags): """ if not tags: return None - elif not options['whitelist']: - return tags[0].title() if options.get('c14n'): # Use the canonicalization tree. - for tag in tags: - return _find_allowed(find_parents(tag, options['branches'])) + tags = find_parents(tags[0], options['branches']) + + tags = [t.title() for t in tags] + if config['lastgenre']['multiple']: + return u', '.join(tags[:config['lastgenre']['max_genres'].get(int)]) else: - # Just use the flat whitelist. - return _find_allowed(tags) + return tags[0] def fetch_genre(lastfm_obj): """Return the genre for a pylast entity or None if no suitable genre @@ -214,7 +211,8 @@ class LastGenrePlugin(plugins.BeetsPlugin): self.config.add({ 'whitelist': os.path.join(os.path.dirname(__file__), 'genres.txt'), 'multiple': False, - 'min_weight': -1, + 'min_weight': 10, + 'max_genres': 3, 'fallback': None, 'canonical': None, 'source': 'album', diff --git a/docs/plugins/lastgenre.rst b/docs/plugins/lastgenre.rst index 51e47aacd..000c194b9 100644 --- a/docs/plugins/lastgenre.rst +++ b/docs/plugins/lastgenre.rst @@ -86,22 +86,31 @@ Multiple Genres --------------- By default, the plugin chooses the most popular tag on Last.fm as a genre. If -you prefer to use a list of *all available* genre tags, turn on the -``multiple`` config option:: +you prefer to use a *list* of popular genre tags, turn on the ``multiple`` +config option:: lastgenre: multiple: true Comma-separated lists of genres will then be used instead of single genres. -If you want to filter out less popular tags, you can set the ``min_weight`` -config option:: +`Last.fm`_ provides a popularity factor aka *weight* for each *tag* ranging +from 100 for the most popular *tag* down to 0 for the least popular *tags*. +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:: lastgenre: - min_weight: 50 + min_weight: 15 -Only tags with a weight greater then ``min_weight`` will be used. The weight -ranges from 0 (unpopular) to 100 (most popular). +However, if no tag with a *weight* greater then ``min_weight`` is found, the +plugin uses the next best popular *tag*. + +By default, the pulgin uses a maxmimum of 3 genres. You can change this by +setting the ``max_genres`` config option:: + + lastgenre: + max_genres: 5 Running Manually From d339e2f815b0737390905b9b85e501b3498f2f35 Mon Sep 17 00:00:00 2001 From: Peter Schnebel Date: Wed, 16 Oct 2013 08:45:13 +0200 Subject: [PATCH 3/8] Workaround for pylast issue https://code.google.com/p/pylast/issues/detail?id=85 as suggested by @sampsyo --- beetsplug/lastgenre/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/beetsplug/lastgenre/__init__.py b/beetsplug/lastgenre/__init__.py index 12b26adc3..790e34da0 100644 --- a/beetsplug/lastgenre/__init__.py +++ b/beetsplug/lastgenre/__init__.py @@ -55,7 +55,7 @@ def _tags_for(obj): not found or another error occurs. """ try: - res = obj.get_top_tags() + res = super(pylast.Album, obj).get_top_tags() except PYLAST_EXCEPTIONS as exc: log.debug(u'last.fm error: %s' % unicode(exc)) return [] From fa105157998439dc0849d61f48b3817d8aefd211 Mon Sep 17 00:00:00 2001 From: Peter Schnebel Date: Wed, 16 Oct 2013 11:31:18 +0200 Subject: [PATCH 4/8] Bugfix for Album.get_top_tracks workaround. --- beetsplug/lastgenre/__init__.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/beetsplug/lastgenre/__init__.py b/beetsplug/lastgenre/__init__.py index 790e34da0..ddb295acd 100644 --- a/beetsplug/lastgenre/__init__.py +++ b/beetsplug/lastgenre/__init__.py @@ -55,7 +55,10 @@ def _tags_for(obj): not found or another error occurs. """ try: - res = super(pylast.Album, obj).get_top_tags() + if isinstance(el, pylast.Album): + res = super(pylast.Album, obj).get_top_tags() + else: + res = obj.get_top_tags() except PYLAST_EXCEPTIONS as exc: log.debug(u'last.fm error: %s' % unicode(exc)) return [] From 0ff15c5c4db660cbecbaaa71b42f049db12616f4 Mon Sep 17 00:00:00 2001 From: Peter Schnebel Date: Wed, 16 Oct 2013 11:35:26 +0200 Subject: [PATCH 5/8] The way of the impatient is filled with useless commits :( --- beetsplug/lastgenre/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/beetsplug/lastgenre/__init__.py b/beetsplug/lastgenre/__init__.py index ddb295acd..2b4452ab3 100644 --- a/beetsplug/lastgenre/__init__.py +++ b/beetsplug/lastgenre/__init__.py @@ -55,7 +55,7 @@ def _tags_for(obj): not found or another error occurs. """ try: - if isinstance(el, pylast.Album): + if isinstance(obj, pylast.Album): res = super(pylast.Album, obj).get_top_tags() else: res = obj.get_top_tags() From b2a23a58e27727bf2f47ed44ff277131287d5a49 Mon Sep 17 00:00:00 2001 From: Peter Schnebel Date: Thu, 17 Oct 2013 09:30:26 +0200 Subject: [PATCH 6/8] fixed indentation --- beetsplug/lastgenre/__init__.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/beetsplug/lastgenre/__init__.py b/beetsplug/lastgenre/__init__.py index 2b4452ab3..233a4fe37 100644 --- a/beetsplug/lastgenre/__init__.py +++ b/beetsplug/lastgenre/__init__.py @@ -83,12 +83,12 @@ def _tags_for(obj): weight = 100 tag = el.get_name().lower() if _is_allowed(tag): - if min_weight > -1 and min_weight > weight and len(tags) > 0: - return tags - log.debug(u'lastfm.tag (min. {}): {} [{}]'.format(min_weight, tag, weight)) - tags.append(tag) - if len(tags) == max_genres: - return tags + if min_weight > -1 and min_weight > weight and len(tags) > 0: + return tags + log.debug(u'lastfm.tag (min. {}): {} [{}]'.format(min_weight, tag, weight)) + tags.append(tag) + if len(tags) == max_genres: + return tags return tags def _is_allowed(genre): @@ -115,9 +115,9 @@ def _strings_to_genre(tags): tags = [t.title() for t in tags] if config['lastgenre']['multiple']: - return u', '.join(tags[:config['lastgenre']['max_genres'].get(int)]) + return u', '.join(tags[:config['lastgenre']['max_genres'].get(int)]) else: - return tags[0] + return tags[0] def fetch_genre(lastfm_obj): """Return the genre for a pylast entity or None if no suitable genre From b1570beba9ebf3ee1192c580fe9650ba48213d9a Mon Sep 17 00:00:00 2001 From: Peter Schnebel Date: Thu, 17 Oct 2013 09:52:35 +0200 Subject: [PATCH 7/8] Merged the ```multiple``` and ```max_genres``` option into one ```count``` option. Updated debug output to waste less lines. --- beetsplug/lastgenre/__init__.py | 36 ++++++++++----------------------- docs/plugins/lastgenre.rst | 14 ++++--------- 2 files changed, 15 insertions(+), 35 deletions(-) diff --git a/beetsplug/lastgenre/__init__.py b/beetsplug/lastgenre/__init__.py index 233a4fe37..13b2be815 100644 --- a/beetsplug/lastgenre/__init__.py +++ b/beetsplug/lastgenre/__init__.py @@ -64,31 +64,21 @@ def _tags_for(obj): return [] tags = [] - multiple = config['lastgenre']['multiple'].get(bool) - if multiple: - min_weight = config['lastgenre']['min_weight'].get(int) - max_genres = config['lastgenre']['max_genres'].get(int) - else: - min_weight = -1 - max_genres = 1 + min_weight = config['lastgenre']['min_weight'].get(int) + count = config['lastgenre']['count'].get(int) + dbg = [] for el in res: - # pylast 0.5.x does not use Album.getTopTags, so we don't have a - # weight for album tags. However: Album.getInfo (they use that) - # returns only ~5 tags, so we use maximum weight for them - if isinstance(el, pylast.TopItem): - weight = int(el.weight) - tag = el.item.get_name().lower() - else: - weight = 100 - tag = el.get_name().lower() + weight = int(el.weight) + tag = el.item.get_name().lower() if _is_allowed(tag): if min_weight > -1 and min_weight > weight and len(tags) > 0: return tags - log.debug(u'lastfm.tag (min. {}): {} [{}]'.format(min_weight, tag, weight)) tags.append(tag) - if len(tags) == max_genres: - return tags + dbg.append(u'{} [{}]'.format(tag, weight)) + if len(tags) == count: + break + log.debug(u'lastfm.tag (min. {}): {}'.format(min_weight, u', '.join(dbg))) return tags def _is_allowed(genre): @@ -114,10 +104,7 @@ def _strings_to_genre(tags): tags = find_parents(tags[0], options['branches']) tags = [t.title() for t in tags] - if config['lastgenre']['multiple']: - return u', '.join(tags[:config['lastgenre']['max_genres'].get(int)]) - else: - return tags[0] + return u', '.join(tags[:config['lastgenre']['count'].get(int)]) def fetch_genre(lastfm_obj): """Return the genre for a pylast entity or None if no suitable genre @@ -213,9 +200,8 @@ class LastGenrePlugin(plugins.BeetsPlugin): self.config.add({ 'whitelist': os.path.join(os.path.dirname(__file__), 'genres.txt'), - 'multiple': False, 'min_weight': 10, - 'max_genres': 3, + 'count': 1, 'fallback': None, 'canonical': None, 'source': 'album', diff --git a/docs/plugins/lastgenre.rst b/docs/plugins/lastgenre.rst index 000c194b9..371cd1795 100644 --- a/docs/plugins/lastgenre.rst +++ b/docs/plugins/lastgenre.rst @@ -86,13 +86,13 @@ Multiple Genres --------------- By default, the plugin chooses the most popular tag on Last.fm as a genre. If -you prefer to use a *list* of popular genre tags, turn on the ``multiple`` -config option:: +you prefer to use a *list* of popular genre tags, you can increase the number +of the ``count`` config option:: lastgenre: - multiple: true + count: 3 -Comma-separated lists of genres will then be used instead of single genres. +Comma-separated lists of up to 3 genres will then be used instead of single genres. `Last.fm`_ provides a popularity factor aka *weight* for each *tag* ranging from 100 for the most popular *tag* down to 0 for the least popular *tags*. @@ -106,12 +106,6 @@ the ``min_weight`` config option:: However, if no tag with a *weight* greater then ``min_weight`` is found, the plugin uses the next best popular *tag*. -By default, the pulgin uses a maxmimum of 3 genres. You can change this by -setting the ``max_genres`` config option:: - - lastgenre: - max_genres: 5 - Running Manually ---------------- From 6cb5e8064cd647e8b489cff18c3fa5bdc48b7ba6 Mon Sep 17 00:00:00 2001 From: Peter Schnebel Date: Thu, 17 Oct 2013 09:57:56 +0200 Subject: [PATCH 8/8] make the doc 'clearer' --- docs/plugins/lastgenre.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/plugins/lastgenre.rst b/docs/plugins/lastgenre.rst index 371cd1795..1449bc385 100644 --- a/docs/plugins/lastgenre.rst +++ b/docs/plugins/lastgenre.rst @@ -92,7 +92,8 @@ of the ``count`` config option:: lastgenre: count: 3 -Comma-separated lists of up to 3 genres will then be used instead of single genres. +Comma-separated lists of up to *count* genres will then be used instead of +single genres. `Last.fm`_ provides a popularity factor aka *weight* for each *tag* ranging from 100 for the most popular *tag* down to 0 for the least popular *tags*.