LastGenre Plugin
================


The ``lastgenre`` plugin fetches *tags* from `Last.fm`_ and assigns them as genres
to your albums and items.

.. _Last.fm: https://last.fm/

Installation
------------

To use the ``lastgenre`` plugin, first enable it in your configuration (see
:ref:`using-plugins`). Then, install ``beets`` with ``lastgenre`` extra

.. code-block:: bash

    pip install "beets[lastgenre]"

Usage
-----

The plugin chooses genres based on a *whitelist*, meaning that only certain
tags can be considered genres. This way, tags like "my favorite music" or "seen
live" won't be considered genres. The plugin ships with a fairly extensive
`internal whitelist`_, but you can set your own in the config file using the
``whitelist`` configuration value or forgo a whitelist altogether by setting
the option to ``no``.

The genre list file should contain one genre per line. Blank lines are ignored.
For the curious, the default genre list is generated by a `script that scrapes
Wikipedia`_.

.. _script that scrapes Wikipedia: https://gist.github.com/1241307
.. _internal whitelist: https://raw.githubusercontent.com/beetbox/beets/master/beetsplug/lastgenre/genres.txt

Canonicalization
^^^^^^^^^^^^^^^^

The plugin can also *canonicalize* genres, meaning that more obscure genres can
be turned into coarser-grained ones that are present in the whitelist. This
works using a `tree of nested genre names`_, represented using `YAML`_, where the
leaves of the tree represent the most specific genres.

The most common way to use this would be with a custom whitelist containing only
a desired subset of genres. Consider for a example this minimal whitelist::

    rock
    heavy metal
    pop

together with the default genre tree. Then an item that has its genre specified
as *viking metal* would actually be tagged as *heavy metal* because neither
*viking metal* nor its parent *black metal* are in the whitelist. It always
tries to use the most specific genre that's available in the whitelist.

The relevant subtree path in the default tree looks like this::

    - rock:
        - heavy metal:
            - black metal:
                - viking metal

Considering that, it's not very useful to use the default whitelist (which
contains about any genre contained in the tree) with canonicalization because
nothing would ever be matched to a more generic node since all the specific
subgenres are in the whitelist to begin with.


.. _YAML: https://yaml.org/
.. _tree of nested genre names: https://raw.githubusercontent.com/beetbox/beets/master/beetsplug/lastgenre/genres-tree.yaml


Genre Source
^^^^^^^^^^^^

When looking up genres for albums or individual tracks, you can choose whether
to use Last.fm tags on the album, the artist, or the track. For example, you
might want all the albums for a certain artist to carry the same genre.
The default is "album". When set to "track", the plugin will fetch *both*
album-level and track-level genres for your music when importing albums.


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, you can increase the number
of the ``count`` config option.

Lists of up to *count* genres will then be used instead of single genres. The
genres are separated by commas by default, but you can change this with the
``separator`` config option.

`Last.fm`_ provides a popularity factor, a.k.a. *weight*, for each tag ranging
from 100 for the most popular tag down to 0 for the least popular.
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.

Handling pre-populated tags
^^^^^^^^^^^^^^^^^^^^^^^^^^^

The ``force``, ``keep_existing`` and ``whitelist`` options control how
pre-existing genres are handled.

As you would assume, setting ``force: no`` **won't touch pre-existing genre
tags** and will only **fetch new genres for empty tags**. When ``force`` is
``yes`` the setting of the ``whitelist`` option (as documented in `Usage`_)
applies to any existing or newly fetched genres.

The follwing configurations are possible:

**Setup 1** (default)

Add new last.fm genres when **empty**. Any present tags stay **untouched**.

.. code-block:: yaml

    force: no
    keep_existing: no

**Setup 2**

**Overwrite all**. Only fresh last.fm genres remain.

.. code-block:: yaml

    force: yes
    keep_existing: no

**Setup 3**

**Combine** genres in present tags with new ones (be aware of that with an
enabled ``whitelist`` setting, of course some genres might get cleaned up. To
make sure any existing genres remain, set ``whitelist: no``).

.. code-block:: yaml

    force: yes
    keep_existing: yes

.. attention::
    If ``force`` is disabled the ``keep_existing`` option is simply ignored (since ``force:
    no`` means `not touching` existing tags anyway).



Configuration
-------------

To configure the plugin, make a ``lastgenre:`` section in your
configuration file. The available options are:

- **auto**: Fetch genres automatically during import.
  Default: ``yes``.
- **canonical**: Use a canonicalization tree. Setting this to ``yes`` will use
  a built-in tree. You can also set it to a path, like the ``whitelist``
  config value, to use your own tree.
  Default: ``no`` (disabled).
- **count**: Number of genres to fetch.
  Default: 1
- **fallback**: A string to use as a fallback genre when no genre is found `or`
  the original genre is not desired to be kept (``keep_existing: no``). You can
  use the empty string ``''`` to reset the genre.
  Default: None.
- **force**: By default, lastgenre will fetch new genres for empty tags only,
  enable this option to always try to fetch new last.fm genres. Enable the
  ``keep_existing`` option to combine existing and new genres. (see `Handling
  pre-populated tags`_).
  Default: ``no``.
- **keep_existing**: This option alters the ``force`` behavior.
  If both ``force`` and ``keep_existing`` are enabled, existing genres are
  combined with new ones. Depending on the ``whitelist`` setting, existing and
  new genres are filtered accordingly. To ensure only fresh last.fm genres,
  disable this option. (see `Handling pre-populated tags`_)
  Default: ``no``.
- **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. Note that this option requires a ``canonical`` tree,
  and if not configured it will automatically enable and use the built-in tree.
  Default: ``no``.
- **source**: Which entity to look up in Last.fm. Can be
  either ``artist``, ``album`` or ``track``.
  Default: ``album``.
- **separator**: A separator for multiple genres.
  Default: ``', '``.
- **whitelist**: The filename of a custom genre list, ``yes`` to use
  the internal whitelist, or ``no`` to consider all genres valid.
  Default: ``yes``.
- **title_case**: Convert the new tags to TitleCase before saving.
  Default: ``yes``.
- **extended_debug**: Add additional debug logging messages that show what
  last.fm tags were fetched for tracks, albums and artists. This is done before
  any canonicalization and whitelist filtering is applied. It's useful for
  tuning the plugin's settings and understanding how it works, but it can be
  quite verbose.
  Default: ``no``.

Running Manually
----------------

In addition to running automatically on import, the plugin can also be run manually
from the command line. Use the command ``beet lastgenre [QUERY]`` to fetch
genres for albums or items matching a certain query.

By default, ``beet lastgenre`` matches albums. To match
individual tracks or singletons, use the ``-A`` switch:
``beet lastgenre -A [QUERY]``.

To disable automatic genre fetching on import, set the ``auto`` config option
to false.
