Exemple #1
0
def write_tracks(tracks, text_file):
    log.info(u'Writing {0} tracks to {1}'.format(tracks['total'], text_file))
    track_urls = []
    with open(text_file, 'a') as file_out:
        while True:
            for item in tracks['items']:
                if 'track' in item:
                    track = item['track']
                else:
                    track = item
                try:
                    track_url = track['external_urls']['spotify']
                    log.debug(track_url)
                    file_out.write(track_url + '\n')
                    track_urls.append(track_url)
                except KeyError:
                    log.warning(
                        u'Skipping track {0} by {1} (local only?)'.format(
                            track['name'], track['artists'][0]['name']))
            # 1 page = 50 results
            # check if there are more pages
            if tracks['next']:
                tracks = spotify.next(tracks)
            else:
                break
    return track_urls
Exemple #2
0
    def stop(self):
        log.info("Stopping PlayListDownloader")

        if self.future:
            return self.future.cancel()
        else:
            return False
Exemple #3
0
def _download_songs(folder, songs, progress_func):
    log.info(u'Preparing to download {} songs'.format(len(songs)))
    downloaded_songs = []

    for number, raw_song in enumerate(songs):
        try:
            progress_func({"current": number, "total": len(songs)})

            _download_single(folder, raw_song, number=number)
        # token expires after 1 hour
        except spotipy.client.SpotifyException:
            # refresh token when it expires
            log.debug('Token expired, generating new one and authorizing')
            spotify_tools.init()
            _download_single(folder, raw_song, number=number)
        # detect network problems
        except (urllib.request.URLError, TypeError, IOError):
            songs.append(raw_song)

            log.warning(
                'Failed to download song. Will retry after other songs\n',
                exc_info=True)

            # wait 0.5 sec to avoid infinite looping
            time.sleep(0.5)
            continue

        downloaded_songs.append(raw_song)

    return downloaded_songs
Exemple #4
0
def write_playlist(username, playlist_id):
    results = spotify.user_playlist(username,
                                    playlist_id,
                                    fields='tracks,next,name')
    text_file = u'{0}.txt'.format(slugify(results['name'], ok='-_()[]{}'))
    log.info(u'Writing {0} tracks to {1}'.format(results['tracks']['total'],
                                                 text_file))
    tracks = results['tracks']
    write_tracks(text_file, tracks)
Exemple #5
0
def song(input_song, output_song, folder, avconv=False):
    """ Do the audio format conversion. """
    if not input_song == output_song:
        convert = Converter(input_song, output_song, folder)
        log.info('Converting {0} to {1}'.format(input_song,
                                                output_song.split('.')[-1]))
        if avconv:
            exit_code = convert.with_avconv()
        else:
            exit_code = convert.with_ffmpeg()
        return exit_code
    return 0
Exemple #6
0
def embed(music_file, meta_tags):
    """ Embed metadata. """
    embed = EmbedMetadata(music_file, meta_tags)
    if music_file.endswith('.m4a'):
        log.info('Applying metadata')
        return embed.as_m4a()
    elif music_file.endswith('.mp3'):
        log.info('Applying metadata')
        return embed.as_mp3()
    else:
        log.warning('Cannot embed metadata into given output extension')
        return False
def get_settings():
    log.info("Retrieving settings")

    try:
        settings = {
            'settings': config.get_config_dict()
        }

        return jsonify(settings)
    except Exception:
        log.error("Error Retrieving settings", exc_info=True)

        abort(500, 'Error Retrieving settings')
def input_link(links):
    """ Let the user input a choice. """
    while True:
        try:
            log.info('Choose your number:')
            the_chosen_one = int(input('> '))
            if 1 <= the_chosen_one <= len(links):
                return links[the_chosen_one - 1]
            elif the_chosen_one == 0:
                return None
            else:
                log.warning('Choose a valid number!')
        except ValueError:
            log.warning('Choose a valid number!')
def put_settings():
    if not request.json or 'settings' not in request.json:
        abort(400, 'Error Saving settings: settings obligatory')

    log.info("Saving settings")

    try:
        config.save_config(request.json['settings'])

        config.init_config()

        return jsonify({'status': 'OK'})
    except Exception:
        log.error("Error Saving settings", exc_info=True)

        abort(500, 'Error Saving settings')
def info():
    if not request.json or 'url' not in request.json:
        abort(400, 'Error getting playlist info: url obligatory')

    try:
        url = request.json['url']

        log.info("Get playlist info[%s]", url)

        info = spotdl.fetch_info(url)

        return jsonify(info)
    except Exception:
        log.error("Error getting playlist info", exc_info=True)

        abort(500, 'Error getting playlist info')
Exemple #11
0
    def run(self):
        log.info("Init Downloader")

        try:
            self.init_date = time.localtime()

            self.download()

            self.end_date = time.localtime()

            log.info("Finished Downloader")
        except Exception as e:
            log.error("Error in Downloader", exc_info=True)

            self.end_date = time.localtime()

            raise Exception("Error in Downloader")
def search():
    if not request.json or 'query' not in request.json:
        abort(400, 'Error searching: query obligatory')

    try:
        query = request.json['query']

        log.info("Searching[%s]", query)

        items = spotdl.search(query, max_results_per_type=int(
            const.config.search_max_results))

        return jsonify(items)
    except Exception:
        log.error("Error searching", exc_info=True)

        abort(500, 'Error searching')
def youtube():
    if 'url' not in request.args:
        abort(400,
              'Error getting playlist redirecting to youtube: url obligatory')

    try:
        url = request.args['url']

        log.info("Redirecting to youtube[%s]", url)

        yt_url = spotdl.fetch_yt_url(url)

        return redirect(yt_url, code=302)
    except Exception:
        log.error("Error redirecting to youtube", exc_info=True)

        abort(500, 'Error redirecting to youtube')
Exemple #14
0
def fetch_playlist(playlist):
    splits = internals.get_splits(playlist)
    try:
        username = splits[-3]
    except IndexError:
        # Wrong format, in either case
        log.error('The provided playlist URL is not in a recognized format!')
        sys.exit(10)
    playlist_id = splits[-1]
    try:
        results = spotify.user_playlist(username, playlist_id,
                                        fields='tracks,next,name')
    except spotipy.client.SpotifyException:
        log.error('Unable to find playlist')
        log.info('Make sure the playlist is set to publicly visible and then try again')
        sys.exit(11)

    return results
Exemple #15
0
def _fetch_playlist(playlist):
    splits = internals.get_splits(playlist)
    try:
        username = splits[-3]
    except IndexError:
        # Wrong format, in either case
        log.error('The provided playlist URL is not in a recognized format!')
        return None
    playlist_id = splits[-1]
    try:
        results = _getClient().user_playlist(
            username,
            playlist_id,
            fields='tracks,next,name,images,artists,description')
    except spotipy.client.SpotifyException:
        log.error('Unable to find playlist', exc_info=True)
        log.info('Make sure the playlist is set to ' +
                 'publicly visible and then try again')
        return None

    return results
def download_history():
    log.info("Retrieving download_history")

    try:
        items = list(map(lambda d: {
            'url': d[0],
            'name': d[1].get_name(),
            'status': d[1].get_status().__dict__,
            'init_date': d[1].get_init_date(format="%H:%M:%S"),
            'end_date': d[1].get_end_date(format="%H:%M:%S"),
        },
            current_downloads.items()))

        response = {
            'items': items
        }

        return jsonify(response)
    except Exception:
        log.error("Error retrieving download_history", exc_info=True)

        abort(500, 'Error retrieving download_history')
    def run(self):
        log.info("Init Downloader")
        self.status = "Init Downloader"

        try:
            self.init_date = time.localtime()

            self.download()

            self.end_date = time.localtime()

            log.info("Finished Downloader")

            self.status = "Finished Downloader"
        except Exception as e:
            log.error("Error in Downloader", exc_info=True)

            self.status = "Error in Downloader: {0}".format(str(e))

            self.end_date = time.localtime()

            raise Exception("Error in Downloader")
def post_download():
    if not request.json or 'url' not in request.json:
        abort(400, 'Error downloading playlist info: url obligatory')

    try:
        url = request.json['url']

        log.info("Downloading url[%s]", url)

        if url not in current_downloads:
            downloader = SpotifyDownloader(url)

            downloader.start()

            current_downloads[url] = downloader

            return jsonify({'status': 'OK'})
        else:
            return jsonify({'status': 'ALREADY_ADDED'})
    except Exception:
        log.error("Error downloading playlist info", exc_info=True)

        abort(400, 'Error downloading playlist info')
Exemple #19
0
def get_playlists(username):
    """ Fetch user playlists when using the -u option. """
    playlists = spotify.user_playlists(username)
    links = []
    check = 1

    while True:
        for playlist in playlists['items']:
            # in rare cases, playlists may not be found, so playlists['next']
            # is None. Skip these. Also see Issue #91.
            if playlist['name'] is not None:
                log.info(u'{0:>5}. {1:<30}  ({2} tracks)'.format(
                    check, playlist['name'], playlist['tracks']['total']))
                playlist_url = playlist['external_urls']['spotify']
                log.debug(playlist_url)
                links.append(playlist_url)
                check += 1
        if playlists['next']:
            playlists = spotify.next(playlists)
        else:
            break

    return links
def _check_exists(folder, music_file, raw_song, meta_tags):
    """ Check if the input song already exists in the given folder. """
    log.debug('Cleaning any temp files and checking '
              'if "{}" already exists'.format(music_file))
    if not os.path.isdir(folder):
        os.mkdir(folder)

    songs = os.listdir(folder)
    for song in songs:
        if song.endswith('.temp'):
            os.remove(os.path.join(folder, song))
            continue
        # check if a song with the same name is
        # already present in the given folder
        if os.path.splitext(song)[0] == music_file:
            log.debug('Found an already existing song: "{}"'.format(song))
            if internals.is_spotify(raw_song):
                # check if the already downloaded song has correct metadata
                # if not, remove it and download again without prompt
                already_tagged = metadata.compare(os.path.join(folder, song),
                                                  meta_tags)
                log.debug(
                    'Checking if it is already tagged correctly? {}'.format(
                        already_tagged))
                if not already_tagged:
                    os.remove(os.path.join(folder, song))
                    return False

            log.warning('"{}" already exists'.format(song))
            if const.config.overwrite == 'force':
                os.remove(os.path.join(folder, song))
                log.info('Overwriting "{}"'.format(song))
                return False
            elif const.config.overwrite == 'skip':
                log.info('Skipping "{}"'.format(song))
                return True
    return False
Exemple #21
0
def main():
    init()

    from api.routes import app as routes

    log.info("Launching Flash app")

    app = Flask(__name__, static_url_path='')

    SECRET_KEY = os.urandom(32)
    app.config['SECRET_KEY'] = SECRET_KEY

    app.register_blueprint(routes, url_prefix='/')

    log.info("Loaded routes")
    log.info(app.url_map)

    threaded = True
    if os.getenv('LOCAL_MODE', "false").lower() == "true":
        from flask_cors import CORS
        CORS(app)
        threaded = False

    app.run(host='0.0.0.0', threaded=threaded)
Exemple #22
0
def init():
    log.info("Launching Spotify-Downloader")

    config.init_config()
def _download_single(folder, raw_song, number=None):
    """ Logic behind downloading a song. """
    if internals.is_youtube(raw_song):
        log.debug('Input song is a YouTube URL')
        content = youtube_tools.go_pafy(raw_song, meta_tags=None)
        raw_song = slugify(content.title).replace('-', ' ')
        meta_tags = spotify_tools.generate_metadata(raw_song)
        meta_tags['number'] = number
    else:
        meta_tags = spotify_tools.generate_metadata(raw_song)
        meta_tags['number'] = number
        content = youtube_tools.go_pafy(raw_song, meta_tags)

    if content is None:
        log.debug('Found no matching video')
        return

    if const.config.download_only_metadata and meta_tags is None:
        log.info('Found no metadata. Skipping the download')
        return

    # "[number]. [artist] - [song]" if downloading from list
    # otherwise "[artist] - [song]"
    youtube_title = youtube_tools.get_youtube_title(content, number)
    log.info('{} ({})'.format(youtube_title, content.watchv_url))

    # generate file name of the song to download
    songname = content.title

    if meta_tags is not None:
        refined_songname = internals.format_string(const.config.file_format,
                                                   meta_tags,
                                                   slugification=True)
        log.debug('Refining songname from "{0}" to "{1}"'.format(
            songname, refined_songname))
        if not refined_songname == ' - ':
            songname = refined_songname
    else:
        log.warning('Could not find metadata')
        songname = internals.sanitize(songname)

    if not _check_exists(folder, songname, raw_song, meta_tags):
        # deal with file formats containing slashes to non-existent directories
        songpath = os.path.join(folder, os.path.dirname(songname))
        os.makedirs(songpath, exist_ok=True)
        input_song = songname + const.config.input_ext
        output_song = songname + const.config.output_ext
        if youtube_tools.download_song(songpath, input_song, content):
            try:
                convert.song(input_song,
                             output_song,
                             folder,
                             avconv=const.config.avconv,
                             trim_silence=const.config.trim_silence)
            except FileNotFoundError:
                encoder = 'avconv' if const.config.avconv else 'ffmpeg'
                log.warning(
                    'Could not find {0}, skipping conversion'.format(encoder))
                const.config.output_ext = const.config.input_ext
                output_song = songname + const.config.output_ext

            if not const.config.input_ext == const.config.output_ext:
                os.remove(os.path.join(folder, input_song))
            if not const.config.no_metadata and meta_tags is not None:
                metadata.embed(os.path.join(folder, output_song), meta_tags)
            return True
        else:
            log.exception('Error downloading song {}'.format(raw_song))
    def start(self):
        log.info("Starting PlayListDownloader")

        self.future = _downloaders_thread_pool.submit(self.run)
Exemple #25
0
def write_album(album):
    tracks = spotify.album_tracks(album['id'])
    text_file = u'{0}.txt'.format(slugify(album['name'], ok='-_()[]{}'))
    log.info(u'writing {0} tracks to {1}'.format(tracks['total'], text_file))
    write_tracks(text_file, tracks)
def _download_songs(folder, songs, progress_func, max_retries=3):
    log.info(u'Preparing to download {} songs'.format(len(songs)))
    downloaded_songs = []

    failed = 0
    pending = len(songs)
    retrying = 0
    total = len(songs)
    failed_songs = {}

    songs_enumeration = list(enumerate(songs))

    for number, raw_song in songs_enumeration:
        try:
            progress_func({
                "retrying": retrying,
                "failed": failed,
                "pending": pending,
                "total": total,
            })

            _download_single(folder, raw_song, number=number)

            downloaded_songs.append(raw_song)

            pending = pending - 1

            if failed_songs.get(str(number), 0) > 0:
                retrying = retrying - 1
        # token expires after 1 hour
        except spotipy.client.SpotifyException:
            # refresh token when it expires
            log.debug('Token expired, generating new one and authorizing')
            spotify_tools.init()
            _download_single(folder, raw_song, number=number)
        # Retry if possible
        except Exception:
            if failed_songs.get(str(number), 0) == max_retries:
                log.exception('Error downloading song {}'.format(raw_song))

                failed = failed + 1
                retrying = retrying - 1
                pending = pending - 1
            else:
                retry = failed_songs.get(str(number), 0)

                if retry == 0:
                    retrying = retrying + 1

                log.warning(
                    'Failed to download song. Will retry after other songs\n',
                    exc_info=True)

                failed_songs[str(number)] = retry + 1
                songs_enumeration.append((number, raw_song))

                time.sleep(0.5)

    progress_func({
        "retrying": retrying,
        "failed": failed,
        "pending": pending,
        "total": total,
    })

    return downloaded_songs