コード例 #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
コード例 #2
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
コード例 #3
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
コード例 #4
0
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!')
コード例 #5
0
    def with_avconv(self):
        if log.level == 10:
            level = 'debug'
        else:
            level = '0'

        command = [
            'avconv', '-loglevel', level, '-i', self.input_file, '-ab', '192k',
            self.output_file, '-y'
        ]

        if self.trim_silence:
            log.warning('--trim-silence not supported with avconv')

        log.debug(command)
        return subprocess.call(command)
コード例 #6
0
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
コード例 #7
0
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
コード例 #8
0
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))