Exemplo n.º 1
0
    def available_devices(self):
        """ Get all devices currently available.

        Returns:
            List[str]: All available device ids.

        Calls endpoints:
            - GET     /v1/me/player/devices

        Required token scopes:
            - user-read-playback-state
        """
        response_json, status_code = utils.request(
            self._session,
            request_type=const.REQUEST_GET,
            endpoint=Endpoints.PLAYER_AVAILABLE_DEVICES,
            body=None,
            uri_params=None)

        if status_code != 200:
            raise utils.SpotifyError(status_code, response_json)

        try:
            devices = response_json['devices']
            result = [elem['id'] for elem in devices]
        except KeyError:
            raise utils.SpotifyError(KEYSTRING)

        return result
Exemplo n.º 2
0
    def _player_data(self,
                     key,
                     market=const.TOKEN_REGION,
                     should_raise_error=True):
        """ Helper function for the getter methods.

        Wraps calling the player endpoint and handling a missing key.

        Args:
            key: the key to get from the currently playing context
            market: see the :class:`shared args documentation <Player>`
            should_raise_error:
                if False: returns None when no device available
                if True: raises SpotifyError when no device available

        Returns:
            None if there is no active device and should_raise_error is False
            The result of player_data[key] otherwise

        Raises:
            SpotifyError: if Spotify returns an error or the key isn't found
            NetworkError: for misc network failures

        Calls endpoints:
            - GET    /v1/me/player

        Required token scopes:
            - user-read-playback-state
        """
        response_json, status_code = utils.request(
            self._session,
            request_type=const.REQUEST_GET,
            endpoint=Endpoints.PLAYER_DATA,
            body=None,
            uri_params={'market': market})

        # No active device
        # TODO: update when bug report is resolved
        if status_code == 204:
            if not should_raise_error:
                return None
            raise utils.SpotifyError('No active device', status_code,
                                     response_json)

        # Valid Player errors
        if status_code in [403, 404]:
            raise utils.SpotifyError(status_code, response_json)

        # Misc other failure
        if status_code != 200:
            raise utils.NetworkError(status_code, response_json)

        if key not in response_json:
            raise utils.SpotifyError(KEYSTRING + ': key <%s> not found' % key)

        return response_json[key]
Exemplo n.º 3
0
    def _follow_unfollow_help(self, other, request_type):
        """ follow and unfollow are identical, except for the request type.

        This function implements that functionality to remove duplicate code.
        """
        # Validate input
        if not isinstance(other, list):
            other = [other]

        for elem in other:
            if type(elem) not in [Artist, User, Playlist]:
                raise TypeError(elem)

        # Split up input
        artists = utils.separate(other, Artist)
        users = utils.separate(other, User)
        playlists = utils.separate(other, Playlist)

        for batch in utils.create_batches(utils.map_ids(artists)):
            response_json, status_code = utils.request(
                self._session,
                request_type=request_type,
                endpoint=Endpoints.USER_FOLLOW_ARTIST_USER,
                body=None,
                uri_params={
                    'type': 'artist',
                    'ids': batch
                })

            if status_code != 204:
                raise utils.SpotifyError(status_code, response_json)

        for batch in utils.create_batches(utils.map_ids(users)):
            response_json, status_code = utils.request(
                self._session,
                request_type=request_type,
                endpoint=Endpoints.USER_FOLLOW_ARTIST_USER,
                body=None,
                uri_params={
                    'type': 'user',
                    'ids': batch
                })

            if status_code != 204:
                raise utils.SpotifyError(status_code, response_json)

        for playlist in playlists:
            response_json, status_code = utils.request(
                self._session,
                request_type=request_type,
                endpoint=Endpoints.USER_FOLLOW_PLAYLIST % playlist,
                body=None,
                uri_params=None)

            if status_code != 200:
                raise utils.SpotifyError(status_code, response_json)
Exemplo n.º 4
0
    def related_artists(self, search_limit=20):
        """ Get artists similar to this artist, as defined by Spotify.

        Args:
            search_limit (int): the maximum number of results to return. Must be
                between 1 and 20, inclusive (this is Spotify's limit).

        Returns:
            List[Artist]: The artists related to this artist.

        Calls endpoints:
            - GET	/v1/artists/{id}/related-artists
        """

        # This search limit is not part of the API, Spotify always returns up to
        # 20.

        # TODO: limit can't be None...

        # Type validation
        if search_limit is not None and not isinstance(search_limit, int):
            raise TypeError('search_limit should be None or int')

        # Argument validation
        if search_limit < 0 or search_limit > 20:
            raise ValueError('search_limit should be >= 0 and <= 20')

        # Save params for lazy loading check
        search_query = (search_limit)

        # Construct params for API call
        endpoint = Endpoints.ARTIST_RELATED_ARTISTS % self.spotify_id()

        # Lazy loading check
        if search_query == self._related_artists_query_params:
            return self._related_artists

        # Update stored params for lazy loading
        response_json, status_code = utils.request(session=self._session,
                                                   request_type=\
                                                       const.REQUEST_GET,
                                                   endpoint=endpoint
                                                   )

        if status_code != 200:
            raise utils.SpotifyError(status_code, response_json)
        if 'artists' not in response_json:
            raise utils.SpotifyError('Malformed response, missing key artists')

        result = [
            Artist(self._session, x) for x in response_json.get('artists')
        ]
        self._related_artists = result
        self._related_artists_query_params = search_query

        return self._related_artists
Exemplo n.º 5
0
    def replace_all_tracks(self, tracks):
        """ Replace all of the tracks in the playlist.

        Args:
            tracks (List[Track]): The tracks to populate the playlist.

        Warning:
            All previously present tracks in the playlist will be removed.

        Required token scopes:
            - playlist-modify-public: If the playlist is public.
            - playlist-modify-private: If the playlist is private or
              collaborative.

        Calls endpoints:
            - PUT /v1/playlists/{playlist_id}/tracks
        """
        endpoint = Endpoints.PLAYLIST_TRACKS % self.spotify_id()
        if not all([isinstance(track, Track) for track in tracks]):
            raise TypeError('All elements of tracks must be Track objects')
        body = {}
        body['uris'] = [track.uri() for track in tracks]
        response_json, status_code = utils.request(
            self._session,
            request_type='PUT',
            endpoint=endpoint,
            body=body
        )
        if status_code != 201:
            raise utils.SpotifyError(status_code, response_json)
Exemplo n.º 6
0
    def get_repeat(self):
        """ Get the repeat state for the current playback.

        Uses the currently active device, if one exists.

        Returns:
            One of

            - sp.TRACKS
            - sp.CONTEXT
            - sp.OFF

        Calls endpoints:
            - GET    /v1/me/player

        Required token scopes:
            - user-read-playback-state
        """
        result = self._player_data('repeat_state')

        states = {
            const.TRACKS: 'track',
            const.CONTEXT: 'context',
            const.OFF: 'off'
        }
        if result not in states:
            raise utils.SpotifyError('Repeat state <%s> not defined' % result)

        return states[result]
Exemplo n.º 7
0
    def update_description(self, description):
        """ Updates the playlist description as it appears on Spotify.

        Args:
            description (str): the new new description of this playlist.

        Required token scopes:
            - playlist-modify-public: If the playlist is public.
            - playlist-modify-private: If the playlist is private or
              collaborative.

        Calls endpoints:
            - PUT /v1/playlists/{playlist_id}
        """
        endpoint = Endpoints.PLAYLIST % self.spotify_id()
        if not isinstance(description, str):
            raise TypeError('The description must be a string')
        body = {}
        body['description'] = description
        response_json, status_code = utils.request(
            self._session,
            request_type='PUT',
            endpoint=endpoint,
            body=body
        )
        if status_code != 200:
            raise utils.SpotifyError(status_code, response_json)
Exemplo n.º 8
0
    def set_playback_position(self, position, device_id=None):
        """ Set the current position in the currently playing track in ms.

        Args:
            position (int): the position (in ms). Must be non-negative. If
                greater than the len of the track, will play the next song.

        Returns:
            None

        Calls endpoints:
            - PUT     /v1/me/player/seek

        Required token scopes:
            - user-modify-playback-state
        """
        if position < 0:
            raise ValueError(position)

        uri_params = {'position_ms': position}
        if device_id is not None:
            uri_params['device_id'] = device_id

        response_json, status_code = utils.request(
            self._session,
            request_type=const.REQUEST_PUT,
            endpoint=Endpoints.PLAYER_SEEK,
            body=None,
            uri_params=uri_params)

        if status_code != 204:
            raise utils.SpotifyError(status_code, response_json)
Exemplo n.º 9
0
    def set_shuffle(self, shuffle_state, device_id=None):
        """ Set the shuffle state of the active device.

        Args:
            shuffle_state (bool): True to set shuffle to on, False to set
                shuffle to off

        Returns:
            None

        Calls endpoints:
            - PUT     /v1/me/player/shuffle

        Required token scopes:
            - user-modify-playback-state
        """
        uri_params = {'state': shuffle_state}
        if device_id is not None:
            uri_params['device_id'] = device_id

        response_json, status_code = utils.request(
            self._session,
            request_type=const.REQUEST_PUT,
            endpoint=Endpoints.PLAYER_SHUFFLE,
            body=None,
            uri_params=uri_params)

        if status_code != 204:
            raise utils.SpotifyError(status_code, response_json)
Exemplo n.º 10
0
    def set_active_device(self, device_id, force_play=const.KEEP_PLAY_STATE):
        """ Transfer playback to a different available device.

        Args:
            force_play: one of:

                - sp.FORCE_PLAY: resume playback after transfering to new device
                - sp.KEEP_PLAY_STATE: keep the current playback state.

        Returns:
            None

        Calls endpoints:
            - PUT     /v1/me/player

        Required token scopes:
            - user-modify-playback-state
        """
        if force_play not in [const.FORCE_PLAY, const.KEEP_PLAY_STATE]:
            raise ValueError(force_play)

        body = {
            'device_ids': [device_id],
            'play': force_play == const.FORCE_PLAY
        }

        response_json, status_code = utils.request(
            self._session,
            request_type=const.REQUEST_PUT,
            endpoint=Endpoints.PLAYER_TRANSFER,
            body=body,
            uri_params=None)

        if status_code != 204:
            raise utils.SpotifyError(status_code, response_json)
Exemplo n.º 11
0
    def _update_fields(self):
        """ Update self._raw using the User id.

        Calls endpoints:
            GET     /v1/users/{id}
            GET     /v1/me

        Note:
            This method only includes private fields if those are allowed by the
            token scopes and the token was created by this user.

        Required token scopes:
            user-read-email: to read the email
            user-read-private: to read the country and subscription
        """
        other = self._session.current_user()
        if self == other:
            # We know other is a 'User' object, so this protected access is okay
            #pylint: disable=protected-access
            self._raw.update(other._raw)

            # other._raw has superset of data gotten below, skip extra api call
            return

        response_json, status_code = utils.request(
            session=self._session,
            request_type=const.REQUEST_GET,
            endpoint=Endpoints.USER_DATA % self.spotify_id())

        if status_code != 200:
            raise utils.SpotifyError(status_code, response_json)

        self._raw.update(response_json)
Exemplo n.º 12
0
    def _update_fields(self):
        """ If field is not present, update it using the object's artist id.

        Raises:
            ValueError if artist id not present in the raw object data.

        Calls endpoints:
            - GET     /v1/artists/{id}
        """
        endpoint = Endpoints.ARTIST_DATA % self.spotify_id()
        response_json, status_code = utils.request(
            session=self._session,
            request_type=const.REQUEST_GET,
            endpoint=endpoint,
        )

        if status_code != 200:
            raise utils.SpotifyError(status_code, response_json)

        # Updates _raw with new values. One liner : for each key in union of
        # keys in self._raw and response_json, takes value for key from
        # response_json if present, else takes value for key from self._raw.
        # TODO: this is weird notation, make a utility function for it.
        # Especially useful since it is an action necessary for many classes.
        self._raw = {**self._raw, **response_json}
Exemplo n.º 13
0
    def set_volume(self, volume, device_id=None):
        """ Set the current volume for the playback.

        Args:
            volume (int): volume (in percent) from 0 to 100 inclusive

        Returns:
            None

        Calls endpoints:
            - PUT     /v1/me/player/volume

        Required token scopes:
            - user-modify-playback-state
        """
        if volume < 0 or volume > 100:
            raise ValueError(volume)

        uri_params = {'volume_percent': volume}
        if device_id is not None:
            uri_params['device_id'] = device_id

        response_json, status_code = utils.request(
            self._session,
            request_type=const.REQUEST_PUT,
            endpoint=Endpoints.PLAYER_VOLUME,
            body=None,
            uri_params=uri_params)

        if status_code != 204:
            raise utils.SpotifyError(status_code, response_json)
Exemplo n.º 14
0
    def audio_analysis(self):
        #pylint: disable=line-too-long
        """ Get the audio analysis for this track.

        The Audio Analysis describes the track’s structure and musical content,
        including rhythm, pitch, and timbre. All information is precise to the
        audio sample.

        For more information on track audio analysis, see Spotify's
        `documentation <https://developer.spotify.com/documentation/web-api/reference/tracks/get-audio-analysis/>`__

        Returns:
            dict: a dictionary containing the audio analysis as defined at the
            above link.

        Calls endpoints:
            - GET     /v1/audio-analysis/{id}
        """
        response_json, status_code = utils.request(
            session=self._session,
            request_type=const.REQUEST_GET,
            endpoint=Endpoints.TRACK_ANALYSIS % self.spotify_id()
        )

        if status_code != 200:
            raise utils.SpotifyError(status_code, response_json)

        return response_json
Exemplo n.º 15
0
    def previous(self, device_id=None):
        """ Skip to the previous song in the playback.

        Note:
            Will skip to the previous song in the playback regardless of where
            in the current song playback is.

        Returns:
            None

        Calls endpoints:
            - POST    /v1/me/player/previous

        Required token scopes:
            - user-modify-playback-state
        """
        uri_params = None if device_id is None else {'device_id': device_id}

        response_json, status_code = utils.request(
            self._session,
            request_type=const.REQUEST_POST,
            endpoint=Endpoints.PLAYER_PREVIOUS,
            body=None,
            uri_params=uri_params)

        if status_code != 204:
            raise utils.SpotifyError(status_code, response_json)
Exemplo n.º 16
0
    def current_user(self):
        """
        Returns:
            User: The user associated with the current Spotify API token.

        Raises:
            ValueError: if the Spotify API key is not valid.
            ValueError: if the response is empty.
            HTTPError: if failure or partial failure.

        Calls endpoints:
            - GET /v1/me
        """

        # Construct params for API call
        endpoint = Endpoints.SEARCH_CURRENT_USER

        # Execute requests
        response_json, status_code = utils.request(
            session=self, request_type=const.REQUEST_GET, endpoint=endpoint)

        if status_code != 200:
            raise utils.SpotifyError(status_code, response_json)

        return User(self, response_json)
Exemplo n.º 17
0
    def resume(self, device_id=None):
        """ Resume the current playback.

        Returns:
            None

        Calls endpoints:
            - PUT     /v1/me/player/play

        Required token scopes:
            - user-modify-playback-state

        Raises:
            SpotifyError: if playback is already playing
        """
        uri_params = None if device_id is None else {'device_id': device_id}

        response_json, status_code = utils.request(
            self._session,
            request_type=const.REQUEST_PUT,
            endpoint=Endpoints.PLAYER_PLAY,
            body=None,
            uri_params=uri_params)

        if status_code != 204:
            raise utils.SpotifyError(status_code, response_json)
Exemplo n.º 18
0
    def update_visibility(self, visibility):
        """ Updates whether the playlist is public/private/collaborative.

        Args:
            visibility: One of:

                - sp.PUBLIC
                - sp.PRIVATE
                - sp.PRIVATE_COLLAB

        Required token scopes:
            - playlist-modify-public: If the playlist is public.
            - playlist-modify-private: If the playlist is private or
              collaborative.

        Calls endpoints:
            - PUT /v1/playlists/{playlist_id}
        """
        endpoint = Endpoints.PLAYLIST % self.spotify_id()
        body = {}
        if visibility not in [const.PUBLIC, const.PRIVATE,
                              const.PRIVATE_COLLAB]:
            raise ValueError('Invalid visibility, must be one of [sp.PUBLIC,' +
                             'sp.PRIVATE, sp.PRIVATE_COLLAB]')
        body['public'] = (visibility == const.PUBLIC)
        body['collaborative'] = (visibility == const.PRIVATE_COLLAB)
        response_json, status_code = utils.request(
            self._session,
            request_type='PUT',
            endpoint=endpoint,
            body=body
        )
        if status_code != 200:
            raise utils.SpotifyError(status_code, response_json)
Exemplo n.º 19
0
    def play(self, item, offset=0, device_id=None):
        """ Change the current track and context for the player.

        Args:
            item: an instance of:

                - sp.Track
                - sp.Album
                - sp.Playlist
                - or sp.Artist.

            offset (int): the position in item to start playback. Ignored if
                item is not an Album or Playlist. 0 <= offset < len(item).

        Note:
            Playback will start at the beginning of the track. Use in
            combination with Player.set_playback_position to start elsewhere in
            the track.

        Returns:
            None

        Calls endpoints:
            - PUT     /v1/me/player/play

        Required token scopes:
            - user-modify-playback-state
        """
        # Validate inputs
        if type(item) not in [Track, Album, Playlist, Artist]:
            raise TypeError(item)

        if type(item) in [Album, Playlist]:
            if offset < 0 or offset >= len(item):
                raise ValueError(offset)

        # Build up the request
        uri_params = None if device_id is None else {'device_id': device_id}
        if isinstance(item, Track):
            body = {'uris': [item.uri()]}
        else:
            body = {'context_uri': item.uri()}

        if type(item) in [Album, Playlist]:
            body['offset'] = {'position': offset}

        response_json, status_code = utils.request(
            self._session,
            request_type=const.REQUEST_PUT,
            endpoint=Endpoints.PLAYER_PLAY,
            body=body,
            uri_params=uri_params)

        if status_code != 204:
            raise utils.SpotifyError(status_code, response_json)
Exemplo n.º 20
0
    def get_artists(self, artist_ids):
        """ Gets the artists with the given Spotify ids.

        Args:
            artist_ids (str, List[str): The Spotify artist id(s) to get.

        Returns:
            Union[Album, List[Album]]: The requested artist(s).

        Raises:
            TypeError: for invalid types in any argument.
            HTTPError: if failure or partial failure.

        Calls endpoints:
            - GET   /v1/artists

        Note: the following endpoint is not used.
            - GET   /v1/artists/{id}
        """

        # Type validation
        if not isinstance(artist_ids, str) and\
            not all(isinstance(x, str) for x in artist_ids):
            raise TypeError('artist_ids should be str or list of str')

        if isinstance(artist_ids, str):
            artist_ids = list(artist_ids)

        # Construct params for API call
        endpoint = Endpoints.SEARCH_ALBUMS
        uri_params = dict()

        # A maximum of 50 artists can be returned per API call
        batches = utils.create_batches(artist_ids, 50)

        result = list()
        for batch in batches:
            uri_params['ids'] = batch

            # Execute requests
            response_json, status_code = utils.request(
                session=self,
                request_type=const.REQUEST_GET,
                endpoint=endpoint,
                uri_params=uri_params)

            if status_code != 200:
                raise utils.SpotifyError(status_code, response_json)

            items = response_json['artists']
            for item in items:
                result.append(Artist(self, item))

        return result if len(result) != 1 else result[0]
Exemplo n.º 21
0
    def _save_remove_help(self, other, request_type):
        """ save and remove are identical, except for the request type and
        return codes.

        This function implements that functionality to remove duplicate code.
        """
        # Validate input
        if not isinstance(other, list):
            other = [other]

        for elem in other:
            if type(elem) not in [Album, Track]:
                raise TypeError(elem)

        # Split up input
        albums = utils.separate(other, Album)
        tracks = utils.separate(other, Track)

        for batch in utils.create_batches(utils.map_ids(albums)):
            response_json, status_code = utils.request(
                self._session,
                request_type=request_type,
                endpoint=Endpoints.USER_SAVE_ALBUMS,
                body=None,
                uri_params={'ids': batch})

            # All success codes are 200, except saving an album
            success = 201 if request_type == const.REQUEST_PUT else 200
            if status_code != success:
                raise utils.SpotifyError(status_code, response_json)

        for batch in utils.create_batches(utils.map_ids(tracks)):
            response_json, status_code = utils.request(
                self._session,
                request_type=request_type,
                endpoint=Endpoints.USER_SAVE_TRACKS,
                body=None,
                uri_params={'ids': batch})

            if status_code != 200:
                raise utils.SpotifyError(status_code, response_json)
Exemplo n.º 22
0
    def add_tracks(self, tracks, position=None):
        """ Adds one or more tracks to the playlist.

        Args:
            tracks: A Track object or list of Track objects to be added.

            position: An integer specifying the 0-indexed position in the
                playlist to insert tracks. A negative integer will be evaluated
                from the end of the playlist as negative indices behave in
                lists. This must be a valid index into a list of length
                len(playlist). Position can be omitted to append to the
                playlist instead.

        Required token scopes:
            - playlist-modify-public: If the playlist is public.
            - playlist-modify-private: If the playlist is private or
              collaborative.

        Calls endpoints:
            - POST /v1/playlists/{playlist_id}/tracks
        """
        endpoint = Endpoints.PLAYLIST_TRACKS % self.spotify_id()
        body = {}
        uris = []
        if isinstance(tracks, list):
            for track in tracks:
                if not isinstance(track, Track):
                    raise TypeError('The elements of tracks must be Track ' +
                                    'objects')
                uris.append(track.uri())
        else:
            if not isinstance(tracks, Track):
                raise TypeError('The type of tracks must either be a Track ' +
                                'object or a list of Track objects')
            uris.append(tracks.uri())
        body['uris'] = uris
        if position:
            if not isinstance(position, int):
                raise TypeError('The position must be an integer')
            original_position = position
            if position < 0:
                position += len(self)
            if position < 0 or position >= len(self):
                raise ValueError(f'Invalid position: {original_position}')
            body['position'] = position
        response_json, status_code = utils.request(
            self._session,
            request_type='POST',
            endpoint=endpoint,
            body=body
        )
        if status_code != 201:
            raise utils.SpotifyError(status_code, response_json)
Exemplo n.º 23
0
    def create_playlist(self, name, visibility=const.PUBLIC, description=None):
        """ Create a new playlist owned by the current user.

        Args:
            name (str): The name for the new playlist. Does not need to be
                unique; a user may have several playlists with the same name.
            visibility: How other users interact with this playlist. One of:

                    - sp.PUBLIC: publicly viewable, not collaborative
                    - sp.PRIVATE: not publicly viewable, not collaborative
                    - sp.PRIVATE_COLLAB: not publicly viewable, collaborative

            description (str): The viewable description of the playlist.

        Returns:
            Playlist: The newly created Playlist object. Note that this function
            modifies the user's library.

        Required token scopes:
            - playlist-modify-public
            - playlist-modify-private

        Calls endpoints:
            - POST    /v1/users/{user_id}/playlists
        """
        # Validate inputs
        if visibility not in [
                const.PUBLIC, const.PRIVATE, const.PRIVATE_COLLAB
        ]:
            raise TypeError(visibility)

        body = {
            'name': name,
            'public': visibility == const.PUBLIC,
            'collaborative': visibility == const.PRIVATE_COLLAB
        }

        if description is not None:
            body['description'] = description

        response_json, status_code = utils.request(
            self._session,
            request_type=const.REQUEST_POST,
            endpoint=Endpoints.USER_CREATE_PLAYLIST % self.spotify_id(),
            body=body,
            uri_params=None)

        if status_code != 201:
            raise utils.SpotifyError(status_code, response_json)

        return Playlist(self._session, response_json)
Exemplo n.º 24
0
    def context(self, market=const.TOKEN_REGION):
        """ Get the currently playing context for the playback.

        Uses the currently active device, if one exists.

        Args:
            market: see the :class:`shared args documentation <Player>`

        Returns:
            An Album, Artist, or Playlist if there is a context for the
            playback, else None if there is no context

        Calls endpoints:
            - GET    /v1/me/player

        Required token scopes:
            - user-read-playback-state
        """
        context = self._player_data('context', market)
        if context is None:
            return None

        # Validate context
        if 'uri' not in context or 'type' not in context:
            raise utils.SpotifyError(KEYSTRING)

        # uri of form 'spotify:type:id'
        context_id = context['uri'].split(':')[-1]

        # Context can only be one of Artist, Album, or Playlist
        if context['type'] == 'album':
            return self._session.get_albums(context_id)
        if context['type'] == 'artist':
            return self._session.get_artists(context_id)
        if context['type'] == 'playlist':
            return self._session.get_playlists(context_id)

        raise utils.SpotifyError('Unrecognized context: %s' % str(context))
Exemplo n.º 25
0
    def image(self):
        """ Get the the playlist cover image.

        Returns:
            Image: an image object if the Playlist has a cover image.
            None: if the Playlist has no cover image.

        Calls endpoints:
            - PUT /v1/playlists/{playlist_id}
        """
        endpoint = Endpoints.PLAYLIST_IMAGES % self.spotify_id()
        response_json, status_code = utils.request(
            self._session,
            request_type='GET',
            endpoint=endpoint
        )
        if status_code != 200:
            raise utils.SpotifyError(status_code, response_json)

        if len(response_json) > 1:
            raise utils.SpotifyError('Playlist has more than one cover image!')

        return None if len(response_json) == 0 else Image(response_json[0])
Exemplo n.º 26
0
    def enqueue(self, item, device_id=None):
        """ Add an item to the end of the queue.

        Args:
            item: the item to add to the queue. One of:
                - Album
                - Track
                - Playlist

        Returns:
            None

        Note:
            If a playlist is added, the order of added songs may be
            inconsistent.

        Note:
            When adding an Album or Playlist, this method can fail partway
            through, resulting in only some Tracks being added to the queue.

        Calls endpoints:
            - POST    /v1/me/player/queue

        Required token scopes:
            - user-modify-playback-state
        """
        if type(item) not in [Album, Track, Playlist]:
            raise ValueError(item)

        # Make into an iterable
        if isinstance(item, Track):
            item = [item]

        uri_params = {} if device_id is None else {'device_id': device_id}

        # Can only enqueue one item at a time
        for track in item:
            uri_params['uri'] = track.uri()

            response_json, status_code = utils.request(
                self._session,
                request_type=const.REQUEST_POST,
                endpoint=Endpoints.PLAYER_QUEUE,
                body=None,
                uri_params=uri_params)

            if status_code != 204:
                raise utils.SpotifyError(status_code, response_json)
Exemplo n.º 27
0
    def image(self):
        """ Get the User's profile picture.

        Returns:
            Union[Image, None]: An image object if the User has a profile
            picture, else None.

        Calls endpoints:
            - GET     /v1/users/{id}
        """
        result = utils.get_field(self, 'images')

        if len(result) > 1:
            raise utils.SpotifyError('User has more than one profile picture!')

        return None if len(result) == 0 else Image(result[0])
Exemplo n.º 28
0
    def get_users(self, user_ids):
        """ Gets the users with the given Spotify ids.

        Args:
            user_ids (str, List[str]): The Spotify user id(s) to get.

        Returns:
            Union[User, List[User]]: The requested user(s).

        Raises:
            TypeError: for invalid types in any argument.
            HTTPError: if failure or partial failure.

        Calls endpoints:
            - GET	/v1/users/{user_id}
        """

        # Type validation
        if not isinstance(user_ids, str) and\
            not all(isinstance(x, str) for x in user_ids):
            raise TypeError('user_ids should be str or list of str')

        if isinstance(user_ids, str):
            user_ids = list('user_ids should be str')

        # Construct params for API call
        uri_params = dict()

        # Each API call can return at most 1 user. Therefore there is no need
        # to batch this query.
        result = list()
        for user_id in user_ids:
            # Execute requests
            # TODO: Partial failure - if user with user_id does not exist,
            # status_code is 404
            response_json, status_code = utils.request(
                session=self,
                request_type=const.REQUEST_GET,
                endpoint=Endpoints.SEARCH_USER % user_id,
                uri_params=uri_params)

            if status_code != 200:
                raise utils.SpotifyError(status_code, response_json)

            result.append(User(self, response_json))

        return result if len(result) != 1 else result[0]
Exemplo n.º 29
0
    def get_volume(self):
        """ Get the current volume for the playback.

        Returns:
            int: The volume (in percent) from 0 to 100 inclusive.

        Calls endpoints:
            - GET    /v1/me/player

        Required token scopes:
            - user-read-playback-state
        """
        device = self._player_data('device')
        if 'volume_percent' not in device:
            raise utils.SpotifyError(KEYSTRING +
                                     ': key volume_percent not found')

        return device['volume_percent']
Exemplo n.º 30
0
    def recently_played(self, limit=50):
        """
        Args:
            limit (int): Max number of items to return. Must be between 1 and
                50, inclusive.

        Returns:
            List[Tracks]: The user's recently played tracks. Could be empty.

        Required token scopes:
            - user-read-recently-played

        Calls endpoints:
            - GET     /v1/me/player/recently-played

        Note:
            - The 'before' and 'after' functionalities are not supported.
            - Does not return the time the tracks were played
            - A track must be played for >30s to be included in the history.
              Tracks played while in a 'private session' are not recorded.
        """
        # Validate arguments
        if limit <= 0 or limit > 50:
            raise ValueError(limit)

        # Execute requests
        response_json, status_code = utils.request(
            self._session,
            request_type=const.REQUEST_GET,
            endpoint=Endpoints.USER_RECENTLY_PLAYED,
            body=None,
            uri_params={'limit': limit})

        if status_code != 200:
            raise utils.SpotifyError(status_code, response_json)

        results = []
        for elem in response_json['items']:
            results.append(Track(self._session, elem))

        return results