Merge branch 'master' of github.com:spotipy-dev/spotipy into v3_rebase_rebase

This commit is contained in:
Stéphane Bruckert 2025-01-13 17:02:45 +00:00
commit 3c75886229
19 changed files with 312 additions and 59 deletions

View File

@ -5,12 +5,55 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 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). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## Unreleased ## Unreleased [3.0.0-alpha]
Add your changes below.
While this is unreleased, please only add v3 features here.
Rebasing master onto v3 doesn't require a changelog update.
### Added ### Added
* `Scope` - An enum which contains all of the authorization scopes (see [here](https://github.com/plamere/spotipy/issues/652#issuecomment-797461311)).
### Changed
* Made `CacheHandler` an abstract base class
* Modified the return structure of the `audio_features` function (wrapping the [Get Audio Features for Several Tracks](https://developer.spotify.com/documentation/web-api/reference/#endpoint-get-several-audio-features) API) to conform to the return structure of the similar methods listed below. The functions wrapping these APIs do not unwrap the single key JSON response, and this is currently the only function that does this.
* [Get Several Tracks](https://developer.spotify.com/documentation/web-api/reference/#endpoint-get-several-tracks)
* [Get Multiple Artists](https://developer.spotify.com/documentation/web-api/reference/#endpoint-get-multiple-artists)
* [Get Multiple Albums](https://developer.spotify.com/documentation/web-api/reference/#endpoint-get-multiple-albums)
* Renamed the `auth` parameter of `Spotify.__init__` to `access_token` for better clarity.
* Removed the `client_credentials_manager` and `oauth_manager` parameters because they are redundant.
* Replaced the `set_auth` and `auth_manager` properties with standard attributes.
### Removed
* Removed the following deprecated methods from `Spotify`:
* `playlist_tracks`
* `user_playlist`
* `user_playlist_tracks`
* `user_playlist_change_details`
* `user_playlist_unfollow`
* `user_playlist_add_tracks`
* `user_playlist_replace_tracks`
* `user_playlist_reorder_tracks`
* `user_playlist_remove_all_occurrences_of_tracks`
* `user_playlist_remove_specific_occurrences_of_tracks`
* `user_playlist_follow_playlist`
* `user_playlist_is_following`
* Removed the deprecated `as_dict` parameter from the `get_access_token` method of `SpotifyOAuth` and `SpotifyPKCE`.
* Removed the deprecated `get_cached_token` and `_save_token_info` methods of `SpotifyOAuth` and `SpotifyPKCE`.
* Removed `SpotifyImplicitGrant`.
* Removed `prompt_for_user_token`.
## Unreleased [2.x.x]
### Added
- Added examples for audiobooks, shows and episodes methods to examples directory
### Fixed ### Fixed
- 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
### Removed ### Removed

View File

@ -0,0 +1,28 @@
"""
Add episodes to current user's library
Usage: add_saved_episodes.py -e episode_id episode_id ...
"""
import argparse
import spotipy
from spotipy.oauth2 import SpotifyOAuth
scope = 'user-library-modify'
def get_args():
parser = argparse.ArgumentParser(description='Add episodes to library')
# Default args set to This American Life episodes 814 and 815
parser.add_argument('-e', '--eids', nargs='+',
default=['6rxg9Lpt2ywNHFea8LxEBO', '7q8or6oYYRFQFYlA0remoy'],
help='Episode ids')
return parser.parse_args()
def main():
args = get_args()
print('Adding following episode ids to library: ' + str(args.eids))
sp = spotipy.Spotify(auth_manager=SpotifyOAuth(scope=scope))
sp.current_user_saved_episodes_add(episodes=args.eids)
if __name__ == '__main__':
main()

View File

@ -0,0 +1,28 @@
"""
Add shows to current user's library
Usage: add_saved_shows.py -s show_id show_id ...
"""
import argparse
import spotipy
from spotipy.oauth2 import SpotifyOAuth
scope = 'user-library-modify'
def get_args():
parser = argparse.ArgumentParser(description='Add shows to library')
# Default args set to Radiolab and 99% invisible
parser.add_argument('-s', '--sids', nargs='+',
default=['2hmkzUtix0qTqvtpPcMzEL', '2VRS1IJCTn2Nlkg33ZVfkM'],
help='Show ids')
return parser.parse_args()
def main():
args = get_args()
print('Adding following show ids to library: ' + str(args.sids))
sp = spotipy.Spotify(auth_manager=SpotifyOAuth(scope=scope))
sp.current_user_saved_shows_add(shows=args.sids)
if __name__ == '__main__':
main()

View File

@ -0,0 +1,31 @@
"""
Check if shows are saved in user's library
Usage: check_show_is_saved -s show_id show_id ...
"""
import argparse
import spotipy
from spotipy.oauth2 import SpotifyOAuth
scope = 'user-library-read'
def get_args():
parser = argparse.ArgumentParser(description='Check that a show is saved')
# Default args set to Radiolab and 99% invisible
parser.add_argument('-s', '--sids', nargs='+',
default=['2hmkzUtix0qTqvtpPcMzEL', '2VRS1IJCTn2Nlkg33ZVfkM'],
help='Show ids')
return parser.parse_args()
def main():
args = get_args()
sp = spotipy.Spotify(auth_manager=SpotifyOAuth(scope=scope))
results = sp.current_user_saved_episodes_contains(episodes=args.sids)
show_names = sp.shows(shows=args.sids)
# Print show names and if show is saved by current user
for i, show in enumerate(show_names['shows']):
print(show['name'] + ': ' + str(results[i]))
if __name__ == '__main__':
main()

View File

@ -0,0 +1,28 @@
"""
Delete episodes from current user's library
Usage: delete_saved_episodes.py -e episode_id episode_id ...
"""
import argparse
import spotipy
from spotipy.oauth2 import SpotifyOAuth
scope = 'user-library-modify'
def get_args():
parser = argparse.ArgumentParser(description='Delete episodes from library')
# Default args set to This American Life episodes 814 and 815
parser.add_argument('-e', '--eids', nargs='+',
default=['6rxg9Lpt2ywNHFea8LxEBO', '7q8or6oYYRFQFYlA0remoy'],
help='Episode ids')
return parser.parse_args()
def main():
args = get_args()
print('Deleting following episode ids from library: ' + str(args.eids))
sp = spotipy.Spotify(auth_manager=SpotifyOAuth(scope=scope))
sp.current_user_saved_episodes_delete(episodes=args.eids)
if __name__ == '__main__':
main()

View File

@ -1,27 +1,22 @@
import argparse # Follow a playlist
import argparse
import spotipy import spotipy
from spotipy.oauth2 import SpotifyOAuth from spotipy.oauth2 import SpotifyOAuth
scope = 'playlist-modify-public'
sp = spotipy.Spotify(auth_manager=SpotifyOAuth(scope=scope))
def get_args(): def get_args():
parser = argparse.ArgumentParser(description='Follows a playlist based on playlist ID') parser = argparse.ArgumentParser(description='Follows a playlist based on playlist ID')
parser.add_argument('-p', '--playlist', required=True, help='Playlist ID') # Default to Top 50 Global if no playlist is provided
parser.add_argument('-p', '--playlist', help='Playlist ID', nargs='?', default='37i9dQZEVXbMDoHDwVN2tF')
return parser.parse_args() return parser.parse_args()
def main(): def main():
args = get_args() args = get_args()
sp.current_user_follow_playlist(args.playlist)
if args.playlist is None:
# Uses the Spotify Global Top 50 playlist
spotipy.Spotify(auth_manager=SpotifyOAuth()).current_user_follow_playlist(
'37i9dQZEVXbMDoHDwVN2tF')
else:
spotipy.Spotify(auth_manager=SpotifyOAuth()).current_user_follow_playlist(args.playlist)
if __name__ == '__main__': if __name__ == '__main__':
main() main()

View File

@ -0,0 +1,29 @@
"""
Print chapter titles and lengths for given audiobook
Usage: get_audiobooks_chapters_info.py -a audiobook_id
"""
import argparse
import spotipy
from spotipy.oauth2 import SpotifyOAuth
def get_args():
parser = argparse.ArgumentParser(description='Get chapter info for an audiobook')
# Default set to Dune
parser.add_argument('-a', '--audiobook', default='2h01INWMBvfpzNMpGFzhdF', help='Audiobook id')
return parser.parse_args()
def main():
args = get_args()
print('Getting chapter info for follow audiobook id: ' + str(args.audiobook))
sp = spotipy.Spotify(auth_manager=SpotifyOAuth())
results = sp.get_audiobook_chapters(id=args.audiobook)
# Print chapter name and length
for item in results['items']:
print('Name: ' + item['name'] + ', length: ' + str(round(item['duration_ms']/60000,1)) + ' minutes')
if __name__ == '__main__':
main()

View File

@ -0,0 +1,29 @@
"""
Print audiobook title and description for a list of audiobook ids
Usage: get_audiobooks_info.py -a audiobook_id audiobook_id ...
"""
import argparse
import spotipy
from spotipy.oauth2 import SpotifyOAuth
def get_args():
parser = argparse.ArgumentParser(description='Get information for a list of audiobooks')
# Defaults set to The Great Gatsby, The Chronicles of Narnia and Dune
parser.add_argument('-a', '--aids', nargs='+',
default=['6qjpt1CUHhKXiNoeNoU7nu', '1ezmXd68LbDtxebvygEQ2U', '2h01INWMBvfpzNMpGFzhdF'],
help='Audiobook ids')
return parser.parse_args()
def main():
args = get_args()
print('Getting info for follow audiobook ids: ' + str(args.aids) + '\n')
sp = spotipy.Spotify(auth_manager=SpotifyOAuth())
results = sp.get_audiobooks(ids=args.aids)
# Print book title and description
for book in results['audiobooks']:
print('Title: ' + book['name'] + '\n' + book['description'] + '\n')
if __name__ == '__main__':
main()

View File

@ -1,12 +0,0 @@
# Add a list of items (URI) to a playlist (URI)
import spotipy
from spotipy.oauth2 import SpotifyOAuth
sp = spotipy.Spotify(auth_manager=SpotifyOAuth(client_id="YOUR_APP_CLIENT_ID",
client_secret="YOUR_APP_CLIENT_SECRET",
redirect_uri="YOUR_APP_REDIRECT_URI",
scope="playlist-modify-private"
))
sp.playlist_add_items('playlist_id', ['list_of_items'])

View File

@ -5,6 +5,6 @@ import json
auth_manager = SpotifyClientCredentials() auth_manager = SpotifyClientCredentials()
sp = spotipy.Spotify(auth_manager=auth_manager) sp = spotipy.Spotify(auth_manager=auth_manager)
playlist_id = 'spotify:user:spotifycharts:playlist:37i9dQZEVXbJiZcmkrIHGU' playlist_id = '37i9dQZEVXbJiZcmkrIHGU'
results = sp.playlist(playlist_id) results = sp.playlist(playlist_id)
print(json.dumps(results, indent=4)) print(json.dumps(results, indent=4))

View File

@ -7,8 +7,8 @@ from spotipy.oauth2 import SpotifyOAuth
if len(sys.argv) > 2: if len(sys.argv) > 2:
playlist_id = sys.argv[2] playlist_id = sys.argv[1]
track_ids = sys.argv[3:] track_ids = sys.argv[2:]
else: else:
print(f"Usage: {sys.argv[0]} playlist_id track_id ...") print(f"Usage: {sys.argv[0]} playlist_id track_id ...")
sys.exit() sys.exit()

View File

@ -6,7 +6,7 @@ import sys
import spotipy import spotipy
from spotipy.oauth2 import SpotifyOAuth from spotipy.oauth2 import SpotifyOAuth
if len(sys.argv) > 3: if len(sys.argv) > 2:
playlist_id = sys.argv[1] playlist_id = sys.argv[1]
track_ids = sys.argv[2:] track_ids = sys.argv[2:]
else: else:

View File

@ -3,21 +3,26 @@
given a list of track IDs show the artist and track name given a list of track IDs show the artist and track name
''' '''
from spotipy.oauth2 import SpotifyClientCredentials from spotipy.oauth2 import SpotifyOAuth
import sys
import spotipy import spotipy
import argparse
def get_args():
parser = argparse.ArgumentParser(description='Print artist and track name given a list of track IDs')
parser.add_argument('-u', '--uris', nargs='+',
required=True, help='Track ids')
return parser.parse_args()
def main():
args = get_args()
sp = spotipy.Spotify(auth_manager=SpotifyOAuth())
track_list = sp.tracks(args.uris)
for track in track_list['tracks']:
print(track['name'] + ' - ' + track['artists'][0]['name'])
if __name__ == '__main__': if __name__ == '__main__':
max_tracks_per_call = 50 main()
if len(sys.argv) > 1:
file = open(sys.argv[1])
else:
file = sys.stdin
tids = file.read().split()
auth_manager = SpotifyClientCredentials()
sp = spotipy.Spotify(auth_manager=auth_manager)
for start in range(0, len(tids), max_tracks_per_call):
results = sp.tracks(tids[start: start + max_tracks_per_call])
for track in results['tracks']:
print(track['name'] + ' - ' + track['artists'][0]['name'])

View File

@ -1,11 +0,0 @@
import spotipy
from spotipy.oauth2 import SpotifyOAuth
scope = "user-library-read"
sp = spotipy.Spotify(auth_manager=SpotifyOAuth(scope=scope))
results = sp.current_user_saved_tracks()
for idx, item in enumerate(results['items']):
track = item['track']
print(idx, track['artists'][0]['name'], " ", track['name'])

View File

@ -4,6 +4,8 @@ import logging
import spotipy import spotipy
from spotipy.oauth2 import SpotifyOAuth from spotipy.oauth2 import SpotifyOAuth
scope = 'playlist-modify-public'
logger = logging.getLogger('examples.unfollow_playlist') logger = logging.getLogger('examples.unfollow_playlist')
logging.basicConfig(level='DEBUG') logging.basicConfig(level='DEBUG')
@ -23,9 +25,9 @@ def get_args():
def main(): def main():
args = get_args() args = get_args()
sp = spotipy.Spotify(auth_manager=SpotifyOAuth()) sp = spotipy.Spotify(auth_manager=SpotifyOAuth(scope=scope))
sp.current_user_unfollow_playlist(args.playlist) sp.current_user_unfollow_playlist(args.playlist)
if __name__ == '__main__': if __name__ == '__main__':
main() main()

View File

@ -0,0 +1,29 @@
"""
List current user's saved episodes
Usage: user_saved_episodes -l <num> -o <num>
"""
import argparse
import spotipy
from spotipy.oauth2 import SpotifyOAuth
scope = 'user-library-read'
def get_args():
parser = argparse.ArgumentParser(description='Show user\'s saved episodes')
parser.add_argument('-l', '--limit', default=20, help='Num of episodes to return')
parser.add_argument('-o', '--offset', default=0, help='Index of first show to return')
return parser.parse_args()
def main():
args = get_args()
sp = spotipy.Spotify(auth_manager=SpotifyOAuth(scope=scope))
results = sp.current_user_saved_episodes(limit=args.limit, offset=args.offset)
# Print episode names
for item in results['items']:
print(item['episode']['name'])
if __name__ == '__main__':
main()

View File

@ -0,0 +1,29 @@
"""
List current user's saved shows
Usage: user_saved_shows -l <num> -o <num>
"""
import argparse
import spotipy
from spotipy.oauth2 import SpotifyOAuth
scope = 'user-library-read'
def get_args():
parser = argparse.ArgumentParser(description='Show user\'s saved shows')
parser.add_argument('-l', '--limit', default=20, help='Num of shows to return')
parser.add_argument('-o', '--offset', default=0, help='Index of first show to return')
return parser.parse_args()
def main():
args = get_args()
sp = spotipy.Spotify(auth_manager=SpotifyOAuth(scope=scope))
results = sp.current_user_saved_shows(limit=args.limit, offset=args.offset)
# Print episode names
for item in results['items']:
print(item['show']['name'])
if __name__ == '__main__':
main()

View File

@ -1178,7 +1178,7 @@ class Spotify:
""" Get the current user's top artists """ Get the current user's top artists
Parameters: Parameters:
- limit - the number of entities to return - limit - the number of entities to return (max 50)
- offset - the index of the first entity to return - offset - the index of the first entity to return
- time_range - Over what time frame are the affinities computed - time_range - Over what time frame are the affinities computed
Valid-values: short_term, medium_term, long_term Valid-values: short_term, medium_term, long_term