Example #1
0
    def search(self,
               query_types: List[Uri.ObjectType],
               query: str = None,
               track: str = None,
               album: str = None,
               artist: str = None,
               response_limit: int = 20) -> SearchResponse:

        if query is None and track is None and album is None and artist is None:
            raise ValueError('no query parameters')

        queries = []

        if query is not None:
            queries.append(query)
        if track is not None:
            queries.append(f'track:{track}')
        if album is not None:
            queries.append(f'album:{album}')
        if artist is not None:
            queries.append(f'artist:{artist}')

        logger.info(f'querying track: {track}, album: {album}, artist: {artist}')

        resp = self.get_request(url='search',
                                q=' '.join(queries),
                                type=','.join([i.name for i in query_types]),
                                limit=response_limit)

        albums = [init_with_key_filter(SimplifiedAlbum, i) for i in resp.get('albums', {}).get('items', [])]
        artists = [init_with_key_filter(ArtistFull, i) for i in resp.get('artists', {}).get('items', [])]
        tracks = [init_with_key_filter(TrackFull, i) for i in resp.get('tracks', {}).get('items', [])]
        playlists = [init_with_key_filter(SimplifiedPlaylist, i) for i in resp.get('playlists', {}).get('items', [])]

        return SearchResponse(tracks=tracks, albums=albums, artists=artists, playlists=playlists)
Example #2
0
    def playlist(self,
                 uri: Uri,
                 tracks: bool = True) -> FullPlaylist:
        """get playlist object with tracks for uri

        :param uri: target request uri
        :param tracks: populate tracks of playlist during generation
        :return: playlist object
        """

        logger.info(f"retrieving {uri}")

        resp = self.get_request(f'playlists/{uri.object_id}')
        playlist = init_with_key_filter(FullPlaylist, resp)

        if resp.get('tracks') and tracks:
            if 'next' in resp['tracks']:
                logger.debug(f'paging tracks for {uri}')

                track_pager = PageCollection(net=self, page=resp['tracks'])
                track_pager.continue_iteration()

                playlist.tracks = [init_with_key_filter(PlaylistTrack, i) for i in track_pager.items]
            else:
                logger.debug(f'parsing {len(resp.get("tracks"))} tracks for {uri}')
                playlist.tracks = [init_with_key_filter(PlaylistTrack, i) for i in resp.get('tracks', [])]

        return playlist
Example #3
0
    def __post_init__(self):
        if isinstance(self.track, dict):

            # below seems more intuitive, currently parsing episode to track/album/artist structure for
            # serialising over api, below could be implemented

            # obj_type = None
            # if self.track['type'] == 'track':
            #     obj_type = TrackFull
            #
            # if self.track['type'] == 'episode':
            #     obj_type = EpisodeFull
            #
            # if obj_type is None:
            #     raise TypeError(f'unkown obj type found {self.track["type"]}')

            obj_type = TrackFull

            self.track = init_with_key_filter(obj_type, self.track)

        if isinstance(self.added_by, dict):
            self.added_by = init_with_key_filter(
                spotframework.model.user.PublicUser, self.added_by)

        if isinstance(self.added_at, str):
            self.added_at = datetime.strptime(self.added_at,
                                              '%Y-%m-%dT%H:%M:%S%z')
Example #4
0
    def __post_init__(self):

        if isinstance(self.uri, str):
            self.uri = Uri(self.uri)

        if self.uri:
            if self.uri.object_type != Uri.ObjectType.episode:
                raise TypeError('provided uri not for an episode')

        if isinstance(self.resume_point, ResumePoint):
            self.resume_point = init_with_key_filter(ResumePoint,
                                                     self.resume_point)

        if all((isinstance(i, dict) for i in self.images)):
            self.images = [init_with_key_filter(Image, i) for i in self.images]

        if isinstance(self.release_date, str):
            if self.release_date_precision == 'year':
                self.release_date = datetime.strptime(self.release_date, '%Y')
            elif self.release_date_precision == 'month':
                self.release_date = datetime.strptime(self.release_date,
                                                      '%Y-%m')
            elif self.release_date_precision == 'day':
                self.release_date = datetime.strptime(self.release_date,
                                                      '%Y-%m-%d')
Example #5
0
 def __post_init__(self):
     if isinstance(self.context, dict):
         self.context = init_with_key_filter(Context, self.context)
     if isinstance(self.track, dict):
         self.track = init_with_key_filter(TrackFull, self.track)
     if isinstance(self.played_at, str):
         self.played_at = datetime.strptime(self.played_at,
                                            '%Y-%m-%dT%H:%M:%S%z')
Example #6
0
    def __post_init__(self):
        if isinstance(self.context, Context):
            self.context = init_with_key_filter(Context, self.context)

        if isinstance(self.item, spotframework.model.track.SimplifiedTrack):
            self.item = init_with_key_filter(
                spotframework.model.track.SimplifiedTrack, self.item)

        if isinstance(self.device, Device):
            self.device = init_with_key_filter(Device, self.device)
Example #7
0
    def __post_init__(self):
        if all((isinstance(i, dict) for i in self.seeds)):
            self.seeds = [
                init_with_key_filter(RecommendationsSeed, i)
                for i in self.seeds
            ]

        if all((isinstance(i, dict) for i in self.tracks)):
            self.tracks = [
                init_with_key_filter(spotframework.model.track.TrackFull, i)
                for i in self.tracks
            ]
Example #8
0
    def __post_init__(self):

        if isinstance(self.album_type, str):
            self.album_type = SimplifiedAlbum.Type[
                self.album_type.strip().lower()]

        if isinstance(self.uri, str):
            self.uri = Uri(self.uri)

        if self.uri:
            if self.uri.object_type not in [
                    Uri.ObjectType.album, Uri.ObjectType.show
            ]:
                raise TypeError('provided uri not for an album')

        if all((isinstance(i, dict) for i in self.artists)):
            self.artists = [
                init_with_key_filter(
                    spotframework.model.artist.SimplifiedArtist, i)
                for i in self.artists
            ]

        if all((isinstance(i, dict) for i in self.images)):
            self.images = [
                init_with_key_filter(spotframework.model.service.Image, i)
                for i in self.images
            ]

        if isinstance(self.release_date, str):
            try:
                if self.release_date_precision == 'year':
                    self.release_date = datetime.strptime(
                        self.release_date, '%Y')
                elif self.release_date_precision == 'month':
                    self.release_date = datetime.strptime(
                        self.release_date, '%Y-%m')
                elif self.release_date_precision == 'day':
                    self.release_date = datetime.strptime(
                        self.release_date, '%Y-%m-%d')
                else:
                    logger.error(
                        f'invalid release date type {self.release_date_precision} - {self.release_date}'
                    )
            except ValueError:
                self.release_date = datetime(year=1900, month=1, day=1)
                logger.error(
                    f'failed to parse release date for album {self.uri} {self.name} {self.artists_names} {self.release_date_precision} - {self.release_date}'
                )

        elif self.release_date is None and self.release_date_precision is None:  # for podcasts
            self.release_date = datetime(year=1900, month=1, day=1)
Example #9
0
    def create_playlist(self,
                        username: str,
                        name: str = 'New Playlist',
                        public: bool = True,
                        collaborative: bool = False,
                        description: str = None) -> FullPlaylist:
        """create playlist for user

        :param username: username for playlist creation
        :param name: new playlist name
        :param public: make playlist public
        :param collaborative: make playlist collaborative
        :param description: description for new playlist
        :return: newly created playlist object
        """
        logger.info(f'creating {name} for {username}, '
                    f'public: {public}, collaborative: {collaborative}, description: {description}')

        if collaborative and public:
            public = False
            logger.warning(f'public collaborative playlist requested, defaulting to private {username} / {name}')

        req = self.post_request(f'users/{username}/playlists',
                                name=name,
                                public=public,
                                collaborative=collaborative,
                                description=description)
        return init_with_key_filter(FullPlaylist, req)
Example #10
0
    def __post_init__(self):
        if isinstance(self.tracks, dict):
            self.tracks = []

        if isinstance(self.uri, str):
            self.uri = Uri(self.uri)

        if self.uri:
            if self.uri.object_type != Uri.ObjectType.playlist:
                raise TypeError('provided uri not for a playlist')

        if all((isinstance(i, dict) for i in self.images)):
            self.images = [init_with_key_filter(Image, i) for i in self.images]

        if isinstance(self.owner, dict):
            self.owner = init_with_key_filter(PublicUser, self.owner)
Example #11
0
    def recently_played_tracks(self,
                               response_limit: int = None,
                               after: datetime.datetime = None,
                               before: datetime.datetime = None) -> Optional[List[PlayedTrack]]:
        """get list of recently played tracks

        :param response_limit: max number of tracks to return
        :param after: datetime after which to return tracks
        :param before: datetime before which to return tracks
        :return: list of recently played tracks if available
        """

        logger.info(f"paging {'all' if response_limit is None else response_limit} recent tracks ({after}/{before})")

        params = dict()
        if after and before:
            raise ValueError('cant have before and after')
        if after:
            params['after'] = int(after.timestamp() * 1000)
        if before:
            params['before'] = int(before.timestamp() * 1000)

        resp = self.get_request('me/player/recently-played', params=params)

        pager = PageCollection(self, page=resp)
        if response_limit:
            pager.total_limit = response_limit
        else:
            pager.total_limit = 20
        pager.continue_iteration()

        return [init_with_key_filter(PlayedTrack, i) for i in pager.items]
Example #12
0
    def player(self) -> CurrentlyPlaying:
        """get currently playing snapshot (player)"""

        logger.info("polling player")

        resp = self.get_request('me/player')
        return init_with_key_filter(CurrentlyPlaying, resp)
Example #13
0
    def __post_init__(self):
        if isinstance(self.album, dict):
            self.album = init_with_key_filter(AlbumFull, self.album)

        if isinstance(self.added_at, str):
            self.added_at = datetime.strptime(self.added_at,
                                              '%Y-%m-%dT%H:%M:%S%z')
Example #14
0
    def playlist_tracks(self,
                        uri: Uri,
                        response_limit: int = None,
                        reduced_mem: bool = False) -> List[PlaylistTrack]:
        """get list of playlists tracks for uri

        :param uri: target playlist uri
        :param response_limit: max tracks to return
        :return: list of playlist tracks if available
        """

        logger.info(f"paging tracks for {uri}")

        pager = PageCollection(net=self, url=f'playlists/{uri.object_id}/tracks', name='getPlaylistTracks')
        if response_limit:
            pager.total_limit = response_limit
        pager.iterate()

        result = pager.items

        if reduced_mem:
            result = [filter_response(i, Network.unneeded_keys) for i in result]

        return_items = [init_with_key_filter(PlaylistTrack, i) for i in result]

        if len(return_items) == 0:
            logger.error('no tracks returned')

        return return_items
Example #15
0
    def __post_init__(self):
        super().__post_init__()

        if all((isinstance(i, dict) for i in self.tracks)):
            self.tracks = [
                init_with_key_filter(spotframework.model.track.SimplifiedTrack,
                                     i) for i in self.tracks
            ]
Example #16
0
    def show(self, uri, episodes: bool = True) -> Optional[ShowFull]:

        logger.info(f"retrieving {uri}")

        resp = self.get_request(f'shows/{uri.object_id}')
        show = init_with_key_filter(ShowFull, resp)

        if resp.get('episodes') and episodes:
            if 'next' in resp['episodes']:
                logger.debug(f'paging episodes for {uri}')

                track_pager = PageCollection(net=self, page=resp['episodes'])
                track_pager.continue_iteration()

                show.episodes = [init_with_key_filter(SimplifiedEpisode, i) for i in track_pager.items]
            else:
                logger.debug(f'parsing {len(resp.get("episodes"))} tracks for {uri}')
                show.episodes = [init_with_key_filter(SimplifiedEpisode, i) for i in resp.get('episodes', [])]
        return show
Example #17
0
    def available_devices(self) -> List[Device]:
        """get users available devices"""

        logger.info("polling available devices")

        resp = self.get_request('me/player/devices')

        if len(resp['devices']) == 0:
            logger.error('no devices returned')
        return [init_with_key_filter(Device, i) for i in resp['devices']]
Example #18
0
    def __post_init__(self):
        if isinstance(self.uri, str):
            self.uri = Uri(self.uri)

        if self.uri:
            if self.uri.object_type != Uri.ObjectType.user:
                raise TypeError('provided uri not for a user')

        if all((isinstance(i, dict) for i in self.images)):
            self.images = [init_with_key_filter(Image, i) for i in self.images]
Example #19
0
    def tracks(self, uris: List[Uri]) -> List[TrackFull]:

        logger.info(f'getting {len(uris)} tracks')

        tracks = []
        chunked_uris = list(self.chunk(uris, 50))
        for chunk in chunked_uris:
            resp = self.get_request(url='tracks', ids=','.join([i.object_id for i in chunk]))
            if resp:
                tracks += [init_with_key_filter(TrackFull, i) for i in resp.get('tracks', [])]

        return tracks
Example #20
0
    def episodes(self, uris) -> List[EpisodeFull]:

        logger.info(f'getting {len(uris)} episodes')

        episodes = []
        chunked_uris = list(self.chunk(uris, 50))
        for chunk in chunked_uris:
            resp = self.get_request(url='episodes', ids=','.join([i.object_id for i in chunk]))
            if resp:
                episodes += [init_with_key_filter(EpisodeFull, i) for i in resp.get('episodes', [])]

        return episodes
Example #21
0
    def shows(self, uris) -> List[SimplifiedShow]:

        logger.info(f'getting {len(uris)} shows')

        shows = []
        chunked_uris = list(self.chunk(uris, 50))
        for chunk in chunked_uris:
            resp = self.get_request(url='shows', ids=','.join([i.object_id for i in chunk]))
            if resp:
                shows += [init_with_key_filter(SimplifiedShow, i) for i in resp.get('shows', [])]

        return shows
Example #22
0
    def artists(self, uris) -> List[ArtistFull]:

        logger.info(f'getting {len(uris)} artists')

        artists = []
        chunked_uris = list(self.chunk(uris, 50))
        for chunk in chunked_uris:
            resp = self.get_request(url='artists', ids=','.join([i.object_id for i in chunk]))
            if resp:
                artists += [init_with_key_filter(ArtistFull, i) for i in resp.get('artists', [])]

        return artists
Example #23
0
    def __post_init__(self):
        if isinstance(self.uri, str):
            self.uri = Uri(self.uri)

        if self.uri:
            if self.uri.object_type not in [
                    Uri.ObjectType.track, Uri.ObjectType.episode
            ]:
                raise TypeError('provided uri not for a track')

        if all((isinstance(i, dict) for i in self.artists)):
            self.artists = [
                init_with_key_filter(
                    spotframework.model.artist.SimplifiedArtist, i)
                for i in self.artists
            ]
Example #24
0
    def track_audio_features(self, uris: List[Uri]) -> Optional[List[AudioFeatures]]:
        logger.info(f'getting {len(uris)} features')

        audio_features = []
        chunked_uris = list(self.chunk(uris, 100))
        for chunk in chunked_uris:
            resp = self.get_request(url='audio-features', ids=','.join(i.object_id for i in chunk))

            if resp.get('audio_features', None):
                return [init_with_key_filter(AudioFeatures, i) for i in resp['audio_features']]
            else:
                logger.error('no audio features included')

        if len(audio_features) == len(uris):
            return audio_features
        else:
            logger.error('mismatched length of input and response')
Example #25
0
    def saved_tracks(self, response_limit: int = None) -> Optional[List[LibraryTrack]]:
        """get user library tracks

        :param response_limit: max tracks to return
        :return: List of saved library trakcs if available
        """

        logger.info(f"paging library tracks")

        pager = PageCollection(net=self, url='me/tracks', name='getLibraryTracks')
        if response_limit:
            pager.total_limit = response_limit
        pager.iterate()

        return_items = [init_with_key_filter(LibraryTrack, i) for i in pager.items]

        if len(return_items) == 0:
            logger.error('no tracks returned')

        return return_items
Example #26
0
    def playlists(self, response_limit: int = None) -> Optional[List[SimplifiedPlaylist]]:
        """get current users playlists

        :param response_limit: max playlists to return
        :return: List of user created and followed playlists if available
        """

        logger.info(f"paging playlists")

        pager = PageCollection(net=self, url='me/playlists', name='getPlaylists')
        if response_limit:
            pager.total_limit = response_limit
        pager.iterate()

        return_items = [init_with_key_filter(SimplifiedPlaylist, i) for i in pager.items]

        if len(return_items) == 0:
            logger.error('no playlists returned')

        return return_items
Example #27
0
    def recommendations(self,
                        tracks: List[str] = None,
                        artists: List[str] = None,
                        response_limit=10) -> Optional[Recommendations]:

        logger.info(f'getting {response_limit} recommendations, '
                    f'tracks: {len(tracks) if tracks is not None else 0}, '
                    f'artists: {len(artists) if artists is not None else 0}')

        params = {'limit': response_limit}

        if tracks:
            random.shuffle(tracks)
            params['seed_tracks'] = tracks[:5]
        if artists:
            random.shuffle(artists)
            params['seed_artists'] = artists[:5]

        if len(params) == 1:
            logger.warning('update dictionairy length 0')
        else:
            return init_with_key_filter(Recommendations, self.get_request('recommendations', params=params))
Example #28
0
    def show_episodes(self,
                      uri: Uri,
                      response_limit: int = None) -> List[SimplifiedEpisode]:
        """get list of shows episodes for uri

        :param uri: target show uri
        :param response_limit: max episodes to return
        :return: list of show episodes if available
        """

        logger.info(f"paging episodes for {uri}")

        pager = PageCollection(net=self, url=f'shows/{uri.object_id}/episodes', name='getShowEpisodes')
        if response_limit:
            pager.total_limit = response_limit
        pager.iterate()

        return_items = [init_with_key_filter(SimplifiedEpisode, i) for i in pager.items]

        if len(return_items) == 0:
            logger.error('no episodes returned')

        return return_items
Example #29
0
    def episode(self, uri) -> EpisodeFull:

        logger.info(f"retrieving {uri}")

        resp = self.get_request(f'episodes/{uri.object_id}')
        return init_with_key_filter(EpisodeFull, resp)
Example #30
0
    def current_user(self) -> PublicUser:
        logger.info(f"getting current user")

        resp = self.get_request('me')
        return init_with_key_filter(PublicUser, resp)