Приношу извинения за объем, но я пытаюсь использовать Spotipy, чтобы отписаться от 1400 артистов по Artist ID. Я начинаю с подмножества 5 ID, используя большой кусок кода client.py из документации spotipy:
https://raw.githubusercontent.com/plamere/spotipy/master/spotipy/client.py
Он возвращает ошибку:
spotipy.exceptions.SpotifyException: http status: 401, code:-1... no token provided.
У меня сложилось впечатление, что если вы создадите приложение в Spotify для разработчиков, введете идентификатор клиента, секретный идентификатор и URI перенаправления, запустите его, и оно перенесет вас на страницу аутентификации Spotify в браузере и вы нажимаете ОК, что у вас все в порядке: он запоминает ваш токен.
На всякий случай я пытался перейти на https://developer.spotify.com/console/delete-following/, но когда я нажимаю, чтобы запросить токен «вручную», он просто загружается https://developer.spotify.com/callback/ и не дает мне никакой информации.
Кто-нибудь может сказать мне, что мне не хватает?
scope = 'user-follow-modify'
username = "half-truth"
sp = spotipy.Spotify(auth_manager=SpotifyOAuth(username=username, scope=scope))
try:
token = util.prompt_for_user_token(username, scope)
except (AttributeError, JSONDecodeError):
os.remove(f".cache-{username}")
token = util.prompt_for_user_token(username, scope)
class Spotify(object):
max_retries = 3
default_retry_codes = (429, 500, 502, 503, 504)
country_codes = ["AD", "AR", "AU", "AT", "BE", "BO", "BR", "BG", "CA", "CL", "CO", "CR", "CY", "CZ", "DK", "DO", "EC", "SV", "EE", "FI", "FR", "DE", "GR", "GT", "HN", "HK", "HU", "IS", "ID", "IE", "IT", "JP", "LV", "LI", "LT", "LU", "MY", "MT", "MX", "MC", "NL", "NZ", "NI", "NO", "PA", "PY", "PE", "PH", "PL", "PT", "SG", "ES", "SK", "SE", "CH", "TW", "TR", "GB", "US", "UY"]
def __init__(
self,
auth=None,
requests_session=True,
client_credentials_manager=None,
oauth_manager=None,
auth_manager=None,
proxies=None,
requests_timeout=5,
status_forcelist=None,
retries=max_retries,
status_retries=max_retries,
backoff_factor=0.3,
):
self.prefix = "https://api.spotify.com/v1/"
self._auth = auth
self.client_credentials_manager = client_credentials_manager
self.oauth_manager = oauth_manager
self.auth_manager = auth_manager
self.proxies = proxies
self.requests_timeout = requests_timeout
self.status_forcelist = status_forcelist or self.default_retry_codes
self.backoff_factor = backoff_factor
self.retries = retries
self.status_retries = status_retries
if isinstance(requests_session, requests.Session):
self._session = requests_session
else:
if requests_session: # Build a new session.
self._build_session()
else: # Use the Requests API module as a "session".
self._session = requests.api
def set_auth(self, auth):
self._auth = auth
@property
def auth_manager(self):
return self._auth_manager
@auth_manager.setter
def auth_manager(self, auth_manager):
if auth_manager is not None:
self._auth_manager = auth_manager
else:
self._auth_manager = (
self.client_credentials_manager or self.oauth_manager
)
def __del__(self):
"""Make sure the connection (pool) gets closed"""
if isinstance(self._session, requests.Session):
self._session.close()
def _build_session(self):
self._session = requests.Session()
retry = urllib3.Retry(
total=self.retries,
connect=None,
read=False,
status=self.status_retries,
backoff_factor=self.backoff_factor,
status_forcelist=self.status_forcelist)
adapter = requests.adapters.HTTPAdapter(max_retries=retry)
self._session.mount('http://', adapter)
self._session.mount('https://', adapter)
def _auth_headers(self):
if self._auth:
return {"Authorization": "Bearer {0}".format(self._auth)}
if not self.auth_manager:
return {}
try:
token = self.auth_manager.get_access_token(as_dict=False)
except TypeError:
token = self.auth_manager.get_access_token()
return {"Authorization": "Bearer {0}".format(token)}
def _internal_call(self, method, url, payload, params):
args = dict(params=params)
if not url.startswith("http"):
url = self.prefix + url
headers = self._auth_headers()
if "content_type" in args["params"]:
headers["Content-Type"] = args["params"]["content_type"]
del args["params"]["content_type"]
if payload:
args["data"] = payload
else:
headers["Content-Type"] = "application/json"
if payload:
args["data"] = json.dumps(payload)
logging.debug('Sending %s to %s with Headers: %s and Body: %r ',
method, url, headers, args.get('data'))
try:
response = self._session.request(
method, url, headers=headers, proxies=self.proxies,
timeout=self.requests_timeout, **args
)
response.raise_for_status()
results = response.json()
except requests.exceptions.HTTPError:
try:
msg = response.json()["error"]["message"]
except (ValueError, KeyError):
msg = "error"
logging.error('HTTP Error for %s to %s returned %s due to %s',
method, url, response.status_code, msg)
raise SpotifyException(
response.status_code,
-1,
"%s:\n %s" % (response.url, msg),
headers=response.headers,
)
except requests.exceptions.RetryError:
logging.error('Max Retries reached')
raise SpotifyException(
599,
-1,
"%s:\n %s" % (response.url, "Max Retries"),
headers=response.headers,
)
except ValueError:
results = None
logging.debug('RESULTS: %s', results)
return results
def _get(self, url, args=None, payload=None, **kwargs):
if args:
kwargs.update(args)
return self._internal_call("GET", url, payload, kwargs)
def _post(self, url, args=None, payload=None, **kwargs):
if args:
kwargs.update(args)
return self._internal_call("POST", url, payload, kwargs)
def _delete(self, url, args=None, payload=None, **kwargs):
if args:
kwargs.update(args)
return self._internal_call("DELETE", url, payload, kwargs)
def _put(self, url, args=None, payload=None, **kwargs):
if args:
kwargs.update(args)
return self._internal_call("PUT", url, payload, kwargs)
def next(self, result):
if result["next"]:
return self._get(result["next"])
else:
return None
def previous(self, result):
if result["previous"]:
return self._get(result["previous"])
else:
return None
def user_unfollow_artists(self, ids=[]):
return self._delete("me/following?type=artist&ids=" + ",".join(ids))
unfollow = Spotify()
unfollow.user_unfollow_artists(artist_ids[:5])