Skip to content

Commit

Permalink
Podcast support
Browse files Browse the repository at this point in the history
  • Loading branch information
felix-hilden committed Apr 4, 2020
1 parent 863e3a3 commit 5e8b58f
Show file tree
Hide file tree
Showing 29 changed files with 851 additions and 122 deletions.
18 changes: 18 additions & 0 deletions docs/src/release_notes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,24 @@ Unreleased
----------
Added
*****
- Support for podcasts: new APIs for
:class:`episodes <tekore.client.api.SpotifyEpisode>`
and :class:`shows <tekore.client.api.SpotifyShow>`.
New :class:`scope <tekore.scope.AuthorisationScopes>`
``user-read-playback-position`` for returning episode resume points.
New endpoints for saving shows in
:class:`library <tekore.client.api.SpotifyLibrary>`.
:meth:`playback_queue_add <tekore.client.api.SpotifyPlayer.playback_queue_add>`
now accepts episode URIs.
:meth:`playback <tekore.client.api.SpotifyPlayer.playback>` and
:meth:`playback_currently_playing <tekore.client.api.SpotifyPlayer.playback_currently_playing>`
can return currently playing episodes and shows.
:meth:`playlist <tekore.client.api.SpotifyPlaylist.playlist>` and
:meth:`playlist_tracks <tekore.client.api.SpotifyPlaylist.playlist_tracks>`
can return episodes on playlists.
:meth:`search <tekore.client.api.SpotifySearch.search>` allows for searching
episodes and shows.
(:issue:`164`)
- Dependency to HTTPX upgraded to include version ``0.12.*`` (:issue:`166`)

Fixed
Expand Down
12 changes: 11 additions & 1 deletion tekore/client/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,11 @@
----------
.. autoclass:: SpotifyBrowse
:members:
:inherited-members:
Episode API
-----------
.. autoclass:: SpotifyEpisode
:members:
Follow API
----------
Expand Down Expand Up @@ -85,6 +89,12 @@
.. autoclass:: SpotifySearch
:members:
Show API
--------
.. autoclass:: SpotifyShow
:members:
Track API
---------
.. autoclass:: SpotifyTrack
Expand Down
2 changes: 2 additions & 0 deletions tekore/client/api/__init__.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
from tekore.client.api.album import SpotifyAlbum
from tekore.client.api.artist import SpotifyArtist
from tekore.client.api.browse import SpotifyBrowse
from tekore.client.api.episode import SpotifyEpisode
from tekore.client.api.follow import SpotifyFollow
from tekore.client.api.library import SpotifyLibrary
from tekore.client.api.personalisation import SpotifyPersonalisation
from tekore.client.api.player import SpotifyPlayer
from tekore.client.api.playlist import SpotifyPlaylist
from tekore.client.api.search import SpotifySearch
from tekore.client.api.show import SpotifyShow
from tekore.client.api.track import SpotifyTrack
from tekore.client.api.user import SpotifyUser
63 changes: 63 additions & 0 deletions tekore/client/api/episode.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
from tekore.client.process import single, model_list
from tekore.client.chunked import chunked, join_lists
from tekore.client.decor import send_and_process
from tekore.client.base import SpotifyBase
from tekore.serialise import ModelList
from tekore.model import FullEpisode


class SpotifyEpisode(SpotifyBase):
@send_and_process(single(FullEpisode))
def episode(
self,
episode_id: str,
market: str = None
) -> FullEpisode:
"""
Get information for an episode.
Parameters
----------
episode_id
episode ID
market
an ISO 3166-1 alpha-2 country code.
If a user token is used to authenticate, the country associated
with it overrides this parameter.
If an application token is used and no market is specified,
the episode is considered unavailable.
Returns
-------
FullEpisode
episode object
"""
return self._get('episodes/' + episode_id, market=market)

@chunked('episode_ids', 1, 50, join_lists)
@send_and_process(model_list(FullEpisode, 'episodes'))
def episodes(
self,
episode_ids: list,
market: str = None
) -> ModelList:
"""
Get information for multiple episodes.
Parameters
----------
episode_ids
the episode IDs, max 50 without chunking
market
an ISO 3166-1 alpha-2 country code.
If a user token is used to authenticate, the country associated
with it overrides this parameter.
If an application token is used and no market is specified,
the episode is considered unavailable.
Returns
-------
ModelList
list of episode objects
"""
return self._get('episodes/?ids=' + ','.join(episode_ids), market=market)
84 changes: 83 additions & 1 deletion tekore/client/api/library.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from tekore.client.chunked import chunked, join_lists, return_none
from tekore.client.decor import send_and_process, maximise_limit
from tekore.client.base import SpotifyBase
from tekore.model import SavedAlbumPaging, SavedTrackPaging
from tekore.model import SavedAlbumPaging, SavedTrackPaging, SavedShowPaging


class SpotifyLibrary(SpotifyBase):
Expand Down Expand Up @@ -165,3 +165,85 @@ def saved_tracks_delete(self, track_ids: list) -> None:
list of track IDs, max 50 without chunking
"""
return self._delete('me/tracks/?ids=' + ','.join(track_ids))

@send_and_process(single(SavedShowPaging))
@maximise_limit(50)
def saved_shows(
self,
market: str = None,
limit: int = 20,
offset: int = 0
) -> SavedShowPaging:
"""
Get a list of the shows saved in the current user's Your Music library.
Requires the user-library-read scope.
Parameters
----------
market
an ISO 3166-1 alpha-2 country code or 'from_token'
limit
the number of items to return (1..50)
offset
the index of the first item to return
Returns
-------
SavedShowPaging
paging object containing saved shows
"""
return self._get('me/shows', market=market, limit=limit, offset=offset)

@chunked('show_ids', 1, 50, join_lists)
@send_and_process(nothing)
def saved_shows_contains(self, show_ids: list) -> List[bool]:
"""
Check if user has saved shows.
Requires the user-library-read scope.
Parameters
----------
show_ids
list of show IDs, max 50 without chunking
Returns
-------
list
list of booleans in the same order the show IDs were given
"""
return self._get('me/shows/contains?ids=' + ','.join(show_ids))

@chunked('show_ids', 1, 50, return_none)
@send_and_process(nothing)
def saved_shows_add(self, show_ids: list) -> None:
"""
Save shows for current user.
Requires the user-library-modify scope.
Parameters
----------
show_ids
list of show IDs, max 50 without chunking
"""
return self._put('me/shows/?ids=' + ','.join(show_ids))

@chunked('show_ids', 1, 50, return_none)
@send_and_process(nothing)
def saved_shows_delete(self, show_ids: list, market: str = None) -> None:
"""
Remove shows for current user.
Requires the user-library-modify scope.
Parameters
----------
show_ids
list of show IDs, max 50 without chunking
market
an ISO 3166-1 alpha-2 country code, only remove shows that are
available in the specified market, overrided by token's country
"""
return self._delete('me/shows/?ids=' + ','.join(show_ids), market=market)
6 changes: 3 additions & 3 deletions tekore/client/api/player/modify.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,16 +125,16 @@ def playback_start_context(
@send_and_process(nothing)
def playback_queue_add(self, uri: str, device_id: str = None) -> None:
"""
Add a track to a user's queue.
Add a track or an episode to a user's queue.
Requires the user-modify-playback-state scope.
Parameters
----------
uri
resource to add, currently only tracks are supported
resource to add, track or episode
device_id
devide to add the track on
devide to extend the queue on
"""
return self._post('me/player/queue', uri=uri, device_id=device_id)

Expand Down
40 changes: 33 additions & 7 deletions tekore/client/api/player/view.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,47 +16,73 @@ class SpotifyPlayerView(SpotifyBase):
@send_and_process(single(CurrentlyPlayingContext))
def playback(
self,
market: str = None
market: str = None,
tracks_only: bool = False
) -> CurrentlyPlayingContext:
"""
Get information about user's current playback.
Requires the user-read-playback-state or
the user-read-currently-playing scope.
Requires the user-read-playback-state scope.
Parameters
----------
market
an ISO 3166-1 alpha-2 country code or 'from_token'
tracks_only
return only tracks in the currently playing item,
if True, episodes have None as the currently playing item
Returns
-------
CurrentlyPlayingContext
information about current playback
"""
return self._get('me/player', market=market)
if tracks_only is True:
additional_types = None
else:
additional_types = 'episode'

return self._get(
'me/player',
market=market,
additional_types=additional_types
)

@send_and_process(single(CurrentlyPlaying))
def playback_currently_playing(
self,
market: str = None
market: str = None,
tracks_only: bool = False
) -> CurrentlyPlaying:
"""
Get user's currently playing track.
Requires the user-read-playback-state scope.
Requires the user-read-playback-state or
the user-read-currently-playing scope.
Parameters
----------
market
an ISO 3166-1 alpha-2 country code or 'from_token'
tracks_only
return only tracks in the currently playing item,
if True, episodes have None as the currently playing item
Returns
-------
CurrentlyPlaying
information about the current track playing
"""
return self._get('me/player/currently-playing', market=market)
if tracks_only is True:
additional_types = None
else:
additional_types = 'episode'

return self._get(
'me/player/currently-playing',
market=market,
additional_types=additional_types
)

@send_and_process(single(PlayHistoryPaging))
@maximise_limit(50)
Expand Down
Loading

0 comments on commit 5e8b58f

Please sign in to comment.