def album_distance(self, items, album_info, mapping): dist = hooks.Distance() # check if the album has a log file (from EAC/XLD): release_ids = _process_items(items) has_log = release_ids is not None # get bitdepths and samplerates bitdepths = set(map(lambda item: item.bitdepth, items)) samplerates = set(map(lambda item: item.samplerate, items)) # Boolean flags to determine media type is_not_cd = max(bitdepths) > 16 or max(samplerates) != 44100 could_be_cd = not is_not_cd candidate_media_is_cd = \ "CD" in album_info.media.upper() if album_info.media else False # penalty for CDs if it's clearly not a CD if is_not_cd and candidate_media_is_cd: dist.add('media', self.config['media_weight'].as_number()) album_info.data_source+='+' + ui.colorize('text_warning', 'guess_media_NOT_A_CD') # penalty if we think it's a CD (found a log file and # bitdepths/samplerates are correct) but the candidate media is wrong: if has_log and could_be_cd and not candidate_media_is_cd: dist.add('media', self.config['media_weight'].as_number()) album_info.data_source+='+' + ui.colorize('text_warning', 'guess_media_IS_A_CD') # penalty if we found an album id from the log file, # and the album id does not match: if release_ids is not None and album_info.album_id not in release_ids: dist.add('album_id', self.config['album_id_weight'].as_number()) album_info.data_source+='+' + ui.colorize('text_warning', 'guess_media_ALBUM_ID_FROM_LOG_WRONG') return dist
def check_bad(self, lib, opts, args): for item in lib.items(ui.decargs(args)): # First, check whether the path exists. If not, the user # should probably run `beet update` to cleanup your library. dpath = displayable_path(item.path) self._log.debug("checking path: {}", dpath) if not os.path.exists(item.path): ui.print_("{}: file does not exist".format( ui.colorize('text_error', dpath))) # Run the checker against the file if one is found ext = os.path.splitext(item.path)[1][1:] checker = self.get_checker(ext) if not checker: continue path = item.path if not isinstance(path, unicode): path = item.path.decode(sys.getfilesystemencoding()) status, errors, output = checker(path) if status > 0: ui.print_("{}: checker exited withs status {}" .format(ui.colorize('text_error', dpath), status)) for line in output: ui.print_(" {}".format(displayable_path(line))) elif errors > 0: ui.print_("{}: checker found {} errors or warnings" .format(ui.colorize('text_warning', dpath), errors)) for line in output: ui.print_(" {}".format(displayable_path(line))) else: ui.print_("{}: ok".format(ui.colorize('text_success', dpath)))
def search(self, lib, opts, args): password = config['gmusic']['password'] email = config['gmusic']['email'] password.redact = True email.redact = True # Since Musicmanager doesn't support library management # we need to use mobileclient interface mobile = Mobileclient() try: mobile.login(email.as_str(), password.as_str(), Mobileclient.FROM_MAC_ADDRESS) files = mobile.get_all_songs() except NotLoggedIn: ui.print_( u'Authentication error. Please check your email and password.' ) return if not args: for i, file in enumerate(files, start=1): print(i, ui.colorize('blue', file['artist']), file['title'], ui.colorize('red', file['album'])) else: if opts.track: self.match(files, args, 'title') else: self.match(files, args, 'artist')
def play_music(lib, opts, args): """Execute query, create temporary playlist and execute player command passing that playlist. """ command = config['play']['command'].get() # If a command isn't set then let the OS decide how to open the playlist. if not command: sys_name = platform.system() if sys_name == 'Darwin': command = 'open' elif sys_name == 'Windows': command = 'start' else: # If not Mac or Win then assume Linux(or posix based). command = 'xdg-open' # Preform search by album and add folders rather then tracks to playlist. if opts.album: albums = lib.albums(ui.decargs(args)) paths = [] for album in albums: paths.append(album.item_dir()) item_type = 'album' # Preform item query and add tracks to playlist. else: paths = [item.path for item in lib.items(ui.decargs(args))] item_type = 'track' item_type += 's' if len(paths) > 1 else '' if not paths: ui.print_(ui.colorize('yellow', 'No {0} to play.'.format(item_type))) return # Warn user before playing any huge playlists. if len(paths) > 100: ui.print_( ui.colorize( 'yellow', 'You are about to queue {0} {1}.'.format( len(paths), item_type))) if ui.input_options(('Continue', 'Abort')) == 'a': return # Create temporary m3u file to hold our playlist. m3u = NamedTemporaryFile('w', suffix='.m3u', delete=False) for item in paths: m3u.write(item + '\n') m3u.close() # Invoke the command and log the output. output = util.command_output([command, m3u.name]) if output: log.debug(u'Output of {0}: {1}'.format(command, output)) ui.print_(u'Playing {0} {1}.'.format(len(paths), item_type))
def metadata_retriever( self, lib, albums ): #Get album cover for each of the albums. This implements the metadata_retriever CLI command. for album in albums: if (album.artpath and os.path.isfile(album.artpath)): message = ui.colorize('action', 'already has cover art') self._log.info('{0}: {1}', album, message) #prints out to command line else: localpath = [album.path] start_time = datetime.datetime.now() result = self.source[0].get(album, self, localpath) end_time = datetime.datetime.now() if (result): image = True else: image = None if (image): #if the album art is found album.store() #storing the changes in the database message = ui.colorize('text_success', 'found cover art') #print in green else: message = ui.colorize('text_error', 'cover art not found') #print in red self._log.info('{0}: {1}', album, message) #prints out to command line print("Time for fetching cover art: ", end_time - start_time)
def getlyrics(self, lib, item): #get lyrics from web and store them in the database if (item.lyrics): #if lyrics already exists message = ui.colorize('text_highlight', 'lyrics already exist') self._log.info('{0}: {1}', message, item) #prints out to command line return lyrics = self.backends[0].fetch( item.artist, item.title) #call fetch function defined in Genius class if (lyrics): #if we find the lyrics message = ui.colorize('text_success', 'fetched lyrics') self._log.info('{0}: {1}', message, item) #prints out to command line else: #if lyrics not found message = ui.colorize('text_error', 'lyrics not found') self._log.info('{0}: {1}', message, item) #prints out to command line default_lyrics = None #default_lyrics value is defined as None lyrics = default_lyrics item.lyrics = lyrics.strip( ) #assign lyrics to item's lyrics deleting whitespaces at the beginning and at the end of the text item.lyrics = re.sub(r"[\(\[].*?[\)\]]", "", item.lyrics) item.store() #store item in the database
def search(self, lib, opts, args): password = config['gmusic']['password'] email = config['gmusic']['email'] password.redact = True email.redact = True # Since Musicmanager doesn't support library management # we need to use mobileclient interface mobile = Mobileclient() try: mobile.login(email.as_str(), password.as_str(), Mobileclient.FROM_MAC_ADDRESS) files = mobile.get_all_songs() except NotLoggedIn: ui.print_( u'Authentication error. Please check your email and password.') return if not args: for i, file in enumerate(files, start=1): print(i, ui.colorize('blue', file['artist']), file['title'], ui.colorize('red', file['album'])) else: if opts.track: self.match(files, args, 'title') else: self.match(files, args, 'artist')
def fetch_review(album, log, force): rating = album.get('pitchfork_score') if rating != None and rating != '' and not force: message = ui.colorize('text_highlight_minor', u'has rating %s' % rating) log.info(u'{0}: {1}', album, message) return try: log.debug(u'Querying {0.albumartist} - {0.album}', album) review = search(album['albumartist'], album['album']) score = float(review.score()) except IndexError as e: log.debug(u'No review found: {0}', e) message = ui.colorize('text_error', u'no review found') log.info(u'{0}: {1}', album, message) return except Exception as e: log.debug(u'Error trying to get review: {0}', e) message = ui.colorize('text_error', u'could not fetch review') log.info(u'{0}: {1}', album, message) return message = ui.colorize('text_success', u'found review %s' % review.score()) album['pitchfork_bnm'] = review.best_new_music() album['pitchfork_description'] = review.abstract() album['pitchfork_score'] = score album['pitchfork_url'] = 'https://pitchfork.com%s' % review.url album.store() log.info(u'{0}: {1}', album, message)
def play_music(lib, opts, args): """Execute query, create temporary playlist and execute player command passing that playlist. """ command = config['play']['command'].get() # If a command isn't set then let the OS decide how to open the playlist. if not command: sys_name = platform.system() if sys_name == 'Darwin': command = 'open' elif sys_name == 'Windows': command = 'start' else: # If not Mac or Win then assume Linux(or posix based). command = 'xdg-open' # Preform search by album and add folders rather then tracks to playlist. if opts.album: albums = lib.albums(ui.decargs(args)) paths = [] for album in albums: paths.append(album.item_dir()) item_type = 'album' # Preform item query and add tracks to playlist. else: paths = [item.path for item in lib.items(ui.decargs(args))] item_type = 'track' item_type += 's' if len(paths) > 1 else '' if not paths: ui.print_(ui.colorize('yellow', 'No {0} to play.'.format(item_type))) return # Warn user before playing any huge playlists. if len(paths) > 100: ui.print_(ui.colorize( 'yellow', 'You are about to queue {0} {1}.'.format(len(paths), item_type))) if ui.input_options(('Continue', 'Abort')) == 'a': return # Create temporary m3u file to hold our playlist. m3u = NamedTemporaryFile('w', suffix='.m3u', delete=False) for item in paths: m3u.write(item + '\n') m3u.close() # Invoke the command and log the output. output = util.command_output([command, m3u.name]) if output: log.debug(u'Output of {0}: {1}'.format(command, output)) ui.print_(u'Playing {0} {1}.'.format(len(paths), item_type))
def show_change(cur_artist, cur_album, items, info, dist, color=True): """Print out a representation of the changes that will be made if tags are changed from (cur_artist, cur_album, items) to info with distance dist. """ def show_album(artist, album): if artist: print_(' %s - %s' % (artist, album)) else: print_(' %s' % album) # Identify the album in question. if cur_artist != info['artist'] or \ (cur_album != info['album'] and info['album'] != VARIOUS_ARTISTS): artist_l, artist_r = cur_artist or '', info['artist'] album_l, album_r = cur_album or '', info['album'] if artist_r == VARIOUS_ARTISTS: # Hide artists for VA releases. artist_l, artist_r = u'', u'' if color: artist_l, artist_r = ui.colordiff(artist_l, artist_r) album_l, album_r = ui.colordiff(album_l, album_r) print_("Correcting tags from:") show_album(artist_l, album_l) print_("To:") show_album(artist_r, album_r) else: print_("Tagging: %s - %s" % (info['artist'], info['album'])) # Distance/similarity. print_('(Similarity: %s)' % dist_string(dist, color)) # Tracks. for i, (item, track_data) in enumerate(zip(items, info['tracks'])): cur_track = str(item.track) new_track = str(i+1) cur_title = item.title new_title = track_data['title'] # Possibly colorize changes. if color: cur_title, new_title = ui.colordiff(cur_title, new_title) if cur_track != new_track: cur_track = ui.colorize('red', cur_track) new_track = ui.colorize('red', new_track) if cur_title != new_title and cur_track != new_track: print_(" * %s (%s) -> %s (%s)" % ( cur_title, cur_track, new_title, new_track )) elif cur_title != new_title: print_(" * %s -> %s" % (cur_title, new_title)) elif cur_track != new_track: print_(" * %s (%s -> %s)" % (item.title, cur_track, new_track))
def show_change(cur_artist, cur_album, items, info, dist, color=True): """Print out a representation of the changes that will be made if tags are changed from (cur_artist, cur_album, items) to info with distance dist. """ def show_album(artist, album): if artist: print_(' %s - %s' % (artist, album)) else: print_(' %s' % album) # Identify the album in question. if cur_artist != info['artist'] or \ (cur_album != info['album'] and info['album'] != VARIOUS_ARTISTS): artist_l, artist_r = cur_artist or '', info['artist'] album_l, album_r = cur_album or '', info['album'] if artist_r == VARIOUS_ARTISTS: # Hide artists for VA releases. artist_l, artist_r = u'', u'' if color: artist_l, artist_r = ui.colordiff(artist_l, artist_r) album_l, album_r = ui.colordiff(album_l, album_r) print_("Correcting tags from:") show_album(artist_l, album_l) print_("To:") show_album(artist_r, album_r) else: print_("Tagging: %s - %s" % (info['artist'], info['album'])) # Distance/similarity. print_('(Similarity: %s)' % dist_string(dist, color)) # Tracks. for i, (item, track_data) in enumerate(zip(items, info['tracks'])): cur_track = str(item.track) new_track = str(i + 1) cur_title = item.title new_title = track_data['title'] # Possibly colorize changes. if color: cur_title, new_title = ui.colordiff(cur_title, new_title) if cur_track != new_track: cur_track = ui.colorize('red', cur_track) new_track = ui.colorize('red', new_track) if cur_title != new_title and cur_track != new_track: print_(" * %s (%s) -> %s (%s)" % (cur_title, cur_track, new_title, new_track)) elif cur_title != new_title: print_(" * %s -> %s" % (cur_title, new_title)) elif cur_track != new_track: print_(" * %s (%s -> %s)" % (item.title, cur_track, new_track))
def tag(mf, lyrics): try: if( lyrics ): mf.lyrics = lyrics mf.save() print_(" -%s:" % (mf.title), ui.colorize('green', 'Updated!')) else: print_(" -%s: " % (mf.title), ui.colorize('red', 'Not Found')) except: return
def list_tools(self): checkers = [(checker.name, checker.available()) for checker in IntegrityChecker.all()] prog_length = max(map(lambda c: len(c[0]), checkers)) + 3 for name, available in checkers: msg = name + (prog_length - len(name)) * u' ' if available: msg += colorize('green', u'found') else: msg += colorize('red', u'not found') print(msg)
def list_tools(self): checkers = [(checker.name, checker.available()) for checker in IntegrityChecker.all()] prog_length = max(map(lambda c: len(c[0]), checkers)) + 3 for name, available in checkers: msg = name + (prog_length-len(name))*u' ' if available: msg += colorize('green', u'found') else: msg += colorize('red', u'not found') print(msg)
def _update_cover(self, artist_info, force): all_exist = FetchArtistPlugin._check_for_existing_covers(artist_info) if force or not all_exist: if self._fetch_cover(artist_info): self._write_covers(artist_info, force) message = ui.colorize('text_success', 'artist cover found') else: message = ui.colorize('text_error', 'no artist cover found') else: message = ui.colorize('text_highlight_minor', 'has artist cover') self._log.info(u'{0}: {1}', artist_info.name, message)
def dist_string(dist): """Formats a distance (a float) as a colorized similarity percentage string. """ out = '%.1f%%' % ((1 - dist) * 100) if dist <= config['match']['strong_rec_thresh'].as_number(): out = ui.colorize('green', out) elif dist <= config['match']['medium_rec_thresh'].as_number(): out = ui.colorize('yellow', out) else: out = ui.colorize('red', out) return out
def dist_string(dist): """Formats a distance (a float) as a similarity percentage string. The string is colorized if color is True. """ out = '%.1f%%' % ((1 - dist) * 100) if dist <= autotag.STRONG_REC_THRESH: out = ui.colorize('green', out) elif dist <= autotag.MEDIUM_REC_THRESH: out = ui.colorize('yellow', out) else: out = ui.colorize('red', out) return out
def dist_string(dist): """Formats a distance (a float) as a colorized similarity percentage string. """ out = "%.1f%%" % ((1 - dist) * 100) if dist <= config["match"]["strong_rec_thresh"].as_number(): out = ui.colorize("green", out) elif dist <= config["match"]["medium_rec_thresh"].as_number(): out = ui.colorize("yellow", out) else: out = ui.colorize("red", out) return out
def check_item(self, item): # First, check whether the path exists. If not, the user # should probably run `beet update` to cleanup your library. dpath = displayable_path(item.path) self._log.debug("checking path: {}", dpath) if not os.path.exists(item.path): ui.print_("{}: file does not exist".format( ui.colorize('text_error', dpath))) # Run the checker against the file if one is found ext = os.path.splitext(item.path)[1][1:].decode('utf8', 'ignore') checker = self.get_checker(ext) if not checker: self._log.error("no checker specified in the config for {}", ext) return [] path = item.path if not isinstance(path, str): path = item.path.decode(sys.getfilesystemencoding()) try: status, errors, output = checker(path) except CheckerCommandException as e: if e.errno == errno.ENOENT: self._log.error( "command not found: {} when validating file: {}", e.checker, e.path ) else: self._log.error("error invoking {}: {}", e.checker, e.msg) return [] error_lines = [] if status > 0: error_lines.append( "{}: checker exited with status {}" .format(ui.colorize('text_error', dpath), status)) for line in output: error_lines.append(f" {line}") elif errors > 0: error_lines.append( "{}: checker found {} errors or warnings" .format(ui.colorize('text_warning', dpath), errors)) for line in output: error_lines.append(f" {line}") elif self.verbose: error_lines.append( "{}: ok".format(ui.colorize('text_success', dpath))) return error_lines
def dist_string(config, dist, color): """Formats a distance (a float) as a similarity percentage string. The string is colorized if color is True. """ out = '%.1f%%' % ((1 - dist) * 100) if color: if dist <= config.strong_rec_thresh: out = ui.colorize('green', out) elif dist <= config.medium_rec_thresh: out = ui.colorize('yellow', out) else: out = ui.colorize('red', out) return out
def dist_string(dist, color): """Formats a distance (a float) as a similarity percentage string. The string is colorized if color is True. """ out = "%.1f%%" % ((1 - dist) * 100) if color: if dist <= autotag.STRONG_REC_THRESH: out = ui.colorize("green", out) elif dist <= autotag.MEDIUM_REC_THRESH: out = ui.colorize("yellow", out) else: out = ui.colorize("red", out) return out
def dist_string(dist, color): """Formats a distance (a float) as a string. The string is colorized if color is True. """ out = str(dist) if color: if dist <= autotag.STRONG_REC_THRESH: out = ui.colorize('green', out) elif dist <= autotag.MEDIUM_REC_THRESH: out = ui.colorize('yellow', out) else: out = ui.colorize('red', out) return out
def dist_string(dist, color): """Formats a distance (a float) as a similarity percentage string. The string is colorized if color is True. """ out = '%.1f%%' % ((1 - dist) * 100) if color: if dist <= autotag.STRONG_REC_THRESH: out = ui.colorize('green', out) elif dist <= autotag.MEDIUM_REC_THRESH: out = ui.colorize('yellow', out) else: out = ui.colorize('red', out) return out
def check(item): try: verify_checksum(item) if self.check_integrity: verify_integrity(item) log.debug('{}: {}'.format(colorize('green', 'OK'), item.path)) except ChecksumError: log.error('{}: {}'.format(colorize('red', 'FAILED'), item.path)) status['failures'] += 1 except IntegrityError as ex: log.warn('{} {}: {}'.format(colorize('yellow', 'WARNING'), ex.reason, item.path)) status['integrity'] += 1
def _update_cover(self, artist_info, force): all_exist = FetchArtistPlugin._check_for_existing_covers(artist_info) if force or not all_exist: if self._fetch_cover(artist_info): if self._write_covers(artist_info, force): message = ui.colorize('text_success', 'artist cover found') else: message = ui.colorize('text_error', 'path not found') else: message = ui.colorize('text_error', 'no artist cover found') else: message = ui.colorize('text_highlight_minor', 'has artist cover') self._log.info(u'{0}: {1}', artist_info.name, message)
def find_items_in_lib(self, lib, track_names, artist_name): """Returns a list of items found, and list of items not found in library from a given list of track names. """ items, missing_items = [], [] for track_nr, track_name in enumerate(track_names): item = _find_item_in_lib(lib, track_name, artist_name) if item: items += [item] message = ui.colorize('text_success', u'found') else: missing_items += [item] message = ui.colorize('text_error', u'not found') self._log.info("{0} {1}: {2}".format( (track_nr+1), track_name, message)) return items, missing_items
def update(item): log.debug(u'updating checksum: {}' .format(displayable_path(item.path))) try: set_checksum(item) except IOError as exc: log.error(u'{} {}'.format(colorize('red', u'ERROR'), exc))
def check(item): try: if 'checksum' in item: verify_checksum(item) fixer = IntegrityChecker.fixer(item) if fixer: fixer.check(item) log.debug(u'{}: {}'.format(colorize('green', u'OK'), displayable_path(item.path))) except IntegrityError: failed.append(item) except ChecksumError: log.error(u'{}: {}'.format(colorize('red', u'FAILED checksum'), displayable_path(item.path))) except IOError as exc: log.error(u'{} {}'.format(colorize('red', u'ERROR'), exc))
def fix(item): fixer = IntegrityChecker.fixer(item) if fixer: fixer.fix(item) log.debug(u'{}: {}'.format(colorize('green', u'FIXED'), displayable_path(item.path))) set_checksum(item)
def update(item): log.debug(u'updating checksum: {}'.format( displayable_path(item.path))) try: set_checksum(item) except IOError as exc: log.error(u'{} {}'.format(colorize('red', u'ERROR'), exc))
def fetch(mf): try: print_(" -%s:" % (mf.title), ui.colorize('yellow', 'Fetching')) lyrics = self.fetchLyrics(scrub(mf.artist), scrub(mf.title)) result = (mf, lyrics); return result except: return None
def func(lib, opts, args): #main functionalities of the plugin print("\n") print("Please run the command with one of the following options: ") print("-c or --cover for finding the album cover") print("-l or --lyric for finding lyrics") print("-a or --all for finding the cover arts for all releases") print("-p or --print for printing lyrics to command line") print( "-w or --write for writing lyrics to a file and creating a word cloud" ) print("\n\n") if (opts.coverart): self.metadata_retriever(lib, lib.albums(ui.decargs(args))) print("\n") albums = lib.albums( ui.decargs(args)) #from database we reach out to items table for album in albums: if (opts.allreleases): start_time = datetime.datetime.now() self.allreleases(lib, album) end_time = datetime.datetime.now() print("Time for fetching all cover arts: ", end_time - start_time) items = lib.items( ui.decargs(args)) #from database we reach out to items table for item in items: #for each item in items table if (opts.lyrics): start_time = datetime.datetime.now() self.getlyrics(lib, item) #call getlyrics function end_time = datetime.datetime.now() duration = end_time - start_time print("Time for fetching lyrics: ", end_time - start_time) if (item.lyrics): #if the lyrics are found if (opts.printlyrics): #if there is a -p or --print option title = item.artist + " - " + item.title title = ui.colorize('action', title) ui.print_(title) ui.print_(item.lyrics) #print lyrics to console ui.print_( "\n") #print a space character after each song if (opts.writetofile): #if there is a -w or --write option start_time = datetime.datetime.now() self.writetofile(lib, item) #writing lyrics to file end_time = datetime.datetime.now() print( "Time for writing {} to file : ".format( item.title), end_time - start_time)
def check_bad(self, lib, opts, args): for item in lib.items(ui.decargs(args)): # First, check whether the path exists. If not, the user # should probably run `beet update` to cleanup your library. dpath = displayable_path(item.path) self._log.debug(u"checking path: {}", dpath) if not os.path.exists(item.path): ui.print_(u"{}: file does not exist".format( ui.colorize('text_error', dpath))) # Run the checker against the file if one is found ext = os.path.splitext(item.path)[1][1:].decode('utf8', 'ignore') checker = self.get_checker(ext) if not checker: self._log.error(u"no checker specified in the config for {}", ext) continue path = item.path if not isinstance(path, six.text_type): path = item.path.decode(sys.getfilesystemencoding()) try: status, errors, output = checker(path) except CheckerCommandException as e: if e.errno == errno.ENOENT: self._log.error( u"command not found: {} when validating file: {}", e.checker, e.path ) else: self._log.error(u"error invoking {}: {}", e.checker, e.msg) continue if status > 0: ui.print_(u"{}: checker exited with status {}" .format(ui.colorize('text_error', dpath), status)) for line in output: ui.print_(u" {}".format(displayable_path(line))) elif errors > 0: ui.print_(u"{}: checker found {} errors or warnings" .format(ui.colorize('text_warning', dpath), errors)) for line in output: ui.print_(u" {}".format(displayable_path(line))) elif opts.verbose: ui.print_(u"{}: ok".format(ui.colorize('text_success', dpath)))
def add(item): log.debug('adding checksum for {0}'.format(item.path)) set_checksum(item) if self.check_integrity: try: verify_integrity(item) except IntegrityError as ex: log.warn('{} {}: {}'.format(colorize('yellow', 'WARNING'), ex.reason, item.path))
def before_choose(self, session, task): """Prints a helpful message to tell which candidate corresponds to the scanned barcodes. This is useful to quickly see if the chosen release is the correct one. """ # Note: task.candidates = list of AlbumMatch if not task.candidates: return None ids = set() barcodes = set() for candidate in task.candidates: # TODO we don't have to check ALL candidates, # because they all use the same file paths.. tracks = candidate.mapping paths = set(map(lambda i: os.path.dirname(i.path), tracks)) for path in paths: if path in _matches: ids.update(_matches[path].album_ids) barcodes.update(_matches[path].barcodes) if len(barcodes) == 0: return None #print("------------------------") if len(ids) == 0: print("{}: {}".format( ui.colorize('text_warning', "Found barcode(s) but no matching releases"), ' '.join(barcodes))) else: print("{}: {}".format( ui.colorize('text_success', "Found barcode(s)"), ' '.join(barcodes))) # print("Candidates with matching IDs:") # for index, candidate in enumerate(task.candidates): # info = candidate.info # if info.album_id in ids: # print(u"{:2d}. {}".format(index + 1, _get_debug_str(info))) #print("------------------------") return None
def check(item): try: if external: verify_integrity(item) elif item.get('checksum', None): verify_checksum(item) log.debug(u'{}: {}'.format(colorize('green', u'OK'), displayable_path(item.path))) except ChecksumError: log.error(u'{}: {}'.format(colorize('red', u'FAILED'), displayable_path(item.path))) failures[0] += 1 except IntegrityError as ex: log.warn(u'{} {}: {}'.format(colorize('yellow', u'WARNING'), ex.reason, displayable_path(item.path))) failures[0] += 1 except IOError as exc: log.error(u'{} {}'.format(colorize('red', u'ERROR'), exc)) failures[0] += 1
def add(item): log.debug(u'adding checksum for {0}'.format( displayable_path(item.path))) set_checksum(item) if self.check_integrity: try: verify_integrity(item) except IntegrityError as ex: log.warn(u'{} {}: {}'.format( colorize('yellow', u'WARNING'), ex.reason, displayable_path(item.path)))
def automigrate(): """Migrate the configuration, database, and state files. If any migration occurs, print out a notice with some helpful next steps. """ config_fn = migrate_config() db_fn = migrate_db() migrate_state() if config_fn: ui.print_(ui.colorize('fuchsia', u'MIGRATED CONFIGURATION')) ui.print_( CONFIG_MIGRATED_MESSAGE.format( newconfig=util.displayable_path(config_fn))) if db_fn: ui.print_( DB_MIGRATED_MESSAGE.format(newdb=util.displayable_path(db_fn))) ui.input_(ui.colorize('fuchsia', u'Press ENTER to continue:')) ui.print_()
def automigrate(): """Migrate the configuration, database, and state files. If any migration occurs, print out a notice with some helpful next steps. """ config_fn = migrate_config() db_fn = migrate_db() migrate_state() if config_fn: ui.print_(ui.colorize('fuchsia', u'MIGRATED CONFIGURATION')) ui.print_(CONFIG_MIGRATED_MESSAGE.format( newconfig=util.displayable_path(config_fn)) ) if db_fn: ui.print_(DB_MIGRATED_MESSAGE.format( newdb=util.displayable_path(db_fn) )) ui.input_(ui.colorize('fuchsia', u'Press ENTER to continue:')) ui.print_()
def batch_fetch_art(self, lib, albums, force): """Fetch album art for each of the albums. This implements the manual fetchart CLI command. """ for album in albums: if album.artpath and not force and os.path.isfile(album.artpath): message = ui.colorize('text_highlight_minor', u'has album art') else: # In ordinary invocations, look for images on the # filesystem. When forcing, however, always go to the Web # sources. local_paths = None if force else [album.path] candidate = self.art_for_album(album, local_paths) if candidate: self._set_art(album, candidate) message = ui.colorize('text_success', u'found album art') else: message = ui.colorize('text_error', u'no art found') self._log.info(u'{0}: {1}', album, message)
def show_change(cur_artist, cur_album, items, info, dist, color=True): """Print out a representation of the changes that will be made if tags are changed from (cur_artist, cur_album, items) to info with distance dist. """ if cur_artist != info['artist'] or cur_album != info['album']: artist_l, artist_r = cur_artist or '', info['artist'] album_l, album_r = cur_album or '', info['album'] if color: artist_l, artist_r = ui.colordiff(artist_l, artist_r) album_l, album_r = ui.colordiff(album_l, album_r) print_("Correcting tags from:") print_(' %s - %s' % (artist_l, album_l)) print_("To:") print_(' %s - %s' % (artist_r, album_r)) else: print_("Tagging: %s - %s" % (info['artist'], info['album'])) print_('(Distance: %s)' % dist_string(dist, color)) for i, (item, track_data) in enumerate(zip(items, info['tracks'])): cur_track = str(item.track) new_track = str(i+1) cur_title = item.title new_title = track_data['title'] # Possibly colorize changes. if color: cur_title, new_title = ui.colordiff(cur_title, new_title) if cur_track != new_track: cur_track = ui.colorize('red', cur_track) new_track = ui.colorize('red', new_track) if cur_title != new_title and cur_track != new_track: print_(" * %s (%s) -> %s (%s)" % ( cur_title, cur_track, new_title, new_track )) elif cur_title != new_title: print_(" * %s -> %s" % (cur_title, new_title)) elif cur_track != new_track: print_(" * %s (%s -> %s)" % (item.title, cur_track, new_track))
def batch_fetch_art(self, lib, albums, force): """Fetch album art for each of the albums. This implements the manual fetchart CLI command. """ for album in albums: if album.artpath and not force: message = 'has album art' else: # In ordinary invocations, look for images on the # filesystem. When forcing, however, always go to the Web # sources. local_paths = None if force else [album.path] path = self.art_for_album(album, local_paths) if path: album.set_art(path, False) album.store() message = ui.colorize('green', 'found album art') else: message = ui.colorize('red', 'no art found') self._log.info(u'{0}: {1}', album, message)
def batch_fetch_art(self, lib, albums, force): """Fetch album art for each of the albums. This implements the manual fetchart CLI command. """ for album in albums: if album.artpath and not force: message = 'has album art' else: # In ordinary invocations, look for images on the # filesystem. When forcing, however, always go to the Web # sources. local_paths = None if force else [album.path] path = self.art_for_album(album, local_paths) if path: album.set_art(path, False) album.store() message = ui.colorize('text_success', 'found album art') else: message = ui.colorize('text_error', 'no art found') self._log.info(u'{0}: {1}', album, message)
def batch_fetch_art(lib, albums, force, maxwidth=None): """Fetch album art for each of the albums. This implements the manual fetchart CLI command. """ for album in albums: if album.artpath and not force: message = "has album art" else: # In ordinary invocations, look for images on the # filesystem. When forcing, however, always go to the Web # sources. local_paths = None if force else [album.path] path = art_for_album(album, local_paths, maxwidth) if path: album.set_art(path, False) album.store() message = ui.colorize("green", "found album art") else: message = ui.colorize("red", "no art found") log.info(u"{0} - {1}: {2}".format(album.albumartist, album.album, message))
def penalty_string(distance, limit=None): """Returns a colorized string that indicates all the penalties applied to a distance object. """ penalties = [] for key in distance.keys(): key = key.replace("album_", "") key = key.replace("track_", "") key = key.replace("_", " ") penalties.append(key) if penalties: if limit and len(penalties) > limit: penalties = penalties[:limit] + ["..."] return ui.colorize("yellow", "(%s)" % ", ".join(penalties))
def penalty_string(distance, limit=None): """Returns a colorized string that indicates all the penalties applied to a distance object. """ penalties = [] for key in distance.keys(): key = key.replace('album_', '') key = key.replace('track_', '') key = key.replace('_', ' ') penalties.append(key) if penalties: if limit and len(penalties) > limit: penalties = penalties[:limit] + ['...'] return ui.colorize('yellow', '(%s)' % ', '.join(penalties))
def show_album(artist, album, partial=False): if artist: album_description = u' %s - %s' % (artist, album) elif album: album_description = u' %s' % album else: album_description = u' (unknown album)' out = album_description # Add a suffix if this is a partial match. if partial: out += u' ' + ui.colorize('yellow', PARTIAL_MATCH_MESSAGE) print_(out)
def show_album(artist, album, partial=False): if artist: album_description = u' %s - %s' % (artist, album) elif album: album_description = u' %s' % album else: album_description = u' (unknown album)' out = album_description # Add a suffix if this is a partial match. if partial: out += u' %s' % ui.colorize('yellow', PARTIAL_MATCH_MESSAGE) print_(out)
def _play_command(self, lib, opts, args): """The CLI command function for `beet play`. Create a list of paths from query, determine if tracks or albums are to be played. """ use_folders = config['play']['use_folders'].get(bool) relative_to = config['play']['relative_to'].get() if relative_to: relative_to = util.normpath(relative_to) # Perform search by album and add folders rather than tracks to # playlist. if opts.album: selection = lib.albums(ui.decargs(args)) paths = [] sort = lib.get_default_album_sort() for album in selection: if use_folders: paths.append(album.item_dir()) else: paths.extend(item.path for item in sort.sort(album.items())) item_type = 'album' # Perform item query and add tracks to playlist. else: selection = lib.items(ui.decargs(args)) paths = [item.path for item in selection] item_type = 'track' if relative_to: paths = [relpath(path, relative_to) for path in paths] if not selection: ui.print_( ui.colorize('text_warning', u'No {0} to play.'.format(item_type))) return open_args = self._playlist_or_paths(paths) command_str = self._command_str(opts.args) # Check if the selection exceeds configured threshold. If True, # cancel, otherwise proceed with play command. if opts.yes or not self._exceeds_threshold(selection, command_str, open_args, item_type): play(command_str, selection, paths, open_args, self._log, item_type)
def _play_command(self, lib, opts, args): """The CLI command function for `beet play`. Create a list of paths from query, determine if tracks or albums are to be played. """ use_folders = config['play']['use_folders'].get(bool) relative_to = config['play']['relative_to'].get() if relative_to: relative_to = util.normpath(relative_to) # Perform search by album and add folders rather than tracks to # playlist. if opts.album: selection = lib.albums(ui.decargs(args)) paths = [] sort = lib.get_default_album_sort() for album in selection: if use_folders: paths.append(album.item_dir()) else: paths.extend(item.path for item in sort.sort(album.items())) item_type = 'album' # Perform item query and add tracks to playlist. else: selection = lib.items(ui.decargs(args)) paths = [item.path for item in selection] item_type = 'track' if relative_to: paths = [relpath(path, relative_to) for path in paths] if not selection: ui.print_(ui.colorize('text_warning', u'No {0} to play.'.format(item_type))) return open_args = self._playlist_or_paths(paths) command_str = self._command_str(opts.args) # Check if the selection exceeds configured threshold. If True, # cancel, otherwise proceed with play command. if opts.yes or not self._exceeds_threshold( selection, command_str, open_args, item_type): play(command_str, selection, paths, open_args, self._log, item_type)
def show_album(artist, album, partial=False): if artist: album_description = u' %s - %s' % (artist, album) elif album: album_description = u' %s' % album else: album_description = u' (unknown album)' # Add a suffix if this is a partial match. if partial: warning = PARTIAL_MATCH_MESSAGE else: warning = None if color and warning: warning = ui.colorize('yellow', warning) out = album_description if warning: out += u' ' + warning print_(out)
def album_distance(self, items, album_info, mapping): dist = hooks.Distance() # Add a penalty if these items have a barcode, but the album_id # does not correspond to the barcode(s). paths = set(map(lambda item: os.path.dirname(item.path), items)) release_ids = set() for path in paths: if path in _matches: release_ids.update(_matches[path].album_ids) # Penalty only if we actually found barcodes for this path, # to avoid penalizing all relases if we haven't found any barcodes. if len(release_ids) != 0: dist.add_expr('barcode', album_info.album_id not in release_ids) if album_info.album_id in release_ids: album_info.data_source += '+' + ui.colorize( 'text_success', 'barcode') return dist
def on_import_task_before_choice(self, task, session): if hasattr(task, '_badfiles_checks_failed'): ui.print_('{} one or more files failed checks:'.format( ui.colorize('text_warning', 'BAD'))) for error in task._badfiles_checks_failed: for error_line in error: ui.print_(error_line) ui.print_() ui.print_('What would you like to do?') sel = ui.input_options(['aBort', 'skip', 'continue']) if sel == 's': return importer.action.SKIP elif sel == 'c': return None elif sel == 'b': raise importer.ImportAbort() else: raise Exception('Unexpected selection: {}'.format(sel))