diff --git a/README.rst b/README.rst index b894daddc..62fbfc298 100644 --- a/README.rst +++ b/README.rst @@ -80,7 +80,7 @@ shockingly simple if you know a little Python. Install ------- -You can install beets by typing ``pip install beets``. +You can install beets by typing ``pip install beets`` or directly from Github (see details [here](https://beets.readthedocs.io/en/latest/faq.html#run-the-latest-source-version-of-beets)). Beets has also been packaged in the `software repositories`_ of several distributions. Check out the `Getting Started`_ guide for more information. diff --git a/beets/autotag/mb.py b/beets/autotag/mb.py index f380cd033..cee2bdfd9 100644 --- a/beets/autotag/mb.py +++ b/beets/autotag/mb.py @@ -29,6 +29,9 @@ from beets import util from beets import config from collections import Counter from urllib.parse import urljoin +from beets.util.id_extractors import extract_discogs_id_regex, \ + spotify_id_regex, deezer_id_regex, beatport_id_regex +from beets.plugins import MetadataSourcePlugin VARIOUS_ARTISTS_ID = '89ad4ac3-39f7-470e-963a-56509c546377' @@ -70,7 +73,7 @@ log = logging.getLogger('beets') RELEASE_INCLUDES = ['artists', 'media', 'recordings', 'release-groups', 'labels', 'artist-credits', 'aliases', 'recording-level-rels', 'work-rels', - 'work-level-rels', 'artist-rels', 'isrcs'] + 'work-level-rels', 'artist-rels', 'isrcs', 'url-rels'] BROWSE_INCLUDES = ['artist-credits', 'work-rels', 'artist-rels', 'recording-rels', 'release-rels'] if "work-level-rels" in musicbrainzngs.VALID_BROWSE_INCLUDES['recording']: @@ -511,6 +514,56 @@ def album_info(release: Dict) -> beets.autotag.hooks.AlbumInfo: in sorted(genres.items(), key=lambda g: -g[1]) ) + # We might find links to external sources (Discogs, Bandcamp, ...) + if (any(config['musicbrainz']['external_ids'].get().values()) + and release.get('url-relation-list')): + discogs_url, bandcamp_url, spotify_url = None, None, None + deezer_url, beatport_url = None, None + fetch_discogs, fetch_bandcamp, fetch_spotify = False, False, False + fetch_deezer, fetch_beatport = False, False + + if config['musicbrainz']['external_ids']['discogs'].get(): + fetch_discogs = True + if config['musicbrainz']['external_ids']['bandcamp'].get(): + fetch_bandcamp = True + if config['musicbrainz']['external_ids']['spotify'].get(): + fetch_spotify = True + if config['musicbrainz']['external_ids']['deezer'].get(): + fetch_deezer = True + if config['musicbrainz']['external_ids']['beatport'].get(): + fetch_beatport = True + + for url in release['url-relation-list']: + if fetch_discogs and url['type'] == 'discogs': + log.debug('Found link to Discogs release via MusicBrainz') + discogs_url = url['target'] + if fetch_bandcamp and 'bandcamp.com' in url['target']: + log.debug('Found link to Bandcamp release via MusicBrainz') + bandcamp_url = url['target'] + if fetch_spotify and 'spotify.com' in url['target']: + log.debug('Found link to Spotify album via MusicBrainz') + spotify_url = url['target'] + if fetch_deezer and 'deezer.com' in url['target']: + log.debug('Found link to Deezer album via MusicBrainz') + deezer_url = url['target'] + if fetch_beatport and 'beatport.com' in url['target']: + log.debug('Found link to Beatport release via MusicBrainz') + beatport_url = url['target'] + + if discogs_url: + info.discogs_albumid = extract_discogs_id_regex(discogs_url) + if bandcamp_url: + info.bandcamp_album_id = bandcamp_url + if spotify_url: + info.spotify_album_id = MetadataSourcePlugin._get_id( + 'album', spotify_url, spotify_id_regex) + if deezer_url: + info.deezer_album_id = MetadataSourcePlugin._get_id( + 'album', deezer_url, deezer_id_regex) + if beatport_url: + info.beatport_album_id = MetadataSourcePlugin._get_id( + 'album', beatport_url, beatport_id_regex) + extra_albumdatas = plugins.send('mb_album_extract', data=release) for extra_albumdata in extra_albumdatas: info.update(extra_albumdata) diff --git a/beets/config_default.yaml b/beets/config_default.yaml index 2798b3872..6dcadccb2 100644 --- a/beets/config_default.yaml +++ b/beets/config_default.yaml @@ -128,6 +128,12 @@ musicbrainz: searchlimit: 5 extra_tags: [] genres: no + external_ids: + discogs: no + bandcamp: no + spotify: no + deezer: no + beatport: no match: strong_rec_thresh: 0.04 diff --git a/beets/importer.py b/beets/importer.py index c0319fc96..feebadc09 100644 --- a/beets/importer.py +++ b/beets/importer.py @@ -839,6 +839,19 @@ class ImportTask(BaseImportTask): dup_item.id, displayable_path(item.path) ) + # We exclude certain flexible attributes from the preserving + # process since they might have been fetched from MusicBrainz + # and been set in beets.autotag.apply_metadata(). + # discogs_albumid might also have been set but is not a + # flexible attribute, thus no exclude is required. + if item.get('bandcamp_album_id'): + dup_item.bandcamp_album_id = item.bandcamp_album_id + if item.get('spotify_album_id'): + dup_item.spotify_album_id = item.spotify_album_id + if item.get('deezer_album_id'): + dup_item.deezer_album_id = item.deezer_album_id + if item.get('beatport_album_id'): + dup_item.beatport_album_id = item.beatport_album_id item.update(dup_item._values_flex) log.debug( 'Reimported item flexible attributes {0} ' diff --git a/beets/util/id_extractors.py b/beets/util/id_extractors.py index b1020e78c..93fc2056c 100644 --- a/beets/util/id_extractors.py +++ b/beets/util/id_extractors.py @@ -35,7 +35,7 @@ beatport_id_regex = { # A note on Bandcamp: There is no such thing as a Bandcamp album or artist ID, # the URL can be used as the identifier. The Bandcamp metadata source plugin -# works that way - https://github.com/unrblt/beets-bandcamp. Bandcamp album +# works that way - https://github.com/snejus/beetcamp. Bandcamp album # URLs usually look like: https://nameofartist.bandcamp.com/album/nameofalbum diff --git a/beetsplug/beatport.py b/beetsplug/beatport.py index eabf5dc31..bede8071d 100644 --- a/beetsplug/beatport.py +++ b/beetsplug/beatport.py @@ -432,6 +432,7 @@ class BeatportPlugin(BeetsPlugin): tracks = [self._get_track_info(x) for x in release.tracks] return AlbumInfo(album=release.name, album_id=release.beatport_id, + beatport_album_id=release.beatport_id, artist=artist, artist_id=artist_id, tracks=tracks, albumtype=release.category, va=va, year=release.release_date.year, diff --git a/beetsplug/deezer.py b/beetsplug/deezer.py index 86c182c39..77f7edc85 100644 --- a/beetsplug/deezer.py +++ b/beetsplug/deezer.py @@ -98,6 +98,7 @@ class DeezerPlugin(MetadataSourcePlugin, BeetsPlugin): return AlbumInfo( album=album_data['title'], album_id=deezer_id, + deezer_album_id=deezer_id, artist=artist, artist_credit=self.get_artist([album_data['artist']])[0], artist_id=artist_id, diff --git a/docs/changelog.rst b/docs/changelog.rst index dc911f540..d2f9e4c6e 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -65,6 +65,11 @@ New features: * :doc:`/plugins/fromfilename`: Add debug log messages that inform when the plugin replaced bad (missing) artist, title or tracknumber metadata. :bug:`4561` :bug:`4600` +* :ref:`musicbrainz-config`: MusicBrainz release pages often link to related + metadata sources like Discogs, Bandcamp, Spotify, Deezer and Beatport. When + enabled via the :ref:`musicbrainz.external_ids` options, release ID's will be + extracted from those URL's and imported to the library. + :bug:`4220` Bug fixes: diff --git a/docs/faq.rst b/docs/faq.rst index 5e1c73666..e7f5cc600 100644 --- a/docs/faq.rst +++ b/docs/faq.rst @@ -137,6 +137,8 @@ it's helpful to run on the "bleeding edge". To run the latest source: ``pip uninstall beets``. 2. Install from source. Choose one of these methods: + - Directly from GitHub using + ``python -m pip install git+https://github.com/beetbox/beets.git`` command. Depending on your system, you may need to use ``pip3`` and ``python3`` instead of ``pip`` and ``python`` respectively. - Use ``pip`` to install the latest snapshot tarball. Type: ``pip install https://github.com/beetbox/beets/tarball/master`` - Grab the source using git. First, clone the repository: diff --git a/docs/plugins/convert.rst b/docs/plugins/convert.rst index 7adb94079..622a8f2cd 100644 --- a/docs/plugins/convert.rst +++ b/docs/plugins/convert.rst @@ -94,9 +94,9 @@ file. The available options are: output. Note that this does not guarantee that all converted files will have a lower bitrate---that depends on the encoder and its configuration. Default: none. -- **no_convert**: Does not transcode items matching provided query string - (see :doc:`/reference/query`). (i.e. ``format:AAC, format:WMA`` or - ``path::\.(m4a|wma)$``) +- **no_convert**: Does not transcode items matching the query string provided + (see :doc:`/reference/query`). For example, to not convert AAC or WMA formats, you can use ``format:AAC, format:WMA`` or + ``path::\.(m4a|wma)$``. If you only want to transcode WMA format, you can use a negative query, e.g., ``^path::\.(wma)$``, to not convert any other format except WMA. - **never_convert_lossy_files**: Cross-conversions between lossy codecs---such as mp3, ogg vorbis, etc.---makes little sense as they will decrease quality even further. If set to ``yes``, lossy files are always copied. diff --git a/docs/plugins/index.rst b/docs/plugins/index.rst index ea13d2feb..c36c8ad10 100644 --- a/docs/plugins/index.rst +++ b/docs/plugins/index.rst @@ -129,6 +129,8 @@ following to your configuration:: web zero +.. _autotagger_extensions: + Autotagger Extensions --------------------- @@ -458,6 +460,9 @@ Here are a few of the plugins written by the beets community: Lets you perform regex replacements on incoming metadata. +`beets-jiosaavn`_ + Adds JioSaavn.com as a tagger data source.. + `beets-mosaic`_ Generates a montage of a mosaic from cover art. @@ -522,6 +527,7 @@ Here are a few of the plugins written by the beets community: .. _beets-usertag: https://github.com/igordertigor/beets-usertag .. _beets-popularity: https://github.com/abba23/beets-popularity .. _beets-plexsync: https://github.com/arsaboo/beets-plexsync +.. _beets-jiosaavn: https://github.com/arsaboo/beets-jiosaavn .. _beets-ydl: https://github.com/vmassuchetto/beets-ydl .. _beet-summarize: https://github.com/steven-murray/beet-summarize .. _beets-mosaic: https://github.com/SusannaMaria/beets-mosaic diff --git a/docs/reference/config.rst b/docs/reference/config.rst index b6fa8fea6..f162c6762 100644 --- a/docs/reference/config.rst +++ b/docs/reference/config.rst @@ -842,6 +842,32 @@ release and the release-group on MusicBrainz, separated by "; " and sorted by the total number of votes. Default: ``no`` +.. _musicbrainz.external_ids: + +external_ids +~~~~~~~~~~~~ + +Set any of the ``external_ids`` options to ``yes`` to enable the MusicBrainz +importer to look for links to related metadata sources. If such a link is +available the release ID will be extracted from the URL provided and imported +to the beets library. + + musicbrainz: + external_ids: + discogs: yes + spotify: yes + bandcamp: yes + beatport: yes + deezer: yes + + +The library fields of the corresponding :ref:`autotagger_extensions` are used +to save the data (``discogs_albumid``, ``bandcamp_album_id``, +``spotify_album_id``, ``beatport_album_id``, ``deezer_album_id``). On +re-imports existing data will be overwritten. + +The default of all options is ``no``. + .. _match-config: Autotagger Matching Options