Repository: beetbox/beets

Issue:
acousticbrainz and absubmit plugins should be deprecated
Acousticbrainz has [shut down](https://blog.metabrainz.org/2022/02/16/acousticbrainz-making-a-hard-decision-to-end-the-project/) and will go completely offline in early 2023 according to that blog post. The `acousticbrainz` and `absubmit` plugins should be deprecated.

Attempting to use it now just results in a lot of "acousticbrainz did not provide info about..." messages.


Discussion:
Thanks! Printing out a more useful error message would be good.
I'm working on a change to deprecate both plugins. I need to test still, but should hopefully have a PR up soon.
Wow; wonderful news!!
@sampsyo - How do I get this PR reviewed?

Resolving patch:
diff --git a/beets/autotag/hooks.py b/beets/autotag/hooks.py
--- a/beets/autotag/hooks.py
+++ b/beets/autotag/hooks.py
@@ -18,7 +18,7 @@
 from collections import namedtuple
 from functools import total_ordering
 import re
-from typing import Dict, List, Tuple, Iterator, Union, NewType, Any, Optional,\
+from typing import Dict, List, Tuple, Iterator, Union, Any, Optional,\
     Iterable, Callable, TypeVar
 
 from beets import logging
diff --git a/beetsplug/absubmit.py b/beetsplug/absubmit.py
--- a/beetsplug/absubmit.py
+++ b/beetsplug/absubmit.py
@@ -56,10 +56,13 @@ class AcousticBrainzSubmitPlugin(plugins.BeetsPlugin):
     def __init__(self):
         super().__init__()
 
+        self._log.warning("This plugin is deprecated.")
+
         self.config.add({
             'extractor': '',
             'force': False,
-            'pretend': False
+            'pretend': False,
+            'base_url': ''
         })
 
         self.extractor = self.config['extractor'].as_str()
@@ -79,7 +82,7 @@ def __init__(self):
             except OSError:
                 raise ui.UserError(
                     'No extractor command found: please install the extractor'
-                    ' binary from https://acousticbrainz.org/download'
+                    ' binary from https://essentia.upf.edu/'
                 )
             except ABSubmitError:
                 # Extractor found, will exit with an error if not called with
@@ -96,7 +99,15 @@ def __init__(self):
             self.extractor_sha.update(extractor.read())
         self.extractor_sha = self.extractor_sha.hexdigest()
 
-    base_url = 'https://acousticbrainz.org/api/v1/{mbid}/low-level'
+        self.url = ''
+        base_url = self.config['base_url'].as_str()
+        if base_url:
+            if not base_url.startswith('http'):
+                raise ui.UserError('AcousticBrainz server base URL must start '
+                                   'with an HTTP scheme')
+            elif base_url[-1] != '/':
+                base_url = base_url + '/'
+            self.url = base_url + '{mbid}/low-level'
 
     def commands(self):
         cmd = ui.Subcommand(
@@ -118,10 +129,17 @@ def commands(self):
         return [cmd]
 
     def command(self, lib, opts, args):
-        # Get items from arguments
-        items = lib.items(ui.decargs(args))
-        self.opts = opts
-        util.par_map(self.analyze_submit, items)
+        if not self.url:
+            raise ui.UserError(
+                'This plugin is deprecated since AcousticBrainz no longer '
+                'accepts new submissions. See the base_url configuration '
+                'option.'
+            )
+        else:
+            # Get items from arguments
+            items = lib.items(ui.decargs(args))
+            self.opts = opts
+            util.par_map(self.analyze_submit, items)
 
     def analyze_submit(self, item):
         analysis = self._get_analysis(item)
@@ -179,7 +197,7 @@ def _get_analysis(self, item):
     def _submit_data(self, item, data):
         mbid = item['mb_trackid']
         headers = {'Content-Type': 'application/json'}
-        response = requests.post(self.base_url.format(mbid=mbid),
+        response = requests.post(self.url.format(mbid=mbid),
                                  json=data, headers=headers)
         # Test that request was successful and raise an error on failure.
         if response.status_code != 200:
diff --git a/beetsplug/acousticbrainz.py b/beetsplug/acousticbrainz.py
--- a/beetsplug/acousticbrainz.py
+++ b/beetsplug/acousticbrainz.py
@@ -22,7 +22,6 @@
 from beets import plugins, ui
 from beets.dbcore import types
 
-ACOUSTIC_BASE = "https://acousticbrainz.org/"
 LEVELS = ["/low-level", "/high-level"]
 ABSCHEME = {
     'highlevel': {
@@ -138,12 +137,23 @@ class AcousticPlugin(plugins.BeetsPlugin):
     def __init__(self):
         super().__init__()
 
+        self._log.warning("This plugin is deprecated.")
+
         self.config.add({
             'auto': True,
             'force': False,
-            'tags': []
+            'tags': [],
+            'base_url': ''
         })
 
+        self.base_url = self.config['base_url'].as_str()
+        if self.base_url:
+            if not self.base_url.startswith('http'):
+                raise ui.UserError('AcousticBrainz server base URL must start '
+                                   'with an HTTP scheme')
+            elif self.base_url[-1] != '/':
+                self.base_url = self.base_url + '/'
+
         if self.config['auto']:
             self.register_listener('import_task_files',
                                    self.import_task_files)
@@ -171,8 +181,13 @@ def import_task_files(self, session, task):
         self._fetch_info(task.imported_items(), False, True)
 
     def _get_data(self, mbid):
+        if not self.base_url:
+            raise ui.UserError(
+                'This plugin is deprecated since AcousticBrainz has shut '
+                'down. See the base_url configuration option.'
+            )
         data = {}
-        for url in _generate_urls(mbid):
+        for url in _generate_urls(self.base_url, mbid):
             self._log.debug('fetching URL: {}', url)
 
             try:
@@ -328,8 +343,8 @@ def _data_to_scheme_child(self, subdata, subscheme, composites):
                                 'because key {} was not found', subdata, v, k)
 
 
-def _generate_urls(mbid):
+def _generate_urls(base_url, mbid):
     """Generates AcousticBrainz end point urls for given `mbid`.
     """
     for level in LEVELS:
-        yield ACOUSTIC_BASE + mbid + level
+        yield base_url + mbid + level


Test patch: