Example #1
0
    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
Example #2
0
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
Example #3
0
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
Example #4
0
    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
Example #5
0
    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
Example #6
0
    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
Example #7
0
    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
Example #8
0
 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
Example #9
0
 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
Example #10
0
 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
Example #11
0
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
Example #12
0
 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
Example #13
0
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
Example #14
0
 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
Example #15
0
 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
Example #16
0
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))
Example #17
0
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))
Example #18
0
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()
Example #19
0
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()
Example #20
0
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
Example #21
0
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()
Example #22
0
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
Example #23
0
                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)