Compare commits

..

No commits in common. "master" and "2.25.1" have entirely different histories.

15 changed files with 118 additions and 323 deletions

View File

@ -4,7 +4,7 @@ on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-22.04
runs-on: ubuntu-latest
env:
SPOTIPY_CLIENT_ID: ${{ secrets.SPOTIPY_CLIENT_ID }}
SPOTIPY_CLIENT_SECRET: ${{ secrets.SPOTIPY_CLIENT_SECRET }}

View File

@ -4,7 +4,8 @@ on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-22.04
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v4
- name: Set up Python

View File

@ -10,7 +10,8 @@ on:
jobs:
build-n-publish:
name: Build and publish Python 🐍 distributions 📦 to PyPI and TestPyPI
runs-on: ubuntu-22.04
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python

View File

@ -6,10 +6,10 @@ on:
jobs:
# Enforces the update of a changelog file on every pull request
changelog:
runs-on: ubuntu-22.04
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dangoslen/changelog-enforcer@v3.6.1
with:
changeLogPath: 'CHANGELOG.md'
skipLabels: 'skip-changelog'
skipLabel: 'skip-changelog'

View File

@ -4,7 +4,7 @@ on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-22.04
runs-on: ubuntu-20.04
strategy:
matrix:
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]

View File

@ -6,6 +6,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## Unreleased
Add your changes below.
### Added
@ -14,32 +15,6 @@ Add your changes below.
### Removed
## [2.26.0] - 2026-03-03
### Added
- Created generic methods to get user saved items
### Fixed
- Updated `/tracks` endpoints to `/items`
- Switching IDs to URIs to use `/me/library` endpoint
- Fixed playlist limit to 50 (according to API)
- Added warnings for deprecated methods
### Removed
## [2.25.2] - 2025-11-26
### Added
- Adds `additional_types` parameter to retrieve currently playing episode
- Add deprecation warnings to documentation
### Fixed
- Fixed dead link in README.md
- Corrected Spotify/Spotipy typo in documentation
- Sanitize HTML error message output for OAuth flow: https://github.com/spotipy-dev/spotipy/security/advisories/GHSA-r77h-rpp9-w2xm
## [2.25.1] - 2025-02-27
### Added
@ -51,8 +26,6 @@ Add your changes below.
- Fixed scripts in examples directory that didn't run correctly
- Updated documentation for `Client.current_user_top_artists` to indicate maximum number of artists limit
- Set auth cache file permissions to `600`: https://github.com/spotipy-dev/spotipy/security/advisories/GHSA-pwhh-q4h6-w599
- Fixed `__del__` methods by preventing garbage collection for `requests.Session`
- Improved retry warning by using `logger` instead of `logging` and making sure that `retry_header` is an int
### Changed

View File

@ -39,7 +39,8 @@ To give you a flavour of what we mean, here are some examples of what PRs go whe
Just choose v3 if you are unsure which branch to work on.
### Create virtual environment, install dependencies, run tests
### Create virtual environment, install dependencies, run tests:
```bash
$ virtualenv --python=python3 env
@ -50,7 +51,7 @@ $ source env/bin/activate
### Lint
pip install ".[test]"
pip install .[test]
To automatically fix some of the code style:
@ -74,9 +75,9 @@ Don't forget to add a short description of your change in the [CHANGELOG](CHANGE
### Publishing (by maintainer)
- Bump version in setup.py
- Bump and date changelog
- Add to changelog:
- Bump version in setup.py
- Bump and date changelog
- Add to changelog:
## Unreleased
Add your changes below.
@ -87,8 +88,9 @@ Don't forget to add a short description of your change in the [CHANGELOG](CHANGE
### Removed
- Commit changes
- Push tag to trigger PyPI build & release workflow
- Create github release <https://github.com/plamere/spotipy/releases> with the changelog content
- Commit changes
- Push tag to trigger PyPI build & release workflow
- Create github release https://github.com/plamere/spotipy/releases with the changelog content
for the version and a short name that describes the main addition
- Verify doc uses latest <https://readthedocs.org/projects/spotipy/>
- Verify doc uses latest https://readthedocs.org/projects/spotipy/

View File

@ -36,7 +36,7 @@ pip install spotipy --upgrade
## Quick Start
A full set of examples can be found in the [online documentation](http://spotipy.readthedocs.org/) and in the [Spotipy examples directory](https://github.com/spotipy-dev/spotipy-examples).
A full set of examples can be found in the [online documentation](http://spotipy.readthedocs.org/) and in the [Spotipy examples directory](https://github.com/plamere/spotipy/tree/master/examples).
To get started, [install spotipy](#installation), create a new account or log in on https://developers.spotify.com/. Go to the [dashboard](https://developer.spotify.com/dashboard), create an app and add your new ID and SECRET (ID and SECRET can be found on an app setting) to your environment:

View File

@ -23,7 +23,7 @@ Install or upgrade *Spotipy* with::
pip install spotipy --upgrade
You can also obtain the source code from the `Spotipy GitHub repository <https://github.com/plamere/spotipy>`_.
You can also obtain the source code from the `Spotify GitHub repository <https://github.com/plamere/spotipy>`_.
Getting Started

View File

@ -1,3 +1,3 @@
Sphinx~=8.1.3
sphinx-rtd-theme~=3.1.0
sphinx-rtd-theme~=3.0.2
redis>=3.5.3

View File

@ -13,15 +13,15 @@ extra_reqs = {
],
'test': [
'autopep8>=2.3.2',
'flake8>=7.3.0',
'flake8-use-fstring>=1.4',
'isort>=7.0.0'
'flake8>=7.1.1',
'flake8-string-format>=0.3.0',
'isort>=5.13.2'
]
}
setup(
name='spotipy',
version='2.26.0',
version='2.25.1',
description='A light weight Python library for the Spotify Web API',
long_description=long_description,
long_description_content_type="text/markdown",

View File

@ -11,7 +11,7 @@ from collections import defaultdict
import requests
from spotipy.exceptions import SpotifyException
from spotipy.util import REQUESTS_SESSION, Retry
from spotipy.util import Retry
logger = logging.getLogger(__name__)
@ -211,8 +211,11 @@ class Spotify:
def __del__(self):
"""Make sure the connection (pool) gets closed"""
if getattr(self, "_session", None) and isinstance(self._session, REQUESTS_SESSION):
self._session.close()
try:
if isinstance(self._session, requests.Session):
self._session.close()
except AttributeError:
pass
def _build_session(self):
self._session = requests.Session()
@ -400,14 +403,10 @@ class Spotify:
return self._get("artists/?ids=" + ",".join(tlist))
def artist_albums(
self, artist_id, album_type=None, include_groups=None, country=None, limit=10, offset=0
self, artist_id, album_type=None, include_groups=None, country=None, limit=20, offset=0
):
""" Get Spotify catalog information about an artist's albums
.. deprecated::
This method is deprecated and may be removed in a future version. Use
`artist_albums(..., include_groups='...')` instead.
Parameters:
- artist_id - the artist ID, URI or URL
- include_groups - the types of items to return. One or more of 'album', 'single',
@ -445,11 +444,6 @@ class Spotify:
- country - limit the response to one particular country.
"""
warnings.warn(
"You're using `artist_top_tracks(...)`, "
"which is marked as deprecated by Spotify.",
DeprecationWarning,
)
trid = self._get_id("artist", artist_id)
return self._get("artists/" + trid + "/top-tracks", country=country)
@ -458,9 +452,6 @@ class Spotify:
identified artist. Similarity is based on analysis of the
Spotify community's listening history.
.. deprecated::
This endpoint has been removed by Spotify and is no longer available.
Parameters:
- artist_id - the artist ID, URI or URL
"""
@ -652,11 +643,6 @@ class Spotify:
Parameters:
- user - the id of the usr
"""
warnings.warn(
"You're using `user(...)`, "
"which is marked as deprecated by Spotify.",
DeprecationWarning,
)
return self._get("users/" + user)
def current_user_playlists(self, limit=50, offset=0):
@ -690,17 +676,13 @@ class Spotify:
self,
playlist_id,
fields=None,
limit=50,
limit=100,
offset=0,
market=None,
additional_types=("track",)
):
""" Get full details of the tracks of a playlist.
.. deprecated::
This method is deprecated and may be removed in a future version. Use
`playlist_items(playlist_id, ..., additional_types=('track',))` instead.
Parameters:
- playlist_id - the playlist ID, URI or URL
- fields - which fields to return
@ -722,7 +704,7 @@ class Spotify:
self,
playlist_id,
fields=None,
limit=50,
limit=100,
offset=0,
market=None,
additional_types=("track", "episode")
@ -740,7 +722,7 @@ class Spotify:
"""
plid = self._get_id("playlist", playlist_id)
return self._get(
f"playlists/{plid}/items",
f"playlists/{plid}/tracks",
limit=limit,
offset=offset,
fields=fields,
@ -773,22 +755,18 @@ class Spotify:
)
def user_playlist(self, user, playlist_id=None, fields=None, market=None):
""" Gets a single playlist of a user
warnings.warn(
"You should use `playlist(playlist_id)` instead",
DeprecationWarning,
)
.. deprecated::
This method is deprecated and may be removed in a future version. Use
`playlist(playlist_id)` instead.
""" Gets a single playlist of a user
Parameters:
- user - the id of the user
- playlist_id - the id of the playlist
- fields - which fields to return
"""
warnings.warn(
"You should use `playlist(playlist_id)` instead",
DeprecationWarning,
)
if playlist_id is None:
return self._get(f"users/{user}/starred")
return self.playlist(playlist_id, fields=fields, market=market)
@ -802,11 +780,12 @@ class Spotify:
offset=0,
market=None,
):
""" Get full details of the tracks of a playlist owned by a user.
warnings.warn(
"You should use `playlist_tracks(playlist_id)` instead",
DeprecationWarning,
)
.. deprecated::
This method is deprecated and may be removed in a future version. Use
`playlist_tracks(playlist_id)` instead.
""" Get full details of the tracks of a playlist owned by a user.
Parameters:
- user - the id of the user
@ -816,10 +795,6 @@ class Spotify:
- offset - the index of the first track to return
- market - an ISO 3166-1 alpha-2 country code.
"""
warnings.warn(
"You should use `playlist_tracks(playlist_id)` instead",
DeprecationWarning,
)
return self.playlist_tracks(
playlist_id,
limit=limit,
@ -836,12 +811,6 @@ class Spotify:
- limit - the number of items to return
- offset - the index of the first item to return
"""
warnings.warn(
"You're using `user_playlists(...)`, "
"which is marked as deprecated by Spotify. Use "
"current_user_playlists(...) instead.",
DeprecationWarning,
)
return self._get(
f"users/{user}/playlists", limit=limit, offset=offset
)
@ -856,12 +825,6 @@ class Spotify:
- collaborative - is the created playlist collaborative
- description - the description of the playlist
"""
warnings.warn(
"You're using `user_playlist_create(...)`, "
"which is marked as deprecated by Spotify. Use "
"current_user_playlist_create(...) instead.",
DeprecationWarning,
)
data = {
"name": name,
"public": public,
@ -871,24 +834,6 @@ class Spotify:
return self._post(f"users/{user}/playlists", payload=data)
def current_user_playlist_create(self, name, public=True, collaborative=False, description=""):
""" Creates a playlist for the current user
Parameters:
- name - the name of the playlist
- public - is the created playlist public
- collaborative - is the created playlist collaborative
- description - the description of the playlist
"""
data = {
"name": name,
"public": public,
"collaborative": collaborative,
"description": description
}
return self._post("me/playlists", payload=data)
def user_playlist_change_details(
self,
user,
@ -902,10 +847,6 @@ class Spotify:
Changes a playlist's name and/or public/private state
.. deprecated::
This method is deprecated and may be removed in a future version. Use
`playlist_change_details(playlist_id, ...)` instead.
Parameters:
- user - the id of the user
- playlist_id - the id of the playlist
@ -927,10 +868,6 @@ class Spotify:
Unfollows (deletes) a playlist for a user
.. deprecated::
This method is deprecated and may be removed in a future version. Use
`current_user_unfollow_playlist(playlist_id)` instead.
Parameters:
- user - the id of the user
- name - the name of the playlist
@ -948,10 +885,6 @@ class Spotify:
Adds tracks to a playlist
.. deprecated::
This method is deprecated and may be removed in a future version. Use
`playlist_add_items(playlist_id, tracks)` instead.
Parameters:
- user - the id of the user
- playlist_id - the id of the playlist
@ -973,10 +906,6 @@ class Spotify:
Adds episodes to a playlist
.. deprecated::
This method is deprecated and may be removed in a future version. Use
`playlist_add_items(playlist_id, episodes)` instead.
Parameters:
- user - the id of the user
- playlist_id - the id of the playlist
@ -996,10 +925,6 @@ class Spotify:
Replace all tracks in a playlist for a user
.. deprecated::
This method is deprecated and may be removed in a future version. Use
`playlist_replace_items(playlist_id, tracks)` instead.
Parameters:
- user - the id of the user
- playlist_id - the id of the playlist
@ -1024,10 +949,6 @@ class Spotify:
Reorder tracks in a playlist from a user
.. deprecated::
This method is deprecated and may be removed in a future version. Use
`playlist_reorder_items(playlist_id, ...)` instead.
Parameters:
- user - the id of the user
- playlist_id - the id of the playlist
@ -1053,10 +974,6 @@ class Spotify:
Removes all occurrences of the given tracks from the given playlist
.. deprecated::
This method is deprecated and may be removed in a future version. Use
`playlist_remove_all_occurrences_of_items(playlist_id, tracks)` instead.
Parameters:
- user - the id of the user
- playlist_id - the id of the playlist
@ -1077,10 +994,7 @@ class Spotify:
):
""" This function is no longer in use, please use the recommended function in the warning!
Removes specific occurrences of the given tracks from the given playlist
.. deprecated::
This endpoint has been removed by Spotify and is no longer available.
Removes all occurrences of the given tracks from the given playlist
Parameters:
- user - the id of the user
@ -1093,8 +1007,8 @@ class Spotify:
- snapshot_id - optional id of the playlist snapshot
"""
warnings.warn(
"You're using `user_playlist_remove_specific_occurrences_of_tracks(...)`, "
"which is marked as deprecated by Spotify.",
"You should use `playlist_remove_specific_occurrences_of_items"
"(playlist_id, tracks)` instead",
DeprecationWarning,
)
plid = self._get_id("playlist", playlist_id)
@ -1118,10 +1032,6 @@ class Spotify:
Add the current authenticated user as a follower of a playlist.
.. deprecated::
This method is deprecated and may be removed in a future version. Use
`current_user_follow_playlist(playlist_id)` instead.
Parameters:
- playlist_owner_id - the user id of the playlist owner
- playlist_id - the id of the playlist
@ -1139,10 +1049,6 @@ class Spotify:
Check to see if the given users are following the given playlist
.. deprecated::
This method is deprecated and may be removed in a future version. Use
`playlist_is_following(playlist_id, user_ids)` instead.
Parameters:
- playlist_owner_id - the user id of the playlist owner
- playlist_id - the id of the playlist
@ -1211,7 +1117,7 @@ class Spotify:
plid = self._get_id("playlist", playlist_id)
ftracks = [self._get_uri("track", tid) for tid in items]
return self._post(
f"playlists/{plid}/items",
f"playlists/{plid}/tracks",
payload=ftracks,
position=position,
)
@ -1227,7 +1133,7 @@ class Spotify:
ftracks = [self._get_uri("track", tid) for tid in items]
payload = {"uris": ftracks}
return self._put(
f"playlists/{plid}/items", payload=payload
f"playlists/{plid}/tracks", payload=payload
)
def playlist_reorder_items(
@ -1258,7 +1164,7 @@ class Spotify:
if snapshot_id:
payload["snapshot_id"] = snapshot_id
return self._put(
f"playlists/{plid}/items", payload=payload
f"playlists/{plid}/tracks", payload=payload
)
def playlist_remove_all_occurrences_of_items(
@ -1275,11 +1181,11 @@ class Spotify:
plid = self._get_id("playlist", playlist_id)
ftracks = [self._get_uri("track", tid) for tid in items]
payload = {"items": [{"uri": track} for track in ftracks]}
payload = {"tracks": [{"uri": track} for track in ftracks]}
if snapshot_id:
payload["snapshot_id"] = snapshot_id
return self._delete(
f"playlists/{plid}/items", payload=payload
f"playlists/{plid}/tracks", payload=payload
)
def playlist_remove_specific_occurrences_of_items(
@ -1306,14 +1212,14 @@ class Spotify:
"positions": tr["positions"],
}
)
payload = {"items": ftracks}
payload = {"tracks": ftracks}
if snapshot_id:
payload["snapshot_id"] = snapshot_id
return self._delete(
f"playlists/{plid}/items", payload=payload
f"playlists/{plid}/tracks", payload=payload
)
def current_user_follow_playlist(self, playlist_id):
def current_user_follow_playlist(self, playlist_id, public=True):
"""
Add the current authenticated user as a follower of a playlist.
@ -1321,7 +1227,10 @@ class Spotify:
- playlist_id - the id of the playlist
"""
return self._put("me/library", uris=self._get_uri("playlist", playlist_id))
return self._put(
f"playlists/{playlist_id}/followers",
payload={"public": public}
)
def playlist_is_following(
self, playlist_id, user_ids
@ -1335,27 +1244,9 @@ class Spotify:
if they follow the playlist. Maximum: 5 ids.
"""
warnings.warn(
"You're using `playlist_is_following(..., user_ids=...)`, "
"which is marked as deprecated by Spotify. Use ",
"current_user_follow_playlist(...) instead.",
DeprecationWarning,
)
endpoint = f"playlists/{playlist_id}/followers/contains?ids={','.join(user_ids)}"
return self._get(endpoint)
def current_user_saved_items(self, uris):
"""
Check if the current user is following the given artists, users, or playlists
Parameters:
- uris - a list of URIs to check for following status. Maximum: 40 ids.
"""
valid_uris = [uri for uri in uris if self._is_uri(uri)]
return self._get("me/library/contains", uris=",".join(valid_uris))
def me(self):
""" Get detailed profile information about the current user.
An alias for the 'current_user' method.
@ -1368,20 +1259,10 @@ class Spotify:
"""
return self.me()
def current_user_playing_track(self, market=None, additional_types=("track",)):
def current_user_playing_track(self):
""" Get information about the current users currently playing track.
Parameters:
- market - An ISO 3166-1 alpha-2 country code or the
string from_token.
- additional_types - list of item types to return.
valid types are: track and episode
"""
return self._get(
"me/player/currently-playing",
market=market,
additional_types=",".join(additional_types)
)
return self._get("me/player/currently-playing")
def current_user_saved_albums(self, limit=20, offset=0, market=None):
""" Gets a list of the albums saved in the current authorized user's
@ -1402,8 +1283,8 @@ class Spotify:
- albums - a list of album URIs, URLs or IDs
"""
alist = [self._get_uri("album", a) for a in albums]
return self._put("me/library", uris=",".join(alist))
alist = [self._get_id("album", a) for a in albums]
return self._put("me/albums?ids=" + ",".join(alist))
def current_user_saved_albums_delete(self, albums=[]):
""" Remove one or more albums from the current user's
@ -1412,8 +1293,8 @@ class Spotify:
Parameters:
- albums - a list of album URIs, URLs or IDs
"""
alist = [self._get_uri("album", a) for a in albums]
return self._delete("me/library", uris=",".join(alist))
alist = [self._get_id("album", a) for a in albums]
return self._delete("me/albums/?ids=" + ",".join(alist))
def current_user_saved_albums_contains(self, albums=[]):
""" Check if one or more albums is already saved in
@ -1422,8 +1303,8 @@ class Spotify:
Parameters:
- albums - a list of album URIs, URLs or IDs
"""
alist = [self._get_uri("album", a) for a in albums]
return self._get("me/library/contains", uris=",".join(alist))
alist = [self._get_id("album", a) for a in albums]
return self._get("me/albums/contains?ids=" + ",".join(alist))
def current_user_saved_tracks(self, limit=20, offset=0, market=None):
""" Gets a list of the tracks saved in the current authorized user's
@ -1446,8 +1327,8 @@ class Spotify:
"""
tlist = []
if tracks is not None:
tlist = [self._get_uri("track", t) for t in tracks]
return self._put("me/library", uris=",".join(tlist))
tlist = [self._get_id("track", t) for t in tracks]
return self._put("me/tracks/?ids=" + ",".join(tlist))
def current_user_saved_tracks_delete(self, tracks=None):
""" Remove one or more tracks from the current user's
@ -1458,8 +1339,8 @@ class Spotify:
"""
tlist = []
if tracks is not None:
tlist = [self._get_uri("track", t) for t in tracks]
return self._delete("me/library", uris=",".join(tlist))
tlist = [self._get_id("track", t) for t in tracks]
return self._delete("me/tracks/?ids=" + ",".join(tlist))
def current_user_saved_tracks_contains(self, tracks=None):
""" Check if one or more tracks is already saved in
@ -1470,8 +1351,8 @@ class Spotify:
"""
tlist = []
if tracks is not None:
tlist = [self._get_uri("track", t) for t in tracks]
return self._get("me/library/contains", uris=",".join(tlist))
tlist = [self._get_id("track", t) for t in tracks]
return self._get("me/tracks/contains?ids=" + ",".join(tlist))
def current_user_saved_episodes(self, limit=20, offset=0, market=None):
""" Gets a list of the episodes saved in the current authorized user's
@ -1494,8 +1375,8 @@ class Spotify:
"""
elist = []
if episodes is not None:
elist = [self._get_uri("episode", e) for e in episodes]
return self._put("me/library", uris=",".join(elist))
elist = [self._get_id("episode", e) for e in episodes]
return self._put("me/episodes/?ids=" + ",".join(elist))
def current_user_saved_episodes_delete(self, episodes=None):
""" Remove one or more episodes from the current user's
@ -1506,8 +1387,8 @@ class Spotify:
"""
elist = []
if episodes is not None:
elist = [self._get_uri("episode", e) for e in episodes]
return self._delete("me/library", uris=",".join(elist))
elist = [self._get_id("episode", e) for e in episodes]
return self._delete("me/episodes/?ids=" + ",".join(elist))
def current_user_saved_episodes_contains(self, episodes=None):
""" Check if one or more episodes is already saved in
@ -1539,8 +1420,8 @@ class Spotify:
Parameters:
- shows - a list of show URIs, URLs or IDs
"""
slist = [self._get_uri("show", s) for s in shows]
return self._put("me/library", uris=",".join(slist))
slist = [self._get_id("show", s) for s in shows]
return self._put("me/shows?ids=" + ",".join(slist))
def current_user_saved_shows_delete(self, shows=[]):
""" Remove one or more shows from the current user's
@ -1549,8 +1430,8 @@ class Spotify:
Parameters:
- shows - a list of show URIs, URLs or IDs
"""
slist = [self._get_uri("show", s) for s in shows]
return self._delete("me/library", uris=",".join(slist))
slist = [self._get_id("show", s) for s in shows]
return self._delete("me/shows/?ids=" + ",".join(slist))
def current_user_saved_shows_contains(self, shows=[]):
""" Check if one or more shows is already saved in
@ -1559,8 +1440,8 @@ class Spotify:
Parameters:
- shows - a list of show URIs, URLs or IDs
"""
slist = [self._get_uri("show", s) for s in shows]
return self._get("me/library/contains", uris=",".join(slist))
slist = [self._get_id("show", s) for s in shows]
return self._get("me/shows/contains?ids=" + ",".join(slist))
def current_user_followed_artists(self, limit=20, after=None):
""" Gets a list of the artists followed by the current authorized user
@ -1583,11 +1464,11 @@ class Spotify:
Parameters:
- ids - a list of artist URIs, URLs or IDs
"""
ulist = []
idlist = []
if ids is not None:
ulist = [self._get_uri("artist", i) for i in ids]
idlist = [self._get_id("artist", i) for i in ids]
return self._get(
"me/library/contains", uris=",".join(ulist)
"me/following/contains", ids=",".join(idlist), type="artist"
)
def current_user_following_users(self, ids=None):
@ -1598,11 +1479,11 @@ class Spotify:
Parameters:
- ids - a list of user URIs, URLs or IDs
"""
ulist = []
idlist = []
if ids is not None:
ulist = [self._get_uri("user", i) for i in ids]
idlist = [self._get_id("user", i) for i in ids]
return self._get(
"me/library/contains", uris=",".join(ulist)
"me/following/contains", ids=",".join(idlist), type="user"
)
def current_user_top_artists(
@ -1659,41 +1540,34 @@ class Spotify:
Parameters:
- ids - a list of artist IDs
"""
alist = [self._get_uri("artist", a) for a in ids]
return self._put("me/library", uris=",".join(alist))
return self._put("me/following?type=artist&ids=" + ",".join(ids))
def user_follow_users(self, ids=[]):
""" Follow one or more users
Parameters:
- ids - a list of user IDs
"""
ulist = [self._get_uri("user", a) for a in ids]
return self._put("me/library", uris=",".join(ulist))
return self._put("me/following?type=user&ids=" + ",".join(ids))
def user_unfollow_artists(self, ids=[]):
""" Unfollow one or more artists
Parameters:
- ids - a list of artist IDs
"""
alist = [self._get_uri("artist", a) for a in ids]
return self._delete("me/library", uris=",".join(alist))
return self._delete("me/following?type=artist&ids=" + ",".join(ids))
def user_unfollow_users(self, ids=[]):
""" Unfollow one or more users
Parameters:
- ids - a list of user IDs
"""
ulist = [self._get_uri("user", a) for a in ids]
return self._delete("me/library", uris=",".join(ulist))
return self._delete("me/following?type=user&ids=" + ",".join(ids))
def featured_playlists(
self, locale=None, country=None, timestamp=None, limit=20, offset=0
):
""" Get a list of Spotify featured playlists
.. deprecated::
This endpoint has been removed by Spotify and is no longer available.
Parameters:
- locale - The desired language, consisting of a lowercase ISO
639-1 alpha-2 language code and an uppercase ISO 3166-1 alpha-2
@ -1740,11 +1614,6 @@ class Spotify:
(the first object). Use with limit to get the next set of
items.
"""
warnings.warn(
"You're using `new_release(...)`, "
"which is marked as deprecated by Spotify.",
DeprecationWarning,
)
return self._get(
"browse/new-releases", country=country, limit=limit, offset=offset
)
@ -1760,11 +1629,6 @@ class Spotify:
language code and an ISO 3166-1 alpha-2 country code, joined
by an underscore.
"""
warnings.warn(
"You're using `category(...)`, "
"which is marked as deprecated by Spotify.",
DeprecationWarning,
)
return self._get(
"browse/categories/" + category_id,
country=country,
@ -1787,11 +1651,6 @@ class Spotify:
(the first object). Use with limit to get the next set of
items.
"""
warnings.warn(
"You're using `categories(...)`, "
"which is marked as deprecated by Spotify.",
DeprecationWarning,
)
return self._get(
"browse/categories",
country=country,
@ -1805,9 +1664,6 @@ class Spotify:
):
""" Get a list of playlists for a specific Spotify category
.. deprecated::
This endpoint has been removed by Spotify and is no longer available.
Parameters:
- category_id - The Spotify category ID for the category.
@ -1845,9 +1701,6 @@ class Spotify:
(at least one of `seed_artists`, `seed_tracks` and `seed_genres`
are needed)
.. deprecated::
This endpoint has been removed by Spotify and is no longer available.
Parameters:
- seed_artists - a list of artist IDs, URIs or URLs
- seed_tracks - a list of track IDs, URIs or URLs
@ -1908,24 +1761,17 @@ class Spotify:
return self._get("recommendations", **params)
def recommendation_genre_seeds(self):
""" Get a list of genres available for the recommendations function.
.. deprecated::
This endpoint has been removed by Spotify and is no longer available.
"""
warnings.warn(
"You're using `recommendation_genre_seeds(...)`, "
"which is marked as deprecated by Spotify.",
DeprecationWarning,
)
""" Get a list of genres available for the recommendations function.
"""
return self._get("recommendations/available-genre-seeds")
def audio_analysis(self, track_id):
""" Get audio analysis for a track based upon its Spotify ID
.. deprecated::
This endpoint has been removed by Spotify and is no longer available.
Parameters:
- track_id - a track URI, URL or ID
"""
@ -1939,10 +1785,6 @@ class Spotify:
def audio_features(self, tracks=[]):
""" Get audio features for one or multiple tracks based upon their Spotify IDs
.. deprecated::
This endpoint has been removed by Spotify and is no longer available.
Parameters:
- tracks - a list of track URIs, URLs or IDs, maximum: 100 ids
"""

View File

@ -8,7 +8,6 @@ __all__ = [
]
import base64
import html
import logging
import os
import time
@ -22,8 +21,7 @@ import requests
from spotipy.cache_handler import CacheFileHandler, CacheHandler
from spotipy.exceptions import SpotifyOauthError, SpotifyStateError
from spotipy.util import (CLIENT_CREDS_ENV_VARS, REQUESTS_SESSION,
get_host_port, normalize_scope)
from spotipy.util import CLIENT_CREDS_ENV_VARS, get_host_port, normalize_scope
logger = logging.getLogger(__name__)
@ -124,7 +122,7 @@ class SpotifyAuthBase:
def __del__(self):
"""Make sure the connection (pool) gets closed"""
if getattr(self, "_session", None) and isinstance(self._session, REQUESTS_SESSION):
if isinstance(self._session, requests.Session):
self._session.close()
@ -187,7 +185,7 @@ class SpotifyClientCredentials(SpotifyAuthBase):
Else fetches a new token and returns it
Parameters:
- as_dict: (deprecated) a boolean indicating if returning the access token
- as_dict - a boolean indicating if returning the access token
as a token_info dictionary, otherwise it will be returned
as a string.
"""
@ -485,8 +483,8 @@ class SpotifyOAuth(SpotifyAuthBase):
""" Gets the access token for the app given the code
Parameters:
- code: the response code
- as_dict: (deprecated) a boolean indicating if returning the access token
- code - the response code
- as_dict - a boolean indicating if returning the access token
as a token_info dictionary, otherwise it will be returned
as a string.
"""
@ -579,11 +577,6 @@ class SpotifyOAuth(SpotifyAuthBase):
return token_info
def get_cached_token(self):
""" Gets the cached token for the app
.. deprecated::
This method is deprecated and may be removed in a future version.
"""
warnings.warn("Calling get_cached_token directly on the SpotifyOAuth object will be " +
"deprecated. Instead, please specify a CacheFileHandler instance as " +
"the cache_handler in SpotifyOAuth and use the CacheFileHandler's " +
@ -1210,11 +1203,6 @@ class SpotifyImplicitGrant(SpotifyAuthBase):
return token_info
def get_cached_token(self):
""" Gets the cached token for the app
.. deprecated::
This method is deprecated and may be removed in a future version.
"""
warnings.warn("Calling get_cached_token directly on the SpotifyImplicitGrant " +
"object will be deprecated. Instead, please specify a " +
"CacheFileHandler instance as the cache_handler in SpotifyOAuth " +
@ -1253,7 +1241,7 @@ class RequestHandler(BaseHTTPRequestHandler):
if self.server.auth_code:
status = "successful"
elif self.server.error:
status = f"failed ({html.escape(str(self.server.error))})"
status = f"failed ({self.server.error})"
else:
self._write("<html><body><h1>Invalid request</h1></body></html>")
return

View File

@ -9,12 +9,11 @@ import os
import warnings
from types import TracebackType
import requests
import urllib3
import spotipy
logger = logging.getLogger(__name__)
LOGGER = logging.getLogger(__name__)
CLIENT_CREDS_ENV_VARS = {
"client_id": "SPOTIPY_CLIENT_ID",
@ -23,9 +22,6 @@ CLIENT_CREDS_ENV_VARS = {
"redirect_uri": "SPOTIPY_REDIRECT_URI",
}
# workaround for garbage collection
REQUESTS_SESSION = requests.Session
def prompt_for_user_token(
username=None,
@ -37,11 +33,15 @@ def prompt_for_user_token(
oauth_manager=None,
show_dialog=False
):
""" Prompt the user to login if necessary and returns a user token
suitable for use with the spotipy.Spotify constructor.
.. deprecated::
This method is deprecated and may be removed in a future version.
warnings.warn(
"'prompt_for_user_token' is deprecated."
"Use the following instead: "
" auth_manager=SpotifyOAuth(scope=scope)"
" spotipy.Spotify(auth_manager=auth_manager)",
DeprecationWarning
)
"""Prompt the user to login if necessary and returns a user token
suitable for use with the spotipy.Spotify constructor.
Parameters:
- username - the Spotify username. (optional)
@ -53,14 +53,6 @@ def prompt_for_user_token(
- oauth_manager - OAuth manager object. (optional)
- show_dialog - If True, a login prompt always shows or defaults to False. (optional)
"""
warnings.warn(
"'prompt_for_user_token' is deprecated."
"Use the following instead: "
" auth_manager=SpotifyOAuth(scope=scope)"
" spotipy.Spotify(auth_manager=auth_manager)",
DeprecationWarning
)
if not oauth_manager:
if not client_id:
client_id = os.getenv("SPOTIPY_CLIENT_ID")
@ -72,7 +64,7 @@ def prompt_for_user_token(
redirect_uri = os.getenv("SPOTIPY_REDIRECT_URI")
if not client_id:
logger.warning(
LOGGER.warning(
"""
You need to set your Spotify API credentials.
You can do this by setting environment variables like so:
@ -174,9 +166,8 @@ class Retry(urllib3.Retry):
if response:
retry_header = response.headers.get("Retry-After")
if self.is_retry(method, response.status, bool(retry_header)):
retry_header = retry_header or 0
logger.warning("Your application has reached a rate/request limit. "
f"Retry will occur after: {retry_header} s")
logging.warning("Your application has reached a rate/request limit. "
f"Retry will occur after: {retry_header}")
return super().increment(method,
url,
response=response,

View File

@ -8,9 +8,6 @@ commands=python -m unittest discover -v tests/unit
max-line-length = 99
exclude=
.git,
.venv,
build,
dist,
docs,
examples,
spotipy.egg-info
examples