Ejemplo n.º 1
0
def download_playlistspo(URL,
                         output=stock_output + "/",
                         recursive_download=stock_recursive_download,
                         not_interface=stock_not_interface,
                         zips=stock_zip):
    global spo

    array = []

    URL = (URL.split("?")[0].split("/"))

    try:
        tracks = spo.user_playlist_tracks(URL[-3], URL[-1])
    except Exception as a:
        if not "The access token expired" in str(a):
            raise exceptions.InvalidLink("Invalid link ;)")

        spo = Spotify(generate_token())

        tracks = spo.user_playlist_tracks(URL[-3], URL[-1])

    def lazy(tracks):
        for a in tracks['items']:
            try:
                array.append(
                    download_trackspo(a['track']['external_urls']['spotify'],
                                      output, recursive_download,
                                      not_interface))
            except:
                print("Track not found :(")
                array.append("None")

    lazy(tracks)
    tot = tracks['total']

    for a in range(tot // 100 - 1):
        try:
            tracks = spo.next(tracks)
        except:
            spo = Spotify(generate_token())

            tracks = spo.next(tracks)

        lazy(tracks)

    if zips:
        zip_name = "{}playlist {}.zip".format(output, URL[-1])
        create_zip(zip_name, array)
        return array, zip_name

    return array
Ejemplo n.º 2
0
class SpotifyAPI:
    def __init__(self):
        credentials = SpotifyClientCredentials(
            get_config("spotify", "CLIENT_ID"),
            get_config("spotify", "CLIENT_SECRET"))
        self.sp = Spotify(client_credentials_manager=credentials)

    def get_playlist(self, user: str, playlist: str, ignores: list) -> list:
        artists = {}
        try:
            res = self.sp.user_playlist(user, playlist)
            while res:
                try:
                    tracks = res['tracks']
                except KeyError:
                    break
                for i, item in enumerate(tracks['items']):
                    track = item['track']
                    artist = track['artists'][0]['name']
                    if artist and artist not in ignores:
                        artists[(track['artists'][0]['name'])] = 1
                if tracks['next']:
                    res = self.sp.next(tracks)
        except SpotifyException as e:
            raise SpotifyAPIError(str(e))
        return list(artists.keys())
Ejemplo n.º 3
0
def resolve_playlist(
    spotify: spotipy.Spotify,
    username: str,
    title: str,
    description: str,
    create: bool = False,
    collab: bool = False,
) -> Dict[str, Dict]:
    existing_playlists = spotify.user_playlists(username)
    playlists_list: list = existing_playlists["items"]
    ret: Dict[str, Dict] = dict()
    while playlists_list:
        playlist = playlists_list.pop()
        if playlist["description"] == description and (
                not collab or
            (collab and playlist.get("collaborative", False))):
            ret[playlist["id"]] = playlist
        if len(playlists_list) == 0 and existing_playlists["next"]:
            existing_playlists = spotify.next(existing_playlists)
            playlists_list = existing_playlists["items"]
        else:
            playlist = None
    if not ret and create:
        playlist = spotify.user_playlist_create(username,
                                                title,
                                                public=False,
                                                description=description)
        ret[playlist["id"]] = playlist
    return ret
Ejemplo n.º 4
0
def get_all(spotify: spotipy.Spotify, results: dict):
    """Grab more results until none remain."""
    items = results["items"]
    while results["next"]:
        results = spotify.next(results)
        items.extend(results["items"])

    return items
Ejemplo n.º 5
0
def get_playlist_tracks(sp: Spotify, playlist_id: str) -> List[Dict[str, Any]]:
    """Get playlists tracks"""
    tracks = []  # type: List[Dict[str, Any]]
    results = sp.playlist_items(playlist_id, limit=100)
    tracks.extend(results["items"])
    while results["next"]:
        results = sp.next(results)
        tracks.extend(results["items"])
    return tracks
Ejemplo n.º 6
0
def get_playlists(sp: Spotify) -> List[Dict[str, Any]]:
    """Get user's playlists"""
    playlists = []  # type: List[Dict[str, Any]]
    results = sp.current_user_playlists(limit=50)
    playlists.extend(results["items"])
    while results["next"]:
        results = sp.next(results)
        playlists.extend(results["items"])
    return playlists
Ejemplo n.º 7
0
def get_saved_tracks(sp: Spotify) -> List[Dict[str, Any]]:
    """Returns the list of tracks saved in user library"""
    tracks = []  # type: List[Dict[str, Any]]
    results = sp.current_user_saved_tracks(limit=50)
    tracks.extend(results["items"])
    while results["next"]:
        results = sp.next(results)
        tracks.extend(results["items"])
    return tracks
Ejemplo n.º 8
0
def auto_paginate(spotify: spotipy.Spotify, paginated):
    while True:
        for item in paginated["items"]:
            yield item

        if paginated["next"]:
            paginated = spotify.next(paginated)
        else:
            break
Ejemplo n.º 9
0
def get_followed_artists(sp: Spotify) -> List[Dict[str, Any]]:
    """Returns the full list of followed artists"""
    artists = []  # type: List[Dict[str, Any]]
    results = sp.current_user_followed_artists(limit=50)["artists"]
    artists.extend(results["items"])
    while results["next"]:
        results = sp.next(results)["artists"]
        artists.extend(results["items"])
    artists.sort(key=lambda artist: artist["name"].lower())
    return artists
Ejemplo n.º 10
0
def get_all_objects(initial_results: Dict[str, Any],
                    spotify_client: Spotify) -> List[Any]:
    results = initial_results
    objects = results["items"]

    while results["next"] is not None:
        results = spotify_client.next(results)
        objects.extend(results["items"])

    return objects
Ejemplo n.º 11
0
def get_song_ids(sp: spotipy.Spotify, playlist_id: str) -> List[str]:
    playlist = sp.playlist_tracks(playlist_id, fields="items(track(id)), next")
    ret = []
    cond = True
    while cond:
        items = playlist["items"]
        ret.extend([item["track"]["id"] for item in items])
        cond = playlist.get("next", None)
        if cond:
            playlist = sp.next(playlist)
    return ret
Ejemplo n.º 12
0
def get(_spotify: spotipy.Spotify, playlistId: str, publicOnly=False) -> dict:
    result = _spotify.playlist_items(playlist_id=playlistId)

    while result['next']:
        tmp = _spotify.next(result)
        result['items'].extend(tmp['items'])
        result['next'] = tmp['next']

    if publicOnly:
        result['items'] = [
            item for item in result['items'] if not item['is_local']
        ]

    return result
Ejemplo n.º 13
0
class SpotifyDumper:
    def __init__(self):
        self.sp = Spotify(auth_manager=SpotifyOAuth(scope="user-library-read"))

    def dump_saved_songs(self, spotify_user_id: str):
        with track_writer() as w:
            for track in self._get_liked_artists():
                w.write_track(track)

    def _get_liked_artists(self) -> Iterable[Track]:
        results = self.sp.current_user_saved_tracks()
        yield from self._consume_all(_parse_saved_tracks_chunk, results)

    def _consume_all(self, parser: Callable[[dict], Iterable[T]],
                     result: dict) -> Iterable[T]:
        yield from parser(result)
        while result['next']:
            result = self.sp.next(result)
            yield from parser(result)
Ejemplo n.º 14
0
async def get_spotify_playlist(url):
    # Url can be a spotify url or uri.
    user = ''
    playlist_id = ''
    songs = []

    token = prompt_for_user_token('mdeception', 'user-library-read')

    spotify = Spotify(auth=token)
    if not 'http' in url:
        user = url.split('user:'******':')[0]
        playlist_id = url.split(':')[-1]
    else:
        user = url.split('user/')[1].split('/')[0]
        playlist_id = url.split('/')[-1]

    playlist = spotify.user_playlist(user,
                                     playlist_id,
                                     fields='tracks, next, name')

    tracks = playlist['tracks']
    for t in tracks['items']:
        track = t['track']
        songs.append('%s %s' %
                     (track['name'], ' & '.join(x['name']
                                                for x in track['artists'])))

    while tracks['next']:
        tracks = spotify.next(tracks)
        for t in tracks['items']:
            track = t['track']
            songs.append(
                '%s %s' %
                (track['name'], ' & '.join(x['name']
                                           for x in track['artists'])))

    return (playlist['name'], user, songs)
Ejemplo n.º 15
0
def trackInPlaylist(spotify: Spotify, trackname, playlist):

    result = spotify.playlist_tracks(playlist, offset=0, limit=50)

    while result["next"]:
        try:
            if result["items"]["track"][trackname]:
                print("match")
                return True
        except KeyError:
            result = spotify.next(result)
        '''for track in result["items"]:
            track = track["track"]

            if track["name"] == trackname:
                print("match")
                print(trackname)
                return True'''

        #result = spotify.next(result)

    print("miss")
    print(trackname)
    return False
Ejemplo n.º 16
0
class Login:
    def __init__(self, token):
        self.spo = Spotify(generate_token())

        self.req = Session()
        self.req.cookies['arl'] = token

        user_id = (self.req.get("https://www.deezer.com/").text.split(
            "'deezer_user_id': ")[1].split(",")[0])

        if user_id == "0":
            raise exceptions.BadCredentials("Wrong token: %s :(" % token)

    def get_api(self, method, api_token="null", json=None):
        params = {
            "api_version": "1.0",
            "api_token": api_token,
            "input": "3",
            "method": method
        }

        try:
            return self.req.post(api_link, params=params,
                                 json=json).json()['results']
        except:
            return self.req.post(api_link, params=params,
                                 json=json).json()['results']

    def download(self,
                 link,
                 name,
                 quality,
                 recursive_quality,
                 recursive_download,
                 datas,
                 not_interface,
                 directory,
                 zips=False):
        if not quality in qualities:
            raise exceptions.QualityNotFound(
                "The qualities have to be FLAC or MP3_320 or MP3_256 or MP3_128"
            )

        self.token = self.get_api("deezer.getUserData")['checkForm']

        def get_infos(method, json):
            infos = None

            while not "MD5_ORIGIN" in str(infos):
                infos = self.get_api(method, self.token, json)

            return infos

        def ultimatum(infos, datas, name, quality):
            extension = ".mp3"
            ids = infos['SNG_ID']
            key = "FILESIZE_" + quality

            if int(infos[key]) > 0 and quality == "FLAC":
                quality = "9"
                extension = ".flac"
                qualit = "FLAC"

            elif int(infos[key]) > 0 and quality == "MP3_320":
                quality = "3"
                qualit = "320"

            elif int(infos[key]) > 0 and quality == "MP3_256":
                quality = "5"
                qualit = "256"

            elif int(infos[key]) > 0 and quality == "MP3_128":
                quality = "1"
                qualit = "128"

            else:
                if not recursive_quality:
                    raise exceptions.QualityNotFound(
                        "The quality chosen can't be downloaded")

                for a in qualities:
                    if int(infos['FILESIZE_' + a]) > 0:
                        quality = qualities[a]['quality']
                        extension = qualities[a]['extension']
                        qualit = qualities[a]['qualit']
                        break
                    else:
                        if a == "MP3_128":
                            raise exceptions.TrackNotFound(
                                "There isn't any quality avalaible for download this song: %s"
                                % name)

            name += " ({}){}".format(qualit, extension)

            if os.path.isfile(name):
                if recursive_download:
                    return name

                ans = input(
                    "Track %s already exists, do you want to redownload it?(y or n):"
                    % name)

                if not ans in answers:
                    return name

            try:
                md5 = infos['FALLBACK']['MD5_ORIGIN']
            except KeyError:
                md5 = infos['MD5_ORIGIN']

            hashs = genurl(md5, quality, ids, infos['MEDIA_VERSION'])

            try:
                crypt = request(
                    "https://e-cdns-proxy-{}.dzcdn.net/mobile/1/{}".format(
                        md5[0], hashs))
            except IndexError:
                raise exceptions.TrackNotFound("Track: %s not found:(" % name)

            if len(crypt.content) == 0:
                raise exceptions.TrackNotFound(
                    "Something is wrong with %s :(" % name)

            encry = open(name, "wb")
            encry.write(crypt.content)
            encry.close()
            decry = open(name, "wb")

            decryptfile(crypt.iter_content(2048), calcbfkey(ids), decry)

            write_tags(name, add_more_tags(datas, infos, ids))

            return name

        def add_more_tags(datas, infos, ids):
            json = {"sng_id": ids}

            try:
                datas['author'] = " & ".join(
                    infos['SNG_CONTRIBUTORS']['author'])
            except:
                datas['author'] = ""

            try:
                datas['composer'] = " & ".join(
                    infos['SNG_CONTRIBUTORS']['composer'])
            except:
                datas['composer'] = ""

            try:
                datas['lyricist'] = " & ".join(
                    infos['SNG_CONTRIBUTORS']['lyricist'])
            except:
                datas['lyricist'] = ""

            try:
                datas['version'] = infos['VERSION']
            except KeyError:
                datas['version'] = ""

            need = self.get_api("song.getLyrics", self.token, json)

            try:
                datas['lyric'] = need['LYRICS_TEXT']
                datas['copyright'] = need['LYRICS_COPYRIGHTS']
                datas['lyricist'] = need['LYRICS_WRITERS']
            except KeyError:
                datas['lyric'] = ""
                datas['copyright'] = ""
                datas['lyricist'] = ""

            return datas

        ids = link.split("/")[-1]

        if "track" in link:
            json = {"sng_id": ids}

            infos = get_infos(method_get_track, json)
            image = choose_img(infos['ALB_PICTURE'])
            datas['image'] = image
            song = "{} - {}".format(datas['music'], datas['artist'])

            if not not_interface:
                print("Downloading: %s" % song)

            try:
                nams = ultimatum(infos, datas, name, quality)
            except exceptions.TrackNotFound:
                url = request(
                    "https://api.deezer.com/search/track/?q=%s" %
                    song.replace("#", ""), True).json()

                try:
                    for b in range(url['total'] + 1):
                        if url['data'][b]['title'] == datas['music'] or datas[
                                'music'] in url['data'][b]['title_short']:
                            URL = url['data'][b]['link']
                            break
                except IndexError:
                    raise exceptions.TrackNotFound("Track not found: %s" %
                                                   song)

                json = {"sng_id": URL.split("/")[-1]}

                infos = get_infos(method_get_track, json)
                nams = ultimatum(infos, datas, name, quality)

            return nams

        nams = []
        detas = {}
        zip_name = ""
        quali = ""

        json = {"alb_id": ids, "nb": -1}

        infos = get_infos(method_get_album, json)['data']

        image = choose_img(infos[0]['ALB_PICTURE'])

        detas['image'] = image
        detas['album'] = datas['album']
        detas['year'] = datas['year']
        detas['genre'] = datas['genre']
        detas['ar_album'] = datas['ar_album']
        detas['label'] = datas['label']

        for a in tqdm(range(len(name)), disable=not_interface):
            detas['music'] = datas['music'][a]
            detas['artist'] = datas['artist'][a]
            detas['tracknum'] = datas['tracknum'][a]
            detas['discnum'] = datas['discnum'][a]
            detas['bpm'] = datas['bpm'][a]
            detas['gain'] = datas['gain'][a]
            detas['duration'] = datas['duration'][a]
            detas['isrc'] = datas['isrc'][a]
            song = "{} - {}".format(detas['music'], detas['artist'])

            try:
                nams.append(ultimatum(infos[a], detas, name[a], quality))
            except exceptions.TrackNotFound:
                try:
                    url = request(
                        "https://api.deezer.com/search/track/?q=%s" %
                        song.replace("#", ""), True).json()

                    for b in range(url['total'] + 1):
                        if url['data'][b]['title'] == detas['music'] or detas[
                                'music'] in url['data'][b]['title_short']:
                            URL = url['data'][b]['link']
                            break

                    json = {"sng_id": URL.split("/")[-1]}

                    nams.append(
                        ultimatum(get_infos(method_get_track, json), detas,
                                  name[a], quality))
                except (exceptions.TrackNotFound, IndexError,
                        exceptions.InvalidLink):
                    nams.append(name[a])
                    print("Track not found: %s :(" % song)
                    continue

            quali = (nams[a].split("(")[-1].split(")")[0])

        if zips:
            zip_name = ("%s%s (%s).zip" %
                        (directory, directory.split("/")[-2], quali))

            create_zip(zip_name, nams)

        return nams, zip_name

    def download_trackdee(self,
                          URL,
                          output=stock_output + "/",
                          quality=stock_quality,
                          recursive_quality=stock_recursive_quality,
                          recursive_download=stock_recursive_download,
                          not_interface=stock_not_interface):
        datas = {}
        URL = URL.split("?utm")[0]
        URL1 = "https://www.deezer.com/track/%s" % URL.split("/")[-1]
        URL2 = "https://api.deezer.com/track/%s" % URL.split("/")[-1]
        url = request(URL2, True).json()

        url1 = request("http://api.deezer.com/album/%d" % url['album']['id'],
                       True).json()

        datas['music'] = url['title']
        array = []

        for a in url['contributors']:
            if a['name'] != "":
                array.append(a['name'])

        array.append(url['artist']['name'])

        datas['artist'] = artist_sort(array)
        datas['album'] = url1['title']
        datas['tracknum'] = str(url['track_position'])
        datas['discnum'] = str(url['disk_number'])
        datas['year'] = url['release_date']
        datas['genre'] = []

        try:
            for a in url1['genres']['data']:
                datas['genre'].append(a['name'])
        except KeyError:
            pass

        datas['genre'] = " & ".join(datas['genre'])
        datas['ar_album'] = []

        for a in url1['contributors']:
            if a['role'] == "Main":
                datas['ar_album'].append(a['name'])

        datas['ar_album'] = " & ".join(datas['ar_album'])
        datas['label'] = url1['label']
        datas['bpm'] = str(url['bpm'])
        datas['gain'] = str(url['gain'])
        datas['duration'] = str(url['duration'])
        datas['isrc'] = url['isrc']
        album = var_excape(datas['album'])

        directory = ("%s%s %s/" % (output, album, url1['upc']))

        check_dir(directory)

        name = ("%s%s CD %s TRACK %s" %
                (directory, album, datas['discnum'], datas['tracknum']))

        name = self.download(URL, name, quality, recursive_quality,
                             recursive_download, datas, not_interface,
                             directory)

        return name

    def download_albumdee(self,
                          URL,
                          output=stock_output + "/",
                          quality=stock_quality,
                          recursive_quality=stock_recursive_quality,
                          recursive_download=stock_recursive_download,
                          not_interface=stock_not_interface,
                          zips=stock_zip):
        datas = {}
        datas['music'] = []
        datas['artist'] = []
        datas['tracknum'] = []
        datas['discnum'] = []
        datas['bpm'] = []
        datas['gain'] = []
        datas['duration'] = []
        datas['isrc'] = []
        names = []
        array = []
        URL = URL.split("?utm")[0]
        URL1 = "https://www.deezer.com/album/%s" % URL.split("/")[-1]
        URL2 = "https://api.deezer.com/album/%s" % URL.split("/")[-1]
        url = request(URL2, True).json()
        datas['album'] = url['title']
        datas['label'] = url['label']
        datas['year'] = url['release_date']
        datas['genre'] = []

        try:
            for a in url['genres']['data']:
                datas['genre'].append(a['name'])
        except KeyError:
            pass

        datas['genre'] = " & ".join(datas['genre'])
        datas['ar_album'] = []

        for a in url['contributors']:
            if a['role'] == "Main":
                datas['ar_album'].append(a['name'])

        datas['ar_album'] = " & ".join(datas['ar_album'])
        album = var_excape(datas['album'])

        directory = ("%s%s %s/" % (output, album, url['upc']))

        for a in url['tracks']['data']:
            del array[:]
            datas['music'].append(a['title'])

            ur = request("https://api.deezer.com/track/%d" % a['id'],
                         True).json()

            discnum = str(ur['disk_number'])
            tracknum = str(ur['track_position'])

            names.append("%s%s CD %s TRACK %s" %
                         (directory, album, discnum, tracknum))

            datas['tracknum'].append(tracknum)
            datas['discnum'].append(discnum)

            datas['bpm'].append(str(ur['bpm']))

            datas['gain'].append(str(ur['gain']))

            datas['duration'].append(str(ur['duration']))

            datas['isrc'].append(ur['isrc'])

            for a in ur['contributors']:
                if a['name'] != "":
                    array.append(a['name'])

            array.append(ur['artist']['name'])

            datas['artist'].append(artist_sort(array))

        check_dir(directory)

        names, zip_name = self.download(URL, names, quality, recursive_quality,
                                        recursive_download, datas,
                                        not_interface, directory, zips)

        if zips:
            return names, zip_name

        return names

    def download_playlistdee(self,
                             URL,
                             output=stock_output + "/",
                             quality=stock_quality,
                             recursive_quality=True,
                             recursive_download=True,
                             not_interface=stock_not_interface,
                             zips=stock_zip):
        array = []
        URL = URL.split("?utm")[0]
        ids = URL.split("/")[-1]

        url = request("https://api.deezer.com/playlist/%s" % ids, True).json()

        for a in url['tracks']['data']:
            try:
                array.append(
                    self.download_trackdee(a['link'], output, quality,
                                           recursive_quality,
                                           recursive_download, not_interface))
            except (exceptions.TrackNotFound, exceptions.NoDataApi):
                song = "{} - {}".format(a['title'], a['artist']['name'])
                print("Track not found: %s" % song)
                array.append(song)

        if zips:
            zip_name = "{}playlist {}.zip".format(output, ids)
            create_zip(zip_name, array)
            return array, zip_name

        return array

    def download_trackspo(self,
                          URL,
                          output=stock_output + "/",
                          quality=stock_quality,
                          recursive_quality=stock_recursive_quality,
                          recursive_download=stock_recursive_download,
                          not_interface=stock_not_interface):
        URL = URL.split("?")[0]

        try:
            url = self.spo.track(URL)
        except Exception as a:
            if not "The access token expired" in str(a):
                raise exceptions.InvalidLink("Invalid link ;)")

            self.spo = Spotify(generate_token())

            url = self.spo.track(URL)

        isrc = url['external_ids']['isrc']

        url = request("https://api.deezer.com/track/isrc:%s" % isrc,
                      True).json()

        name = self.download_trackdee(url['link'], output, quality,
                                      recursive_quality, recursive_download,
                                      not_interface)

        return name

    def download_albumspo(self,
                          URL,
                          output=stock_output + "/",
                          quality=stock_quality,
                          recursive_quality=stock_recursive_quality,
                          recursive_download=stock_recursive_download,
                          not_interface=stock_not_interface,
                          zips=stock_zip):
        URL = URL.split("?")[0]

        try:
            tracks = self.spo.album(URL)
        except Exception as a:
            if not "The access token expired" in str(a):
                raise exceptions.InvalidLink("Invalid link ;)")

            self.spo = Spotify(generate_token())

            tracks = self.spo.album(URL)

        tot = tracks['total_tracks']

        try:
            upc = tracks['external_ids']['upc']

            while upc[0] == "0":
                upc = upc[1:]

            url = request("https://api.deezer.com/album/upc:%s" % upc).json()

            names = self.download_albumdee(url['link'], output, quality,
                                           recursive_quality,
                                           recursive_download, not_interface,
                                           zips)
        except KeyError:
            search = tot // 5

            try:
                url = self.spo.track(tracks['tracks']['items'][search]
                                     ['external_urls']['spotify'])
            except:
                self.spo = Spotify(generate_token())

                url = self.spo.track(tracks['tracks']['items'][search]
                                     ['external_urls']['spotify'])

            isrc = url['external_ids']['isrc']

            try:
                ids = request("https://api.deezer.com/track/isrc:%s" % isrc,
                              True).json()['album']['id']

                tracks = request("https://api.deezer.com/album/%d" % ids,
                                 True).json()

                if tot != tracks['nb_tracks']:
                    raise exceptions.TrackNotFound("")

                names = self.download_albumdee(tracks['link'], output, quality,
                                               recursive_quality,
                                               recursive_download,
                                               not_interface, zips)
            except (exceptions.TrackNotFound, exceptions.NoDataApi):
                raise exceptions.AlbumNotFound("Album %s not found :(" %
                                               tracks['name'])

        return names

    def download_playlistspo(self,
                             URL,
                             output=stock_output + "/",
                             quality=stock_quality,
                             recursive_quality=stock_recursive_quality,
                             recursive_download=stock_recursive_download,
                             not_interface=stock_not_interface,
                             zips=stock_zip):
        array = []
        URL = URL.split("?")[0].split("/")

        try:
            tracks = self.spo.user_playlist_tracks(URL[-3],
                                                   playlist_id=URL[-1])
        except Exception as a:
            if not "The access token expired" in str(a):
                raise exceptions.InvalidLink("Invalid link ;)")

            self.spo = Spotify(generate_token())

            tracks = self.spo.user_playlist_tracks(URL[-3],
                                                   playlist_id=URL[-1])

        for a in tracks['items']:
            try:
                array.append(
                    self.download_trackspo(
                        a['track']['external_urls']['spotify'], output,
                        quality, recursive_quality, recursive_download,
                        not_interface))
            except:
                print("Track not found :(")
                array.append("None")

        if tracks['total'] != 100:
            for a in range(tracks['total'] // 100):
                try:
                    tracks = self.spo.next(tracks)
                except:
                    self.spo = Spotify(generate_token())

                    tracks = self.spo.next(tracks)

                for a in tracks['items']:
                    try:
                        array.append(
                            self.download_trackspo(
                                a['track']['external_urls']['spotify'], output,
                                quality, recursive_quality, recursive_download,
                                not_interface))
                    except:
                        print("Track not found :(")
                        array.append("None")

        if zips:
            zip_name = "{}playlist {}.zip".format(output, URL[-1])
            create_zip(zip_name, array)
            return array, zip_name

        return array

    def download_name(self,
                      artist,
                      song,
                      output=stock_output + "/",
                      quality=stock_quality,
                      recursive_quality=stock_recursive_quality,
                      recursive_download=stock_recursive_download,
                      not_interface=stock_not_interface):
        query = "track:{} artist:{}".format(song, artist)

        try:
            search = self.spo.search(query)
        except:
            self.spo = Spotify(generate_token())

            search = self.spo.search(query)

        try:
            return self.download_trackspo(
                search['tracks']['items'][0]['external_urls']['spotify'],
                output, quality, recursive_quality, recursive_download,
                not_interface)
        except IndexError:
            raise exceptions.TrackNotFound("Track not found: :(")
Ejemplo n.º 17
0
scope = 'user-library-read'
client_id = os.environ.get('CLIENT_ID')
client_secret = os.environ.get('CLIENT_SECRET')

token = util.prompt_for_user_token(username, scope, client_id=client_id, client_secret=client_secret,\
     redirect_uri='http://localhost:5000')

sp = Spotify(auth=token)

saved_tracks = sp.current_user_saved_tracks(limit=20)

songs = list()

while saved_tracks:
    for track in saved_tracks['items']:
        name = track['track']['name']
        artists = list()
        for artist in track['track']['artists']:
            artists.append(artist['name'])
        songs.append(name + ' ' + ' '.join(artists))
        print('done....')
    if saved_tracks['next']:
        saved_tracks = sp.next(saved_tracks)
    else:
        saved_tracks = None

f = open('likes.txt', 'w')

for song in songs:
    f.write(song + os.linesep)
Ejemplo n.º 18
0
class Sortify(object):
    def __init__(self, client_id, client_secret, redirect_uri, user_id):
        """read readme pls

        Arguments:
            client_id {str} -- Client ID for Spotify app
            client_secret {str} -- Client secret for Spotify app
            redirect_uri {str} -- Redirect URI for Spotify app
            user_id {str} -- Your user ID
        """
        self.user_id = user_id

        os.environ["SPOTIPY_CLIENT_ID"] = CLIENT_ID
        os.environ["SPOTIPY_CLIENT_SECRET"] = CLIENT_SECRET
        os.environ["SPOTIPY_REDIRECT_URI"] = REDIRECT_URI

        self.sp = Spotify(auth=util.prompt_for_user_token(
            self.user_id, "playlist-modify-public"))
        self.sp.trace = False

    def sort_by_attributes(self, playlist, attrs, attr_ix):
        """Recursively sorts the playlist one attribute at a time

        Arguments:
            playlist {list(str)} -- Playlist response object.
            attrs {list(str)} -- List of attributes to sort by.
            attr_ix {int} -- Index of the attributes to recurse with.

        Returns:
            {list(str)} -- Sorted playlist - who could've guessed?
        """
        if attr_ix:

            attr = attrs[attr_ix - 1]
            attr_ix -= 1

            if "artists" in attr:
                playlist.sort(key=lambda x: x["artists"][0]["name"].lower())
                if "reversed" in attr:
                    playlist.reverse()
                return self.sort_by_attributes(playlist, attrs, attr_ix)

            elif "tracks" in attr:
                playlist.sort(key=lambda x: x["name"].lower())
                if "reversed" in attr:
                    playlist.reverse()
                return self.sort_by_attributes(playlist, attrs, attr_ix)

            elif "album_release_date" in attr:
                playlist.sort(key=lambda x: x["album"]["release_date"])
                if "reversed" in attr:
                    playlist.reverse()
                return self.sort_by_attributes(playlist, attrs, attr_ix)

            elif "track_number" in attr:
                playlist.sort(key=lambda x: x["track_number"])
                if "reversed" in attr:
                    playlist.reverse()
                return self.sort_by_attributes(playlist, attrs, attr_ix)

            raise AttributeError(
                "Invalid attribute provided. For a customized "
                "attribute, use the `custom` parameter.")

        return playlist

    def dump(self, playlist_name, playlist_id, playlist):
        """Dumps the track number, track name and artist in the updated playlist

        Arguments:
            playlist_name {str} -- Parameter name describes itself >:(
            playlist_id {str} -- Parameter name describes itself >:(
            playlist {list(obj)} -- Parameter name describes itself >:(
        """
        print("Playlist name: {}".format(playlist_name))
        print("Playlist ID: {}\n".format(playlist_id))
        print("{:6s} {:60s} {:s}".format("", "Track", "Artist"))
        print("{:6s} {:60s} {:s}".format("", "-----", "------"))
        for i, track in enumerate(playlist):
            print("   {:3d} {:60s} {:s}".format(i + 1, track["name"],
                                                track["artists"][0]["name"]))

    def sort_playlist(self,
                      playlist_names,
                      sort_by,
                      ready,
                      reverse=False,
                      custom=None,
                      dump=False):
        """Sorts playlist lol

        Arguments:
            playlist_names {list(str)} -- Names of the playlists duh. Will
                                          raise an exception if any playlist
                                          isn't associated with the username.
                                          Which is also a good indication the
                                          user does not own the playlist.
            sort_by {list(str)} -- List of attributes to sort by.
            ready {bool} -- Option to commit changes to the updated playlist.
            custom {func} -- Optional custom sorting key, like if you wanna
                             sort by track name string length or something
                             similarly absurd.
            dump {bool} (default: False) -- Option to dump the reordered
                                            playlist in a purdy format.
        """
        playlists = self.sp.user_playlists(self.user_id)
        new_playlist = []

        for playlist in playlists["items"]:
            if playlist["name"] in playlist_names:

                playlist_names.remove(playlist["name"])
                tracks = self.sp.user_playlist(self.user_id,
                                               playlist["id"])["tracks"]
                new_playlist.extend(i["track"] for i in tracks["items"])

                while tracks["next"]:
                    tracks = self.sp.next(tracks)
                    new_playlist.extend(i["track"] for i in tracks["items"])

                new_playlist = self.sort_by_attributes(new_playlist, sort_by,
                                                       len(sort_by))

                if custom:
                    new_playlist.sort(key=custom)

                if reverse:
                    new_playlist.reverse()

                if dump:
                    self.dump(playlist["name"], playlist["id"], new_playlist)

                if ready:

                    if len(new_playlist) > 99:
                        max_tracks = 100
                        new_playlist = [
                            new_playlist[i * max_tracks:(i + 1) * max_tracks]
                            for i in range((len(new_playlist) + max_tracks -
                                            1) // max_tracks)
                        ]
                        self.sp.user_playlist_replace_tracks(
                            self.user_id, playlist["id"],
                            [x["uri"] for x in new_playlist[0]])
                        for playlist_chunk in new_playlist[1:]:
                            self.sp.user_playlist_add_tracks(
                                self.user_id, playlist["id"],
                                [x["uri"] for x in playlist_chunk])

                    else:
                        self.sp.user_playlist_replace_tracks(
                            self.user_id, playlist["id"],
                            [x["uri"] for x in new_playlist])

                    print("Playlist successfully updated")

                new_playlist = []

        if len(playlist_names):
            raise AttributeError("Some playlists were not found: {}".format(
                " ,".join(playlist_names)))
Ejemplo n.º 19
0
class AuthTestSpotipy(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'
    """

    playlist = "spotify:user:plamere:playlist:2oCEWyyAPbZp9xhVSxZavx"
    playlist_new_id = "spotify:playlist:7GlxpQjjxRjmbb3RP2rDqI"
    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"]

    album_ids = ["spotify:album:6kL09DaURb7rAoqqaA51KU",
                 "spotify:album:6RTzC0rDbvagTSJLlY7AKl"]

    bad_id = 'BAD_ID'

    @classmethod
    def setUpClass(self):
        if sys.version_info >= (3, 2):
            # >= Python3.2 only
            warnings.filterwarnings(
                "ignore",
                category=ResourceWarning,  # noqa
                message="unclosed.*<ssl.SSLSocket.*>")

        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 = (
            'playlist-modify-public '
            'user-library-read '
            'user-follow-read '
            'user-library-modify '
            'user-read-private '
            'user-top-read '
            'user-follow-modify '
            'user-read-recently-played '
            'ugc-image-upload'
        )

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

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

    # Helper
    def get_or_create_spotify_playlist(self, playlist_name):
        playlists = self.spotify.user_playlists(self.username)
        while playlists:
            for item in playlists['items']:
                if item['name'] == playlist_name:
                    return item
            playlists = self.spotify.next(playlists)
        return self.spotify.user_playlist_create(
            self.username, playlist_name)

    # Helper
    def get_as_base64(self, url):
        import base64
        return base64.b64encode(requests.get(url).content).decode("utf-8")

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

    def test_basic_user_profile(self):
        user = self.spotify.user(self.username)
        self.assertTrue(user['id'] == self.username.lower())

    def test_current_user(self):
        user = self.spotify.current_user()
        self.assertTrue(user['id'] == self.username.lower())

    def test_me(self):
        user = self.spotify.me()
        self.assertTrue(user['id'] == self.username.lower())

    def test_user_playlists(self):
        playlists = self.spotify.user_playlists(self.username, limit=5)
        self.assertTrue('items' in playlists)
        self.assertTrue(len(playlists['items']) == 5)

    def test_user_playlist_tracks(self):
        playlists = self.spotify.user_playlists(self.username, limit=5)
        self.assertTrue('items' in playlists)
        for playlist in playlists['items']:
            user = playlist['owner']['id']
            pid = playlist['id']
            results = self.spotify.user_playlist_tracks(user, pid)
            self.assertTrue(len(results['items']) >= 0)

    def test_current_user_saved_albums(self):
        # List
        albums = self.spotify.current_user_saved_albums()
        self.assertTrue(len(albums['items']) > 1)

        # Add
        self.spotify.current_user_saved_albums_add(self.album_ids)

        # Contains
        self.assertTrue(
            self.spotify.current_user_saved_albums_contains(
                self.album_ids) == [
                True, True])

        # Remove
        self.spotify.current_user_saved_albums_delete(self.album_ids)
        albums = self.spotify.current_user_saved_albums()
        self.assertTrue(len(albums['items']) > 1)

    def test_current_user_playlists(self):
        playlists = self.spotify.current_user_playlists(limit=10)
        self.assertTrue('items' in playlists)
        self.assertTrue(len(playlists['items']) == 10)

    def test_user_playlist_follow(self):
        self.spotify.user_playlist_follow_playlist(
            'plamere', '4erXB04MxwRAVqcUEpu30O')
        follows = self.spotify.user_playlist_is_following(
            'plamere', '4erXB04MxwRAVqcUEpu30O', [
                self.spotify.current_user()['id']])

        self.assertTrue(len(follows) == 1, 'proper follows length')
        self.assertTrue(follows[0], 'is following')
        self.spotify.user_playlist_unfollow(
            'plamere', '4erXB04MxwRAVqcUEpu30O')

        follows = self.spotify.user_playlist_is_following(
            'plamere', '4erXB04MxwRAVqcUEpu30O', [
                self.spotify.current_user()['id']])
        self.assertTrue(len(follows) == 1, 'proper follows length')
        self.assertFalse(follows[0], 'is no longer following')

    def test_current_user_saved_tracks(self):
        tracks = self.spotify.current_user_saved_tracks()
        self.assertTrue(len(tracks['items']) > 0)

    def test_current_user_save_and_unsave_tracks(self):
        tracks = self.spotify.current_user_saved_tracks()
        total = tracks['total']
        self.spotify.current_user_saved_tracks_add(self.four_tracks)

        tracks = self.spotify.current_user_saved_tracks()
        new_total = tracks['total']
        self.assertTrue(new_total - total == len(self.four_tracks))

        tracks = self.spotify.current_user_saved_tracks_delete(
            self.four_tracks)
        tracks = self.spotify.current_user_saved_tracks()
        new_total = tracks['total']
        self.assertTrue(new_total == total)

    def test_categories(self):
        response = self.spotify.categories()
        self.assertTrue(len(response['categories']) > 0)

    def test_category_playlists(self):
        response = self.spotify.categories()
        for cat in response['categories']['items']:
            cat_id = cat['id']
            response = self.spotify.category_playlists(category_id=cat_id)
            if len(response['playlists']["items"]) > 0:
                break
        self.assertTrue(True)

    def test_new_releases(self):
        response = self.spotify.new_releases()
        self.assertTrue(len(response['albums']) > 0)

    def test_featured_releases(self):
        response = self.spotify.featured_playlists()
        self.assertTrue(len(response['playlists']) > 0)

    def test_current_user_follows(self):
        response = self.spotify.current_user_followed_artists()
        artists = response['artists']
        self.assertTrue(len(artists['items']) > 0)

    def test_current_user_top_tracks(self):
        response = self.spotify.current_user_top_tracks()
        items = response['items']
        self.assertTrue(len(items) > 0)

    def test_current_user_top_artists(self):
        response = self.spotify.current_user_top_artists()
        items = response['items']
        self.assertTrue(len(items) > 0)

    def test_current_user_recently_played(self):
        # No cursor
        res = self.spotify.current_user_recently_played()
        self.assertTrue(len(res['items']) <= 50)
        played_at = res['items'][0]['played_at']

        # Using `before` gives tracks played before
        res = self.spotify.current_user_recently_played(
            before=res['cursors']['after'])
        self.assertTrue(len(res['items']) <= 50)
        self.assertTrue(res['items'][0]['played_at'] < played_at)
        played_at = res['items'][0]['played_at']

        # Using `after` gives tracks played after
        res = self.spotify.current_user_recently_played(
            after=res['cursors']['before'])
        self.assertTrue(len(res['items']) <= 50)
        self.assertTrue(res['items'][0]['played_at'] > played_at)

    def test_user_playlist_ops(self):
        sp = self.spotify
        # create empty playlist
        playlist = self.get_or_create_spotify_playlist(
            'spotipy-testing-playlist-1')
        playlist_id = playlist['id']

        # remove all tracks from it
        sp.user_playlist_replace_tracks(
            self.username, playlist_id, [])
        playlist = sp.user_playlist(self.username, playlist_id)
        self.assertTrue(playlist['tracks']['total'] == 0)
        self.assertTrue(len(playlist['tracks']['items']) == 0)

        # add tracks to it
        sp.user_playlist_add_tracks(
            self.username, playlist_id, self.four_tracks)
        playlist = sp.user_playlist(self.username, playlist_id)
        self.assertTrue(playlist['tracks']['total'] == 4)
        self.assertTrue(len(playlist['tracks']['items']) == 4)

        # remove two tracks from it

        sp.user_playlist_remove_all_occurrences_of_tracks(self.username,
                                                          playlist_id,
                                                          self.two_tracks)
        playlist = sp.user_playlist(self.username, playlist_id)
        self.assertTrue(playlist['tracks']['total'] == 2)
        self.assertTrue(len(playlist['tracks']['items']) == 2)

        # replace with 3 other tracks
        sp.user_playlist_replace_tracks(self.username,
                                        playlist_id,
                                        self.other_tracks)
        playlist = sp.user_playlist(self.username, playlist_id)
        self.assertTrue(playlist['tracks']['total'] == 3)
        self.assertTrue(len(playlist['tracks']['items']) == 3)

    def test_playlist(self):
        # New playlist ID
        pl = self.spotify.playlist(self.playlist_new_id)
        self.assertTrue(pl["tracks"]["total"] > 0)

        # Old playlist ID
        pl = self.spotify.playlist(self.playlist)
        self.assertTrue(pl["tracks"]["total"] > 0)

    def test_playlist_tracks(self):
        # New playlist ID
        pl = self.spotify.playlist_tracks(self.playlist_new_id, limit=2)
        self.assertTrue(len(pl["items"]) == 2)
        self.assertTrue(pl["total"] > 0)

        # Old playlist ID
        pl = self.spotify.playlist_tracks(self.playlist, limit=2)
        self.assertTrue(len(pl["items"]) == 2)
        self.assertTrue(pl["total"] > 0)

    def test_playlist_upload_cover_image(self):
        pl1 = self.get_or_create_spotify_playlist('spotipy-testing-playlist-1')
        plid = pl1['uri']
        old_b64 = pl1['images'][0]['url']

        # Upload random dog image
        r = requests.get('https://dog.ceo/api/breeds/image/random')
        dog_base64 = self.get_as_base64(r.json()['message'])
        self.spotify.playlist_upload_cover_image(plid, dog_base64)

        # Image must be different
        pl1 = self.spotify.playlist(plid)
        new_b64 = self.get_as_base64(pl1['images'][0]['url'])
        self.assertTrue(old_b64 != new_b64)

    def test_playlist_cover_image(self):
        pl = self.get_or_create_spotify_playlist('spotipy-testing-playlist-1')
        plid = pl['uri']
        res = self.spotify.playlist_cover_image(plid)

        self.assertTrue(len(res) > 0)
        first_image = res[0]
        self.assertTrue('width' in first_image)
        self.assertTrue('height' in first_image)
        self.assertTrue('url' in first_image)

    def test_user_follows_and_unfollows_artist(self):
        # Initially follows 1 artist
        res = self.spotify.current_user_followed_artists()
        self.assertTrue(res['artists']['total'] == 1)

        # Follow 2 more artists
        artists = ["6DPYiyq5kWVQS4RGwxzPC7", "0NbfKEOTQCcwd6o7wSDOHI"]
        self.spotify.user_follow_artists(artists)
        res = self.spotify.current_user_followed_artists()
        self.assertTrue(res['artists']['total'] == 3)

        # Unfollow these 2 artists
        self.spotify.user_unfollow_artists(artists)
        res = self.spotify.current_user_followed_artists()
        self.assertTrue(res['artists']['total'] == 1)

    def test_user_follows_and_unfollows_user(self):
        # TODO improve after implementing `me/following/contains`
        users = ["11111204", "xlqeojt6n7on0j7coh9go8ifd"]

        # Follow 2 more users
        self.spotify.user_follow_users(users)

        # Unfollow these 2 users
        self.spotify.user_unfollow_users(users)

    def test_deprecated_starred(self):
        pl = self.spotify.user_playlist(self.username)
        self.assertTrue(pl["tracks"] is None)
        self.assertTrue(pl["owner"] is None)

    def test_deprecated_user_playlist(self):
        # Test without user due to change from
        # https://developer.spotify.com/community/news/2018/06/12/changes-to-playlist-uris/
        pl = self.spotify.user_playlist(None, self.playlist)
        self.assertTrue(pl["tracks"]["total"] > 0)

    def test_deprecated_user_playlis(self):
        # Test without user due to change from
        # https://developer.spotify.com/community/news/2018/06/12/changes-to-playlist-uris/
        pl = self.spotify.user_playlist_tracks(None, self.playlist, limit=2)
        self.assertTrue(len(pl["items"]) == 2)
        self.assertTrue(pl["total"] > 0)
Ejemplo n.º 20
0
def Link(link, chat_id, quality, msg):
	global spo
	global del1

	del1 += 1
	done = 0
	links1 = []
	links2 = []
	quali = quality.split("MP3_")[-1]
	link = link.split("?")[0]

	try:
		if "spotify" in link:
			if "track/" in link:
				try:
					url = spo.track(link)
				except Exception as a:
					if not "The access token expired" in str(a):
						sendMessage(
							chat_id, "Invalid link %s ;)" % link,
							reply_to_message_id = msg['message_id']
						)

						delete(chat_id)
						return

					spo = Spotify(
						generate_token()
					)

					url = spo.track(link)

				try:
					image1 = url['album']['images'][0]['url']
				except IndexError:
					image1 = "https://e-cdns-images.dzcdn.net/images/cover/1000x1000-000000-80-0-0.jpg"

				sendPhoto(
					chat_id, image1,
					caption = (
						send_image_track_query
						% (
							url['name'],
							url['album']['artists'][0]['name'],
							url['album']['name'],
							url['album']['release_date']
						)
					)
				)

				track(link, chat_id, quality)

			elif "album/" in link:
				try:
					tracks = spo.album(link)
				except Exception as a:
					if not "The access token expired" in str(a):
						sendMessage(
							chat_id, "Invalid link %s ;)" % link,
							reply_to_message_id = msg['message_id']
						)

					delete(chat_id)
					return

					spo = Spotify(
						generate_token()
					)

					tracks = spo.album(link)

				try:
					image3 = tracks['images'][2]['url']
					image2 = tracks['images'][1]['url']
					image1 = tracks['images'][0]['url']
				except IndexError:
					image3 = "https://e-cdns-images.dzcdn.net/images/cover/90x90-000000-80-0-0.jpg"
					image2 = "https://e-cdns-images.dzcdn.net/images/cover/320x320-000000-80-0-0.jpg"
					image1 = "https://e-cdns-images.dzcdn.net/images/cover/1000x1000-000000-80-0-0.jpg"

				tot = tracks['total_tracks']
				conn = connect(db_file)
				c = conn.cursor()
				lin = "album/%s" % link.split("/")[-1]
				count = [0]

				sendPhoto(
					chat_id, image1,
					caption = (
						send_image_album_query
						% (
							tracks['name'],
							tracks['artists'][0]['name'],
							tracks['release_date'],
							tot
						)
					)
				)

				def lazy(a):
					count[0] += a['duration_ms']
					lin = "track/%s" % a['external_urls']['spotify'].split("/")[-1]

					c.execute(
						where_query.format(lin, quali)
					)

					links2.append(lin)

					if c.fetchone():
						links1.append(lin)

				for a in tracks['tracks']['items']:
					lazy(a)

				tracks = tracks['tracks']

				if tot != 50:
					for a in range(tot // 50):
						try:
							tracks = spo.next(tracks)
						except:
							spo = Spotify(
								generate_token()
							)

							tracks = spo.next(tracks)

						for a in tracks['items']:
							lazy(a)

				conn.close()

				if (count[0] / 1000) > 40000:
					sendMessage(chat_id, "If you do this again I will come to your home and I will ddos your ass :)")
					delete(chat_id)
					return

				if len(links1) != tot:
					z = downloa.download_albumspo(
						link,
						quality = quality,
						recursive_quality = True,
						recursive_download = True,
						not_interface = True
					)
				else:
					for a in links2:
						track(a, chat_id, quality)

				done = 1

			elif "playlist/" in link:
				musi = link.split("/")

				try:
					tracks = spo.user_playlist(musi[-3], musi[-1])
				except Exception as a:
					if not "The access token expired" in str(a):
						sendMessage(
							chat_id, "Invalid link ;)",
							reply_to_message_id = msg['message_id']
						)

						delete(chat_id)
						return

					spo = Spotify(
						generate_token()
					)

					tracks = spo.user_playlist(musi[-3], musi[-1])

				try:
					image1 = tracks['images'][0]['url']
				except IndexError:
					image1 = "https://e-cdns-images.dzcdn.net/images/cover/1000x1000-000000-80-0-0.jpg"

				tot = tracks['tracks']['total']

				if tot > 400:
					sendMessage(chat_id, "F**k you")
					delete(chat_id)
					return

				sendPhoto(
					chat_id, image1,
					caption = (
						send_image_playlist_query
						% (
							tracks['tracks']['items'][0]['added_at'],
							tracks['owner']['display_name'],
							tot
						)
					)
				)

				def lazy(a):
					try:
						track(
							a['track']['external_urls']['spotify'],
							chat_id,
							quality
						)
					except:
						try:
							sendMessage(chat_id, "%s Not found :(" % a['track']['name'])
						except:
							sendMessage(chat_id, "Error :(")

				for a in tracks['tracks']['items']:
					lazy(a)

				tot = tracks['tracks']['total']
				tracks = tracks['tracks']

				if tot != 100:
					for a in range(tot // 100):
						try:
							tracks = spo.next(tracks)
						except:
							spo = Spotify(
								generate_token()
							)

							tracks = spo.next(tracks)

						for a in tracks['items']:
							lazy(a)

				done = 1

			else:
				sendMessage(chat_id, "Sorry :( The bot doesn't support this link")

		elif "deezer" in link:
			ids = link.split("/")[-1]

			if "track/" in link:
				try:
					url = request(
						"https://api.deezer.com/track/%s" % ids, chat_id, True
					).json()
				except AttributeError:
					delete(chat_id)
					return

				image1 = check_image(
					url['album']['cover_xl'], ids
				)

				sendPhoto(
					chat_id, image1,
					caption = (
						send_image_track_query
						% (
							url['title'],
							url['artist']['name'],
							url['album']['title'],
							url['album']['release_date']
						)
					)
				)

				track(link, chat_id, quality)

			elif "album/" in link:
				try:
					url = request(
						"https://api.deezer.com/album/%s" % ids, chat_id, True
					).json()
				except AttributeError:
					delete(chat_id)
					return

				if url['duration'] > 40000:
					sendMessage(chat_id, "If you do this again I will come to your home and I will ddos your ass :)")
					delete(chat_id)
					return

				image1 = url['cover_xl']

				if not image1:
					URL = "https://www.deezer.com/album/%s" % ids
					image1 = request(URL).text

					image1 = (
						BeautifulSoup(image1, "html.parser")
						.find("img", class_ = "img_main")
						.get("src")
						.replace("200x200", "1000x1000")
					)

				ima = request(image1).content

				if len(ima) == 13:
					image1 = "https://e-cdns-images.dzcdn.net/images/cover/1000x1000-000000-80-0-0.jpg"

				image2 = image1.replace("1000x1000", "320x320")
				image3 = image1.replace("1000x1000", "90x90")
				conn = connect(db_file)
				c = conn.cursor()
				lin = "album/%s" % ids

				for a in url['tracks']['data']:
					lin = "track/%s" % a['link'].split("/")[-1]

					c.execute(
						where_query.format(lin, quali)
					)

					links2.append(lin)

					if c.fetchone():
						links1.append(lin)

				conn.close()
				tot = url['nb_tracks']

				sendPhoto(
					chat_id, image1,
					caption = (
						send_image_album_query
						% (
							url['title'],
							url['artist']['name'],
							url['release_date'],
							tot
						)
					)
				)

				if len(links1) != tot:
					z = downloa.download_albumdee(
						link,
						quality = quality,
						recursive_quality = True,
						recursive_download = True,
						not_interface = True
					)
				else:
					for a in links2:
						track(a, chat_id, quality)

				done = 1

			elif "playlist/" in link:
				try:
					url = request(
						"https://api.deezer.com/playlist/%s" % ids, chat_id, True
					).json()
				except AttributeError:
					delete(chat_id)
					return

				tot = url['nb_tracks']

				if tot > 400:
					sendMessage(chat_id, "F**k you")
					delete(chat_id)
					return

				sendPhoto(
					chat_id, url['picture_xl'],
					caption = (
						send_image_playlist_query
						% (
							url['creation_date'],
							url['creator']['name'],
							tot
						)
					)
				)

				for a in url['tracks']['data']:
					try:
						track(a['link'], chat_id, quality)
					except:
						song = "{} - {}".format(a['title'], a['artist']['name'])
						sendMessage(chat_id, "Cannot download %s :(" % song)

				done = 1

			elif "artist/" in link:
				link = "https://api.deezer.com/artist/%s" % ids

				try:
					url = request(link, chat_id, True).json()
				except AttributeError:
					delete(chat_id)
					return

				sendPhoto(
					chat_id, url['picture_xl'],
					caption = (
						"👤 Artist: %s \n💽 Album numbers: %d \n👥 Fans on Deezer: %d"
						% (
							url['name'],
							url['nb_album'],
							url['nb_fan']
						)
					),
					reply_markup = InlineKeyboardMarkup(
						inline_keyboard = [
							[
								InlineKeyboardButton(
									text = "TOP 30 🔝",
									callback_data = "%s/top?limit=30" % link
								),
								InlineKeyboardButton(
									text = "ALBUMS 💽",
									callback_data = "%s/albums" % link
								)
							],
							[
								InlineKeyboardButton(
									text = "RADIO 📻",
									callback_data = "%s/radio" % link
								),
								InlineKeyboardButton(
									text = "RELATED 🗣",
									callback_data = "%s/related" % link
								)
							]
						]
					)
				)

			else:
				sendMessage(chat_id, "Sorry :( The bot doesn't support this link %s :(" % link)

		else:
			sendMessage(chat_id, "Sorry :( The bot doesn't support this link %s :(" % link)

		try:
			image3 = request(image3).content

			for a in range(
				len(z)
			):
				sendAudio(chat_id, z[a], links2[a], image3)
		except NameError:
			pass

	except exceptions.QuotaExceeded:
		sendMessage(chat_id, "Please send the link %s again :(" % link)

	except exceptions.AlbumNotFound:
		sendMessage(chat_id, "Album %s didn't find on Deezer :(" % link)
		sendMessage(chat_id, "Try to search it throught inline mode or search the link on Deezer")

	except Exception as a:
		logging.warning(a)
		logging.warning(quality)
		logging.warning(link)

		sendMessage(
			chat_id, "OPS :( Something went wrong please send to @An0nimia this link: {} {}, if this happens again".format(link, quality)
		)

	if done == 1:
		sendMessage(
			chat_id, "FINISHED :) Rate me here https://t.me/BotsArchive/298",
			reply_to_message_id = msg['message_id'],
			reply_markup = InlineKeyboardMarkup(
				inline_keyboard = [
					[
						InlineKeyboardButton(
							text = "SHARE",
							url = "tg://msg?text=Start @%s for download all the songs which you want ;)" % bot_name
						)
					]
				]
			)
		)

	delete(chat_id)
Ejemplo n.º 21
0
def Link(link, chat_id, quality, message_id):
    global spo
    global del1

    del1 += 1
    done = 0
    quali = quality.split("MP3_")[-1]
    link = link.split("?")[0]
    ids = link.split("/")[-1]

    try:
        if "track/" in link:
            if "spotify" in link:
                try:
                    url = spo.track(link)
                except Exception as a:
                    if not "The access token expired" in str(a):
                        sendMessage(chat_id,
                                    "Invalid link %s ;)" % link,
                                    reply_to_message_id=message_id)

                        delete(chat_id)
                        return

                    spo = Spotify(generate_token())

                    url = spo.track(link)

                try:
                    image1 = url['album']['images'][0]['url']
                except IndexError:
                    image1 = song_default_image

                name = url['name']
                artist = url['album']['artists'][0]['name']
                album = url['album']['name']
                date = url['album']['release_date']

            elif "deezer" in link:
                kind = "track"
                api_link = api_track % ids

                try:
                    url = reque(api_link, chat_id, True).json()
                except AttributeError:
                    delete(chat_id)
                    return

                image1 = check_image(url['album']['cover_xl'], ids, kind)

                name = url['title']
                artist = url['artist']['name']
                album = url['album']['title']
                date = url['album']['release_date']

            if any(a in link for a in services_supported):
                sendPhoto(chat_id,
                          image1,
                          caption=(send_image_track_query %
                                   (name, artist, album, date)))

                track(link, chat_id, quality)
            else:
                sendMessage(chat_id, not_supported_links % link)

        elif "album/" in link:
            links = []
            count = [0]

            if "spotify" in link:
                try:
                    tracks = spo.album(link)
                except Exception as a:
                    if not "The access token expired" in str(a):
                        sendMessage(chat_id,
                                    "Invalid link %s ;)" % link,
                                    reply_to_message_id=message_id)

                        delete(chat_id)
                        return

                    spo = Spotify(generate_token())

                    tracks = spo.album(link)

                try:
                    image3 = tracks['images'][2]['url']
                    image1 = tracks['images'][0]['url']
                except IndexError:
                    image3 = image_resize(song_default_image, 90)
                    image1 = song_default_image

                name = tracks['name']
                artist = tracks['artists'][0]['name']
                date = tracks['release_date']
                tot = tracks['total_tracks']

                def lazy(a):
                    count[0] += a['duration_ms']

                    links.append(a['external_urls']['spotify'])

                for a in tracks['tracks']['items']:
                    lazy(a)

                tracks = tracks['tracks']

                for a in range(tot // 50 - 1):
                    try:
                        tracks = spo.next(tracks)
                    except:
                        spo = Spotify(generate_token())

                        tracks = spo.next(tracks)

                    for a in tracks['items']:
                        lazy(a)

                count[0] //= 1000
                mode = downloa.download_albumspo

            elif "deezer" in link:
                api_link = api_album % ids
                kind = "album"

                try:
                    url = reque(api_link, chat_id, True).json()
                except AttributeError:
                    delete(chat_id)
                    return

                count[0] = url['duration']
                image1 = check_image(url['cover_xl'], ids, kind)
                image3 = image_resize(image1, 90)
                tot = url['nb_tracks']

                links = [a['link'] for a in url['tracks']['data']]

                name = url['title']
                artist = url['artist']['name']
                date = url['release_date']
                mode = downloa.download_albumdee

            if any(a in link for a in services_supported):
                if count[0] > seconds_limits_album:
                    sendMessage(
                        chat_id,
                        "If you do this again I will come to your home and I will ddos your ass :)"
                    )
                    delete(chat_id)
                    return

                message_id = sendPhoto(
                    chat_id,
                    image1,
                    caption=(send_image_album_query %
                             (name, artist, date, tot)))['message_id']

                conn = connect(db_file)
                c = conn.cursor()
                exists = []

                for a in links:
                    ids = a.split("/")[-1]
                    lins = "track/%s" % ids

                    exist = c.execute(where_query.format(lins,
                                                         quali)).fetchone()

                    if exist:
                        exists.append(exist)

                if len(exists) < len(links) // 3:
                    z = mode(link,
                             quality=quality,
                             recursive_quality=True,
                             recursive_download=True,
                             not_interface=not_interface)

                    image3 = get_image(image3)

                    for a in range(len(z)):
                        sendAudio(chat_id, z[a], links[a], image3)
                else:
                    for a in links:
                        track(a, chat_id, quality)

                done = 1
            else:
                sendMessage(chat_id, not_supported_links % link)

        elif "playlist/" in link:
            links = []

            if "spotify" in link:
                musi = link.split("/")

                try:
                    tracks = spo.user_playlist(musi[-3], musi[-1])
                except Exception as a:
                    if not "The access token expired" in str(a):
                        sendMessage(chat_id,
                                    "Invalid link ;)",
                                    reply_to_message_id=message_id)

                        delete(chat_id)
                        return

                    spo = Spotify(generate_token())

                    tracks = spo.user_playlist(musi[-3], musi[-1])

                try:
                    image1 = tracks['images'][0]['url']
                except IndexError:
                    image1 = song_default_image

                def lazy(a):
                    try:
                        links.append(a['track']['external_urls']['spotify'])
                    except (KeyError, TypeError):
                        links.append("Error :(")

                for a in tracks['tracks']['items']:
                    lazy(a)

                added = tracks['tracks']['items'][0]['added_at']
                owner = tracks['owner']['display_name']
                tot = tracks['tracks']['total']
                tracks = tracks['tracks']

                for a in range(tot // 100 - 1):
                    try:
                        tracks = spo.next(tracks)
                    except:
                        spo = Spotify(generate_token())

                        tracks = spo.next(tracks)

                    for a in tracks['items']:
                        lazy(a)

            elif "deezer" in link:
                api_link = api_playlist % ids

                try:
                    url = reque(api_link, chat_id, True).json()
                except AttributeError:
                    delete(chat_id)
                    return

                links = [a['link'] for a in url['tracks']['data']]

                image1 = url['picture_xl']
                tot = url['nb_tracks']
                added = url['creation_date']
                owner = url['creator']['name']

            if any(a in link for a in services_supported):

                if tot > max_songs:
                    sendMessage(chat_id, "F**k you")
                    delete(chat_id)
                    return

                sendPhoto(chat_id,
                          image1,
                          caption=(send_image_playlist_query %
                                   (added, owner, tot)))

                for a in links:
                    if a.startswith("http"):
                        try:
                            track(a, chat_id, quality)
                        except:
                            sendMessage(chat_id, "Cannot download %s:(" % a)
                    else:
                        sendMessage(chat_id, a)

                done = 1
            else:
                sendMessage(chat_id, not_supported_links % link)

        elif "artist/" in link:
            if "deezer" in link:
                api_link = api_artist % ids

                try:
                    url = reque(api_link, chat_id, True).json()
                except AttributeError:
                    delete(chat_id)
                    return

                keyboard = [[
                    InlineKeyboardButton(
                        queries['top']['text'],
                        callback_data=queries['top']['query'] % api_link),
                    InlineKeyboardButton(
                        queries['albums']['text'],
                        callback_data=queries['albums']['query'] % api_link)
                ],
                            [
                                InlineKeyboardButton(
                                    queries['radio']['text'],
                                    callback_data=queries['radio']['query'] %
                                    api_link),
                                InlineKeyboardButton(
                                    queries['related']['text'],
                                    callback_data=queries['related']['query'] %
                                    api_link)
                            ]]

                image1 = url['picture_xl']
                artist = url['name']
                albums = url['nb_album']
                fans = url['nb_fan']

            if any(a in link for a in services_supported[1:]):
                sendPhoto(chat_id,
                          image1,
                          caption=(send_image_artist_query %
                                   (artist, albums, fans)),
                          reply_markup=InlineKeyboardMarkup(keyboard))
            else:
                sendMessage(chat_id, not_supported_links % link)

        else:
            sendMessage(chat_id, not_supported_links % link)

    except FileNotFoundError:
        sendMessage(chat_id,
                    "Resend link please...",
                    reply_to_message_id=message_id)

    except error.TimedOut:
        sendMessage(chat_id, "Retry after a few minutes")

    except exceptions.QuotaExceeded:
        sendMessage(chat_id, "Please send the link %s again :(" % link)

    except exceptions.AlbumNotFound:
        sendMessage(chat_id, "Album %s didn't find on Deezer :(" % link)
        sendMessage(
            chat_id,
            "Try to search it throught inline mode or search the link on Deezer"
        )

    except Exception as a:
        logging.error(a)
        logging.error(quality)
        logging.error(link)

        sendMessage(
            chat_id,
            "OPS :( Something went wrong please send to @AmineSoukara this link: {} {}, if this happens again"
            .format(link, quality))

    if done == 1:
        sendMessage(chat_id,
                    end_message,
                    reply_to_message_id=message_id,
                    reply_markup=InlineKeyboardMarkup(end_keyboard))

    delete(chat_id)
Ejemplo n.º 22
0
class SpotifyPlaylist(Playlist):
    def __init__(self, *, user: str, client_id: str, client_secret: str,
                 redirect_uri: str, **kwargs):
        """Init SpotifyPlaylist.

        Args:
            user: A str representing the Spotify user id.
            client_id: A str representing the app client id.
            client_secret: A str representing the app client secret.
            redirect_uri: A str representing the app redirect URI.
        """
        # authenticates app to access user's data
        self._token = util.prompt_for_user_token(username=user,
                                                 scope=SCOPE,
                                                 client_id=client_id,
                                                 client_secret=client_secret,
                                                 redirect_uri=redirect_uri)
        self.user = user
        self.spotify = Spotify(auth=self._token)
        self.playlist_count = Counter()

    def remove(self, *, playlist_id: str, n_tracks: int, **kwargs):
        """Delete tracks from specified playlist if adding n_tracks exceeds SONG_LIMIT.

        Delete n tracks from the specified playlist if the size of the
        playlist + n_tracks exceeds SONG_LIMIT, where
        n = len(playlist) + n_tracks - SONG_LIMIT

        Also keeps track of the size of the playlist.

        Args:
            playlist_id: The playlist to add tracks to.
            n_tracks: The number of tracks to add.

        Raises:
            PlaylistError: n_tracks exceeds SONG_LIMIT
        """
        _playlist = self.spotify.user_playlist(user=self.user,
                                               playlist_id=playlist_id,
                                               fields="tracks,next,name")
        tracks = _playlist["tracks"]

        total_tracks = self.playlist_count[playlist_id] + n_tracks
        if total_tracks >= SONG_LIMIT:
            n = total_tracks - SONG_LIMIT

            # make sure we aren't trying to add too many tracks
            if n > self.playlist_count[playlist_id]:
                raise PlaylistError(
                    f"{n_tracks} exceeds playlist song limit of {SONG_LIMIT}")

            count = 0
            remove = []

            for item in tracks["items"]:
                if count >= n:
                    break
                remove.append(item["track"]["id"])
                count += 1

            # remove tracks from playlist
            self.spotify.user_playlist_remove_all_occurrences_of_tracks(
                user=self.user, playlist_id=playlist_id, tracks=remove)
        elif self.playlist_count[playlist_id] == 0:
            while True:
                # count songs in playlist
                self.playlist_count[playlist_id] += tracks["total"]

                if tracks["next"]:
                    tracks = self.spotify.next(tracks)
                else:
                    break

    def add(self, *, playlist_id: str, tracks: list, **kwargs):
        """Add tracks to specified playlist.

        Removes n tracks from the playlist iff the size of the playlist +
        len(tracks) exceeds SONG_LIMIT,
        where n = len(playlist) + len(tracks) - SONG_LIMIT.

        After ensuring the added tracks won't exceed SONG_LIMIT, add
        tracks to the playlist.

        Args:
            playlist_id: A str representing the playlist ID to modify.
            tracks: A list of track IDs to add to the specified playlist.

        Raises:
            PlaylistError: For 4xx - 5xx HTTP response codes or
                if the number of tracks exceeds SONG_LIMIT.

        Returns:
            i'll check
        """
        # remove tracks if the size of the playlist will exceed SONG_LIMIT
        self.remove(playlist_id=playlist_id, n_tracks=len(tracks))

        try:
            # add tracks to the playlist
            results = self.spotify.user_playlist_add_tracks(
                user=self.user, playlist_id=playlist_id, tracks=tracks)

            # keep track of playlist song count
            self.playlist_count[playlist_id] += len(tracks)
            return results
        except (SpotifyException, ) as e:
            raise PlaylistError(str(e))
Ejemplo n.º 23
0
class SpotifyConnection(object):
    def __init__(self, user_data):
        self.user_name = user_data['user_name']
        token = spotipy.util.prompt_for_user_token(
            self.user_name,
            scope='user-read-recently-played',
            client_id=user_data['client_id'],
            client_secret=user_data['client_secret'],
            redirect_uri=user_data['redirect_uri'])
        self.client = Spotify(auth=token)
        self.db = self.init_db()

    def init_db(self):
        return PostgreSQLConnection()

    def get_artist(self, artist_id):
        artist = self.db.session.query(Artist).get(artist_id)
        if artist:
            return artist
        else:
            artist_response = self.client.artist(artist_id)
            artist = Artist()
            artist.artist_id = artist_id
            artist.artist_data = artist_response
            self.db.save_instance(artist)
            print("> Artist {} was not in database.".format(
                artist.artist_data['name']))
            return self.db.session.query(Artist).get(artist_id)

    def get_album(self, album_id):
        album = self.db.session.query(Album).get(album_id)
        if album:
            return album
        else:
            album_response = self.client.album(album_id)
            album = Album()
            album.album_data = album_response
            album.album_id = album_response['id']
            # Artists
            for album_artist_response in album_response['artists']:
                album.artists.append(
                    self.get_artist(album_artist_response['id']))
            self.db.save_instance(album)
            print("> Album {} was not in database.".format(
                album.album_data['name']))
            return self.db.session.query(Album).get(album_id)

    def get_track(self, track_id):
        track = self.db.session.query(Track).get(track_id)
        if track:
            return track
        else:
            response = self.client.track(track_id)

            track = Track()
            track.track_id = track_id
            track.track_data = response
            # Album
            track.album = self.get_album(response['album']['id'])
            # Artists
            for artist_response in response['artists']:
                track.artists.append(self.get_artist(artist_response['id']))
            # Audio feature
            audio_feature_response = self.client.audio_features(track_id)[0]
            if audio_feature_response:  # Some tracks do not have audio features
                track.audio_feature_data = audio_feature_response
            print("> Track {} was not in database.".format(
                track.track_data['name']))
            self.db.save_instance(track)
            return self.db.session.query(Track).get(track_id)

    def get_play_from_played_at_utc_and_track_id(self, played_at_utc,
                                                 track_id):
        played_at_utc = convert_played_at_from_response_to_datetime(
            played_at_utc)
        played_at_utc = set_timezone_to_datetime(played_at_utc, timezone='UTC')
        played_at_cet = convert_datetime_from_timezone_to_timezone(
            played_at_utc, from_tz_code='UTC', to_tz_code='CET')
        # Play
        play = Play()
        play.user_name = self.user_name
        play.played_at_utc_timestamp = played_at_utc.timestamp() * 1000
        play.played_at_utc = played_at_utc
        play.played_at_cet = played_at_cet
        play.day = played_at_cet.day
        play.month = played_at_cet.month
        play.year = played_at_cet.year
        play.hour = played_at_cet.hour
        play.minute = played_at_cet.minute
        play.second = played_at_cet.second
        play.day_of_week = played_at_cet.weekday()
        play.week_of_year = played_at_cet.date().isocalendar()[1]
        # Track
        track = self.get_track(track_id)
        play.track = track
        play.track_id = track_id
        return play

    def _get_play_tuples_from_response(self, response):
        plays = []
        for item in response['items']:
            play_tuple = (item['played_at'], item['track']['id'])
            plays.append(play_tuple)
        return plays

    def _get_play_tuples(self, limit=50, after=None):
        play_tuples = []
        response = self.client._get('me/player/recently-played',
                                    after=after,
                                    limit=limit)
        play_tuples.extend(self._get_play_tuples_from_response(response))

        while response and 'next' in response:
            response = self.client.next(response)
            if response:
                play_tuples.extend(
                    self._get_play_tuples_from_response(response))

        return play_tuples

    def extract_plays(self):
        print("* Extracting latest plays of {}.".format(self.user_name))
        play_tuples = self._get_play_tuples()

        for played_at, track_id in play_tuples:
            play = self.get_play_from_played_at_utc_and_track_id(
                played_at, track_id)
            self.db.save_play(play)
Ejemplo n.º 24
0
def main():

    clientRedirect = "http://localhost/"

    username = "******"

    scope = "playlist-read-collaborative " \
            "playlist-read-private " \
            "user-library-read " \
            "playlist-modify-public " \
            "playlist-modify-private"

    token = util.prompt_for_user_token(username, scope, clientID, clientSecret,
                                       clientRedirect)

    spotify = Spotify(auth=token)

    result = spotify.current_user_saved_tracks(offset=0, limit=50)
    data = {}

    songs = {}

    exceptions = ("Depeche Mode", "Grant Miller", "Madonna", "Ministry",
                  "The Beach Boys", "Mickey & Sylvia", "The Clovers",
                  "Village People", "Frank Sinatra", "Rodríguez",
                  "The Bangles", "U2", "UB40", "Tom Petty", "Faces",
                  "Bobby McFerrin", "Dion", "Fancy", "Eddy Huntington",
                  "Michael Jackson", "OutKast", "Gorillaz", "Diddy",
                  "Lipps Inc.", "Chuck Berry", "Marvin Gaye", "The Kinks",
                  "Count Basie", "Player", "Steve Lawrence", "Nelly",
                  "The Killers", "Billy Idol", "Haddaway", "Blondie")
    dontwant = ("Emile Van Krieken")
    '''while result["next"]:
        for track in result["items"]:
            songs.update(track["track"]["name"])
        result = spotify.next(result)

    data = {username: songs}'''
    num = 0
    playlist_id = ""
    for playlist in spotify.current_user_playlists()["items"]:
        if playlist["name"] == "Computer Generated Old 2":
            playlist_id = playlist["id"]

    exceptions_list = []

    while result["next"]:
        for track in result["items"]:
            num = num + 1
            print(num)

            track = track["track"]
            '''songs.update({track["uri"]:
                              {"track": track["name"],
                               "artist": track["artists"][0]["name"],
                               "artist uri": track["artists"][0]["uri"],
                               "album": track["album"]["name"],
                               "album uri": track["album"]["uri"]
                              }
                          })'''

            album = spotify.album(track["album"]["id"])
            '''if (int(album["release_date"][0:4]) < 2000 or track["artists"][0]["name"] in exceptions): #and int(album["release_date"][0:4]) > 2006)\

                    #print(track["uri"])
                    print(track["id"])
                    print(track["name"])
                    spotify.user_playlist_add_tracks("karan.arora.28", playlist_id, [track["uri"]])'''

            if (int(album["release_date"][0:4]) <
                    2006) or track["artists"][0]["name"] in exceptions:
                spotify.user_playlist_add_tracks("karan.arora.28", playlist_id,
                                                 [track["uri"]])

                if track["artists"][0]["name"] not in exceptions_list:
                    exceptions_list.append(track["artists"][0]["name"])

            print(exceptions_list)
            '''else:
                #pid = getPlaylistIDByName(spotify, "Old??")
                pid = "2qSyS6sDfEGSS38cn4GR8U"
                if trackInPlaylist(spotify, track["name"], pid):
                    print(track["name"])
                    print(track["artists"][0]["name"])
                    num = num +1
                    print(num)'''

        result = spotify.next(result)

        #spotify.
        #spotify.user_playlist_create(clientID, "Python Old", False, "")

    #albumuri
    #artist Name and URI
    #when track was added
    #track name and URI

    #From this data, get when album was released, get genres,

    #username -> playlists -> songs -> songs contain all the data about genres and artists, etc.
    '''data = songs
Ejemplo n.º 25
0
def download_albumspo(URL,
                      output=stock_output + "/",
                      recursive_download=stock_recursive_download,
                      not_interface=stock_not_interface,
                      zips=stock_zip):
    global spo

    datas = {}
    detas = {}
    datas['music'] = []
    datas['artist'] = []
    datas['tracknum'] = []
    datas['discnum'] = []
    datas['duration'] = []
    names = []
    nams = []

    try:
        url = spo.album(URL)
    except Exception as a:
        if not "The access token expired" in str(a):
            raise exceptions.InvalidLink("Invalid link ;)")

        spo = Spotify(generate_token())

        url = spo.album(URL)

    detas['image'] = request(url['images'][0]['url']).content

    detas['album'] = url['name']
    detas['year'] = url['release_date']
    detas['genre'] = " & ".join(url['genres'])

    array = [a['name'] for a in url['artists']]

    detas['ar_album'] = ", ".join(array)
    detas['label'] = url['label']
    detas['bpm'] = ""
    detas['gain'] = "0"
    detas['isrc'] = ""
    album = var_excape(detas['album'])

    directory = ("%s%s %s/" % (output, album, url['external_ids']['upc']))

    check_dir(directory)
    tot = url['total_tracks']

    def lazy(a):
        datas['music'].append(a['name'])
        discnum = str(a['disc_number'])
        tracknum = str(a['track_number'])

        names.append("%s%s CD %s TRACK %s" %
                     (directory, album, discnum, tracknum))

        datas['tracknum'].append(tracknum)
        datas['discnum'].append(discnum)

        datas['duration'].append(str(a['duration_ms'] * 1000))

        array = [b['name'] for b in a['artists']]

        datas['artist'].append(", ".join(array))

    for a in url['tracks']['items']:
        lazy(a)

    for a in range(tot // 50 - 1):
        try:
            url = spo.next(url['tracks'])
        except:
            spo = Spotify(generate_token())

            url = spo.next(url['tracks'])

        for a in url['items']:
            lazy(a)

    for a in tqdm(range(len(names)), disable=not_interface):
        detas['music'] = datas['music'][a]
        detas['artist'] = datas['artist'][a]
        detas['tracknum'] = datas['tracknum'][a]
        detas['discnum'] = datas['discnum'][a]
        detas['duration'] = datas['duration'][a]
        song = "{} - {}".format(detas['music'], detas['artist'])

        try:
            nams.append(
                download(directory, names[a], recursive_download,
                         not_interface, detas))
        except exceptions.TrackNotFound:
            nams.append(names[a])
            print("Track not found: %s :(" % song)
            continue

    if zips:
        zip_name = "{}{}.zip".format(directory, directory.split("/")[-2])

        create_zip(zip_name, nams)
        return nams, zip_name

    return nams
Ejemplo n.º 26
0
class Login:
    def __init__(self, mail, password, token=None):
        self.spo = Spotify(auth=generate_token())
        self.req = requests.Session()
        check = self.get_api("deezer.getUserData")['checkFormLogin']
        post_data = {
            "type": "login",
            "mail": mail,
            "password": password,
            "checkFormLogin": check
        }
        end = self.req.post("https://www.deezer.com/ajax/action.php",
                            post_data).text
        if "success" == end:
            print("Success, you are in :)")
        else:
            if not token:
                raise BadCredentials(end + ", and no token provided")
            self.req.cookies["arl"] = token
            if self.req.get("https://www.deezer.com/").text.split(
                    "'deezer_user_id': ")[1].split(",")[0] == "0":
                raise BadCredentials("Wrong token :(")

    def get_api(self, method=None, api_token="null", json=None):
        params = {
            "api_version": "1.0",
            "api_token": api_token,
            "input": "3",
            "method": method
        }
        try:
            return self.req.post("http://www.deezer.com/ajax/gw-light.php",
                                 params=params,
                                 json=json).json()['results']
        except:
            return self.req.post("http://www.deezer.com/ajax/gw-light.php",
                                 params=params,
                                 json=json).json()['results']

    def download(self,
                 link,
                 name,
                 quality,
                 recursive_quality,
                 recursive_download,
                 datas,
                 create_zip=False):
        if not quality in qualities:
            raise QualityNotFound(
                "The qualities have to be FLAC or MP3_320 or MP3_256 or MP3_128"
            )

        def login(method, json):
            infos = ""
            while not "MD5_ORIGIN" in str(infos):
                self.token = self.get_api("deezer.getUserData")['checkForm']
                infos = self.get_api(method, self.token, json)
            return infos

        def ultimatum(infos, name, datas, quality, recursive_download,
                      recursive_quality):
            extension = ".mp3"
            ids = infos['SNG_ID']
            if int(infos['FILESIZE_' + quality]) > 0 and quality == "FLAC":
                quality = "9"
                extension = ".flac"
                qualit = "FLAC"
            elif int(
                    infos['FILESIZE_' + quality]) > 0 and quality == "MP3_320":
                quality = "3"
                qualit = "320"
            elif int(
                    infos['FILESIZE_' + quality]) > 0 and quality == "MP3_256":
                quality = "5"
                qualit = "256"
            elif int(
                    infos['FILESIZE_' + quality]) > 0 and quality == "MP3_128":
                quality = "1"
                qualit = "128"
            else:
                if recursive_quality:
                    raise QualityNotFound(
                        "The quality chosen can't be downloaded")
                for a in qualities:
                    if int(infos['FILESIZE_' + a]) > 0:
                        quality = qualities[a]['quality']
                        extension = qualities[a]['extension']
                        qualit = qualities[a]['qualit']
                        break
                    else:
                        if a == "MP3_128":
                            raise TrackNotFound(
                                "There isn't any quality avalaible for download this song"
                            )
            name += " (" + qualit + ")" + extension
            if os.path.isfile(name):
                if not recursive_download:
                    return name
                ans = input(
                    "Track " + name +
                    " already exists, do you want to redownload it?(y or n):")
                if ans != "y":
                    return name
            try:
                md5 = infos['FALLBACK']['MD5_ORIGIN']
            except KeyError:
                md5 = infos['MD5_ORIGIN']
            hashs = genurl(md5, quality, ids, infos['MEDIA_VERSION'])
            try:
                crypt = request(
                    "https://e-cdns-proxy-%s.dzcdn.net/mobile/1/%s" %
                    (md5[0], hashs))
            except IndexError:
                raise TrackNotFound("Track not found :(")
            if len(crypt.content) == 0:
                raise TrackNotFound("Error with this track :(")
            open(name, "wb").write(crypt.content)
            decry = open(name, "wb")
            decryptfile(crypt.iter_content(2048), calcbfkey(ids), decry)
            datas = add_more_tags(datas, infos, ids)
            write_tags(name, datas)
            return name

        def add_more_tags(datas, infos, ids):
            try:
                datas['author'] = " & ".join(
                    infos['SNG_CONTRIBUTORS']['author'])
            except:
                datas['author'] = ""
            try:
                datas['composer'] = " & ".join(
                    infos['SNG_CONTRIBUTORS']['composer'])
            except:
                datas['composer'] = ""
            try:
                datas['lyricist'] = " & ".join(
                    infos['SNG_CONTRIBUTORS']['lyricist'])
            except:
                datas['lyricist'] = ""
            try:
                datas['version'] = infos['VERSION']
            except KeyError:
                datas['version'] = ""
            need = self.get_api("song.getLyrics", self.token, {"sng_id": ids})
            try:
                datas['lyric'] = need['LYRICS_TEXT']
                datas['copyright'] = need['LYRICS_COPYRIGHTS']
                datas['lyricist'] = need['LYRICS_WRITERS']
            except KeyError:
                datas['lyric'] = ""
                datas['copyright'] = ""
                datas['lyricist'] = ""
            return datas

        ids = link.split("/")[-1]
        zip_name = ""
        quali = ""
        if "track" in link:
            method = "song.getData"
            json = {"sng_id": ids}
            infos = login(method, json)
            image = request("https://e-cdns-images.dzcdn.net/images/cover/" +
                            infos['ALB_PICTURE'] +
                            "/1200x1200-000000-80-0-0.jpg").content
            if len(image) == 13:
                image = request(
                    "https://e-cdns-images.dzcdn.net/images/cover/1200x1200-000000-80-0-0.jpg"
                ).content
            datas['image'] = image
            song = datas['music'] + " - " + datas['artist']
            song = var_excape(song)
            print("\nDownloading:" + song)
            try:
                nams = ultimatum(infos, name, datas, quality,
                                 recursive_download, recursive_quality)
            except TrackNotFound:
                url = request(
                    "https://api.deezer.com/search/track/?q=" +
                    datas['music'].replace("#", "") + " + " +
                    datas['artist'].replace("#", ""), True).json()
                try:
                    for b in range(url['total'] + 1):
                        if url['data'][b]['title'] == datas['music'] or datas[
                                'music'] in url['data'][b]['title_short']:
                            URL = url['data'][b]['link']
                            break
                except IndexError:
                    raise TrackNotFound("Track not found: " + song)
                json = {"sng_id": URL.split("/")[-1]}
                infos = login(method, json)
                nams = ultimatum(infos, name, datas, quality,
                                 recursive_download, recursive_quality)
        else:
            nams = []
            detas = {}
            method = "song.getListByAlbum"
            json = {"alb_id": ids, "nb": -1}
            infos = login(method, json)['data']
            image = request("https://e-cdns-images.dzcdn.net/images/cover/" +
                            infos[0]['ALB_PICTURE'] +
                            "/1200x1200-000000-80-0-0.jpg").content
            if len(image) == 13:
                image = request(
                    "https://e-cdns-images.dzcdn.net/images/cover/1200x1200-000000-80-0-0.jpg"
                ).content
            detas['image'] = image
            detas['album'] = datas['album']
            detas['year'] = datas['year']
            detas['genre'] = datas['genre']
            detas['ar_album'] = datas['ar_album']
            detas['label'] = datas['label']
            for a in tqdm(range(len(name))):
                detas['music'] = datas['music'][a]
                detas['artist'] = datas['artist'][a]
                detas['tracknum'] = datas['tracknum'][a]
                detas['discnum'] = datas['discnum'][a]
                detas['bpm'] = datas['bpm'][a]
                detas['gain'] = datas['gain'][a]
                detas['duration'] = datas['duration'][a]
                detas['isrc'] = datas['isrc'][a]
                try:
                    nams.append(
                        ultimatum(infos[a], name[a], detas, quality,
                                  recursive_download, recursive_quality))
                except TrackNotFound:
                    url = request(
                        "https://api.deezer.com/search/track/?q=" +
                        detas['music'].replace("#", "") + " + " +
                        detas['artist'].replace("#", ""), True).json()
                    try:
                        for b in range(url['total'] + 1):
                            if url['data'][b]['title'] == detas[
                                    'music'] or detas['music'] in url['data'][
                                        b]['title_short']:
                                URL = url['data'][b]['link']
                                break
                    except IndexError:
                        nams.append(name[a])
                        print("\nTrack not found: " + detas['music'] + " - " +
                              detas['artist'])
                        continue
                    try:
                        method = "song.getData"
                        json = {"sng_id": URL.split("/")[-1]}
                        nams.append(
                            ultimatum(login(method,
                                            json), name[a], detas, quality,
                                      recursive_download, recursive_quality))
                    except TrackNotFound:
                        nams.append(name[a])
                        print("\nTrack not found: " + detas['music'] + " - " +
                              detas['artist'])
                        continue
                quali = nams[a].split("(")[-1].split(")")[0]
            if create_zip:
                dir = "/".join(name[a].split("/")[:-1]) + "/"
                if len(nams) > 0:
                    zip_name = dir + dir.split(
                        "/")[-2] + " (" + quali + ").zip"
                    z = zipfile.ZipFile(zip_name, "w", zipfile.ZIP_DEFLATED)
                    for a in nams:
                        b = a.split("/")[-1]
                        try:
                            z.write(a, b)
                        except FileNotFoundError:
                            pass
                    z.close()
        return nams, zip_name

    def download_trackdee(self,
                          URL,
                          output=localdir + "/Songs/",
                          quality="MP3_320",
                          recursive_quality=True,
                          recursive_download=True):
        datas = {}
        if "?utm" in URL:
            URL, a = URL.split("?utm")
        URL1 = "https://www.deezer.com/track/" + URL.split("/")[-1]
        URL2 = "https://api.deezer.com/track/" + URL.split("/")[-1]
        url = request(URL2, True).json()
        url1 = request(
            "http://api.deezer.com/album/" + str(url['album']['id']),
            True).json()
        datas['music'] = url['title']
        array = []
        for a in url['contributors']:
            array.append(a['name'])
        array.append(url['artist']['name'])
        if len(array) > 1:
            for a in array:
                for b in array:
                    if a in b and a != b:
                        array.remove(b)
        datas['artist'] = ", ".join(OrderedDict.fromkeys(array))
        datas['album'] = url1['title']
        datas['tracknum'] = str(url['track_position'])
        datas['discnum'] = str(url['disk_number'])
        datas['year'] = url['release_date']
        datas['genre'] = []
        try:
            for a in url1['genres']['data']:
                datas['genre'].append(a['name'])
        except KeyError:
            pass
        datas['genre'] = " & ".join(datas['genre'])
        datas['ar_album'] = []
        for a in url1['contributors']:
            if a['role'] == "Main":
                datas['ar_album'].append(a['name'])
        datas['ar_album'] = " & ".join(datas['ar_album'])
        datas['label'] = url1['label']
        datas['bpm'] = str(url['bpm'])
        datas['gain'] = str(url['gain'])
        datas['duration'] = str(url['duration'])
        datas['isrc'] = url['isrc']
        album = var_excape(datas['album'])
        dir = output + album + " " + url1['upc'] + "/"
        try:
            os.makedirs(dir)
        except FileExistsError:
            pass
        name = dir + album + " CD " + datas['discnum'] + " TRACK " + datas[
            'tracknum']
        name, a = self.download(URL, name, quality, recursive_quality,
                                recursive_download, datas)
        return name

    def download_albumdee(self,
                          URL,
                          output=localdir + "/Songs/",
                          quality="MP3_320",
                          recursive_quality=True,
                          recursive_download=True,
                          create_zip=False):
        datas = {}
        datas['music'] = []
        datas['artist'] = []
        datas['tracknum'] = []
        datas['discnum'] = []
        datas['bpm'] = []
        datas['gain'] = []
        datas['duration'] = []
        datas['isrc'] = []
        names = []
        array = []
        if "?utm" in URL:
            URL, a = URL.split("?utm")
        URL1 = "https://www.deezer.com/album/" + URL.split("/")[-1]
        URL2 = "https://api.deezer.com/album/" + URL.split("/")[-1]
        url = request(URL2, True).json()
        datas['album'] = url['title']
        datas['label'] = url['label']
        datas['year'] = url['release_date']
        datas['genre'] = []
        try:
            for a in url['genres']['data']:
                datas['genre'].append(a['name'])
        except KeyError:
            pass
        datas['genre'] = " & ".join(datas['genre'])
        datas['ar_album'] = []
        for a in url['contributors']:
            if a['role'] == "Main":
                datas['ar_album'].append(a['name'])
        datas['ar_album'] = " & ".join(datas['ar_album'])
        album = var_excape(datas['album'])
        dir = output + "/" + album + " " + url['upc'] + "/"
        for a in url['tracks']['data']:
            del array[:]
            datas['music'].append(a['title'])
            ur = request("https://api.deezer.com/track/" + str(a['id']),
                         True).json()
            discnum = str(ur['disk_number'])
            tracknum = str(ur['track_position'])
            names.append(dir + album + " CD " + discnum + " TRACK " + tracknum)
            datas['tracknum'].append(tracknum)
            datas['discnum'].append(discnum)
            datas['bpm'].append(str(ur['bpm']))
            datas['gain'].append(str(ur['gain']))
            datas['duration'].append(str(ur['duration']))
            datas['isrc'].append(ur['isrc'])
            for a in ur['contributors']:
                array.append(a['name'])
            array.append(ur['artist']['name'])
            if len(array) > 1:
                for a in array:
                    for b in array:
                        if a in b and a != b:
                            array.remove(b)
            datas['artist'].append(", ".join(OrderedDict.fromkeys(array)))
        try:
            os.makedirs(dir)
        except FileExistsError:
            pass
        names, zip_name = self.download(URL, names, quality, recursive_quality,
                                        recursive_download, datas, create_zip)
        if create_zip:
            return names, zip_name
        return names

    def download_playlistdee(self,
                             URL,
                             output=localdir + "/Songs/",
                             quality="MP3_320",
                             recursive_quality=True,
                             recursive_download=True):
        array = []
        if "?utm" in URL:
            URL, a = URL.split("?utm")
        url = request("https://api.deezer.com/playlist/" + URL.split("/")[-1],
                      True).json()
        for a in url['tracks']['data']:
            try:
                array.append(
                    self.download_trackdee(a['link'], output, quality,
                                           recursive_quality,
                                           recursive_download))
            except TrackNotFound:
                print("\nTrack not found " + a['title'])
                array.append("None")
        return array

    def download_trackspo(self,
                          URL,
                          output=localdir + "/Songs/",
                          quality="MP3_320",
                          recursive_quality=True,
                          recursive_download=True):
        if "?" in URL:
            URL, a = URL.split("?")
        try:
            url = self.spo.track(URL)
        except Exception as a:
            if not "The access token expired" in str(a):
                raise InvalidLink("Invalid link ;)")
            self.spo = Spotify(auth=generate_token())
            url = self.spo.track(URL)
        isrc = url['external_ids']['isrc']
        url = request("https://api.deezer.com/track/isrc:" + isrc, True).json()
        name = self.download_trackdee(url['link'], output, quality,
                                      recursive_quality, recursive_download)
        return name

    def download_albumspo(self,
                          URL,
                          output=localdir + "/Songs/",
                          quality="MP3_320",
                          recursive_quality=True,
                          recursive_download=True,
                          create_zip=False):
        if "?" in URL:
            URL, a = URL.split("?")
        try:
            tracks = self.spo.album(URL)
        except Exception as a:
            if not "The access token expired" in str(a):
                raise InvalidLink("Invalid link ;)")
            self.spo = Spotify(auth=generate_token())
            tracks = self.spo.album(URL)
        try:
            upc = tracks['external_ids']['upc']
            while upc[0] == "0":
                upc = upc[1:]
            url = request("https://api.deezer.com/album/upc:" + upc).json()
            names = self.download_albumdee(url['link'], output, quality,
                                           recursive_quality,
                                           recursive_download, create_zip)
        except KeyError:
            search = len(tracks['tracks']['items']) // 8
            try:
                url = self.spo.track(tracks['tracks']['items'][search]
                                     ['external_urls']['spotify'])
            except:
                self.spo = Spotify(auth=generate_token())
                url = self.spo.track(tracks['tracks']['items'][search]
                                     ['external_urls']['spotify'])
            isrc = url['external_ids']['isrc']
            try:
                url = request("https://api.deezer.com/track/isrc:" + isrc,
                              True).json()
                names = self.download_albumdee(url['album']['link'], output,
                                               quality, recursive_quality,
                                               recursive_download, create_zip)
            except TrackNotFound:
                raise AlbumNotFound("Album not found :(")
        return names

    def download_playlistspo(self,
                             URL,
                             output=localdir + "/Songs/",
                             quality="MP3_320",
                             recursive_quality=True,
                             recursive_download=True):
        array = []
        if "?" in URL:
            URL, a = URL.split("?")
        URL = URL.split("/")
        try:
            tracks = self.spo.user_playlist_tracks(URL[-3],
                                                   playlist_id=URL[-1])
        except Exception as a:
            if not "The access token expired" in str(a):
                raise InvalidLink("Invalid link ;)")
            self.spo = Spotify(auth=generate_token())
            tracks = self.spo.user_playlist_tracks(URL[-3],
                                                   playlist_id=URL[-1])
        for a in tracks['items']:
            try:
                array.append(
                    self.download_trackspo(
                        a['track']['external_urls']['spotify'], output,
                        quality, recursive_quality, recursive_download))
            except:
                print("\nTrack not found :(")
                array.append("None")
        if tracks['total'] != 100:
            for a in range(tracks['total'] // 100):
                try:
                    tracks = self.spo.next(tracks)
                except:
                    self.spo = Spotify(auth=generate_token())
                    tracks = self.spo.next(tracks)
                for a in tracks['items']:
                    try:
                        array.append(
                            self.download_trackspo(
                                a['track']['external_urls']['spotify'], output,
                                quality, recursive_quality,
                                recursive_download))
                    except:
                        print("\nTrack not found :(")
                        array.append("None")
        return array

    def download_name(self,
                      artist,
                      song,
                      output=localdir + "/Songs/",
                      quality="MP3_320",
                      recursive_quality=True,
                      recursive_download=True):
        try:
            search = self.spo.search(q="track:" + song + " artist:" + artist)
        except:
            self.spo = Spotify(auth=generate_token())
            search = self.spo.search(q="track:" + song + " artist:" + artist)
        try:
            return self.download_trackspo(
                search['tracks']['items'][0]['external_urls']['spotify'],
                output, quality, recursive_quality, recursive_download)
        except IndexError:
            raise TrackNotFound("Track not found: " + artist + " - " + song)
Ejemplo n.º 27
0
class Login:
    def __init__(self, token):
        self.spo = Spotify(utils.generate_token())

        self.req = Session()
        self.req.cookies["arl"] = token
        self.get_user_data = methods.method_get_user_data
        self.private_api_link = deezer_settings.private_api_link
        user_id = self.get_api(self.get_user_data)["USER"]["USER_ID"]

        if user_id == 0:
            raise exceptions.BadCredentials("Wrong token: %s :(" % token)

        self.qualities = deezer_settings.qualities
        self.songs_server = deezer_settings.songs_server
        self.get_song_data = methods.method_get_song_data
        self.get_lyric = methods.method_get_lyric
        self.get_album = methods.method_get_album
        self.get_album_data = methods.method_get_album_data
        self.api_track = deezer_settings.api_track
        self.api_album = deezer_settings.api_album
        self.api_playlist = deezer_settings.api_playlist

    def get_api(self, method, api_token="null", json_data=None):
        params = {
            "api_version": "1.0",
            "api_token": api_token,
            "input": "3",
            "method": method,
        }

        try:
            return self.req.post(
                self.private_api_link, params=params, json=json_data
            ).json()["results"]
        except:
            return self.req.post(
                self.private_api_link, params=params, json=json_data
            ).json()["results"]

    def download(
        self,
        link,
        details,
        recursive_quality=None,
        recursive_download=None,
        not_interface=None,
        zips=False,
    ):
        if not details["quality"] in self.qualities:
            raise exceptions.QualityNotFound(
                "The qualities have to be FLAC or MP3_320 or MP3_256 or MP3_128"
            )

        self.token = self.get_api(self.get_user_data)["checkForm"]
        ids = utils.get_ids(link)
        datas = details["datas"]
        quality = details["quality"]
        output = details["output"]

        def get_infos(method, json_data):
            infos = self.get_api(method, self.token, json_data)
            return infos

        def check_quality_song(infos, datas):
            ids = infos["SNG_ID"]
            num_quality = self.qualities[quality]["n_quality"]
            file_format = self.qualities[quality]["f_format"]
            song_quality = self.qualities[quality]["s_quality"]
            song_md5, version = utils.check_md5_song(infos)
            song_hash = download_utils.genurl(song_md5, num_quality, ids, version)

            try:
                crypted_audio = utils.song_exist(song_md5[0], song_hash)
            except (IndexError, exceptions.TrackNotFound):
                if not recursive_quality:
                    raise exceptions.QualityNotFound(
                        "The quality chosen can't be downloaded"
                    )

                for a in self.qualities:
                    if details["quality"] == a:
                        continue

                    num_quality = self.qualities[a]["n_quality"]
                    file_format = self.qualities[a]["f_format"]
                    song_quality = self.qualities[a]["s_quality"]
                    song_hash = download_utils.genurl(
                        song_md5, num_quality, ids, infos["MEDIA_VERSION"]
                    )

                    try:
                        crypted_audio = utils.song_exist(song_md5[0], song_hash)
                    except exceptions.TrackNotFound:
                        raise exceptions.TrackNotFound("Error with this song %s" % link)

            album = utils.var_excape(datas["album"])

            directory = "%s%s %s/" % ("%s/" % output, album, datas["upc"])

            name = "%s%s CD %s TRACK %s" % (
                directory,
                album,
                datas["discnum"],
                datas["tracknum"],
            )

            utils.check_dir(directory)
            name += " ({}){}".format(song_quality, file_format)

            if isfile(name):
                if recursive_download:
                    return name

                ans = input(
                    "Track %s already exists, do you want to redownload it? (y or n):"
                    % name
                )

                if not ans in answers:
                    return name

            decrypted_audio = open(name, "wb")

            download_utils.decryptfile(
                crypted_audio.iter_content(2048),
                download_utils.calcbfkey(ids),
                decrypted_audio,
            )

            utils.write_tags(name, add_more_tags(datas, infos, ids))

            return name

        def add_more_tags(datas, infos, ids):
            json_data = {"sng_id": ids}

            try:
                datas["author"] = " & ".join(infos["SNG_CONTRIBUTORS"]["author"])
            except:
                datas["author"] = ""

            try:
                datas["composer"] = " & ".join(infos["SNG_CONTRIBUTORS"]["composer"])
            except:
                datas["composer"] = ""

            try:
                datas["lyricist"] = " & ".join(infos["SNG_CONTRIBUTORS"]["lyricist"])
            except:
                datas["lyricist"] = ""

            try:
                datas["version"] = infos["VERSION"]
            except KeyError:
                datas["version"] = ""

            need = get_infos(self.get_lyric, json_data)

            try:
                datas["lyric"] = need["LYRICS_TEXT"]
                datas["copyright"] = need["LYRICS_COPYRIGHTS"]
                datas["lyricist"] = need["LYRICS_WRITERS"]
            except KeyError:
                datas["lyric"] = ""
                datas["copyright"] = ""
                datas["lyricist"] = ""

            return datas

        def tracking2(infos, datas):
            image = utils.choose_img(infos["ALB_PICTURE"])
            datas["image"] = image
            song = "{} - {}".format(datas["music"], datas["artist"])

            if not not_interface:
                print("Downloading: %s" % song)

            try:
                nams = check_quality_song(infos, datas)
            except exceptions.TrackNotFound:
                try:
                    ids = utils.not_found(song, datas["music"])
                except IndexError:
                    raise exceptions.TrackNotFound("Track not found: %s" % song)

                json_data = {"sng_id": ids}

                infos = get_infos(self.get_song_data, json_data)
                nams = check_quality_song(infos, datas)

            return nams

        if "track" in link:
            json_data = {"sng_id": ids}

            infos = get_infos(self.get_song_data, json_data)
            nams = tracking2(infos, datas)
            return nams

        zip_name = ""

        if "album" in link:
            nams = []
            detas = {}
            quali = ""

            json_data = {"alb_id": ids, "nb": -1}

            infos = get_infos(self.get_album, json_data)["data"]

            image = utils.choose_img(infos[0]["ALB_PICTURE"])

            detas["image"] = image
            detas["album"] = datas["album"]
            detas["year"] = datas["year"]
            detas["genre"] = datas["genre"]
            detas["ar_album"] = datas["ar_album"]
            detas["label"] = datas["label"]
            detas["upc"] = datas["upc"]

            t = tqdm(range(len(infos)), desc=detas["album"], disable=not_interface)

            for a in t:
                detas["music"] = datas["music"][a]
                detas["artist"] = datas["artist"][a]
                detas["tracknum"] = datas["tracknum"][a]
                detas["discnum"] = datas["discnum"][a]
                detas["bpm"] = datas["bpm"][a]
                detas["duration"] = datas["duration"][a]
                detas["isrc"] = datas["isrc"][a]
                song = "{} - {}".format(detas["music"], detas["artist"])
                t.set_description_str(song)

                try:
                    nams.append(check_quality_song(infos[a], detas))
                except exceptions.TrackNotFound:
                    try:
                        ids = utils.not_found(song, detas["music"])

                        json = {"sng_id": ids}

                        nams.append(
                            check_quality_song(
                                get_infos(self.get_song_data, json), detas
                            )
                        )
                    except (exceptions.TrackNotFound, IndexError):
                        nams.append(song)
                        print("Track not found: %s :(" % song)
                        continue

                quali = nams[a].split("(")[-1].split(")")[0]

            if zips:
                album = utils.var_excape(datas["album"])

                directory = "%s%s %s/" % ("%s/" % output, album, datas["upc"])

                zip_name = "%s%s (%s).zip" % (directory, album, quali)

                try:
                    utils.create_zip(zip_name, nams)
                except FileNotFoundError:
                    raise exceptions.QualityNotFound(
                        'Can\'t download album "{}" in {} quality'.format(
                            album, details["quality"]
                        )
                    )

        elif "playlist" in link:
            json_data = {"playlist_id": ids, "nb": -1}

            infos = get_infos(methods.method_get_playlist_data, json_data)["data"]
            nams = []

            for a in range(len(infos)):
                try:
                    nams.append(tracking2(infos[a], datas[a]))
                except TypeError:
                    c = infos[a]
                    song = "{} - {}".format(c["SNG_TITLE"], c["ART_NAME"])
                    nams.append("Track not found")

            quali = "ALL"

            if zips:
                zip_name = "%s %s (%s).zip" % ("%s/playlist" % output, ids, quali)

                utils.create_zip(zip_name, nams)

        return nams, zip_name

    def download_trackdee(
        self,
        URL,
        output=stock_output,
        quality=stock_quality,
        recursive_quality=stock_recursive_quality,
        recursive_download=stock_recursive_download,
        not_interface=stock_not_interface,
    ):
        datas = {}
        ids = utils.get_ids(URL)
        URL2 = self.api_track % ids
        datas = utils.tracking(URL2)

        details = {"datas": datas, "quality": quality, "output": output}

        name = self.download(
            URL2, details, recursive_quality, recursive_download, not_interface
        )

        return name

    def download_albumdee(
        self,
        URL,
        output=stock_output,
        quality=stock_quality,
        recursive_quality=stock_recursive_quality,
        recursive_download=stock_recursive_download,
        not_interface=stock_not_interface,
        zips=stock_zip,
    ):
        datas = {}
        datas["music"] = []
        datas["artist"] = []
        datas["tracknum"] = []
        datas["discnum"] = []
        datas["bpm"] = []
        datas["duration"] = []
        datas["isrc"] = []
        names = []
        ids = utils.get_ids(URL)
        URL2 = self.api_album % ids
        album_json = utils.request(URL2, True).json()
        datas["album"] = album_json["title"]
        datas["label"] = album_json["label"]
        datas["year"] = album_json["release_date"]
        datas["upc"] = album_json["upc"]
        datas["genre"] = []

        try:
            for a in album_json["genres"]["data"]:
                datas["genre"].append(a["name"])
        except KeyError:
            pass

        datas["genre"] = " & ".join(datas["genre"])
        datas["ar_album"] = []

        for a in album_json["contributors"]:
            if a["role"] == "Main":
                datas["ar_album"].append(a["name"])

        datas["ar_album"] = " & ".join(datas["ar_album"])

        for a in album_json["tracks"]["data"]:
            URL3 = self.api_track % str(a["id"])
            detas = utils.tracking(URL3, True)
            datas["music"].append(detas["music"])
            discnum = detas["discnum"]
            tracknum = detas["tracknum"]
            datas["tracknum"].append(tracknum)
            datas["discnum"].append(discnum)
            datas["bpm"].append(detas["bpm"])
            datas["duration"].append(detas["duration"])
            datas["isrc"].append(detas["isrc"])
            datas["artist"].append(detas["artist"])

        details = {"datas": datas, "quality": quality, "output": output}

        names, zip_name = self.download(
            URL2, details, recursive_quality, recursive_download, not_interface, zips
        )

        if zips:
            return names, zip_name

        return names

    def download_playlistdee(
        self,
        URL,
        output=stock_output,
        quality=stock_quality,
        recursive_quality=stock_recursive_quality,
        recursive_download=stock_recursive_download,
        not_interface=stock_not_interface,
        zips=stock_zip,
    ):
        datas = []
        ids = utils.get_ids(URL)
        URL2 = self.api_playlist % ids
        playlist_json = utils.request(URL2, True).json()["tracks"]["data"]

        for a in playlist_json:
            URL3 = self.api_track % str(a["id"])

            try:
                detas = utils.tracking(URL3)
                datas.append(detas)
            except exceptions.NoDataApi:
                datas.append(None)

        details = {"datas": datas, "quality": quality, "output": output}

        names, zip_name = self.download(
            URL2, details, recursive_quality, recursive_download, not_interface, zips
        )

        if zips:
            return names, zip_name

        return names

    def download_trackspo(
        self,
        URL,
        output=stock_output,
        quality=stock_quality,
        recursive_quality=stock_recursive_quality,
        recursive_download=stock_recursive_download,
        not_interface=stock_not_interface,
    ):
        URL = URL.split("?")[0]

        try:
            url = self.spo.track(URL)
        except Exception as a:
            if not "The access token expired" in str(a):
                raise exceptions.InvalidLink("Invalid link ;)")

            self.spo = Spotify(utils.generate_token())

            url = self.spo.track(URL)

        isrc = "isrc:%s" % url["external_ids"]["isrc"]

        url = utils.request(self.api_track % isrc, True).json()

        name = self.download_trackdee(
            url["link"],
            output,
            quality,
            recursive_quality,
            recursive_download,
            not_interface,
        )

        return name

    def download_albumspo(
        self,
        URL,
        output=stock_output,
        quality=stock_quality,
        recursive_quality=stock_recursive_quality,
        recursive_download=stock_recursive_download,
        not_interface=stock_not_interface,
        zips=stock_zip,
    ):
        URL = URL.split("?")[0]

        try:
            tracks = self.spo.album(URL)
        except Exception as a:
            if not "The access token expired" in str(a):
                raise exceptions.InvalidLink("Invalid link ;)")

            self.spo = Spotify(utils.generate_token())

            tracks = self.spo.album(URL)

        try:
            upc = "0%s" % tracks["external_ids"]["upc"]

            while upc[0] == "0":
                upc = upc[1:]

                try:
                    upc = "upc:%s" % upc
                    url = utils.request(self.api_album % upc, True).json()

                    names = self.download_albumdee(
                        url["link"],
                        output,
                        quality,
                        recursive_quality,
                        recursive_download,
                        not_interface,
                        zips,
                    )

                    break
                except exceptions.NoDataApi:
                    if upc[0] != "0":
                        raise KeyError
        except KeyError:
            tot = tracks["total_tracks"]

            for a in tracks["tracks"]["items"]:
                try:
                    isrc = self.spo.track(a["external_urls"]["spotify"])[
                        "external_ids"
                    ]["isrc"]
                except:
                    self.spo = Spotify(utils.generate_token())

                    isrc = self.spo.track(a["external_urls"]["spotify"])[
                        "external_ids"
                    ]["isrc"]

                try:
                    isrc = "isrc:%s" % isrc

                    ids = utils.request(self.api_track % isrc, True).json()["album"][
                        "id"
                    ]

                    tracks = utils.request(self.api_album % str(ids), True).json()

                    if tot == tracks["nb_tracks"]:
                        break
                except exceptions.NoDataApi:
                    pass

            try:
                if tot != tracks["nb_tracks"]:
                    raise KeyError

                names = self.download_albumdee(
                    tracks["link"],
                    output,
                    quality,
                    recursive_quality,
                    recursive_download,
                    not_interface,
                    zips,
                )
            except KeyError:
                raise exceptions.AlbumNotFound("Album not found :(")

        return names

    def download_playlistspo(
        self,
        URL,
        output=stock_output,
        quality=stock_quality,
        recursive_quality=stock_recursive_quality,
        recursive_download=stock_recursive_download,
        not_interface=stock_not_interface,
        zips=stock_zip,
    ):
        array = []

        URL = URL.split("?")[0].split("/")

        try:
            tracks = self.spo.user_playlist_tracks(URL[-3], URL[-1])
        except Exception as a:
            if not "The access token expired" in str(a):
                raise exceptions.InvalidLink("Invalid link ;)")

            self.spo = Spotify(utils.generate_token())

            tracks = self.spo.user_playlist_tracks(URL[-3], URL[-1])

        def lazy(tracks):
            for a in tracks["items"]:
                try:
                    array.append(
                        self.download_trackspo(
                            a["track"]["external_urls"]["spotify"],
                            output,
                            quality,
                            recursive_quality,
                            recursive_download,
                            not_interface,
                        )
                    )
                except:
                    print("Track not found :(")
                    array.append("None")

        lazy(tracks)
        tot = tracks["total"]

        for a in range(tot // 100 - 1):
            try:
                tracks = self.spo.next(tracks)
            except:
                self.spo = Spotify(utils.generate_token())

                tracks = self.spo.next(tracks)

            lazy(tracks)

        if zips:
            zip_name = "{}playlist {}.zip".format(output, URL[-1])
            utils.create_zip(zip_name, array)
            return array, zip_name

        return array

    def download_name(
        self,
        artist,
        song,
        output=stock_output,
        quality=stock_quality,
        recursive_quality=stock_recursive_quality,
        recursive_download=stock_recursive_download,
        not_interface=stock_not_interface,
    ):
        query = "track:{} artist:{}".format(song, artist)

        try:
            search = self.spo.search(query)
        except:
            self.spo = Spotify(utils.generate_token())

            search = self.spo.search(query)

        try:
            return self.download_trackspo(
                search["tracks"]["items"][0]["external_urls"]["spotify"],
                output,
                quality,
                recursive_quality,
                recursive_download,
                not_interface,
            )
        except IndexError:
            raise exceptions.TrackNotFound("Track not found")
Ejemplo n.º 28
0
class SpotipyUtils:
    def __init__(self, client_id, client_secret, redirect_uri, scope):
        self._sp = Spotify(
            auth_manager=SpotifyOAuth(client_id=client_id,
                                      client_secret=client_secret,
                                      redirect_uri=redirect_uri,
                                      scope=scope))

    def delete_all_tracks(self, playlist):
        """
        Deletes all tracks in the given playlist
        """

        tracks = self.retrieve_all_tracks_for_playlist(playlist)
        SpotipyUtils._list_of_uris_helper(
            playlist, tracks,
            self._sp.playlist_remove_all_occurrences_of_items)

    def add_all_tracks(self, playlist, tracks: List[Track]):
        """
        Adds all the tracks to the given playlist
        """

        SpotipyUtils._list_of_uris_helper(playlist, tracks,
                                          self._sp.playlist_add_items)

    def retrieve_all_tracks_for_playlist(self, playlist) -> List[Track]:
        """
        Retrieves all tracks for the given playlist
        """

        fields = 'next,' \
                 'items(track(name,album(release_date,release_date_precision),artists(name),uri))'
        all_items = []

        results = self._sp.playlist_items(playlist_id=playlist['id'],
                                          limit=50,
                                          fields=fields)
        while True:
            all_items.extend(results['items'])

            results = self._sp.next(results)
            if results is None:
                break

        return [SpotipyUtils.to_track(item) for item in all_items]

    def num_tracks_in_playlist(self, playlist) -> Optional[int]:
        """
        Returns the number of tracks in this playlist
        """

        playlist = self.search_playlist_in_current_user_library(
            playlist['name'])
        if playlist is None:
            return None
        return len(self.retrieve_all_tracks_for_playlist(playlist))

    # TODO: have a class to represent playlist?
    def search_playlist_in_current_user_library(self, playlist_name: str):
        """
        Searches for a playlist within the current user's library with the given name
        (case insensitive)
        (I don't think Spotify API offers the ability to search for a playlist within
        a user's library yet. See note section here:
        https://developer.spotify.com/documentation/web-api/reference/search/search/)
        """

        batch_results = self._sp.current_user_playlists(limit=50)
        while True:
            for playlist in batch_results['items']:
                if playlist['name'].lower() == playlist_name.lower():
                    return playlist

            batch_results = self._sp.next(batch_results)

            if batch_results is None:
                break

        return None

    def create_temp_playlist(self):
        """
        Creates a temp playlist (named "temp") in the user library.
        If temp already exists, delete all tracks
        """

        playlist = self.search_playlist_in_current_user_library("temp")
        if playlist is None:
            user_id = self._sp.current_user()['id']
            self._sp.user_playlist_create(user_id, "temp")

        return self.search_playlist_in_current_user_library("temp")

    @staticmethod
    def _list_of_uris_helper(playlist, tracks: List[Track], spotify_method):
        """
        Helpers for functions that take in a list of Tracks
        """
        playlist_id = playlist['id']
        curr = 0
        offset = 100
        uris = [track.uri for track in tracks]

        while curr < len(tracks):
            spotify_method(playlist_id, uris[curr:curr + offset])
            curr += offset

    @staticmethod
    def to_album_release_date(release_date: str, release_date_precision: str):
        if release_date_precision is None or release_date is None:
            return None

        pad_date = {
            'day': lambda date: date,
            'month': lambda date: f'{date}-01',
            'year': lambda date: f'{date}-01-01'
        }
        return datetime.datetime.strptime(
            pad_date[release_date_precision](release_date), '%Y-%m-%d')

    @staticmethod
    def to_track(sp_result):
        sp_result = sp_result['track']
        artist = sp_result['artists'][0]['name']
        uri = sp_result['uri']
        album_release_date = SpotipyUtils.to_album_release_date(
            release_date=sp_result['album']['release_date'],
            release_date_precision=sp_result['album']
            ['release_date_precision'])

        return Track(album_release_date=album_release_date,
                     artist=artist,
                     uri=uri,
                     name=sp_result['name'])
Ejemplo n.º 29
0
class AuthTestSpotipy(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'
    """

    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'

    @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 = ('playlist-modify-public '
                      'user-library-read '
                      'user-follow-read '
                      'user-library-modify '
                      'user-read-private '
                      'user-top-read')

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

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

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

    def test_basic_user_profile(self):
        user = self.spotify.user(self.username)
        self.assertTrue(user['id'] == self.username.lower())

    def test_current_user(self):
        user = self.spotify.current_user()
        self.assertTrue(user['id'] == self.username.lower())

    def test_me(self):
        user = self.spotify.me()
        self.assertTrue(user['id'] == self.username.lower())

    def test_user_playlists(self):
        playlists = self.spotify.user_playlists(self.username, limit=5)
        self.assertTrue('items' in playlists)
        self.assertTrue(len(playlists['items']) == 5)

    def test_user_playlist_tracks(self):
        playlists = self.spotify.user_playlists(self.username, limit=5)
        self.assertTrue('items' in playlists)
        for playlist in playlists['items']:
            user = playlist['owner']['id']
            pid = playlist['id']
            results = self.spotify.user_playlist_tracks(user, pid)
            self.assertTrue(len(results['items']) >= 0)

    def user_playlist_tracks(self,
                             user,
                             playlist_id=None,
                             fields=None,
                             limit=100,
                             offset=0):

        # known API issue currently causes this test to fail
        # the issue is that the API doesn't currently respect the
        # limit paramter

        self.assertTrue(len(playlists['items']) == 5)

    def test_current_user_saved_tracks(self):
        tracks = self.spotify.current_user_saved_tracks()
        self.assertTrue(len(tracks['items']) > 0)

    def test_current_user_saved_albums(self):
        albums = self.spotify.current_user_saved_albums()
        self.assertTrue(len(albums['items']) > 0)

    def test_current_user_playlists(self):
        playlists = self.spotify.current_user_playlists(limit=10)
        self.assertTrue('items' in playlists)
        self.assertTrue(len(playlists['items']) == 10)

    def test_user_playlist_follow(self):
        self.spotify.user_playlist_follow_playlist('plamere',
                                                   '4erXB04MxwRAVqcUEpu30O')
        follows = self.spotify.user_playlist_is_following(
            'plamere', '4erXB04MxwRAVqcUEpu30O',
            [self.spotify.current_user()['id']])

        self.assertTrue(len(follows) == 1, 'proper follows length')
        self.assertTrue(follows[0], 'is following')
        self.spotify.user_playlist_unfollow('plamere',
                                            '4erXB04MxwRAVqcUEpu30O')

        follows = self.spotify.user_playlist_is_following(
            'plamere', '4erXB04MxwRAVqcUEpu30O',
            [self.spotify.current_user()['id']])
        self.assertTrue(len(follows) == 1, 'proper follows length')
        self.assertFalse(follows[0], 'is no longer following')

    def test_current_user_save_and_unsave_tracks(self):
        tracks = self.spotify.current_user_saved_tracks()
        total = tracks['total']

        self.spotify.current_user_saved_tracks_add(self.four_tracks)

        tracks = self.spotify.current_user_saved_tracks()
        new_total = tracks['total']
        self.assertTrue(new_total - total == len(self.four_tracks))

        tracks = self.spotify.current_user_saved_tracks_delete(
            self.four_tracks)
        tracks = self.spotify.current_user_saved_tracks()
        new_total = tracks['total']
        self.assertTrue(new_total == total)

    def test_categories(self):
        response = self.spotify.categories()
        self.assertTrue(len(response['categories']) > 0)

    def test_category_playlists(self):
        response = self.spotify.categories()
        for cat in response['categories']['items']:
            cat_id = cat['id']
            response = self.spotify.category_playlists(category_id=cat_id)
            if len(response['playlists']["items"]) > 0:
                break
        self.assertTrue(True)

    def test_new_releases(self):
        response = self.spotify.new_releases()
        self.assertTrue(len(response['albums']) > 0)

    def test_featured_releases(self):
        response = self.spotify.featured_playlists()
        self.assertTrue(len(response['playlists']) > 0)

    def test_current_user_follows(self):
        response = self.spotify.current_user_followed_artists()
        artists = response['artists']
        self.assertTrue(len(artists['items']) > 0)

    def test_current_user_top_tracks(self):
        response = self.spotify.current_user_top_tracks()
        items = response['items']
        self.assertTrue(len(items) > 0)

    def test_current_user_top_artists(self):
        response = self.spotify.current_user_top_artists()
        items = response['items']
        self.assertTrue(len(items) > 0)

    def get_or_create_spotify_playlist(self, playlist_name):
        playlists = self.spotify.user_playlists(self.username)
        while playlists:
            for item in playlists['items']:
                if item['name'] == playlist_name:
                    return item['id']
            playlists = self.spotify.next(playlists)
        playlist = self.spotify.user_playlist_create(self.username,
                                                     playlist_name)
        playlist_id = playlist['uri']
        return playlist_id

    def test_user_playlist_ops(self):
        # create empty playlist
        playlist_id = self.get_or_create_spotify_playlist(
            'spotipy-testing-playlist-1')

        # remove all tracks from it

        self.spotify.user_playlist_replace_tracks(self.username, playlist_id,
                                                  [])

        playlist = self.spotify.user_playlist(self.username, playlist_id)
        self.assertTrue(playlist['tracks']['total'] == 0)
        self.assertTrue(len(playlist['tracks']['items']) == 0)

        # add tracks to it

        self.spotify.user_playlist_add_tracks(self.username, playlist_id,
                                              self.four_tracks)
        playlist = self.spotify.user_playlist(self.username, playlist_id)
        self.assertTrue(playlist['tracks']['total'] == 4)
        self.assertTrue(len(playlist['tracks']['items']) == 4)

        # remove two tracks from it

        self.spotify.user_playlist_remove_all_occurrences_of_tracks(
            self.username, playlist_id, self.two_tracks)

        playlist = self.spotify.user_playlist(self.username, playlist_id)
        self.assertTrue(playlist['tracks']['total'] == 2)
        self.assertTrue(len(playlist['tracks']['items']) == 2)

        # replace with 3 other tracks
        self.spotify.user_playlist_replace_tracks(self.username, playlist_id,
                                                  self.other_tracks)

        playlist = self.spotify.user_playlist(self.username, playlist_id)
        self.assertTrue(playlist['tracks']['total'] == 3)
        self.assertTrue(len(playlist['tracks']['items']) == 3)
Ejemplo n.º 30
0
class SpotifySubscriber:
    """
    Main class. Description necessary.

    Default storage dir: ../storage
    """
    def __init__(
        self,
        user_id: str = None,
        storage_dir: str = os.path.join(
            os.path.dirname(os.path.abspath(__file__)), "storage"),
    ):
        self.user_id: str = user_id

        # Playlists are stored by ID in dictionaries for quick lookup
        self.user_playlists: dict = {}
        self.followed_playlists: dict = {}
        self.subscribed_playlists: dict = {}

        # This is the playlist in which all new songs will be pushed
        self.subscription_feed: SubscriptionFeed = None

        loaded = False
        save_path = os.path.join(storage_dir, "storage.p")
        # If a save file exists, load it.
        if os.path.isfile(save_path):
            self._load(save_path)
            loaded = True

        # Since we need the user_id, we cannot continue if it was not specified and we did not obtain it from a save file.
        elif user_id is None:
            raise Exception(
                "No save file found and no user_id specified! Please specify user_id."
            )

        # If there is no save file, there may not be a storage directory either
        elif not os.path.isdir(storage_dir):
            os.mkdir(storage_dir)

        # We deliberately set these after loading, so they may be updated if we move the save file to a different location.
        self.storage_dir = storage_dir
        self._save_path = save_path
        self._cache_path = os.path.join(self.storage_dir,
                                        ".cache-{}".format(self.user_id))
        self._feed_log_path = os.path.join(self.storage_dir, "feed_log.npy")

        # If no save file exists, load the client secret and ID from file so that we can request a token.
        if not loaded:
            self._load_client_secrets()

        # Refresh token and create spotify object
        self.token = self._get_token(self.user_id)
        self.sp = Spotify(auth=self.token)

        # If not loaded from save file, perform initial setup
        if not loaded:
            if self.user_id != "jneeven":
                self._follow_user("jneeven")

            self.subscription_feed = SubscriptionFeed(self.sp, self.user_id)
            self.refresh_user_playlists()
            self._save()

    # Load a token from the cache or request one from the spotify API. Will open the browser to ask for permission if necessary.
    def _get_token(self, username: str):
        """
        Alternatively, I can set these variables using the command line too:
        export SPOTIPY_CLIENT_ID='your-spotify-client-id'
        export SPOTIPY_CLIENT_SECRET='your-spotify-client-secret'
        export SPOTIPY_REDIRECT_URI='your-app-redirect-url'
        """
        scopes = " ".join([
            "user-read-recently-played",
            "user-library-modify",
            "playlist-read-private",
            "playlist-modify-public",
            "playlist-modify-private",
            "user-library-read",
            "playlist-read-collaborative",
            "user-read-playback-state",
            "user-follow-read",
            "user-top-read",
            "user-read-currently-playing",
            "user-follow-modify",
        ])

        token = sp_util.prompt_for_user_token(
            username,
            scopes,
            client_id=self._client_id,
            client_secret=self._client_secret,
            redirect_uri="http://localhost",
            cache_path=self._cache_path,
        )
        return token

    # Load the client secret and ID from client_data.json
    def _load_client_secrets(self):
        data_path = os.path.join(os.path.dirname(os.path.abspath(__file__)),
                                 "client_data.json")
        if not os.path.isfile(data_path):
            raise FileNotFoundError(
                "client_data.json does not exist in src folder!")

        with open(data_path, "r") as data_file:
            info = json.load(data_file)

        self._client_id = info["client_id"]
        self._client_secret = info["client_secret"]

    # Save the entire object to a storage file
    def _save(self):
        pickle.dump(self, open(self._save_path, "wb"))

    # Load the storage file and overwrite the current object attributes
    def _load(self, save_path: str):
        load_obj = pickle.load(open(save_path, "rb"))

        # Overwrite own attributes with the ones we just loaded
        for attr, val in load_obj.__dict__.items():
            self.__dict__[attr] = val

    # Obtain the playlists the user owns or follows (both private and public)
    def refresh_user_playlists(self):
        self.user_playlists = {}
        self.followed_playlists = {}

        # Only obtains 50 playlists at a time, is API limit
        playlists_data = self.sp.user_playlists(self.user_id)

        while playlists_data:
            for playlist in playlists_data["items"]:
                # If we own a playlist but it's collaborative, we treat it as a followed one since we might be interested in updates.
                if playlist["owner"]["id"] != self.user_id or playlist[
                        "collaborative"]:
                    self.followed_playlists[playlist["id"]] = playlist
                else:
                    self.user_playlists[playlist["id"]] = playlist

            # If there were more playlists than we just received, query the next batch
            if playlists_data["next"]:
                playlists_data = self.sp.next(playlists_data)
            else:
                break

    # Subscribe to multiple playlists based on their ID or patterns in their names.
    def subscribe_to_playlists(self,
                               playlist_ids: list = [],
                               contains: list = []):
        new_subscriptions = False

        for playlist_id in playlist_ids:
            if playlist_id not in self.followed_playlists.keys():
                raise Exception(
                    "Cannot subscribe to playlist with id {}. Either the user owns it or does not follow it."
                    .format(playlist_id))

            if playlist_id not in self.subscribed_playlists.keys():
                playlist = self.followed_playlists[playlist_id]
                tracks = self._get_playlist_tracks(playlist["owner"]["id"],
                                                   playlist_id)
                self.subscribed_playlists[playlist_id] = SubscribedPlaylist(
                    playlist, tracks)
                new_subscriptions = True
                safe_print("Subscribed to playlist {} by {}".format(
                    playlist["name"], playlist["owner"]["id"]))

        # Lowercase pattern matching with the playlist name
        for pattern in contains:
            pattern = pattern.lower()
            for playlist in self.followed_playlists.values():
                if (pattern in playlist["name"].lower() and playlist["id"]
                        not in self.subscribed_playlists.keys()):
                    tracks = self._get_playlist_tracks(playlist["owner"]["id"],
                                                       playlist["id"])
                    self.subscribed_playlists[
                        playlist["id"]] = SubscribedPlaylist(playlist, tracks)
                    new_subscriptions = True
                    safe_print("Subscribed to playlist {} by {}".format(
                        playlist["name"], playlist["owner"]["id"]))

        # Only save if we actually changed something. TODO: Save these in a separate file.
        if new_subscriptions:
            self._save()

    # Unsubscribe from multiple playlists based on their ID or patterns in their names.
    def unsubscribe_from_playlists(self,
                                   playlist_ids: list = [],
                                   contains: list = []):
        removed_subscriptions = False

        for playlist_id in playlist_ids:
            if playlist_id not in self.subscribed_playlists.keys():
                raise Exception(
                    "Cannot unsubscribe from playlist with id {}, because the user is not subscripted to it."
                    .format(playlist_id))

            playlist = self.subscribed_playlists[playlist_id]
            removed_subscriptions = True
            safe_print("Unsubscribed from playlist {} by {}".format(
                playlist.name, playlist.owner_id))
            del self.subscribed_playlists[playlist_id]

        # Lowercase pattern matching with the playlist name
        subscribed_playlists = list(self.subscribed_playlists.values())
        for pattern in contains:
            pattern = pattern.lower()
            for playlist in subscribed_playlists:
                if pattern in playlist.name.lower():
                    removed_subscriptions = True
                    safe_print("Unsubscribed from playlist {} by {}".format(
                        playlist.name, playlist.owner_id))
                    del self.subscribed_playlists[playlist.id]

        # Only save if we actually changed something. TODO: Save these in a separate file.
        if removed_subscriptions:
            self._save()

    # Print an overview of the playlist the user owns, follows and is subscribed to.
    def print_playlists(self, own=False, follow=False, subscribed=True):
        if own:
            safe_print("Own playlists:")
            for playlist in self.user_playlists.values():
                safe_print(playlist["name"])

        if follow:
            safe_print("\nFollowed playlists:")
            for playlist in self.followed_playlists.values():
                safe_print(playlist["name"], playlist["owner"]["id"])

        if subscribed:
            safe_print("\nCurrently subscribed to the following playlists:")
            for playlist in self.subscribed_playlists.values():
                safe_print(playlist)

        safe_print()

    # Check the subscribed playlists for new songs and add them to the feed list.
    def update_feed(self, add_own=False):
        """
        Add_own denotes whether to add songs that the user added to a playlist themselves.
        This may happen for example in collaborative playlists.
        """

        last_update = self.subscription_feed.last_update

        track_ids = []
        num_added_tracks = 0

        for playlist_id, playlist in self.subscribed_playlists.items():
            # safe_print("Checking playlist {}".format(playlist.name))
            new_tracks, snapshot = self._get_playlist_tracks(
                playlist.owner_id,
                playlist_id,
                min_timestamp=last_update,
                compare_snapshot=playlist.snapshot_id,
                return_snapshot=True,
            )
            # Update the playlist snapshot so that we quickly know if it has changed next time
            playlist.snapshot_id = snapshot

            added = 0
            for track in new_tracks:
                if track.id == None:
                    print(f"Track with id None: {track}")
                if add_own or track.added_by != self.user_id:
                    try:
                        # Only add the track if it wasn't already in the list when we subbed
                        if track.id not in playlist.track_ids.keys():
                            track_ids.append(track.id)
                            # Add the ID to the track ID list so we know not to add it in the future
                            playlist.track_ids[track.id] = datetime.utcnow()
                            added += 1
                    # TODO: correctly upgrade objects if storage consists of SubscribedPlaylists without ID list.
                    except AttributeError:
                        track_ids.append(track.id)
                        added += 1

            if added > 0:
                safe_print("Obtained {} new tracks from playlist {}!".format(
                    added, playlist.name))

        if len(track_ids) > 0:
            unique_ids = np.unique(track_ids)

            # If a feed log exists, filter all track IDs that have already been added to the feed before.
            if os.path.exists(self._feed_log_path):
                feed_log = pickle.load(open(self._feed_log_path, "rb"))
                filtered_indices = np.where(
                    ~np.isin(unique_ids, feed_log["track_ids"]))
                unique_ids = unique_ids[filtered_indices]

            # We can add at most 100 tracks to a playlist in a single request.
            if unique_ids.size <= 100:
                self.sp.user_playlist_add_tracks(self.user_id,
                                                 self.subscription_feed.id,
                                                 unique_ids)
            else:
                # Split array into near-equal sections that are smaller than 100 tracks
                for id_array in np.array_split(
                        unique_ids,
                        np.ceil(unique_ids.size / 100).astype(int)):
                    self.sp.user_playlist_add_tracks(self.user_id,
                                                     self.subscription_feed.id,
                                                     id_array)

            num_added_tracks = unique_ids.size
            self._log_feed_updates(unique_ids)

        # Update the timestamp and save to file
        self.subscription_feed.last_update = datetime.utcnow()
        self._save()

        return num_added_tracks

    # Get all tracks in the specified playlist, added after min_timestamp.
    # If snapshot is provided, ignore playlists of which the snapshot hasn't changed.
    def _get_playlist_tracks(
        self,
        playlist_owner_id: str,
        playlist_id: str,
        min_timestamp: datetime = None,
        compare_snapshot: str = None,
        return_snapshot=False,
    ):
        data = self.sp.user_playlist(playlist_owner_id,
                                     playlist_id,
                                     fields="tracks, snapshot_id")

        return_tracks = []
        if not min_timestamp:
            min_timestamp = datetime.fromtimestamp(0)

        # If the snapshot is still the same, there is nothing interesting for us to see.
        # NOTE: certain playlists like 'Brain Food' seem to have a different snapshot every time.
        if compare_snapshot and data["snapshot_id"] == compare_snapshot:
            # safe_print("Snapshot still the same, ignoring list contents.")
            return [], data["snapshot_id"]

        tracks = data["tracks"]
        while tracks:
            for track in tracks["items"]:
                added_at = track["added_at"]

                # Somehow, it's possible that we receive an empty track. IDK if this is a spotipy bug or what
                if track["track"] is None or track["track"]["id"] is None:
                    print("WARNING: encountered None track! Ignoring.")
                    continue

                timestamp = datetime.strptime(added_at, "%Y-%m-%dT%H:%M:%SZ")
                if timestamp > min_timestamp:
                    return_tracks.append(Track(track, playlist_id))
                    # safe_print("Found track with name {} and timestamp {} ( > {})".format(track_name, timestamp, min_timestamp))

            if tracks["next"]:
                tracks = self.sp.next(tracks)
            else:
                break

        if return_snapshot:
            return return_tracks, data["snapshot_id"]

        return return_tracks

    # Get all tracks from the users library and own playlists (including sub feed).
    def _get_all_user_tracks(self):
        tracks = {}

        self._get_user_library_tracks()

        for playlist_id, playlist in self.user_playlists.items():
            tracks = self._get_playlist_tracks(playlist["owner"]["id"],
                                               playlist_id)
            for track in tracks:
                tracks[track.id] = track

    def _get_user_library_tracks(self):
        tracks = {}

        data = self.sp.current_user_saved_tracks()

        while data:
            for track in data["items"]:
                print(track["track"].keys())
            exit(0)
            exit(0)
            # for playlist in playlists_data['items']:
            #     # If we own a playlist but it's collaborative, we treat it as a followed one since we might be interested in updates.
            #     if playlist['owner']['id'] != self.user_id or playlist['collaborative']:
            #         self.followed_playlists[playlist['id']] = playlist
            #     else:
            #         self.user_playlists[playlist['id']] = playlist

            # # If there were more playlists than we just received, query the next batch
            # if playlists_data['next']:
            #     playlists_data = self.sp.next(playlists_data)
            # else:
            #     break

    # Store the track ids we just added to the feed in the log file.
    def _log_feed_updates(self, track_ids: np.ndarray):
        """
        Log is dict with the following info:
            track_ids: numpy array of track ids that were added to the feed
            timestamps: timestamp at which each of the tracks was added to the feed
        """
        if os.path.exists(self._feed_log_path):
            feed_log = pickle.load(open(self._feed_log_path, "rb"))
        else:
            feed_log = {"track_ids": [], "timestamps": []}

        # Create, append and overwrite timestamps
        new_timestamps = np.repeat(datetime.utcnow(), track_ids.size)
        timestamp_array = np.append(feed_log["timestamps"], new_timestamps)
        feed_log["timestamps"] = timestamp_array

        # Append and overwrite track_ids
        track_id_array = np.append(feed_log["track_ids"], track_ids)
        feed_log["track_ids"] = track_id_array

        pickle.dump(feed_log, open(self._feed_log_path, "wb"))

    # Print the tracks and timestamps saved in the feed log.
    def print_feed_log(self):
        if not os.path.exists(self._feed_log_path):
            print("No feed log exists yet!")
            return

        feed_log = pickle.load(open(self._feed_log_path, "rb"))
        num_tracks = len(feed_log["track_ids"])
        print("Found {} tracks in log.".format(num_tracks))

        batch_size = 50
        tracks = []
        start_idx = 0
        print("Requesting track info...")
        for start_idx in tqdm(range(0, num_tracks, batch_size)):
            end_idx = start_idx + batch_size
            track_ids = feed_log["track_ids"][start_idx:end_idx]
            tracks += self.sp.tracks(track_ids)["tracks"]
            start_idx = end_idx

        for track, timestamp in zip(tracks, feed_log["timestamps"]):
            safe_print("{} - {} - {} - {}".format(track["artists"][0]["name"],
                                                  track["name"], timestamp,
                                                  track["id"]))

    # Follow a user
    def _follow_user(self, username: str):
        self.sp.user_follow_users([username])

    """    STUBS