def process(self, album_task, expected_artist=None, expected_album=None, expected_release_id=None, fallback_taggers=()): """ :param album_task: the task object to tag :param expected_artist: the artist name (not id) to help matching :type expected_artist: str :param expected_album: the album name (not id) to help matching :type expected_album: str :param expected_release_id: expected release id to help tagger (this might cause wrong results, use with care!) :param fallback_taggers: a list of fallback taggers to help tag if no recommendations are extracted from metadata :type fallback_taggers: list[PostProcessor] :rtype BeetsTaggerResult :return: a result object containing album_id, album_info_object, track_mapping_object, extra_items and extra_tracks """ logger.debug("Called with expected id {}".format(expected_release_id)) artist_name, album_name, album_recommendation_list, recommendation = \ tag_album(album_task.items, search_artist=expected_artist, search_album=expected_album, search_ids=[expected_release_id] if expected_release_id else []) if recommendation is Recommendation.none: logger.warning('{} Failed to match album'.format(__name__)) if not fallback_taggers: raise BeetsTaggerException( "Exhausted all tagging options, failing") for tagger in fallback_taggers: logger.debug("Calling {}".format(tagger.__class__.__name__)) fallback_recommendation = tagger.process(album_task) if not fallback_recommendation: continue artist_name, album_name, album_recommendation_list, recommendation = \ tag_album(album_task.items, search_artist=expected_artist, search_album=expected_album, search_ids=[fallback_recommendation]) # if we fail after fallback, raise if recommendation is Recommendation.none: raise BeetsTaggerException( "Exhausted all tagging options, failing") distance, album_info, track_mapping, extra_items, extra_tracks = album_recommendation_list[ 0] logger.debug('Successfully matched album {} !'.format( album_info.album_id)) logger.info( "Successfully tagged album {album_id}, releasegroup {rgid}".format( album_id=album_info.album_id, rgid=album_info.releasegroup_id)) album_task._album_info_object = album_info album_task._track_mapping_object = track_mapping return True
def choose_match(task, config): """Given an initial autotagging of items, go through an interactive dance with the user to ask for a choice of metadata. Returns an (info, items) pair, ASIS, or SKIP. """ # Show what we're tagging. print_() print_(task.path) if config.quiet: # No input; just make a decision. if task.rec == autotag.RECOMMEND_STRONG: dist, items, info = task.candidates[0] show_change(task.cur_artist, task.cur_album, items, info, dist, config.color) return info, items else: return _quiet_fall_back(config) # Loop until we have a choice. candidates, rec = task.candidates, task.rec while True: # Ask for a choice from the user. choice = choose_candidate( candidates, False, rec, config.color, config.timid, task.cur_artist, task.cur_album, itemcount=len(task.items), per_disc_numbering=config.per_disc_numbering, ) # Choose which tags to use. if choice in (importer.action.SKIP, importer.action.ASIS, importer.action.TRACKS): # Pass selection to main control flow. return choice elif choice is importer.action.MANUAL: # Try again with manual search terms. search_artist, search_album = manual_search(False) try: _, _, candidates, rec = autotag.tag_album(task.items, config.timid, search_artist, search_album) except autotag.AutotagError: candidates, rec = None, None elif choice is importer.action.MANUAL_ID: # Try a manually-entered ID. search_id = manual_id(False) if search_id: try: _, _, candidates, rec = autotag.tag_album(task.items, config.timid, search_id=search_id) except autotag.AutotagError: candidates, rec = None, None else: # We have a candidate! Finish tagging. Here, choice is # an (info, items) pair as desired. assert not isinstance(choice, importer.action) return choice
def choose_match(task, config): """Given an initial autotagging of items, go through an interactive dance with the user to ask for a choice of metadata. Returns an (info, items) pair, ASIS, or SKIP. """ # Show what we're tagging. print_() print_(task.path) if config.quiet: # No input; just make a decision. if task.rec == autotag.RECOMMEND_STRONG: dist, items, info = task.candidates[0] show_change(task.cur_artist, task.cur_album, items, info, dist, config.color) return info, items else: return _quiet_fall_back(config) # Loop until we have a choice. candidates, rec = task.candidates, task.rec while True: # Ask for a choice from the user. choice = choose_candidate(candidates, False, rec, config.color, config.timid, task.cur_artist, task.cur_album) # Choose which tags to use. if choice in (importer.action.SKIP, importer.action.ASIS, importer.action.TRACKS): # Pass selection to main control flow. return choice elif choice is importer.action.MANUAL: # Try again with manual search terms. search_artist, search_album = manual_search(False) try: _, _, candidates, rec = \ autotag.tag_album(task.items, config.timid, search_artist, search_album) except autotag.AutotagError: candidates, rec = None, None elif choice is importer.action.MANUAL_ID: # Try a manually-entered ID. search_id = manual_id(False) if search_id: try: _, _, candidates, rec = \ autotag.tag_album(task.items, config.timid, search_id=search_id) except autotag.AutotagError: candidates, rec = None, None else: # We have a candidate! Finish tagging. Here, choice is # an (info, items) pair as desired. assert not isinstance(choice, importer.action) return choice
def choose_match(self, task): """Given an initial autotagging of items, go through an interactive dance with the user to ask for a choice of metadata. Returns an AlbumMatch object, ASIS, or SKIP. """ # Show what we're tagging. print_() print_(task.path) if config['import']['quiet']: # No input; just make a decision. if task.rec == autotag.RECOMMEND_STRONG: match = task.candidates[0] show_change(task.cur_artist, task.cur_album, match) return match else: return _quiet_fall_back() # Loop until we have a choice. candidates, rec = task.candidates, task.rec while True: # Ask for a choice from the user. choice = choose_candidate(candidates, False, rec, task.cur_artist, task.cur_album, itemcount=len(task.items)) # Choose which tags to use. if choice in (importer.action.SKIP, importer.action.ASIS, importer.action.TRACKS): # Pass selection to main control flow. return choice elif choice is importer.action.MANUAL: # Try again with manual search terms. search_artist, search_album = manual_search(False) try: _, _, candidates, rec = \ autotag.tag_album(task.items, search_artist, search_album) except autotag.AutotagError: candidates, rec = None, None elif choice is importer.action.MANUAL_ID: # Try a manually-entered ID. search_id = manual_id(False) if search_id: try: _, _, candidates, rec = \ autotag.tag_album(task.items, search_id=search_id) except autotag.AutotagError: candidates, rec = None, None else: # We have a candidate! Finish tagging. Here, choice is an # AlbumMatch object. assert isinstance(choice, autotag.AlbumMatch) return choice
def choose_match(self, task): """Given an initial autotagging of items, go through an interactive dance with the user to ask for a choice of metadata. Returns an AlbumMatch object, ASIS, or SKIP. """ # Show what we're tagging. print_() print_(displayable_path(task.paths, u'\n')) # Take immediate action if appropriate. action = _summary_judment(task.rec) if action == importer.action.APPLY: match = task.candidates[0] show_change(task.cur_artist, task.cur_album, match) return match elif action is not None: return action # Loop until we have a choice. candidates, rec = task.candidates, task.rec while True: # Ask for a choice from the user. choice = choose_candidate(candidates, False, rec, task.cur_artist, task.cur_album, itemcount=len(task.items)) # Choose which tags to use. if choice in (importer.action.SKIP, importer.action.ASIS, importer.action.TRACKS): # Pass selection to main control flow. return choice elif choice is importer.action.MANUAL: # Try again with manual search terms. search_artist, search_album = manual_search(False) try: _, _, candidates, rec = \ autotag.tag_album(task.items, search_artist, search_album) except autotag.AutotagError: candidates, rec = None, None elif choice is importer.action.MANUAL_ID: # Try a manually-entered ID. search_id = manual_id(False) if search_id: try: _, _, candidates, rec = \ autotag.tag_album(task.items, search_id=search_id) except autotag.AutotagError: candidates, rec = None, None else: # We have a candidate! Finish tagging. Here, choice is an # AlbumMatch object. assert isinstance(choice, autotag.AlbumMatch) return choice
def choose_match(self, task): """Given an initial autotagging of items, go through an interactive dance with the user to ask for a choice of metadata. Returns an AlbumMatch object, ASIS, or SKIP. """ # Show what we're tagging. print_() print_( displayable_path(task.paths, u'\n') + u' ({0} items)'.format(len(task.items))) # Take immediate action if appropriate. action = _summary_judment(task.rec) if action == importer.action.APPLY: match = task.candidates[0] show_change(task.cur_artist, task.cur_album, match) return match elif action is not None: return action # Loop until we have a choice. candidates, rec = task.candidates, task.rec while True: # Ask for a choice from the user. choice = choose_candidate(candidates, False, rec, task.cur_artist, task.cur_album, itemcount=len(task.items)) # Choose which tags to use. if choice in (importer.action.SKIP, importer.action.ASIS, importer.action.TRACKS): # Pass selection to main control flow. return choice elif choice is importer.action.MANUAL: # Try again with manual search terms. search_artist, search_album = manual_search(False) _, _, candidates, rec = autotag.tag_album( task.items, search_artist, search_album) elif choice is importer.action.MANUAL_ID: # Try a manually-entered ID. search_id = manual_id(False) if search_id: _, _, candidates, rec = autotag.tag_album( task.items, search_id=search_id) else: # We have a candidate! Finish tagging. Here, choice is an # AlbumMatch object. assert isinstance(choice, autotag.AlbumMatch) return choice
def process(self, album_task, expected_artist=None, expected_album=None, expected_release_id=None, fallback_taggers=()): """ :param album_task: the task object to tag :param expected_artist: the artist name (not id) to help matching :type expected_artist: str :param expected_album: the album name (not id) to help matching :type expected_album: str :param expected_release_id: expected release id to help tagger (this might cause wrong results, use with care!) :param fallback_taggers: a list of fallback taggers to help tag if no recommendations are extracted from metadata :type fallback_taggers: list[PostProcessor] :rtype BeetsTaggerResult :return: a result object containing album_id, album_info_object, track_mapping_object, extra_items and extra_tracks """ logger.debug("Called with expected id {}".format(expected_release_id)) artist_name, album_name, album_recommendation_list, recommendation = \ tag_album(album_task.items, search_artist=expected_artist, search_album=expected_album, search_ids=[expected_release_id] if expected_release_id else []) if recommendation is Recommendation.none: logger.warning('{} Failed to match album'.format(__name__)) if not fallback_taggers: raise BeetsTaggerException("Exhausted all tagging options, failing") for tagger in fallback_taggers: logger.debug("Calling {}".format(tagger.__class__.__name__)) fallback_recommendation = tagger.process(album_task) if not fallback_recommendation: continue artist_name, album_name, album_recommendation_list, recommendation = \ tag_album(album_task.items, search_artist=expected_artist, search_album=expected_album, search_ids=[fallback_recommendation]) # if we fail after fallback, raise if recommendation is Recommendation.none: raise BeetsTaggerException("Exhausted all tagging options, failing") distance, album_info, track_mapping, extra_items, extra_tracks = album_recommendation_list[0] logger.debug('Successfully matched album {} !'.format(album_info.album_id)) logger.info("Successfully tagged album {album_id}, releasegroup {rgid}".format(album_id=album_info.album_id, rgid=album_info.releasegroup_id)) album_task._album_info_object = album_info album_task._track_mapping_object = track_mapping return True
def lookup_candidates(self): """Retrieve and store candidates for this album. """ artist, album, candidates, recommendation = autotag.tag_album(self.items) self.cur_artist = artist self.cur_album = album self.candidates = candidates self.rec = recommendation
def lookup_candidates(self): """Retrieve and store candidates for this album. """ artist, album, candidates, recommendation = \ autotag.tag_album(self.items) self.cur_artist = artist self.cur_album = album self.candidates = candidates self.rec = recommendation
def search_name(self, task, name, artist): if task.is_album: _, _, prop = autotag.tag_album(task.items, artist, name) else: prop = autotag.tag_item(task.item, artist, name) if len(prop.candidates) > 0: task.candidates = prop.candidates task.rec = prop.recommendation return task
def choose_match(path, items, cur_artist, cur_album, candidates, rec, color=True, quiet=False): """Given an initial autotagging of items, go through an interactive dance with the user to ask for a choice of metadata. Returns an (info, items) pair, CHOICE_ASIS, or CHOICE_SKIP. """ if quiet: # No input; just make a decision. if rec == autotag.RECOMMEND_STRONG: dist, items, info = candidates[0] show_change(cur_artist, cur_album, items, info, dist, color) return info, items else: print_('Skipping: %s' % path) return CHOICE_SKIP # Loop until we have a choice. while True: # Choose from candidates, if available. if candidates: choice = choose_candidate(cur_artist, cur_album, candidates, rec, color) else: # Fallback: if either an error ocurred or no matches found. print_("No match found for:", path) sel = ui.input_options( "[U]se as-is, Skip, Enter manual search, or aBort?", ('u', 's', 'e', 'b'), 'u', 'Enter U, S, E, or B:' ) if sel == 'u': choice = CHOICE_ASIS elif sel == 'e': choice = CHOICE_MANUAL elif sel == 's': choice = CHOICE_SKIP elif sel == 'b': raise ImportAbort() # Choose which tags to use. if choice in (CHOICE_SKIP, CHOICE_ASIS): # Pass selection to main control flow. return choice elif choice is CHOICE_MANUAL: # Try again with manual search terms. search_artist, search_album = manual_search() else: # We have a candidate! Finish tagging. Here, choice is # an (info, items) pair as desired. return choice # Search for entered terms. try: _, _, candidates, rec = \ autotag.tag_album(items, search_artist, search_album) except autotag.AutotagError: candidates, rec = None, None
def search_id(self, task, search_id): if task.is_album: _, _, prop = autotag.tag_album(task.items, search_ids=search_id.split()) else: prop = autotag.tag_item(task.item, search_ids=search_id.split()) if len(prop.candidates) > 0: task.candidates = prop.candidates task.rec = prop.recommendation return task
def choose_match(task): """Given an initial autotagging of items, go through an interactive dance with the user to ask for a choice of metadata. Returns an AlbumMatch object or SKIP. """ # Show what we're tagging. print_() print_( displayable_path(task.paths, u'\n') + u' ({0} items)'.format(len(task.items))) # Loop until we have a choice. candidates, rec = task.candidates, task.rec while True: # Ask for a choice from the user. choice = choose_candidate(candidates, False, rec, task.cur_artist, task.cur_album, itemcount=len(task.items)) # Choose which tags to use. if choice is importer.action.SKIP: # Pass selection to main control flow. return choice elif choice is importer.action.MANUAL: # Try again with manual search terms. search_artist, search_album = manual_search(False) _, _, candidates, rec = autotag.tag_album(task.items, search_artist, search_album) elif choice is importer.action.MANUAL_ID: # Try a manually-entered ID. search_id = manual_id(False) if search_id: _, _, candidates, rec = autotag.tag_album( task.items, search_ids=search_id.split()) else: # We have a candidate! Finish tagging. Here, choice is an # AlbumMatch object. assert isinstance(choice, autotag.AlbumMatch) return choice
def search_name(self, task_id, name, artist): task = self.tasks.get(task_id) if not task: print(task, "not in tasks") return if task.is_album: _, _, prop = autotag.tag_album( task.items, artist, name ) else: prop = autotag.tag_item(task.item, artist, name) if len(prop.candidates) > 0: task.candidates = prop.candidates task.rec = prop.recommendation return task
def search_id(self, task_id, search_id): task = self.tasks.get(task_id) if not task: print(task, "not in tasks") return if task.is_album: _, _, prop = autotag.tag_album( task.items, search_ids=search_id.split() ) else: prop = autotag.tag_item(task.item, search_ids=search_id.split()) if len(prop.candidates) > 0: task.candidates = prop.candidates task.rec = prop.recommendation return task
def initial_lookup(session): """A coroutine for performing the initial MusicBrainz lookup for an album. It accepts lists of Items and yields (items, cur_artist, cur_album, candidates, rec) tuples. If no match is found, all of the yielded parameters (except items) are None. """ task = None while True: task = yield task if task.sentinel: continue plugins.send('import_task_start', session=session, task=task) log.debug('Looking up: %s' % displayable_path(task.paths)) task.set_candidates(*autotag.tag_album(task.items))
def initial_lookup(session): """A coroutine for performing the initial MusicBrainz lookup for an album. It accepts lists of Items and yields (items, cur_artist, cur_album, candidates, rec) tuples. If no match is found, all of the yielded parameters (except items) are None. """ task = None while True: task = yield task if task.should_skip(): continue plugins.send("import_task_start", session=session, task=task) log.debug("Looking up: %s" % displayable_path(task.paths)) task.set_candidates(*autotag.tag_album(task.items))
def initial_lookup(config): """A coroutine for performing the initial MusicBrainz lookup for an album. It accepts lists of Items and yields (items, cur_artist, cur_album, candidates, rec) tuples. If no match is found, all of the yielded parameters (except items) are None. """ task = None while True: task = yield task if task.sentinel: continue log.debug('Looking up: %s' % task.path) try: task.set_match(*autotag.tag_album(task.items, config.timid)) except autotag.AutotagError: task.set_null_match()
def initial_lookup(): """A coroutine for performing the initial MusicBrainz lookup for an album. It accepts lists of Items and yields (items, cur_artist, cur_album, candidates, rec) tuples. If no match is found, all of the yielded parameters (except items) are None. """ toppath, path, items = yield while True: if path is DONE_SENTINEL: cur_artist, cur_album, candidates, rec = None, None, None, None else: try: cur_artist, cur_album, candidates, rec = \ autotag.tag_album(items) except autotag.AutotagError: cur_artist, cur_album, candidates, rec = \ None, None, None, None toppath, path, items = yield toppath, path, items, cur_artist, \ cur_album, candidates, rec
def initial_lookup(session): """A coroutine for performing the initial MusicBrainz lookup for an album. It accepts lists of Items and yields (items, cur_artist, cur_album, candidates, rec) tuples. If no match is found, all of the yielded parameters (except items) are None. """ task = None while True: task = yield task if task.sentinel: continue plugins.send('import_task_start', session=session, task=task) log.debug('Looking up: %s' % task.path) try: task.set_candidates(*autotag.tag_album(task.items, config['import']['timid'])) except autotag.AutotagError: task.set_null_candidates()
def identify_album_from_multiple_paths(paths_list_or_beets_items_list=None, expected_artist=None, expected_album=None, expected_release_id=None): items = paths_list_or_beets_items_list if isinstance(paths_list_or_beets_items_list[0], six.string_types): items = [Item.from_path(x) for x in paths_list_or_beets_items_list if is_media_file(x)] logger.debug("Called with {} items, expected id {}".format(len(items), expected_release_id)) artist_name, album_name, album_recommendation_list, recommendation = \ tag_album(items, search_artist=expected_artist, search_album=expected_album, search_ids=[expected_release_id] if expected_release_id else []) if recommendation is Recommendation.none: return None, None distance, album_info, track_mapping, extra_items, extra_tracks = album_recommendation_list[0] logger.debug('Successfully matched album {} !'.format(album_info.album_id)) logger.info("Successfully tagged album {album_id}, releasegroup {rgid}".format(album_id=album_info.album_id, rgid=album_info.releasegroup_id)) return album_info, track_mapping
lossless_items.append(beets.library.Item.from_path(downloaded_track)) elif any(downloaded_track.lower().endswith('.' + x.lower()) for x in headphones.LOSSY_MEDIA_FORMATS): lossy_items.append(beets.library.Item.from_path(downloaded_track)) else: logger.warn("Skipping: %s because it is not a mutagen friendly file format", downloaded_track.decode(headphones.SYS_ENCODING, 'replace')) except Exception, e: logger.error("Beets couldn't create an Item from: %s - not a media file? %s", downloaded_track.decode(headphones.SYS_ENCODING, 'replace'), str(e)) for items in [lossy_items, lossless_items]: if not items: continue try: cur_artist, cur_album, candidates, rec = autotag.tag_album(items, search_artist=helpers.latinToAscii(release['ArtistName']), search_album=helpers.latinToAscii(release['AlbumTitle'])) except Exception, e: logger.error('Error getting recommendation: %s. Not writing metadata', e) return if str(rec) == 'recommendation.none': logger.warn('No accurate album match found for %s, %s - not writing metadata', release['ArtistName'], release['AlbumTitle']) return if candidates: dist, info, mapping, extra_items, extra_tracks = candidates[0] else: logger.warn('No accurate album match found for %s, %s - not writing metadata', release['ArtistName'], release['AlbumTitle']) return logger.info('Beets recommendation for tagging items: %s' % rec)