def with_ffmpeg(self): ffmpeg_pre = 'ffmpeg -y ' if not log.level == 10: ffmpeg_pre += '-hide_banner -nostats -v panic ' input_ext = self.input_song.split('.')[-1] output_ext = self.output_song.split('.')[-1] if input_ext == 'm4a': if output_ext == 'mp3': ffmpeg_params = '-codec:v copy -codec:a libmp3lame -q:a 2 ' elif output_ext == 'webm': ffmpeg_params = '-c:a libopus -vbr on -b:a 192k -vn ' elif input_ext == 'webm': if output_ext == 'mp3': ffmpeg_params = ' -ab 192k -ar 44100 -vn ' elif output_ext == 'm4a': ffmpeg_params = '-cutoff 20000 -c:a libfdk_aac -b:a 192k -vn ' command = '{0}-i {1} {2}{3}'.format( ffmpeg_pre, os.path.join(self.folder, self.input_song), ffmpeg_params, os.path.join(self.folder, self.output_song)).split(' ') log.debug(command) return subprocess.call(command)
def with_ffmpeg(self): ffmpeg_pre = 'ffmpeg -y ' if not log.level == 10: ffmpeg_pre += '-hide_banner -nostats -v panic ' _, input_ext = os.path.splitext(self.input_file) _, output_ext = os.path.splitext(self.output_file) if input_ext == '.m4a': if output_ext == '.mp3': ffmpeg_params = '-codec:v copy -codec:a libmp3lame -q:a 2 ' elif output_ext == '.webm': ffmpeg_params = '-c:a libopus -vbr on -b:a 192k -vn ' elif input_ext == '.webm': if output_ext == '.mp3': ffmpeg_params = ' -ab 192k -ar 44100 -vn ' elif output_ext == '.m4a': ffmpeg_params = '-cutoff 20000 -c:a libfdk_aac -b:a 192k -vn ' ffmpeg_pre += ' -i' command = ffmpeg_pre.split() + [ self.input_file ] + ffmpeg_params.split() + [self.output_file] log.debug(command) return subprocess.call(command)
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
def with_ffmpeg(self): ffmpeg_pre = 'ffmpeg -y ' if not log.level == 10: ffmpeg_pre += '-hide_banner -nostats -v panic ' _, input_ext = os.path.splitext(self.input_file) _, output_ext = os.path.splitext(self.output_file) ffmpeg_params = '' if input_ext == '.m4a': if output_ext == '.mp3': ffmpeg_params = '-codec:v copy -codec:a libmp3lame -ar 44100 ' elif output_ext == '.webm': ffmpeg_params = '-codec:a libopus -vbr on ' elif input_ext == '.webm': if output_ext == '.mp3': ffmpeg_params = '-codec:a libmp3lame -ar 44100 ' elif output_ext == '.m4a': ffmpeg_params = '-cutoff 20000 -codec:a libfdk_aac -ar 44100 ' if output_ext == '.flac': ffmpeg_params = '-codec:a flac -ar 44100 ' # add common params for any of the above combination ffmpeg_params += '-b:a 192k -vn ' ffmpeg_pre += ' -i' command = ffmpeg_pre.split() + [ self.input_file ] + ffmpeg_params.split() + [self.output_file] log.debug(command) return subprocess.call(command)
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
def write_playlist(playlist_url, path): playlist = fetch_playlist(playlist_url) to_download = os.getenv('PLAYLISTS', '') log.debug(f'downloading playlists: {to_download}') to_download = slugify(to_download, ok=',-_()[]{}').split(sep=',') playlist_name = slugify(playlist['name'], ok='-_()[]{}') if playlist_name in to_download: tracks = playlist['tracks'] text_file = u'{0}/{1}.txt'.format(path, playlist_name) return write_tracks(tracks, text_file)
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] log.debug(command) return subprocess.call(command)
def with_avconv(self): if log.level == 10: level = 'debug' else: level = '0' command = [ 'avconv', '-loglevel', level, '-i', os.path.join(self.folder, self.input_song), '-ab', '192k', os.path.join(self.folder, self.output_song) ] log.debug(command) return subprocess.call(command)
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)
def generate_metadata(raw_song): """ Fetch a song's metadata from Spotify. """ if internals.is_spotify(raw_song): # fetch track information directly if it is spotify link log.debug('Fetching metadata for given track URL') meta_tags = _getClient().track(raw_song) else: # otherwise search on spotify and fetch information from first result log.debug('Searching for "{}" on Spotify'.format(raw_song)) try: search_result = _getClient().search(raw_song, limit=1) meta_tags = search_result['tracks']['items'][0] except IndexError: return None artist = _getClient().artist(meta_tags['artists'][0]['id']) album = _getClient().album(meta_tags['album']['id']) try: meta_tags[u'genre'] = titlecase(artist['genres'][0]) except IndexError: meta_tags[u'genre'] = None try: meta_tags[u'copyright'] = album['copyrights'][0]['text'] except IndexError: meta_tags[u'copyright'] = None try: meta_tags[u'external_ids'][u'isrc'] except KeyError: meta_tags[u'external_ids'][u'isrc'] = None meta_tags[u'release_date'] = album['release_date'] meta_tags[u'publisher'] = album['label'] meta_tags[u'total_tracks'] = album['tracks']['total'] log.debug('Fetching lyrics') try: meta_tags['lyrics'] = lyricwikia.get_lyrics( meta_tags['artists'][0]['name'], meta_tags['name']) except lyricwikia.LyricsNotFound: meta_tags['lyrics'] = None # Some sugar meta_tags['year'], *_ = meta_tags['release_date'].split('-') meta_tags['duration'] = meta_tags['duration_ms'] / 1000.0 # Remove unwanted parameters del meta_tags['duration_ms'] del meta_tags['available_markets'] del meta_tags['album']['available_markets'] log.debug(pprint.pformat(meta_tags)) return meta_tags
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
def play_and_record(track_uri="spotify:track:1L1dpImK36DoZPr7rxy0hJ", filename="foo", songname="bar"): log.debug("entered playing and record") client.playback.stop() client.tracklist.set_repeat(False) client.tracklist.clear() client.tracklist.set_single(True) client.mixer.set_mute(False) client.mixer.set_volume(100) client.tracklist.add(uris=[track_uri]) listener.filename = filename listener.songname = songname try: # Activate cache if caching_enabled: log.debug('caching start') client.playback.play() timeout = time.time() + 20 while listener.get_playback_stopped(): if time.time() > timeout: raise RuntimeWarning('Could not play track for caching {}'.format(songname)) time.sleep(0.05) time.sleep(3) log.debug('stopping cache play') client.playback.stop() timeout = time.time() + 20 while not listener.get_playback_stopped(): if time.time() > timeout: raise RuntimeWarning('Caching record took too long {}'.format(songname)) time.sleep(0.05) log.debug('caching end') # start recording listener.reset_play_time() listener.start_recording() # make sure thread has started timeout = time.time() + 15 while not listener.record_thread.is_alive(): if time.time() > timeout: raise RuntimeError('Record thread still not alive track {}'.format(songname)) time.sleep(3) client.playback.play() timeout = time.time() + 10 while listener.get_playback_stopped(): if time.time() > timeout: raise RuntimeWarning('Could not play track {}'.format(songname)) time.sleep(0.05) # wait for end timeout = time.time() + 60 * 20 while not listener.get_playback_stopped(): if time.time() > timeout: raise RuntimeWarning('Record took longer than 20 minutes track {}'.format(songname)) time.sleep(0.1) # make sure thread has stopped timeout = time.time() + 15 while listener.record_thread.is_alive(): if time.time() > timeout: raise RuntimeError('Record thread still alive track {}'.format(songname)) except RuntimeWarning as e: log.error(str(e)) finally: client.playback.stop() return listener.play_time
def mopidy_playback_started(self): self.play_time = time.time() self.mutex.acquire_write() self.playback_stopped = False self.mutex.release_write() log.debug('started playback')
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
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_recording(self): log.debug('start recording') self.record_thread = threading.Thread( target=record, args=[self.filename, self.songname, self.get_playback_stopped]) self.record_thread.start()
def reset_play_time(self): log.debug('resetting play time') self.play_time = 0