Merge branch 'master' into bug/inheriting-exceptions-in-init

This commit is contained in:
Niko 2024-05-15 16:16:08 +02:00 committed by GitHub
commit 6d8d9d1f9b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 147 additions and 12 deletions

View File

@ -8,7 +8,7 @@ jobs:
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04
strategy: strategy:
matrix: matrix:
python-version: ["2.7", "3.6", "3.7", "3.8", "3.9", "3.10"] python-version: ["3.7", "3.8", "3.9", "3.10"]
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }} - name: Set up Python ${{ matrix.python-version }}

View File

@ -7,11 +7,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## Unreleased ## Unreleased
### Added
- Added support for audiobook endpoints: get_audiobook, get_audiobooks, and get_audiobook_chapters.
- Added integration tests for audiobook endpoints.
- Removed `python 2.7` from GitHub Actions CI workflow. Python v2.7 reached end of life support and is no longer supported by Ubuntu 20.04.
- Removed `python 3.6` from GitHub Actions CI workflow. Ubuntu 20.04 is not available in GitHub Actions for `python 3.6`.
### Changed ### Changed
- Changes the YouTube video link for authentication tutorial (the old video was in low definition, the new one is in high definition) - Changes the YouTube video link for authentication tutorial (the old video was in low definition, the new one is in high definition)
- Updated links to Spotify in documentation - Updated links to Spotify in documentation
- Improve usability on README.md
- Fix `user_playlists_contents` example.
- Updated links to Spotify in documentation
- Fixed error obfuscation when Spotify class is being inherited and an error is raised in the Child's `__init__` - Fixed error obfuscation when Spotify class is being inherited and an error is raised in the Child's `__init__`
### Fixed
- Fixed unused description parameter in playlist creation example
## [2.23.0] - 2023-04-07 ## [2.23.0] - 2023-04-07
### Added ### Added

View File

@ -1,12 +1,26 @@
# Spotipy # Spotipy
##### A light weight Python library for the Spotify Web API ##### Spotipy is a lightweight Python library for the [Spotify Web API](https://developer.spotify.com/documentation/web-api). With Spotipy you get full access to all of the music data provided by the Spotify platform.
![Tests](https://github.com/plamere/spotipy/workflows/Tests/badge.svg?branch=master) [![Documentation Status](https://readthedocs.org/projects/spotipy/badge/?version=latest)](https://spotipy.readthedocs.io/en/latest/?badge=latest) ![Tests](https://github.com/plamere/spotipy/workflows/Tests/badge.svg?branch=master) [![Documentation Status](https://readthedocs.org/projects/spotipy/badge/?version=latest)](https://spotipy.readthedocs.io/en/latest/?badge=latest)
## Table of Contents
- [Features](#features)
- [Documentation](#documentation)
- [Installation](#installation)
- [Quick Start](#quick-start)
- [Reporting Issues](#reporting-issues)
- [Contributing](#contributing)
- [License](#license)
## Features
Spotipy supports all of the features of the Spotify Web API including access to all end points, and support for user authorization. For details on the capabilities you are encouraged to review the [Spotify Web API](https://developer.spotify.com/web-api/) documentation.
## Documentation ## Documentation
Spotipy's full documentation is online at [Spotipy Documentation](http://spotipy.readthedocs.org/). Spotipy's [full documentation is online](http://spotipy.readthedocs.org/). Some function may need a [specific scope](https://developer.spotify.com/documentation/web-api/concepts/scopes). If you do not define the scope properly `ERROR 401 Unauthorized, permission missing` may occur.
## Installation ## Installation
@ -30,10 +44,9 @@ pip install spotipy --upgrade
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). 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 and create an app on https://developers.spotify.com/. 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 ([step-by-step video](https://www.youtube.com/watch?v=kaBVN8uP358)):
Add your new ID and SECRET to your environment:
### Without user authentication ### Example without user authentication
```python ```python
import spotipy import spotipy
@ -46,8 +59,20 @@ results = sp.search(q='weezer', limit=20)
for idx, track in enumerate(results['tracks']['items']): for idx, track in enumerate(results['tracks']['items']):
print(idx, track['name']) print(idx, track['name'])
``` ```
Expected result:
```
0 Island In The Sun
1 Say It Ain't So
2 Buddy Holly
.
.
.
18 Troublemaker
19 Feels Like Summer
```
### With user authentication
### Example with user authentication
A redirect URI must be added to your application at [My Dashboard](https://developer.spotify.com/dashboard/applications) to access user authenticated features. A redirect URI must be added to your application at [My Dashboard](https://developer.spotify.com/dashboard/applications) to access user authenticated features.
@ -65,6 +90,12 @@ for idx, item in enumerate(results['items']):
track = item['track'] track = item['track']
print(idx, track['artists'][0]['name'], " ", track['name']) print(idx, track['artists'][0]['name'], " ", track['name'])
``` ```
Expected result will be the list of music that you liked. For example if you liked Red and Sunflower, the result will be:
```
0 Post Malone Sunflower - Spider-Man: Into the Spider-Verse
1 Taylor Swift Red
```
## Reporting Issues ## Reporting Issues
@ -77,3 +108,9 @@ Dont forget to add the *Spotipy* tag, and any other relevant tags as well, be
If you have suggestions, bugs or other issues specific to this library, If you have suggestions, bugs or other issues specific to this library,
file them [here](https://github.com/plamere/spotipy/issues). file them [here](https://github.com/plamere/spotipy/issues).
Or just send a pull request. Or just send a pull request.
## Contributing
If you are a developer with Python experience, and you would like to contribute to Spotipy, please be sure to follow the guidelines listed on documentation page
> #### [Visit the guideline](https://spotipy.readthedocs.io/en/#contribute)

View File

@ -24,7 +24,7 @@ def main():
scope = "playlist-modify-public" scope = "playlist-modify-public"
sp = spotipy.Spotify(auth_manager=SpotifyOAuth(scope=scope)) sp = spotipy.Spotify(auth_manager=SpotifyOAuth(scope=scope))
user_id = sp.me()['id'] user_id = sp.me()['id']
sp.user_playlist_create(user_id, args.playlist) sp.user_playlist_create(user_id, args.playlist, description=args.description)
if __name__ == '__main__': if __name__ == '__main__':

View File

@ -25,8 +25,7 @@ if __name__ == '__main__':
print(playlist['name']) print(playlist['name'])
print(' total tracks', playlist['tracks']['total']) print(' total tracks', playlist['tracks']['total'])
results = sp.playlist(playlist['id'], fields="tracks,next") tracks = sp.playlist_items(playlist['id'], fields="items,next", additional_types=('tracks', ))
tracks = results['tracks']
show_tracks(tracks) show_tracks(tracks)
while tracks['next']: while tracks['next']:

View File

@ -111,14 +111,14 @@ class Spotify(object):
# #
# [1] https://www.iana.org/assignments/uri-schemes/prov/spotify # [1] https://www.iana.org/assignments/uri-schemes/prov/spotify
# [2] https://developer.spotify.com/documentation/web-api/#spotify-uris-and-ids # [2] https://developer.spotify.com/documentation/web-api/#spotify-uris-and-ids
_regex_spotify_uri = r'^spotify:(?:(?P<type>track|artist|album|playlist|show|episode):(?P<id>[0-9A-Za-z]+)|user:(?P<username>[0-9A-Za-z]+):playlist:(?P<playlistid>[0-9A-Za-z]+))$' # noqa: E501 _regex_spotify_uri = r'^spotify:(?:(?P<type>track|artist|album|playlist|show|episode|audiobook):(?P<id>[0-9A-Za-z]+)|user:(?P<username>[0-9A-Za-z]+):playlist:(?P<playlistid>[0-9A-Za-z]+))$' # noqa: E501
# Spotify URLs are defined at [1]. The assumption is made that they are all # Spotify URLs are defined at [1]. The assumption is made that they are all
# pointing to open.spotify.com, so a regex is used to parse them as well, # pointing to open.spotify.com, so a regex is used to parse them as well,
# instead of a more complex URL parsing function. # instead of a more complex URL parsing function.
# #
# [1] https://developer.spotify.com/documentation/web-api/#spotify-uris-and-ids # [1] https://developer.spotify.com/documentation/web-api/#spotify-uris-and-ids
_regex_spotify_url = r'^(http[s]?:\/\/)?open.spotify.com\/(?P<type>track|artist|album|playlist|show|episode|user)\/(?P<id>[0-9A-Za-z]+)(\?.*)?$' # noqa: E501 _regex_spotify_url = r'^(http[s]?:\/\/)?open.spotify.com\/(?P<type>track|artist|album|playlist|show|episode|user|audiobook)\/(?P<id>[0-9A-Za-z]+)(\?.*)?$' # noqa: E501
_regex_base62 = r'^[0-9A-Za-z]+$' _regex_base62 = r'^[0-9A-Za-z]+$'
@ -2036,3 +2036,51 @@ class Spotify(object):
return results return results
return results return results
def get_audiobook(self, id, market=None):
""" Get Spotify catalog information for a single audiobook identified by its unique
Spotify ID.
Parameters:
- id - the Spotify ID for the audiobook
- market - an ISO 3166-1 alpha-2 country code.
"""
audiobook_id = self._get_id("audiobook", id)
endpoint = f"audiobooks/{audiobook_id}"
if market:
endpoint += f'?market={market}'
return self._get(endpoint)
def get_audiobooks(self, ids, market=None):
""" Get Spotify catalog information for multiple audiobooks based on their Spotify IDs.
Parameters:
- ids - a list of Spotify IDs for the audiobooks
- market - an ISO 3166-1 alpha-2 country code.
"""
audiobook_ids = [self._get_id("audiobook", id) for id in ids]
endpoint = f"audiobooks?ids={','.join(audiobook_ids)}"
if market:
endpoint += f'&market={market}'
return self._get(endpoint)
def get_audiobook_chapters(self, id, market=None, limit=20, offset=0):
""" Get Spotify catalog information about an audiobooks chapters.
Parameters:
- id - the Spotify ID for the audiobook
- market - an ISO 3166-1 alpha-2 country code.
- limit - the maximum number of items to return
- offset - the index of the first item to return
"""
audiobook_id = self._get_id("audiobook", id)
endpoint = f"audiobooks/{audiobook_id}/chapters?limit={limit}&offset={offset}"
if market:
endpoint += f'&market={market}'
return self._get(endpoint)

View File

@ -55,6 +55,16 @@ class AuthTestSpotipy(unittest.TestCase):
heavyweight_ep1_url = 'https://open.spotify.com/episode/68kq3bNz6hEuq8NtdfwERG' heavyweight_ep1_url = 'https://open.spotify.com/episode/68kq3bNz6hEuq8NtdfwERG'
reply_all_ep1_urn = 'spotify:episode:1KHjbpnmNpFmNTczQmTZlR' reply_all_ep1_urn = 'spotify:episode:1KHjbpnmNpFmNTczQmTZlR'
american_gods_urn = 'spotify:audiobook:1IcM9Untg6d3ktuwObYGcN'
american_gods_id = '1IcM9Untg6d3ktuwObYGcN'
american_gods_url = 'https://open.spotify.com/audiobook/1IcM9Untg6d3ktuwObYGcN'
four_books = [
'spotify:audiobook:1IcM9Untg6d3ktuwObYGcN',
'spotify:audiobook:37sRC6carIX2Vf3Vv716T7',
'spotify:audiobook:1Gep4UJ95xQawA55OgRI8n',
'spotify:audiobook:4Sm381mcf5gBsi9yfhqgVB']
@classmethod @classmethod
def setUpClass(self): def setUpClass(self):
self.spotify = Spotify( self.spotify = Spotify(
@ -455,3 +465,31 @@ class AuthTestSpotipy(unittest.TestCase):
self.assertTrue(isinstance(markets, list)) self.assertTrue(isinstance(markets, list))
self.assertIn("US", markets) self.assertIn("US", markets)
self.assertIn("GB", markets) self.assertIn("GB", markets)
def test_get_audiobook(self):
audiobook = self.spotify.get_audiobook(self.american_gods_urn, market="US")
print(audiobook)
self.assertTrue(audiobook['name'] ==
'American Gods: The Tenth Anniversary Edition: A Novel')
def test_get_audiobook_bad_urn(self):
with self.assertRaises(SpotifyException):
self.spotify.get_audiobook("bogus_urn", market="US")
def test_get_audiobooks(self):
results = self.spotify.get_audiobooks(self.four_books, market="US")
self.assertTrue('audiobooks' in results)
self.assertTrue(len(results['audiobooks']) == 4)
self.assertTrue(results['audiobooks'][0]['name'] ==
'American Gods: The Tenth Anniversary Edition: A Novel')
self.assertTrue(results['audiobooks'][1]['name'] == 'The Da Vinci Code: A Novel')
self.assertTrue(results['audiobooks'][2]['name'] == 'Outlander')
self.assertTrue(results['audiobooks'][3]['name'] == 'Pachinko: A Novel')
def test_get_audiobook_chapters(self):
results = self.spotify.get_audiobook_chapters(
self.american_gods_urn, market="US", limit=10, offset=5)
self.assertTrue('items' in results)
self.assertTrue(len(results['items']) == 10)
self.assertTrue(results['items'][0]['chapter_number'] == 5)
self.assertTrue(results['items'][9]['chapter_number'] == 14)