mirror of
https://github.com/spotipy-dev/spotipy.git
synced 2026-06-19 09:13:53 +00:00
Audiobook Support and CI Workflow Update (#1036)
* Implement audiobook endpoints * Update GitHub CI Workflow: Removed Python v2.7 * Update GitHub CI Workflow: Removed Python v3.6 * Add integration tests for audiobook endpoints
This commit is contained in:
parent
d31969108d
commit
1416d47cba
2
.github/workflows/pythonapp.yml
vendored
2
.github/workflows/pythonapp.yml
vendored
@ -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 }}
|
||||||
|
|||||||
@ -7,6 +7,13 @@ 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
|
||||||
|
|||||||
@ -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]+$'
|
||||||
|
|
||||||
@ -2033,3 +2033,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 audiobook’s 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)
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user