Ejemplo n.º 1
0
def get_new_releases(
    sp: Spotify,
    artists: List[Dict[str, Any]],
    date: Optional[datetime.datetime] = None,
    weeks: int = 4,
    album_type: str = "album",
    country: str = "FR",
) -> List[Dict[str, Any]]:
    """Returns a list of released albums from the given artists
    since the a given date (first choice) or during a given interval"""
    if date is None:
        date = datetime.datetime.now() - datetime.timedelta(weeks=weeks)
    new_releases = []
    click.echo(f"Fetching from {date.strftime('%Y-%m-%d')}")
    with click.progressbar(artists, label="Fetching new releases") as progress_bar:
        for artist in progress_bar:
            results = sp.artist_albums(
                artist["id"], album_type=album_type, country=country, limit=1
            )["items"]
            if len(results) == 0:
                continue
            # only get last album
            album = results[0]
            release_date = parse_release_date(album["release_date"])
            if release_date > date:
                new_releases.append(album)
    return new_releases
Ejemplo n.º 2
0
def get_artist_albums(
    sp: Spotify,
    artist_id: str,
    album_type: str = "album",
    country: str = "FR",
    limit: int = 20,
) -> List[Dict[str, Any]]:
    """Returns the albums of an artist given its ID or URI"""
    return sp.artist_albums(artist_id, album_type=album_type, country=country)["items"]
Ejemplo n.º 3
0
class TestSpotipy(unittest.TestCase):
    """
    These tests require user authentication - provide client credentials using the
    following environment variables

    ::

        'SPOTIPY_CLIENT_USERNAME'
        'SPOTIPY_CLIENT_ID'
        'SPOTIPY_CLIENT_SECRET'
        'SPOTIPY_REDIRECT_URI'
    """

    creep_urn = 'spotify:track:3HfB5hBU0dmBt8T0iCmH42'
    creep_id = '3HfB5hBU0dmBt8T0iCmH42'
    creep_url = 'http://open.spotify.com/track/3HfB5hBU0dmBt8T0iCmH42'
    el_scorcho_urn = 'spotify:track:0Svkvt5I79wficMFgaqEQJ'
    el_scorcho_bad_urn = 'spotify:track:0Svkvt5I79wficMFgaqEQK'
    pinkerton_urn = 'spotify:album:04xe676vyiTeYNXw15o9jT'
    weezer_urn = 'spotify:artist:3jOstUTkEu2JkjvRdBA5Gu'
    pablo_honey_urn = 'spotify:album:6AZv3m27uyRxi8KyJSfUxL'
    radiohead_urn = 'spotify:artist:4Z8W4fKeB5YxbusRsdQVPb'
    angeles_haydn_urn = 'spotify:album:1vAbqAeuJVWNAe7UR00bdM'

    bad_id = 'BAD_ID'

    @classmethod
    def setUpClass(self):
        missing = list(filter(lambda var: not os.getenv(CCEV[var]), CCEV))

        if missing:
            raise Exception(
                'Please set the client credentials for the test application using the following environment variables: {}'
                .format(CCEV.values()))

        self.username = os.getenv(CCEV['client_username'])

        self.scope = 'user-library-read'

        self.token = prompt_for_user_token(self.username, scope=self.scope)

        self.spotify = Spotify(auth=self.token)

    def test_artist_urn(self):
        artist = self.spotify.artist(self.radiohead_urn)
        self.assertTrue(artist['name'] == 'Radiohead')

    def test_artists(self):
        results = self.spotify.artists([self.weezer_urn, self.radiohead_urn])
        self.assertTrue('artists' in results)
        self.assertTrue(len(results['artists']) == 2)

    def test_album_urn(self):
        album = self.spotify.album(self.pinkerton_urn)
        self.assertTrue(album['name'] == 'Pinkerton')

    def test_album_tracks(self):
        results = self.spotify.album_tracks(self.pinkerton_urn)
        self.assertTrue(len(results['items']) == 10)

    def test_album_tracks_many(self):
        results = self.spotify.album_tracks(self.angeles_haydn_urn)
        tracks = results['items']
        total, received = results['total'], len(tracks)
        while received < total:
            results = self.spotify.album_tracks(self.angeles_haydn_urn,
                                                offset=received)
            tracks.extend(results['items'])
            received = len(tracks)

        self.assertEqual(received, total)

    def test_albums(self):
        results = self.spotify.albums(
            [self.pinkerton_urn, self.pablo_honey_urn])
        self.assertTrue('albums' in results)
        self.assertTrue(len(results['albums']) == 2)

    def test_track_urn(self):
        track = self.spotify.track(self.creep_urn)
        self.assertTrue(track['name'] == 'Creep')

    def test_track_id(self):
        track = self.spotify.track(self.creep_id)
        self.assertTrue(track['name'] == 'Creep')

    def test_track_url(self):
        track = self.spotify.track(self.creep_url)
        self.assertTrue(track['name'] == 'Creep')

    def test_track_bad_urn(self):
        try:
            track = self.spotify.track(self.el_scorcho_bad_urn)
            self.assertTrue(False)
        except SpotifyException:
            self.assertTrue(True)

    def test_tracks(self):
        results = self.spotify.tracks([self.creep_url, self.el_scorcho_urn])
        self.assertTrue('tracks' in results)
        self.assertTrue(len(results['tracks']) == 2)

    def test_artist_top_tracks(self):
        results = self.spotify.artist_top_tracks(self.weezer_urn)
        self.assertTrue('tracks' in results)
        self.assertTrue(len(results['tracks']) == 10)

    def test_artist_related_artists(self):
        results = self.spotify.artist_related_artists(self.weezer_urn)
        self.assertTrue('artists' in results)
        self.assertTrue(len(results['artists']) == 20)
        for artist in results['artists']:
            if artist['name'] == 'Jimmy Eat World':
                found = True
        self.assertTrue(found)

    def test_artist_search(self):
        results = self.spotify.search(q='weezer', type='artist')
        self.assertTrue('artists' in results)
        self.assertTrue(len(results['artists']['items']) > 0)
        self.assertTrue(results['artists']['items'][0]['name'] == 'Weezer')

    def test_artist_search_with_market(self):
        results = self.spotify.search(q='weezer', type='artist', market='GB')
        self.assertTrue('artists' in results)
        self.assertTrue(len(results['artists']['items']) > 0)
        self.assertTrue(results['artists']['items'][0]['name'] == 'Weezer')

    def test_artist_albums(self):
        results = self.spotify.artist_albums(self.weezer_urn)
        self.assertTrue('items' in results)
        self.assertTrue(len(results['items']) > 0)

        found = False
        for album in results['items']:
            if album['name'] == 'Hurley':
                found = True

        self.assertTrue(found)

    def test_search_timeout(self):
        sp = Spotify(auth=self.token, requests_timeout=.01)
        try:
            results = sp.search(q='my*', type='track')
            self.assertTrue(False, 'unexpected search timeout')
        except requests.Timeout:
            self.assertTrue(True, 'expected search timeout')

    def test_album_search(self):
        results = self.spotify.search(q='weezer pinkerton', type='album')
        self.assertTrue('albums' in results)
        self.assertTrue(len(results['albums']['items']) > 0)
        self.assertTrue(
            results['albums']['items'][0]['name'].find('Pinkerton') >= 0)

    def test_track_search(self):
        results = self.spotify.search(q='el scorcho weezer', type='track')
        self.assertTrue('tracks' in results)
        self.assertTrue(len(results['tracks']['items']) > 0)
        self.assertTrue(results['tracks']['items'][0]['name'] == 'El Scorcho')

    def test_user(self):
        user = self.spotify.user(user='******')
        self.assertTrue(user['uri'] == 'spotify:user:plamere')

    def test_track_bad_id(self):
        try:
            track = self.spotify.track(self.bad_id)
            self.assertTrue(False)
        except SpotifyException:
            self.assertTrue(True)

    def test_track_bad_id(self):
        try:
            track = self.spotify.track(self.bad_id)
            self.assertTrue(False)
        except SpotifyException:
            self.assertTrue(True)

    def test_unauthenticated_post_fails(self):
        with self.assertRaises(SpotifyException) as cm:
            self.spotify.user_playlist_create("spotify",
                                              "Best hits of the 90s")
        self.assertTrue(cm.exception.http_status == 401
                        or cm.exception.http_status == 403)

    def test_custom_requests_session(self):
        sess = requests.Session()
        sess.headers["user-agent"] = "spotipy-test"
        with_custom_session = Spotify(auth=self.token, requests_session=sess)
        self.assertTrue(
            with_custom_session.user(user="******")["uri"] == "spotify:user:akx")

    def test_force_no_requests_session(self):
        with_no_session = Spotify(auth=self.token, requests_session=False)
        self.assertFalse(isinstance(with_no_session._session,
                                    requests.Session))
        self.assertTrue(
            with_no_session.user(user="******")["uri"] == "spotify:user:akx")
class AuthTestSpotipy(unittest.TestCase):
    """
    These tests require client authentication - provide client credentials
    using the following environment variables

    ::

        'SPOTIPY_CLIENT_ID'
        'SPOTIPY_CLIENT_SECRET'
    """

    playlist = "spotify:user:plamere:playlist:2oCEWyyAPbZp9xhVSxZavx"
    four_tracks = [
        "spotify:track:6RtPijgfPKROxEzTHNRiDp",
        "spotify:track:7IHOIqZUUInxjVkko181PB", "4VrWlk8IQxevMvERoX08iC",
        "http://open.spotify.com/track/3cySlItpiPiIAzU3NyHCJf"
    ]

    two_tracks = [
        "spotify:track:6RtPijgfPKROxEzTHNRiDp",
        "spotify:track:7IHOIqZUUInxjVkko181PB"
    ]

    other_tracks = [
        "spotify:track:2wySlB6vMzCbQrRnNGOYKa",
        "spotify:track:29xKs5BAHlmlX1u4gzQAbJ",
        "spotify:track:1PB7gRWcvefzu7t3LJLUlf"
    ]

    bad_id = 'BAD_ID'

    creep_urn = 'spotify:track:6b2oQwSGFkzsMtQruIWm2p'
    creep_id = '6b2oQwSGFkzsMtQruIWm2p'
    creep_url = 'http://open.spotify.com/track/6b2oQwSGFkzsMtQruIWm2p'
    el_scorcho_urn = 'spotify:track:0Svkvt5I79wficMFgaqEQJ'
    el_scorcho_bad_urn = 'spotify:track:0Svkvt5I79wficMFgaqEQK'
    pinkerton_urn = 'spotify:album:04xe676vyiTeYNXw15o9jT'
    weezer_urn = 'spotify:artist:3jOstUTkEu2JkjvRdBA5Gu'
    pablo_honey_urn = 'spotify:album:6AZv3m27uyRxi8KyJSfUxL'
    radiohead_urn = 'spotify:artist:4Z8W4fKeB5YxbusRsdQVPb'
    angeles_haydn_urn = 'spotify:album:1vAbqAeuJVWNAe7UR00bdM'
    heavyweight_urn = 'spotify:show:5c26B28vZMN8PG0Nppmn5G'
    heavyweight_id = '5c26B28vZMN8PG0Nppmn5G'
    heavyweight_url = 'https://open.spotify.com/show/5c26B28vZMN8PG0Nppmn5G'
    reply_all_urn = 'spotify:show:7gozmLqbcbr6PScMjc0Zl4'
    heavyweight_ep1_urn = 'spotify:episode:68kq3bNz6hEuq8NtdfwERG'
    heavyweight_ep1_id = '68kq3bNz6hEuq8NtdfwERG'
    heavyweight_ep1_url = 'https://open.spotify.com/episode/68kq3bNz6hEuq8NtdfwERG'
    reply_all_ep1_urn = 'spotify:episode:1KHjbpnmNpFmNTczQmTZlR'

    @classmethod
    def setUpClass(self):
        self.spotify = Spotify(
            client_credentials_manager=SpotifyClientCredentials())
        self.spotify.trace = False

    def test_audio_analysis(self):
        result = self.spotify.audio_analysis(self.four_tracks[0])
        assert ('beats' in result)

    def test_audio_features(self):
        results = self.spotify.audio_features(self.four_tracks)
        self.assertTrue(len(results) == len(self.four_tracks))
        for track in results:
            assert ('speechiness' in track)

    def test_audio_features_with_bad_track(self):
        bad_tracks = ['spotify:track:bad']
        input = self.four_tracks + bad_tracks
        results = self.spotify.audio_features(input)
        self.assertTrue(len(results) == len(input))
        for track in results[:-1]:
            if track is not None:
                assert ('speechiness' in track)
        self.assertTrue(results[-1] is None)

    def test_recommendations(self):
        results = self.spotify.recommendations(seed_tracks=self.four_tracks,
                                               min_danceability=0,
                                               max_loudness=0,
                                               target_popularity=50)
        self.assertTrue(len(results['tracks']) == 20)

    def test_artist_urn(self):
        artist = self.spotify.artist(self.radiohead_urn)
        self.assertTrue(artist['name'] == 'Radiohead')

    def test_artists(self):
        results = self.spotify.artists([self.weezer_urn, self.radiohead_urn])
        self.assertTrue('artists' in results)
        self.assertTrue(len(results['artists']) == 2)

    def test_album_urn(self):
        album = self.spotify.album(self.pinkerton_urn)
        self.assertTrue(album['name'] == 'Pinkerton')

    def test_album_tracks(self):
        results = self.spotify.album_tracks(self.pinkerton_urn)
        self.assertTrue(len(results['items']) == 10)

    def test_album_tracks_many(self):
        results = self.spotify.album_tracks(self.angeles_haydn_urn)
        tracks = results['items']
        total, received = results['total'], len(tracks)
        while received < total:
            results = self.spotify.album_tracks(self.angeles_haydn_urn,
                                                offset=received)
            tracks.extend(results['items'])
            received = len(tracks)

        self.assertEqual(received, total)

    def test_albums(self):
        results = self.spotify.albums(
            [self.pinkerton_urn, self.pablo_honey_urn])
        self.assertTrue('albums' in results)
        self.assertTrue(len(results['albums']) == 2)

    def test_track_urn(self):
        track = self.spotify.track(self.creep_urn)
        self.assertTrue(track['name'] == 'Creep')

    def test_track_id(self):
        track = self.spotify.track(self.creep_id)
        self.assertTrue(track['name'] == 'Creep')
        self.assertTrue(track['popularity'] > 0)

    def test_track_url(self):
        track = self.spotify.track(self.creep_url)
        self.assertTrue(track['name'] == 'Creep')

    def test_track_bad_urn(self):
        try:
            self.spotify.track(self.el_scorcho_bad_urn)
            self.assertTrue(False)
        except SpotifyException:
            self.assertTrue(True)

    def test_tracks(self):
        results = self.spotify.tracks([self.creep_url, self.el_scorcho_urn])
        self.assertTrue('tracks' in results)
        self.assertTrue(len(results['tracks']) == 2)

    def test_artist_top_tracks(self):
        results = self.spotify.artist_top_tracks(self.weezer_urn)
        self.assertTrue('tracks' in results)
        self.assertTrue(len(results['tracks']) == 10)

    def test_artist_related_artists(self):
        results = self.spotify.artist_related_artists(self.weezer_urn)
        self.assertTrue('artists' in results)
        self.assertTrue(len(results['artists']) == 20)
        for artist in results['artists']:
            if artist['name'] == 'Jimmy Eat World':
                found = True
        self.assertTrue(found)

    def test_artist_search(self):
        results = self.spotify.search(q='weezer', type='artist')
        self.assertTrue('artists' in results)
        self.assertTrue(len(results['artists']['items']) > 0)
        self.assertTrue(results['artists']['items'][0]['name'] == 'Weezer')

    def test_artist_search_with_market(self):
        results = self.spotify.search(q='weezer', type='artist', market='GB')
        self.assertTrue('artists' in results)
        self.assertTrue(len(results['artists']['items']) > 0)
        self.assertTrue(results['artists']['items'][0]['name'] == 'Weezer')

    def test_artist_search_with_multiple_markets(self):
        total = 5
        countries_list = ['GB', 'US', 'AU']
        countries_tuple = ('GB', 'US', 'AU')

        results_multiple = self.spotify.search_markets(q='weezer',
                                                       type='artist',
                                                       markets=countries_list)
        results_all = self.spotify.search_markets(q='weezer', type='artist')
        results_tuple = self.spotify.search_markets(q='weezer',
                                                    type='artist',
                                                    markets=countries_tuple)
        results_limited = self.spotify.search_markets(q='weezer',
                                                      limit=3,
                                                      type='artist',
                                                      markets=countries_list,
                                                      total=total)

        self.assertTrue(
            all('artists' in results_multiple[country]
                for country in results_multiple))
        self.assertTrue(
            all('artists' in results_all[country] for country in results_all))
        self.assertTrue(
            all('artists' in results_tuple[country]
                for country in results_tuple))
        self.assertTrue(
            all('artists' in results_limited[country]
                for country in results_limited))

        self.assertTrue(
            all(
                len(results_multiple[country]['artists']['items']) > 0
                for country in results_multiple))
        self.assertTrue(
            all(
                len(results_all[country]['artists']['items']) > 0
                for country in results_all))
        self.assertTrue(
            all(
                len(results_tuple[country]['artists']['items']) > 0
                for country in results_tuple))
        self.assertTrue(
            all(
                len(results_limited[country]['artists']['items']) > 0
                for country in results_limited))

        self.assertTrue(
            all(results_multiple[country]['artists']['items'][0]['name'] ==
                'Weezer' for country in results_multiple))
        self.assertTrue(
            all(results_all[country]['artists']['items'][0]['name'] == 'Weezer'
                for country in results_all))
        self.assertTrue(
            all(results_tuple[country]['artists']['items'][0]['name'] ==
                'Weezer' for country in results_tuple))
        self.assertTrue(
            all(results_limited[country]['artists']['items'][0]['name'] ==
                'Weezer' for country in results_limited))

        total_limited_results = 0
        for country in results_limited:
            total_limited_results += len(
                results_limited[country]['artists']['items'])
        self.assertTrue(total_limited_results <= total)

    def test_artist_albums(self):
        results = self.spotify.artist_albums(self.weezer_urn)
        self.assertTrue('items' in results)
        self.assertTrue(len(results['items']) > 0)

        found = False
        for album in results['items']:
            if album['name'] == 'Hurley':
                found = True

        self.assertTrue(found)

    def test_search_timeout(self):
        client_credentials_manager = SpotifyClientCredentials()
        sp = spotipy.Spotify(
            requests_timeout=0.01,
            client_credentials_manager=client_credentials_manager)

        # depending on the timing or bandwidth, this raises a timeout or connection error"
        self.assertRaises(
            (requests.exceptions.Timeout, requests.exceptions.ConnectionError),
            lambda: sp.search(q='my*', type='track'))

    def test_album_search(self):
        results = self.spotify.search(q='weezer pinkerton', type='album')
        self.assertTrue('albums' in results)
        self.assertTrue(len(results['albums']['items']) > 0)
        self.assertTrue(
            results['albums']['items'][0]['name'].find('Pinkerton') >= 0)

    def test_track_search(self):
        results = self.spotify.search(q='el scorcho weezer', type='track')
        self.assertTrue('tracks' in results)
        self.assertTrue(len(results['tracks']['items']) > 0)
        self.assertTrue(results['tracks']['items'][0]['name'] == 'El Scorcho')

    def test_user(self):
        user = self.spotify.user(user='******')
        self.assertTrue(user['uri'] == 'spotify:user:plamere')

    def test_track_bad_id(self):
        try:
            self.spotify.track(self.bad_id)
            self.assertTrue(False)
        except SpotifyException:
            self.assertTrue(True)

    def test_show_urn(self):
        show = self.spotify.show(self.heavyweight_urn, market="US")
        self.assertTrue(show['name'] == 'Heavyweight')

    def test_show_id(self):
        show = self.spotify.show(self.heavyweight_id, market="US")
        self.assertTrue(show['name'] == 'Heavyweight')

    def test_show_url(self):
        show = self.spotify.show(self.heavyweight_url, market="US")
        self.assertTrue(show['name'] == 'Heavyweight')

    def test_show_bad_urn(self):
        with self.assertRaises(SpotifyException):
            self.spotify.show("bogus_urn", market="US")

    def test_shows(self):
        results = self.spotify.shows(
            [self.heavyweight_urn, self.reply_all_urn], market="US")
        self.assertTrue('shows' in results)
        self.assertTrue(len(results['shows']) == 2)

    def test_show_episodes(self):
        results = self.spotify.show_episodes(self.heavyweight_urn, market="US")
        self.assertTrue(len(results['items']) > 1)

    def test_show_episodes_many(self):
        results = self.spotify.show_episodes(self.reply_all_urn, market="US")
        episodes = results['items']
        total, received = results['total'], len(episodes)
        while received < total:
            results = self.spotify.show_episodes(self.reply_all_urn,
                                                 offset=received,
                                                 market="US")
            episodes.extend(results['items'])
            received = len(episodes)

        self.assertEqual(received, total)

    def test_episode_urn(self):
        episode = self.spotify.episode(self.heavyweight_ep1_urn, market="US")
        self.assertTrue(episode['name'] == '#1 Buzz')

    def test_episode_id(self):
        episode = self.spotify.episode(self.heavyweight_ep1_id, market="US")
        self.assertTrue(episode['name'] == '#1 Buzz')

    def test_episode_url(self):
        episode = self.spotify.episode(self.heavyweight_ep1_url, market="US")
        self.assertTrue(episode['name'] == '#1 Buzz')

    def test_episode_bad_urn(self):
        with self.assertRaises(SpotifyException):
            self.spotify.episode("bogus_urn", market="US")

    def test_episodes(self):
        results = self.spotify.episodes(
            [self.heavyweight_ep1_urn, self.reply_all_ep1_urn], market="US")
        self.assertTrue('episodes' in results)
        self.assertTrue(len(results['episodes']) == 2)

    def test_unauthenticated_post_fails(self):
        with self.assertRaises(SpotifyException) as cm:
            self.spotify.user_playlist_create("spotify",
                                              "Best hits of the 90s")
        self.assertTrue(cm.exception.http_status == 401
                        or cm.exception.http_status == 403)

    def test_custom_requests_session(self):
        sess = requests.Session()
        sess.headers["user-agent"] = "spotipy-test"
        with_custom_session = spotipy.Spotify(
            client_credentials_manager=SpotifyClientCredentials(),
            requests_session=sess)
        self.assertTrue(
            with_custom_session.user(user="******")["uri"] == "spotify:user:akx")
        sess.close()

    def test_force_no_requests_session(self):
        with_no_session = spotipy.Spotify(
            client_credentials_manager=SpotifyClientCredentials(),
            requests_session=False)
        self.assertNotIsInstance(with_no_session._session, requests.Session)
        user = with_no_session.user(user="******")
        self.assertEqual(user["uri"], "spotify:user:akx")
Ejemplo n.º 5
0
class SpotifyService(MusicService):
    """
    Spotify client music service.
    """
    def __init__(self,
                 state,
                 client_id=None,
                 client_secret=None,
                 redirect_uri=None,
                 device_name=None):
        """
        @see Service.__init__()

        For the real meanings of the kwargs, read the Spotipy documentation:
            https://spotipy.readthedocs.io
        You can also set them as the environment variables::
          * ``SPOTIPY_CLIENT_ID``
          * ``SPOTIPY_CLIENT_SECRET``
          * ``SPOTIPY_REDIRECT_URI``

        :type  client_id: str
        :param client_id:
            The Spotify client ID.
        :type  client_secret: str
        :param client_secret:
            The Spotify client secret.
        :type  redirect_uri: str
        :param redirect_uri:
            The Spotify redirect URI.
        :type  device_name: str
        :param device_name:
            The name of the device to control, if not the default one.
        """
        super(SpotifyService, self).__init__("SpotifyService", state,
                                             "Spotify")

        self._client_id = client_id
        self._client_secret = client_secret
        self._redirect_uri = redirect_uri
        self._device_name = device_name
        self._device_id = None
        self._spotify = None
        self._volume = None

    def set_volume(self, volume):
        """
        @see MusicService.set_volume()
        """
        if MIN_VOLUME <= volume <= MAX_VOLUME:
            self._volume = volume
            self._spotify.volume(100.0 * (volume - MIN_VOLUME) /
                                 (MAX_VOLUME - MIN_VOLUME),
                                 device_id=self._device_id)
        else:
            raise ValueError("Bad volume: %s", volume)

    def get_volume():
        """
        @see MusicService.get_volume()
        """
        return self._volume

    def is_playing(self):
        """
        Whether the player is playing.

        :rtype: bool
        :return:
           Whether the player is playing.
        """
        try:
            cur = self._spotify.currently_playing()
            return cur['is_playing']
        except:
            return False

    def play(self, uris):
        """
        Set the list of URIs playing.
        """
        LOG.info("Playing: %s", ' '.join(uris))
        self._spotify.start_playback(uris=uris, device_id=self._device_id)

    def pause(self):
        """
        Pause any currently playing music.
        """
        self._spotify.pause_playback(device_id=self._device_id)

    def unpause(self):
        """
        Resume any currently paused music.
        """
        self._spotify.start_playback(device_id=self._device_id)

    def _start(self):
        """
        @see Startable._start()
        """
        # This is what we need to be able to do
        scope = ','.join(('user-library-read', 'user-read-playback-state',
                          'user-modify-playback-state'))

        # Create the authorization manager, and then use that to create the
        # client
        auth_manager = SpotifyOAuth(client_id=self._client_id,
                                    client_secret=self._client_secret,
                                    redirect_uri=self._redirect_uri,
                                    scope=scope)
        self._spotify = Spotify(auth_manager=auth_manager)

        # See what devices we have to hand
        try:
            if self._device_name is not None:
                LOG.info("Looking for device named '%s'", self._device_name)

            devices = self._spotify.devices()
            for device in devices['devices']:
                # Say what we see
                name = device['name']
                id_ = device['id']
                type_ = device['type']
                active = device['is_active']
                vol = device['volume_percent']
                LOG.info("Found %sactive %s device: '%s'",
                         '' if active else 'in', type_, name)

                # See if we're looking for a specific device, if not just snoop
                # the volume from the first active one
                if self._device_name is not None:
                    if name == self._device_name:
                        LOG.info("Matched '%s' to ID '%s'", name, id_)
                        self._device_id = id_
                        self._volume = vol / 100.0 * MAX_VOLUME
                else:
                    if active and self._volume is None:
                        self._volume = vol / 100.0 * MAX_VOLUME

        except Exception as e:
            LOG.warning("Unable to determine active Spoify devices: %s", e)

        # If we were looking for a specific device then make sure that we found
        # it in the list
        if self._device_name is not None and self._device_id is None:
            raise ValueError("Failed to find device with name '%s'" %
                             (self._device_name, ))

    def _stop(self):
        """
        @see Startable._stop()
        """
        try:
            self._spotify.pause_playback(device_id=self._device_id)
        except:
            # Best effort
            pass

    def _match_artist(self, artist):
        """
        @see MusicService._match_artist()
        """
        artist = ' '.join(artist).lower()
        LOG.debug("Matching artist '%s'", artist)
        result = self._spotify.search(artist, type='artist')
        if 'artists' in result and 'items' in result['artists']:
            items = result['artists']['items']
            LOG.debug("Checking %d results", len(items))
            for item in items:
                name = item.get('name', '').lower()
                LOG.debug("Matching against '%s'", name)
                if fuzz.ratio(name, artist) > 80:
                    return True
        return False

    def _get_stop_handler(self, tokens):
        """
        @see MusicService._get_stop_handler()
        """
        return _SpotifyServicePauseHandler(self, tokens)

    def _get_play_handler(self, tokens):
        """
        @see MusicService._get_play_handler()
        """
        return _SpotifyServiceUnpauseHandler(self, tokens)

    def _get_toggle_pause_handler(self, tokens):
        """
        @see MusicService._get_toggle_pause_handler()
        """
        return _SpotifyServiceTogglePauseHandler(self, tokens)

    def _get_handler_for(self, tokens, platform_match, genre, artist,
                         song_or_album):
        """
        @see MusicService._get_handler_for()
        """
        # Do nothing if we have no name
        if song_or_album is None or len(song_or_album) == 0:
            return None

        # Normalise to strings
        name = ' '.join(song_or_album).lower()
        if artist is None or len(artist) == 0:
            artist = None
        else:
            artist = ' '.join(artist).lower()

        # We will put all the track URIs in here
        uris = []

        # Search by track name then album name, these are essentially the same
        # logic
        for which in ('track', 'album'):
            LOG.info("Looking for '%s'%s as a %s", name,
                     " by '%s'" % artist if artist else '', which)

            # This is the key in the results
            plural = which + 's'

            # Try using the song_or_album as the name
            result = self._spotify.search(name, type=which)
            if not result:
                LOG.info("No results")
                continue

            # Did we get back any tracks
            if plural not in result:
                LOG.error("%s was not in result keys: %s", plural,
                          result.keys())
                continue

            # We got some results back, let's assign scores to them all
            results = result[plural]
            matches = []
            for item in results.get('items', []):
                # It must have a uri
                if 'uri' not in item and item['uri']:
                    LOG.error("No URI in %s", item)

                # Look at all the candidate entries
                if 'name' in item:
                    # See if this is better than any existing match
                    name_score = fuzz.ratio(name, item['name'].lower())
                    LOG.debug("'%s' matches '%s' with score %d", item['name'],
                              name, name_score)

                    # Check to make sure that we have an artist match as well
                    if artist is None:
                        # Treat as a wildcard
                        artist_score = 100
                    else:
                        artist_score = 0
                        for entry in item.get('artists', []):
                            score = fuzz.ratio(artist,
                                               entry.get('name', '').lower())
                            LOG.debug("Artist match score for '%s' was %d",
                                      entry.get('name', ''), score)
                            if score > artist_score:
                                artist_score = score
                    LOG.debug("Artist match score was %d", artist_score)

                    # Only consider cases where the scores look "good enough"
                    if name_score > 75 and artist_score > 75:
                        LOG.debug("Adding match")
                        matches.append((item, name_score, artist_score))

            # Anything?
            if len(matches) > 0:
                LOG.debug("Got %d matches", len(matches))

                # Order them accordingly
                matches.sort(key=lambda e: (e[1], e[2]))

                # Now, pick the top one
                best = matches[0]
                item = best[0]
                LOG.debug("Best match was: %s", item)

                # Extract the info
                item_name = item.get('name', None) or name
                artists = item.get('artists', [])
                artist_name = (artists[0].get('name', None)
                               if len(artists) > 0 else None) or artist

                # Description of what we are playing
                what = item_name if item_name else name
                if artist_name:
                    what += " by " + artist_name
                what += " on Spotify"

                # The score is the geometric value of the two
                score = sqrt(best[1] * best[1] + best[2] * best[2]) / 100.0

                # The should be here
                assert 'uri' in item, "Missing URI in %s" % (item, )
                uri = item['uri']

                # If we are an album then grab the track URIs
                if which == 'album':
                    tracks = self._spotify.album_tracks(uri)
                    if tracks and 'items' in tracks:
                        uris = [track['uri'] for track in tracks['items']]
                else:
                    # Just the track
                    uris = [uri]

                # And we're done
                break

        # Otherwise assume that it's an artist
        if len(uris) == 0 and artist is None:
            LOG.info("Looking for '%s' as an artist", name)
            result = self._spotify.search(name, type='artist')
            LOG.debug("Got: %s", result)

            if result and 'artists' in result and 'items' in result['artists']:
                items = sorted(result['artists']['items'],
                               key=lambda entry: fuzz.ratio(
                                   name,
                                   entry.get('name', '').lower()),
                               reverse=True)

                # Look at the best one, if any
                LOG.debug("Got %d matches", len(items))
                if len(items) > 0:
                    match = items[0]
                    who = match['name']
                    what = "%s on Spotify" % (who, )
                    score = fuzz.ratio(who.lower(), name)

                    # Find all their albums
                    if 'uri' in match:
                        LOG.debug("Got match: %s", match['uri'])
                        artist_albums = self._spotify.artist_albums(
                            match['uri'])
                        for album in artist_albums.get('items', []):
                            # Append all the tracks
                            LOG.debug("Looking at album: %s", album)
                            if 'uri' in album:
                                tracks = self._spotify.album_tracks(
                                    album['uri'])
                                if tracks and 'items' in tracks:
                                    LOG.debug(
                                        "Adding tracks: %s",
                                        ' '.join(track['name']
                                                 for track in tracks['items']))
                                    uris.extend([
                                        track['uri']
                                        for track in tracks['items']
                                    ])

        # And now we can give it back, if we had something
        if len(uris) > 0:
            return _SpotifyServicePlayHandler(self, tokens, what, uris, score)
        else:
            # We got nothing
            return None
Ejemplo n.º 6
0
class SpotifyReleaseTweeter(object):
    def __init__(self):
        token = spotipy.util.prompt_for_user_token(
            settings.SPOTIFY_USER_NAME,
            scope='user-follow-read',
            client_id=settings.SPOTIFY_CLIENT_ID,
            client_secret=settings.SPOTIFY_CLIENT_SECRET,
            redirect_uri=settings.SPOTIFY_REDIRECT_URI)
        self.spotify = Spotify(auth=token)
        self.is_first_run = is_first_run()
        self.cache = AlreadyHandledCache(settings.TWEETED_IDS_CACHE_PATH)

    def get_ids_of_followed_artists(self):
        result = list()
        last_artist_id = None

        while True:
            artist_result = self.spotify.current_user_followed_artists(
                50, last_artist_id)
            artists = artist_result['artists']['items']

            if not artists:
                break

            for artist in artists:
                last_artist_id = artist['id']
                result.append(artist['id'])

        print("{} followed artists found.".format(len(result)))
        return result

    def filter_releases(self, artist_releases):
        release_ids = [release.release_id for release in artist_releases]
        reduced_ids = self.cache.reduce(release_ids)
        filtered_releases = filter(
            lambda release: release.release_id in reduced_ids, artist_releases)
        return list(filtered_releases)

    def get_releases_per_artist(self,
                                artist_ids,
                                limit=settings.LAST_N_RELEASES):
        ALBUM = 'Album'
        SINGLE = 'Single'
        result = dict()

        for artist_id in artist_ids:
            artist_releases = list()

            # Albums
            try:
                result_albums = self.spotify.artist_albums(
                    artist_id=artist_id,
                    album_type=ALBUM,
                    country=settings.SPOTIFY_MARKET,
                    limit=limit)
                albums = [
                    item_to_spotify_release(item, ALBUM)
                    for item in result_albums['items']
                ]
                artist_releases.extend(albums)
            except ConnectionError:
                print(("Could not establish connection while fetching "
                       "albums for artist with id {}. Skipping."
                       ).format(artist_id))

            # Singles
            try:
                result_singles = self.spotify.artist_albums(
                    artist_id=artist_id,
                    album_type=SINGLE,
                    country=settings.SPOTIFY_MARKET,
                    limit=limit)
                singles = [
                    item_to_spotify_release(item, SINGLE)
                    for item in result_singles['items']
                ]
                artist_releases.extend(singles)
            except ConnectionError:
                print(("Could not establish connection while fetching "
                       "singles for artist with id {}. Skipping."
                       ).format(artist_id))

            # Filter
            filtered_releases = self.filter_releases(artist_releases)
            if filtered_releases:
                result[artist_id] = filtered_releases
        return result

    def process(self):
        print("Start ({}).".format(datetime.now()))
        artist_ids = self.get_ids_of_followed_artists()
        releases_per_artist = self.get_releases_per_artist(artist_ids)
        twitter_status_strings = create_twitter_status_strings_from_releases_per_artist(
            releases_per_artist)
        if not self.is_first_run:
            print("{} releases will be tweeted.".format(
                len(twitter_status_strings)))
            Tweeter().tweet_list(twitter_status_strings)
        else:
            print((
                "Zero tweets at first run, due to Twython API limit (200 tweets a day). "
                "The cache is now initialized and the script will run as promised in the "
                "next run. All releases until now will not be tweeted anymore."
            ))
        print("Done ({}).".format(datetime.now()))
Ejemplo n.º 7
0
class SpotifyReleaseGun(object):

    def __init__(self, spotify_user_data):
        self.user_name = spotify_user_data['user_name']
        token = spotipy.util.prompt_for_user_token(
            self.user_name,
            scope='user-follow-read',
            client_id=spotify_user_data['client_id'],
            client_secret=spotify_user_data['client_secret'],
            redirect_uri=spotify_user_data['redirect_uri']
        )
        self.spotify = Spotify(auth=token)
        self.cache_path = settings.CACHE_PATH_PATTERN.format(self.user_name)
        self.is_first_run = is_first_run(self.cache_path)
        self.cache = AlreadyHandledCache(self.cache_path)

    def get_ids_of_followed_artists(self):
        result = []
        last_artist_id = None

        while True:
            artist_result = self.spotify.\
                current_user_followed_artists(50, last_artist_id)
            artists = artist_result['artists']['items']

            if not artists:
                break

            for artist in artists:
                last_artist_id = artist['id']
                result.append(artist['id'])
        return result

    def filter_releases(self, artist_releases):
        release_ids = [release.release_id for release in artist_releases]
        reduced_ids = self.cache.reduce(release_ids)
        filtered = filter(
            lambda release: release.release_id in reduced_ids, artist_releases
        )
        return list(filtered)

    def get_releases_per_artist(self,
                                artist_ids,
                                limit=settings.LAST_N_RELEASES):
        releases_per_artist_id = {}

        for artist_id in artist_ids:
            artist_releases = []
            try:
                # Albums
                result_albums = self.spotify.artist_albums(
                    artist_id=artist_id,
                    album_type='Album',
                    country=settings.SPOTIFY_MARKET,
                    limit=limit
                )
                albums = [SpotifyRelease(d) for d in result_albums['items']]
                artist_releases.extend(albums)
                # Singles
                result_singles = self.spotify.artist_albums(
                    artist_id=artist_id,
                    album_type='Single',
                    country=settings.SPOTIFY_MARKET,
                    limit=limit
                )
                singles = [SpotifyRelease(d) for d in result_singles['items']]
                artist_releases.extend(singles)
            except ConnectionError:
                print(("> Could not establish connection while "
                       "fetching releases for artist with id {}. "
                       "Skipping.").format(artist_id))
            except JSONDecodeError:
                print(("> Could not decode JSON response "
                       "of artist with id {}. Skipping.").format(artist_id))

            filtered_releases = self.filter_releases(artist_releases)
            if filtered_releases:
                releases_per_artist_id[artist_id] = filtered_releases
        return releases_per_artist_id

    def get_releases_of_artists(self):
        artist_ids = self.get_ids_of_followed_artists()
        print("> {} followed artists found.".format(len(artist_ids)))
        releases_per_artist = self.get_releases_per_artist(artist_ids)
        return releases_per_artist

    def process(self, to_console=True):
        print("> Start ({}).".format(datetime.now()))
        releases = self.get_releases_of_artists()

        if to_console:
            send_to_console(releases, self.user_name)
        if settings.REALLY_SIMPLE_RSS_SERVER_URL:
            send_to_really_simple_rss_server(releases, self.user_name)
        if settings.SLACK_URL:
            send_to_slack(releases, self.user_name)
        print("> Done ({}).".format(datetime.now()))
Ejemplo n.º 8
0
while artists:
    artist_result = spotify.current_user_followed_artists(50, last_artist_id)
    artists = artist_result['artists']['items']

    for artist in artists:
        last_artist_id = artist['id']
        followed_artists.append(artist)

# Get 5 newest albums and singles from artists.
releases = []
for artist in followed_artists:
    try:
        # Albums
        result_albums = spotify.artist_albums(
            artist_id=artist['id'],
            album_type='Album',
            country='NL',
            limit=5
        )
        releases.extend(result_albums['items'])
        # Singles
        result_singles = spotify.artist_albums(
            artist_id=artist['id'],
            album_type='Single',
            country='NL',
            limit=5
        )
        releases.extend(result_singles['items'])
    except ConnectionError:
        print(("Could not establish connection while "
               "fetching releases for artist {0} with id {1}. "
               "Skipping.").format(artist['name'], artist['id']))
Ejemplo n.º 9
0
class AuthTestSpotipy(unittest.TestCase):
    """
    These tests require client authentication - provide client credentials
    using the following environment variables

    ::

        'SPOTIPY_CLIENT_ID'
        'SPOTIPY_CLIENT_SECRET'
    """

    playlist = "spotify:user:plamere:playlist:2oCEWyyAPbZp9xhVSxZavx"
    four_tracks = [
        "spotify:track:6RtPijgfPKROxEzTHNRiDp",
        "spotify:track:7IHOIqZUUInxjVkko181PB", "4VrWlk8IQxevMvERoX08iC",
        "http://open.spotify.com/track/3cySlItpiPiIAzU3NyHCJf"
    ]

    two_tracks = [
        "spotify:track:6RtPijgfPKROxEzTHNRiDp",
        "spotify:track:7IHOIqZUUInxjVkko181PB"
    ]

    other_tracks = [
        "spotify:track:2wySlB6vMzCbQrRnNGOYKa",
        "spotify:track:29xKs5BAHlmlX1u4gzQAbJ",
        "spotify:track:1PB7gRWcvefzu7t3LJLUlf"
    ]

    bad_id = 'BAD_ID'

    creep_urn = 'spotify:track:3HfB5hBU0dmBt8T0iCmH42'
    creep_id = '3HfB5hBU0dmBt8T0iCmH42'
    creep_url = 'http://open.spotify.com/track/3HfB5hBU0dmBt8T0iCmH42'
    el_scorcho_urn = 'spotify:track:0Svkvt5I79wficMFgaqEQJ'
    el_scorcho_bad_urn = 'spotify:track:0Svkvt5I79wficMFgaqEQK'
    pinkerton_urn = 'spotify:album:04xe676vyiTeYNXw15o9jT'
    weezer_urn = 'spotify:artist:3jOstUTkEu2JkjvRdBA5Gu'
    pablo_honey_urn = 'spotify:album:6AZv3m27uyRxi8KyJSfUxL'
    radiohead_urn = 'spotify:artist:4Z8W4fKeB5YxbusRsdQVPb'
    angeles_haydn_urn = 'spotify:album:1vAbqAeuJVWNAe7UR00bdM'

    @classmethod
    def setUpClass(self):
        self.spotify = Spotify(
            client_credentials_manager=SpotifyClientCredentials())
        self.spotify.trace = False

    def test_audio_analysis(self):
        result = self.spotify.audio_analysis(self.four_tracks[0])
        assert ('beats' in result)

    def test_audio_features(self):
        results = self.spotify.audio_features(self.four_tracks)
        self.assertTrue(len(results) == len(self.four_tracks))
        for track in results:
            assert ('speechiness' in track)

    def test_audio_features_with_bad_track(self):
        bad_tracks = ['spotify:track:bad']
        input = self.four_tracks + bad_tracks
        results = self.spotify.audio_features(input)
        self.assertTrue(len(results) == len(input))
        for track in results[:-1]:
            if track is not None:
                assert ('speechiness' in track)
        self.assertTrue(results[-1] is None)

    def test_recommendations(self):
        results = self.spotify.recommendations(seed_tracks=self.four_tracks,
                                               min_danceability=0,
                                               max_loudness=0,
                                               target_popularity=50)
        self.assertTrue(len(results['tracks']) == 20)

    def test_artist_urn(self):
        artist = self.spotify.artist(self.radiohead_urn)
        self.assertTrue(artist['name'] == 'Radiohead')

    def test_artists(self):
        results = self.spotify.artists([self.weezer_urn, self.radiohead_urn])
        self.assertTrue('artists' in results)
        self.assertTrue(len(results['artists']) == 2)

    def test_album_urn(self):
        album = self.spotify.album(self.pinkerton_urn)
        self.assertTrue(album['name'] == 'Pinkerton')

    def test_album_tracks(self):
        results = self.spotify.album_tracks(self.pinkerton_urn)
        self.assertTrue(len(results['items']) == 10)

    def test_album_tracks_many(self):
        results = self.spotify.album_tracks(self.angeles_haydn_urn)
        tracks = results['items']
        total, received = results['total'], len(tracks)
        while received < total:
            results = self.spotify.album_tracks(self.angeles_haydn_urn,
                                                offset=received)
            tracks.extend(results['items'])
            received = len(tracks)

        self.assertEqual(received, total)

    def test_albums(self):
        results = self.spotify.albums(
            [self.pinkerton_urn, self.pablo_honey_urn])
        self.assertTrue('albums' in results)
        self.assertTrue(len(results['albums']) == 2)

    def test_track_urn(self):
        track = self.spotify.track(self.creep_urn)
        self.assertTrue(track['name'] == 'Creep')

    def test_track_id(self):
        track = self.spotify.track(self.creep_id)
        self.assertTrue(track['name'] == 'Creep')
        self.assertTrue(track['popularity'] > 0)

    def test_track_url(self):
        track = self.spotify.track(self.creep_url)
        self.assertTrue(track['name'] == 'Creep')

    def test_track_bad_urn(self):
        try:
            self.spotify.track(self.el_scorcho_bad_urn)
            self.assertTrue(False)
        except SpotifyException:
            self.assertTrue(True)

    def test_tracks(self):
        results = self.spotify.tracks([self.creep_url, self.el_scorcho_urn])
        self.assertTrue('tracks' in results)
        self.assertTrue(len(results['tracks']) == 2)

    def test_artist_top_tracks(self):
        results = self.spotify.artist_top_tracks(self.weezer_urn)
        self.assertTrue('tracks' in results)
        self.assertTrue(len(results['tracks']) == 10)

    def test_artist_related_artists(self):
        results = self.spotify.artist_related_artists(self.weezer_urn)
        self.assertTrue('artists' in results)
        self.assertTrue(len(results['artists']) == 20)
        for artist in results['artists']:
            if artist['name'] == 'Jimmy Eat World':
                found = True
        self.assertTrue(found)

    def test_artist_search(self):
        results = self.spotify.search(q='weezer', type='artist')
        self.assertTrue('artists' in results)
        self.assertTrue(len(results['artists']['items']) > 0)
        self.assertTrue(results['artists']['items'][0]['name'] == 'Weezer')

    def test_artist_search_with_market(self):
        results = self.spotify.search(q='weezer', type='artist', market='GB')
        self.assertTrue('artists' in results)
        self.assertTrue(len(results['artists']['items']) > 0)
        self.assertTrue(results['artists']['items'][0]['name'] == 'Weezer')

    def test_artist_albums(self):
        results = self.spotify.artist_albums(self.weezer_urn)
        self.assertTrue('items' in results)
        self.assertTrue(len(results['items']) > 0)

        found = False
        for album in results['items']:
            if album['name'] == 'Hurley':
                found = True

        self.assertTrue(found)

    def test_search_timeout(self):
        client_credentials_manager = SpotifyClientCredentials()
        sp = spotipy.Spotify(
            client_credentials_manager=client_credentials_manager,
            requests_timeout=.01)

        try:
            sp.search(q='my*', type='track')
            self.assertTrue(False, 'unexpected search timeout')
        except requests.exceptions.Timeout:
            self.assertTrue(True, 'expected search timeout')

    def test_album_search(self):
        results = self.spotify.search(q='weezer pinkerton', type='album')
        self.assertTrue('albums' in results)
        self.assertTrue(len(results['albums']['items']) > 0)
        self.assertTrue(
            results['albums']['items'][0]['name'].find('Pinkerton') >= 0)

    def test_track_search(self):
        results = self.spotify.search(q='el scorcho weezer', type='track')
        self.assertTrue('tracks' in results)
        self.assertTrue(len(results['tracks']['items']) > 0)
        self.assertTrue(results['tracks']['items'][0]['name'] == 'El Scorcho')

    def test_user(self):
        user = self.spotify.user(user='******')
        self.assertTrue(user['uri'] == 'spotify:user:plamere')

    def test_track_bad_id(self):
        try:
            self.spotify.track(self.bad_id)
            self.assertTrue(False)
        except SpotifyException:
            self.assertTrue(True)

    def test_unauthenticated_post_fails(self):
        with self.assertRaises(SpotifyException) as cm:
            self.spotify.user_playlist_create("spotify",
                                              "Best hits of the 90s")
        self.assertTrue(cm.exception.http_status == 401
                        or cm.exception.http_status == 403)

    def test_custom_requests_session(self):
        sess = requests.Session()
        sess.headers["user-agent"] = "spotipy-test"
        with_custom_session = spotipy.Spotify(
            client_credentials_manager=SpotifyClientCredentials(),
            requests_session=sess)
        self.assertTrue(
            with_custom_session.user(user="******")["uri"] == "spotify:user:akx")
        sess.close()

    def test_force_no_requests_session(self):
        from requests import Session
        with_no_session = spotipy.Spotify(
            client_credentials_manager=SpotifyClientCredentials(),
            requests_session=False)
        self.assertFalse(isinstance(with_no_session._session, Session))
        self.assertTrue(
            with_no_session.user(user="******")["uri"] == "spotify:user:akx")
Ejemplo n.º 10
0
class SpotifyReleaseGun(object):
    def __init__(self, spotify_user_data):
        self.user_name = spotify_user_data['user_name']
        token = spotipy.util.prompt_for_user_token(
            self.user_name,
            scope='user-follow-read',
            client_id=spotify_user_data['client_id'],
            client_secret=spotify_user_data['client_secret'],
            redirect_uri=spotify_user_data['redirect_uri'])
        self.spotify = Spotify(auth=token)
        self.cache_path = settings.CACHE_PATH_PATTERN.format(self.user_name)
        self.is_first_run = is_first_run(self.cache_path)
        self.cache = AlreadyHandledCache(self.cache_path)

    def get_ids_of_followed_artists(self):
        result = []
        last_artist_id = None

        while True:
            artist_result = self.spotify.\
                current_user_followed_artists(50, last_artist_id)
            artists = artist_result['artists']['items']

            if not artists:
                break

            for artist in artists:
                last_artist_id = artist['id']
                result.append(artist['id'])
        return result

    def filter_releases(self, artist_releases):
        release_ids = [release.release_id for release in artist_releases]
        reduced_ids = self.cache.reduce(release_ids)
        filtered = filter(lambda release: release.release_id in reduced_ids,
                          artist_releases)
        return list(filtered)

    def get_releases_per_artist(self,
                                artist_ids,
                                limit=settings.LAST_N_RELEASES):
        releases_per_artist_id = {}

        for artist_id in artist_ids:
            artist_releases = []
            try:
                # Albums
                result_albums = self.spotify.artist_albums(
                    artist_id=artist_id,
                    album_type='Album',
                    country=settings.SPOTIFY_MARKET,
                    limit=limit)
                albums = [SpotifyRelease(d) for d in result_albums['items']]
                artist_releases.extend(albums)
                # Singles
                result_singles = self.spotify.artist_albums(
                    artist_id=artist_id,
                    album_type='Single',
                    country=settings.SPOTIFY_MARKET,
                    limit=limit)
                singles = [SpotifyRelease(d) for d in result_singles['items']]
                artist_releases.extend(singles)
            except ConnectionError:
                print(("> Could not establish connection while "
                       "fetching releases for artist with id {}. "
                       "Skipping.").format(artist_id))
            except JSONDecodeError:
                print(("> Could not decode JSON response "
                       "of artist with id {}. Skipping.").format(artist_id))

            filtered_releases = self.filter_releases(artist_releases)
            if filtered_releases:
                releases_per_artist_id[artist_id] = filtered_releases
        return releases_per_artist_id

    def get_releases_of_artists(self):
        artist_ids = self.get_ids_of_followed_artists()
        print("> {} followed artists found.".format(len(artist_ids)))
        releases_per_artist = self.get_releases_per_artist(artist_ids)
        return releases_per_artist

    def process(self, to_console=True):
        print("> Start ({}).".format(datetime.now()))
        releases = self.get_releases_of_artists()

        if to_console:
            send_to_console(releases, self.user_name)
        if settings.REALLY_SIMPLE_RSS_SERVER_URL:
            send_to_really_simple_rss_server(releases, self.user_name)
        if settings.SLACK_URL:
            send_to_slack(releases, self.user_name)
        print("> Done ({}).".format(datetime.now()))
Ejemplo n.º 11
0
class Apollo(object):
    '''
		This object is the main interface between the Google Speech
		transcriber and Spotify. It parses the transcribed text from
		Google into commands, conducts queries with the Spotipy API
		and interfaces with the Spotify desktop app to play music.
	'''
    def __init__(self):
        # Authentication with Spotipy API
        self._SP_USER = "******"
        self._SP_CI = os.getenv('SPOTIPY_CLIENT_ID')
        self._SP_CS = os.getenv('SPOTIPY_CLIENT_SECRET')
        self._SP_CRED = oauth2.SpotifyClientCredentials(
            client_id=self._SP_CI, client_secret=self._SP_CS)
        self._SP_AUTH = self._SP_CRED.get_access_token()
        self._SP = Spotify(auth=self._SP_AUTH)
        self._SP_OAUTH_TOKEN = sp_client.get_oauth_token()
        self._SP_CSRF_TOKEN = sp_client.get_csrf_token()
        self._SP_PLAYLISTS = self._SP.user_playlists(user=self._SP_USER)
        # Dictionary of playlist URI's with names as keys
        self._SP_PLAYLIST_DICT = defaultdict(lambda: defaultdict(lambda: None))
        # Number of items to return from track search
        self._SP_TLIMIT = 10
        # Number of items to return from playlist search
        self._SP_PLIMIT = 100
        self._TRACK_LIST = []
        self.createPlaylistDict()

    #@classmethod
    def createPlaylistDict(self):
        # Populates the playlist dict
        for item in self._SP_PLAYLISTS.items()[0][1]:
            name = item['name'].lower()
            self._SP_PLAYLIST_DICT[name]['uri'] = item['uri']
            self._SP_PLAYLIST_DICT[name]['id'] = item['id']

    #@staticmethod
    def parseCommand(self, phrase):
        '''
			Parses transcribed phrse from Google Speech into 
			command keywords. Curently supports 3 command classes:
				A: 
					- "Play/Pause"
					- "Skip"
				B:
					- "Play <songname>"
					- "Play <songname> by <artist>"
					- "Play playlist <playlistname>"
				C:
					- "Add this song to playlist <playlistname>"

			@param phrase: transcribed phrase from Google Speech API
		'''
        cmd = defaultdict(lambda: None)
        if (not len(phrase)):
            asay("No command received")
            return None
        phrase = phrase.lower().strip(re.search(r'\b(spotify)\b', phrase))
        if ("play" in phrase and len(phrase) <= 2):
            cmd['A'] = "play"
        elif ("pause" in phrase or "does" in phrase):
            cmd['A'] = "pause"
        elif ("skip" in phrase):
            cmd['A'] = "skip"
        elif ("previous" in phrase or "last" in phrase):
            cmd['A'] = "prev"
        elif ("play" in phrase):
            cmd['B'] = phrase
        elif ("by" in phrase):
            keywords = phrase.replace("play ", "").split("by")
            cmd['B'] = keywords
        elif ("play" in phrase and "playlist" in phrase):
            keywords = phrase.split("play ")[-1]
            cmd['B'] = keywords
        else:
            asay("No command received")
            return None
        if (len(cmd) >= 1):
            asay("Sending request to Spotify")
            ret = self.sendRequest(cmd)
        else:
            ret = None
        return ret

    #@classmethod
    def sendRequest(self, command):
        '''
			Sends appropriate request to Spotify
			@param command: parsed command dict (key = ['A','B','C']) 
		'''
        if (len(command) == 0):
            asay("Error: Command was empty")
            return
        key = command.keys()[0]
        cmd = command[key]
        asay("key: %s\t command: %s" % (key, str(cmd)))
        if (key == 'A'):
            if (cmd == "play"):
                sp_client.unpause(self._SP_OAUTH_TOKEN, self._SP_CSRF_TOKEN)
                asay(">> Play")
            elif ("pause" in cmd or "does" in cmd):
                sp_client.pause(self._SP_OAUTH_TOKEN, self._SP_CSRF_TOKEN)
                asay("|| Paused")
            elif ("skip" in cmd or "next" in cmd):
                if (len(self._TRACK_LIST) == 0):
                    asay("Empty track list! Play a song to populate tracklist")
                    return
                track_number = random.randint(0, len(self._TRACK_LIST) - 1)
                asay("Playing track number " + str(track_number))
                sp_client.play(self._SP_OAUTH_TOKEN, self._SP_CSRF_TOKEN,
                               self._TRACK_LIST[track_number])

        elif (key == 'B'):
            # Play playlist
            if ("playlist" in cmd):
                req_playlist = cmd.split("playlist ")[-1]
                playlist_uri = self._SP_PLAYLIST_DICT[req_playlist]['uri']
                playlist_id = self._SP_PLAYLIST_DICT[req_playlist]['id']
                if (playlist_uri == '0'):
                    asay("Couldn't find playlist " + req_playlist)
                    return False
                else:
                    sp_client.play(self._SP_OAUTH_TOKEN, self._SP_CSRF_TOKEN,
                                   playlist_uri)
                    playlist_tracks = self._SP.user_playlist_tracks(
                        user=self._SP_USER, playlist_id=playlist_id)
                    self._TRACK_LIST = [
                        item['track']['uri']
                        for item in playlist_tracks['items']
                    ]
            # Play single track
            else:
                # Artist name only
                if ("artist" in cmd):
                    artist = cmd.split("artist")[-1]
                    asay("Looking for " + str(artist))
                    result = self._SP.search(q=artist,
                                             type='artist',
                                             limit=self._SP_TLIMIT)
                    if (not result):
                        asay("Couldn't find artist " + artist)
                        return False
                    asay("Playing " +
                         str(result['artists']['items'][0]['name']))
                    artist_uri = result['artists']['items'][0]['uri']
                    sp_client.play(self._SP_OAUTH_TOKEN, self._SP_CSRF_TOKEN,
                                   artist_uri)
                    # TODO Get remaining tracks in album to enable skipping

                    return True
                # Track + Artist name provided
                elif ("by" in cmd):
                    cmd = cmd.replace("play ", "")
                    track, artist = cmd.split("by")
                    asay("Looking for " + str(artist) + ":" + str(track))
                    result = self._SP.search(q=track + " " + artist,
                                             type='track',
                                             limit=self._SP_TLIMIT)
                # Track name only
                else:
                    track = cmd[0]
                    asay("Looking for " + str(track))
                    result = self._SP.search(q=track,
                                             type='track',
                                             limit=self._SP_TLIMIT)
                try:
                    track_uri = result['tracks']['items'][0]['uri']
                    sp_client.play(self._SP_OAUTH_TOKEN, self._SP_CSRF_TOKEN,
                                   track_uri)
                    album_uri = result['tracks']['items'][0]['album']['uri']
                    album_tracks = self._SP.album_tracks(album_id=album_uri)
                    self._TRACK_LIST = [
                        item['uri'] for item in album_tracks['items']
                    ]
                    # Single record? (one song in the album): search the artist playlist
                    if (len(self._TRACK_LIST) <= 1):
                        artist = result['tracks']['items'][0]['artists'][0]
                        singles = self._SP.artist_albums(artist['id'])
                        self._TRACK_LIST = []
                        self._TRACK_LIST = [
                            item['uri'] for item in singles['items']
                        ]
                    return True
                except KeyError or IndexError:
                    asay("Couldn't find URI")
                    return False