def get_spotify_data(keywords, num_playlists):
    """Master function get retrieve data from Spotify."""
    # Create instance of Spotify class
    SpotifyMaster = Spotify(CLIENT_ID, CLIENT_SECRET)

    # Only retrieve playlists if not at num_playlists
    playlist_table_size = return_table_len("Playlists")
    if playlist_table_size < num_playlists - 10:
        # Pull playlist data a keyword
        print("Getting Spotify playlists")
        cache_dict = json_helper.read_cache()
        keyword_index = cache_dict["keyword_index"]
        keyword = keywords[keyword_index]
        print("Keyword: " + keyword)

        # Get playlists
        json_result = SpotifyMaster.search(keyword, "playlist")
        playlists = json_result["playlists"]["items"]

        # Write playlists to database
        write_playlists_to_database(SpotifyMaster, playlists)
        playlist_table_size = return_table_len("Playlists")
        print("Playlist table size: " + str(playlist_table_size))

        return

    # Otherwise, start getting tracks until reach limit
    tracks_table_size = return_table_len("Tracks")
    track_features_table_size = return_table_len("TrackFeatures")

    # Finish if over 100 rows for either
    if tracks_table_size > 120 and track_features_table_size > 120:
        print("Gathered sufficient data for the database.")
        return

    if tracks_table_size != num_playlists * 10:
        print("Getting Spotify Tracks")

        # Get the correct playlist href and increment the index counter
        cache_dict = json_helper.read_cache()
        cache_dict["playlist_href_index"] = cache_dict.get(
            "playlist_href_index", -1) + 1
        playlist_href_index = cache_dict["playlist_href_index"]
        json_helper.write_cache(cache_dict)
        playlist_href = cache_dict["playlist_hrefs"][playlist_href_index]

        # Get track ids from the playlist and write to database
        track_ids = SpotifyMaster.get_tracks_from_playlist(playlist_href)
        write_tracks_and_features_to_database(SpotifyMaster, track_ids,
                                              playlist_href,
                                              playlist_href_index + 1)
        print("Tracks table size: " + str(tracks_table_size))
        print("Track Features table size: " + str(track_features_table_size))

        return

    # Done getting data, JOIN time.
    print("Done retrieving Spotify playlists and track data.")
Ejemplo n.º 2
0
class MusicDownloader(object):
    def __init__(self):
        self.__youtube = Youtube()
        self.__spotify = Spotify()
        self.__editor = TagEditor()
        self.__last = LastFM()
        self.__apple = AppleMusic()
        self.__deezer = Deezer()

    def __downloadMusicFromYoutube(self, name, uri, dur):

        #finding song on youtube
        self.__youtube.get(name, dur)

        #downloading video from youtube
        if self.__youtube.download(url=self.__youtube.getResult(),
                                   path=uri,
                                   filename=uri):
            #converting video to mp3 file
            self.__youtube.convertVideoToMusic(uri=uri)
            return True
        else:
            return False

    def __getSongInfoFromSpotify(self, uri):

        try:
            return self.__spotify.getSongInfo(uri)
        except:
            return None

    def getNameFromYoutube(self, url):
        return self.__youtube.getNameFromYoutube(url)

    def getData(self, uri):
        try:
            return self.__spotify.getSongInfo(uri)
        except:
            return None

    def getLastFMTags(self, name):
        return self.__last.get(name)

    def getYoutubeMusicInfo(self, url):
        return self.__youtube.getNameFromYoutube(url)

    def downloadBySpotifyUri(self, uri, path):

        #get info
        info = self.__getSongInfoFromSpotify(uri)

        if info:

            fixed_name = f'{info["artist"][0]} - {info["name"]}'
            fixed_name = fixed_name.replace('.', '')
            fixed_name = fixed_name.replace(',', '')
            fixed_name = fixed_name.replace("'", '')
            fixed_name = fixed_name.replace("/", "")

            #finding and download from YouTube and tagging
            if self.__downloadMusicFromYoutube(fixed_name, info['uri'],
                                               info['duration_ms']):

                self.__editor.setTags(data=info)

                cachepath = os.getcwd() + '/cache'
                fullpath = os.getcwd() + '/Downloads'

                if not os.path.exists(fullpath):
                    os.makedirs(fullpath)

                name = f'{info["artist"][0]} - {info["name"]}'

                os.rename(f"{cachepath}/{info['uri']}/{info['uri']}.mp3",
                          f"{fullpath}/{getCorrect(name)}.mp3")

                print(path)

                if path:

                    os.rename(f"{fullpath}/{getCorrect(name)}.mp3",
                              f"{path}/{getCorrect(name)}.mp3")

                #deleting cache
                try:
                    shutil.rmtree(f"cache/{info['uri']}")
                except:
                    pass

                notify.send(f'{info["artist"][0]} - {info["name"]}')
                return True
        return False

    def downloadBySearchQuery(self, query, path=None):

        #get info
        info = self.__spotify.search(query=query)

        if not info:
            info = self.__last.get(query)

        if info:

            fixed_name = f'{info["artist"][0]} - {info["name"]}'
            fixed_name = fixed_name.replace('.', '')
            fixed_name = fixed_name.replace(',', '')
            fixed_name = fixed_name.replace("'", '')
            fixed_name = fixed_name.replace("/", "")

            #finding and download from YouTube and tagging
            self.__downloadMusicFromYoutube(fixed_name, info['uri'],
                                            info['duration_ms'])

            self.__editor.setTags(data=info)

            cachepath = os.getcwd() + '/cache'
            fullpath = os.getcwd() + '/Downloads'

            if not os.path.exists(fullpath):
                os.makedirs(fullpath)

            name = f'{info["artist"][0]} - {info["name"]}'

            os.rename(f"{cachepath}/{info['uri']}/{info['uri']}.mp3",
                      f"{fullpath}/{getCorrect(name)}.mp3")

            if path:

                os.rename(f"{fullpath}/{getCorrect(name)}.mp3",
                          f"{path}/{getCorrect(name)}.mp3")

            #deleting cache
            try:
                shutil.rmtree(f"cache/{info['uri']}")
            except:
                pass

            notify.send(f'{info["artist"][0]} - {info["name"]}')
            return True, info
        else:
            return False, None

    def downloadBySpotifyUriFromFile(self, filename):
        try:

            with open(filename, 'r') as f:
                data = f.readlines()

        except FileNotFoundError:

            print(f'No such file or directory: "{filename}"')
            exit(2)

        #normalize
        try:
            data.remove('\n')
        except:
            pass
        links = [str(item).replace('\n', '') for item in data]

        for i, song in zip(range(len(links)), links):
            print(f'[{i+1}] - {song}')
            try:
                state = self.downloadBySpotifyUri(str(song).split(':')[-1])
                if not state:
                    notify.send(f'Failed to download', True)
            except:
                print('Something went wrong!')

    def downloadBySpotifyUriPlaylistMode(self, playlist_uri, path):

        user = Spotify.User()
        playlist = user.getPlaylistTracks(playlist_uri)

        for info, i in zip(playlist, range(len(playlist))):

            print(f'Downloading {i+1} of {len(playlist)}')

            fixed_name = f'{info["artist"][0]} - {info["name"]}'
            fixed_name = fixed_name.replace('.', '')
            fixed_name = fixed_name.replace(',', '')
            fixed_name = fixed_name.replace("'", '')
            fixed_name = fixed_name.replace("/", "")

            #finding and download from YouTube and tagging
            self.__downloadMusicFromYoutube(fixed_name, info['uri'],
                                            info['duration_ms'])

            self.__editor.setTags(data=info)

            cachepath = os.getcwd() + '/cache'
            fullpath = os.getcwd() + '/Downloads'

            if not os.path.exists(fullpath):
                os.makedirs(fullpath)

            name = f'{info["artist"][0]} - {info["name"]}'

            os.rename(f"{cachepath}/{info['uri']}/{info['uri']}.mp3",
                      f"{fullpath}/{getCorrect(name)}.mp3")

            if path:

                os.rename(f"{fullpath}/{getCorrect(name)}.mp3",
                          f"{path}/{getCorrect(name)}.mp3")

            #deleting cache
            try:
                shutil.rmtree(f"cache/{info['uri']}")
            except:
                pass

            notify.send(f'{info["artist"][0]} - {info["name"]}')

    def downloadBySpotifyUriAlbumMode(self, album_uri, path):

        user = Spotify()
        playlist = user.getAlbum(album_uri)

        for info, i in zip(playlist['tracks'], range(len(playlist['tracks']))):

            print(f'Downloading {i+1} of {len(playlist["tracks"])}')

            fixed_name = f'{info["artist"][0]} - {info["name"]}'
            fixed_name = fixed_name.replace('.', '')
            fixed_name = fixed_name.replace(',', '')
            fixed_name = fixed_name.replace("'", '')
            fixed_name = fixed_name.replace("/", "")

            #finding and downloading from YouTube and tagging
            self.__downloadMusicFromYoutube(fixed_name, info['uri'],
                                            info['duration_ms'])

            self.__editor.setTags(data=info)

            cachepath = os.getcwd() + '/cache'
            fullpath = os.getcwd() + '/Downloads'

            if not os.path.exists(fullpath):
                os.makedirs(fullpath)

            name = f'{info["artist"][0]} - {info["name"]}'

            os.rename(f"{cachepath}/{info['uri']}/{info['uri']}.mp3",
                      f"{fullpath}/{getCorrect(name)}.mp3")

            if path:

                os.rename(f"{fullpath}/{getCorrect(name)}.mp3",
                          f"{path}/{getCorrect(name)}.mp3")

            #deleting cache
            try:
                shutil.rmtree(f"cache/{info['uri']}")
            except:
                pass

            notify.send(f'{info["artist"][0]} - {info["name"]}')

    def downloadByDeezerUrl(self, url, path):

        link = str(str(url).split('/')[-1]).split('?')[0]

        #get info
        info = self.__deezer.getSongInfo(link)

        if info:

            fixed_name = f'{info["artist"][0]} - {info["name"]}'
            fixed_name = fixed_name.replace('.', '')
            fixed_name = fixed_name.replace(',', '')
            fixed_name = fixed_name.replace("'", '')
            fixed_name = fixed_name.replace("/", "")

            #finding and download from YouTube and tagging
            if self.__downloadMusicFromYoutube(fixed_name, info['uri'],
                                               info['duration_ms']):

                self.__editor.setTags(data=info)

                cachepath = os.getcwd() + '/cache'
                fullpath = os.getcwd() + '/Downloads'

                if not os.path.exists(fullpath):
                    os.makedirs(fullpath)

                name = f'{info["artist"][0]} - {info["name"]}'

                os.rename(f"{cachepath}/{info['uri']}/{info['uri']}.mp3",
                          f"{fullpath}/{getCorrect(name)}.mp3")

                print(path)

                if path:

                    os.rename(f"{fullpath}/{getCorrect(name)}.mp3",
                              f"{path}/{getCorrect(name)}.mp3")

                #deleting cache
                try:
                    shutil.rmtree(f"cache/{info['uri']}")
                except:
                    pass

                notify.send(f'{info["artist"][0]} - {info["name"]}')
                return True
        return False

    def downloadByDeezerUrlAlbumMode(self, album_url, path):

        link = str(str(album_url).split('/')[-1]).split('?')[0]

        #get info
        playlist = self.__deezer.getAlbum(link)

        for info, i in zip(playlist['tracks'], range(len(playlist['tracks']))):

            print(f'Downloading {i+1} of {len(playlist["tracks"])}')

            fixed_name = f'{info["artist"][0]} - {info["name"]}'
            fixed_name = fixed_name.replace('.', '')
            fixed_name = fixed_name.replace(',', '')
            fixed_name = fixed_name.replace("'", '')
            fixed_name = fixed_name.replace("/", "")

            #finding and downloading from YouTube and tagging
            self.__downloadMusicFromYoutube(fixed_name, info['uri'],
                                            info['duration_ms'])

            self.__editor.setTags(data=info)

            cachepath = os.getcwd() + '/cache'
            fullpath = os.getcwd() + '/Downloads'

            if not os.path.exists(fullpath):
                os.makedirs(fullpath)

            name = f'{info["artist"][0]} - {info["name"]}'

            os.rename(f"{cachepath}/{info['uri']}/{info['uri']}.mp3",
                      f"{fullpath}/{getCorrect(name)}.mp3")

            if path:

                os.rename(f"{fullpath}/{getCorrect(name)}.mp3",
                          f"{path}/{getCorrect(name)}.mp3")

            #deleting cache
            try:
                shutil.rmtree(f"cache/{info['uri']}")
            except:
                pass

            notify.send(f'{info["artist"][0]} - {info["name"]}')

    def downloadByDeezerUrlPlaylistMode(self, playlist_url, path):

        link = str(str(playlist_url).split('/')[-1]).split('?')[0]

        #get info
        playlist = self.__deezer.getPlaylist(link)

        for info, i in zip(playlist['tracks'], range(len(playlist['tracks']))):

            print(f'Downloading {i+1} of {len(playlist["tracks"])}')

            fixed_name = f'{info["artist"][0]} - {info["name"]}'
            fixed_name = fixed_name.replace('.', '')
            fixed_name = fixed_name.replace(',', '')
            fixed_name = fixed_name.replace("'", '')
            fixed_name = fixed_name.replace("/", "")

            #finding and download from YouTube and tagging
            self.__downloadMusicFromYoutube(fixed_name, info['uri'],
                                            info['duration_ms'])

            self.__editor.setTags(data=info)

            cachepath = os.getcwd() + '/cache'
            fullpath = os.getcwd() + '/Downloads'

            if not os.path.exists(fullpath):
                os.makedirs(fullpath)

            name = f'{info["artist"][0]} - {info["name"]}'

            os.rename(f"{cachepath}/{info['uri']}/{info['uri']}.mp3",
                      f"{fullpath}/{getCorrect(name)}.mp3")

            if path:

                os.rename(f"{fullpath}/{getCorrect(name)}.mp3",
                          f"{path}/{getCorrect(name)}.mp3")

            #deleting cache
            try:
                shutil.rmtree(f"cache/{info['uri']}")
            except:
                pass

            notify.send(f'{info["artist"][0]} - {info["name"]}')

    def downloadFromYoutubeMusic(self, url, info, path):

        print(info)

        uri = info['uri']

        #downloading video from youtube
        if self.__youtube.download(url=url, path=uri, filename=uri):

            #converting video to mp3 file
            self.__youtube.convertVideoToMusic(uri=uri)

            self.__editor.setTags(data=info)

            cachepath = os.getcwd() + '/cache'
            fullpath = os.getcwd() + '/Downloads'

            if not os.path.exists(fullpath):
                os.makedirs(fullpath)

            name = f'{info["artist"][0]} - {info["name"]}'

            os.rename(f"{cachepath}/{info['uri']}/{info['uri']}.mp3",
                      f"{fullpath}/{getCorrect(name)}.mp3")

            if path:

                os.rename(f"{fullpath}/{getCorrect(name)}.mp3",
                          f"{path}/{getCorrect(name)}.mp3")

            #deleting cache
            try:
                shutil.rmtree(f"cache/{info['uri']}")
            except:
                pass

            notify.send(f'{info["artist"][0]} - {info["name"]}')
            return True, info
        else:
            return False, None

    def search(self, query):
        return self.__spotify.search(query=query)
Ejemplo n.º 3
0
class SpotifyClient(object):
    def __init__(self, host):
        self.host = host

        self.direct = Direct(self)
        self.server = None

        self.sp = None
        self.reconnect_time = None
        self.reconnect_timer = None

        self.ready_event = Event()
        self.messages = []

    def start(self):
        if self.sp:
            self.sp.disconnect()
            self.sp = None

        self.sp = Spotify()

        self.ready_event = Event()
        self.messages = []

        self.sp.on('error', self.on_error)\
               .on('close', self.on_close)

        self.sp.login(self.host.username, self.host.password, self.on_login)

    def on_login(self):
        # Refresh server info
        self.host.refresh()

        # Release request hold
        self.ready_event.set()

    def on_error(self, message):
        self.messages.append((logging.ERROR, message))
        Log.Error(message)

    def on_close(self, code, reason=None):
        # Force re-authentication
        self.sp.authenticated = False

        # Reconnect
        self.connect()

    def connect(self):
        # Rate-limit re-connections
        if self.reconnect_time:
            span = time.time() - self.reconnect_time
            Log.Debug('Last reconnection attempt was %s seconds ago', span)

            # Delay next reconnection
            if span < 120:
                self.connect_delayed()
                return

        Log.Info('Attempting reconnection to Spotify...')

        self.reconnect_time = time.time()

        # Hold requests while we re-connect
        self.ready_event = Event()

        # Start connecting...
        self.sp.connect()

    def connect_delayed(self):
        self.reconnect_timer = Timer(180, self.connect)
        self.reconnect_timer.start()

        Log.Info('Reconnection will be attempted again in 180 seconds')

    @property
    def constructed(self):
        return self.sp and self.ready_event

    @property
    def ready(self):
        if not self.constructed:
            return False

        return self.ready_event.wait(10)

    def shutdown(self):
        self.sp.api.shutdown()
        self.sp = None

    #
    # Public methods
    #

    def search(self, query, query_type='all', max_results=50, offset=0):
        """ Execute a search

        :param query:          A query string.
        """
        return self.sp.search(query, query_type, max_results, offset)

    def artist_uris(self, artist):
        top_tracks = self.artist_top_tracks(artist)

        # Top Track URIs
        track_uris = []

        if top_tracks:
            track_uris = [tr.uri for tr in top_tracks.tracks if tr is not None]

        # Album URIs
        album_uris = [al.uri for al in artist.albums if al is not None]

        return track_uris, album_uris

    def artist_top_tracks(self, artist):
        for tt in artist.top_tracks:
            # TopTracks matches account region?
            if tt.country == self.sp.country:
                return tt

        # Unable to find TopTracks for account region
        return None

    #
    # Streaming
    #

    def track_url(self, track):
        if self.host.proxy_tracks and self.server:
            return self.server.get_track_url(str(track.uri),
                                             hostname=self.host.hostname)

        return function_path('play', uri=str(track.uri), ext='mp3')

    def stream_url(self, uri):
        if self.host.proxy_tracks and self.server:
            return self.server.get_track_url(str(uri),
                                             hostname=self.host.hostname)

        return self.direct.get(uri)

    def last_message(self):
        if not self.messages:
            return None, ''

        return self.messages[-1]
Ejemplo n.º 4
0
class SpotifyClient(object):
    def __init__(self, host):
        self.host = host

        self.direct = Direct(self)
        self.server = None

        self.sp = None
        self.reconnect_time = None
        self.reconnect_timer = None

        self.ready_event = Event()
        self.messages = []

    def start(self):
        if self.sp:
            self.sp.disconnect()
            self.sp = None

        self.sp = Spotify()

        self.ready_event = Event()
        self.messages = []

        self.sp.on('error', self.on_error)\
               .on('close', self.on_close)

        self.sp.login(self.host.username, self.host.password, self.on_login)

    def on_login(self):
        # Refresh server info
        self.host.refresh()

        # Release request hold
        self.ready_event.set()

    def on_error(self, message):
        self.messages.append((logging.ERROR, message))
        Log.Error(message)

    def on_close(self, code, reason=None):
        # Force re-authentication
        self.sp.authenticated = False

        # Reconnect
        self.connect()

    def connect(self):
        # Rate-limit re-connections
        if self.reconnect_time:
            span = time.time() - self.reconnect_time
            Log.Debug('Last reconnection attempt was %s seconds ago', span)

            # Delay next reconnection
            if span < 120:
                self.connect_delayed()
                return

        Log.Info('Attempting reconnection to Spotify...')

        self.reconnect_time = time.time()

        # Hold requests while we re-connect
        self.ready_event = Event()

        # Start connecting...
        self.sp.connect()

    def connect_delayed(self):
        self.reconnect_timer = Timer(180, self.connect)
        self.reconnect_timer.start()

        Log.Info('Reconnection will be attempted again in 180 seconds')

    @property
    def constructed(self):
        return self.sp and self.ready_event

    @property
    def ready(self):
        if not self.constructed:
            return False

        return self.ready_event.wait(10)

    def shutdown(self):
        self.sp.api.shutdown()
        self.sp = None

    #
    # Public methods
    #

    def search(self, query, query_type='all', max_results=50, offset=0):
        """ Execute a search

        :param query:          A query string.
        """
        return self.sp.search(query, query_type, max_results, offset)

    def artist_uris(self, artist):
        top_tracks = self.artist_top_tracks(artist)

        # Top Track URIs
        track_uris = []

        if top_tracks:
            track_uris = [tr.uri for tr in top_tracks.tracks if tr is not None]

        # Album URIs
        album_uris = [al.uri for al in artist.albums if al is not None]

        return track_uris, album_uris

    def artist_top_tracks(self, artist):
        for tt in artist.top_tracks:
            # TopTracks matches account region?
            if tt.country == self.sp.country:
                return tt

        # Unable to find TopTracks for account region
        return None

    #
    # Streaming
    #

    def track_url(self, track):
        if self.host.proxy_tracks and self.server:
            return self.server.get_track_url(str(track.uri), hostname=self.host.hostname)

        return function_path('play', uri=str(track.uri), ext='mp3')

    def stream_url(self, uri):
        if self.host.proxy_tracks and self.server:
            return self.server.get_track_url(str(uri), hostname=self.host.hostname)

        return self.direct.get(uri)

    def last_message(self):
        if not self.messages:
            return None, ''

        return self.messages[-1]