Example #1
0
def _setup(options, lib=None):
    """Prepare and global state and updates it with command line options.

    Returns a list of subcommands, a list of plugins, and a library instance.
    """
    # Configure the MusicBrainz API.
    mb.configure()

    config = _configure(options)

    plugins = _load_plugins(config)

    # Get the default subcommands.
    from beets.ui.commands import default_commands

    subcommands = list(default_commands)
    subcommands.extend(plugins.commands())

    if lib is None:
        lib = _open_library(config)
        plugins.send("library_opened", lib=lib)
    library.Item._types.update(plugins.types(library.Item))
    library.Album._types.update(plugins.types(library.Album))

    return subcommands, plugins, lib
Example #2
0
def album_candidates(items, artist, album, va_likely):
    """Search for album matches. ``items`` is a list of Item objects
    that make up the album. ``artist`` and ``album`` are the respective
    names (strings), which may be derived from the item list or may be
    entered by the user. ``va_likely`` is a boolean indicating whether
    the album is likely to be a "various artists" release.
    """
    out = []

    # Base candidates if we have album and artist to match.
    if artist and album:
        try:
            out.extend(mb.match_album(artist, album, len(items)))
        except mb.MusicBrainzAPIError as exc:
            exc.log(log)

    # Also add VA matches from MusicBrainz where appropriate.
    if va_likely and album:
        try:
            out.extend(mb.match_album(None, album, len(items)))
        except mb.MusicBrainzAPIError as exc:
            exc.log(log)

    # Candidates from plugins.
    out.extend(plugins.candidates(items, artist, album, va_likely))

    # Notify subscribed plugins about fetched album info
    for a in out:
        plugins.send('albuminfo_received', info=a)

    return out
Example #3
0
 def _emit_imported(self, lib):
     # FIXME This shouldn't be here. Skipped tasks should be removed from
     # the pipeline.
     if self.skip:
         return
     for item in self.imported_items():
         plugins.send("item_imported", lib=lib, item=item)
Example #4
0
    def write(self, path=None):
        """Write the item's metadata to a media file.

        Updates the mediafile with properties from itself.

        Can raise either a `ReadError` or a `WriteError`.
        """
        if path is None:
            path = self.path
        else:
            path = normpath(path)
        try:
            mediafile = MediaFile(path)
        except (OSError, IOError) as exc:
            raise ReadError(self.path, exc)

        plugins.send('write', item=self, path=path)

        try:
            mediafile.update(self, id3v23=beets.config['id3v23'].get(bool))
        except (OSError, IOError, MutagenError) as exc:
            raise WriteError(self.path, exc)

        # The file has a new mtime.
        if path == self.path:
            self.mtime = self.current_mtime()
        plugins.send('after_write', item=self, path=path)
Example #5
0
def user_query(session):
    """A coroutine for interfacing with the user about the tagging
    process.

    The coroutine accepts an ImportTask objects. It uses the
    session's ``choose_match`` method to determine the ``action`` for
    this task. Depending on the action additional stages are exectuted
    and the processed task is yielded.

    It emits the ``import_task_choice`` event for plugins. Plugins have
    acces to the choice via the ``taks.choice_flag`` property and may
    choose to change it.
    """
    recent = set()
    task = None
    while True:
        task = yield task
        if task.should_skip():
            continue

        # Ask the user for a choice.
        choice = session.choose_match(task)
        task.set_choice(choice)
        session.log_choice(task)
        plugins.send("import_task_choice", session=session, task=task)

        # As-tracks: transition to singleton workflow.
        if task.choice_flag is action.TRACKS:
            # Set up a little pipeline for dealing with the singletons.
            def emitter(task):
                for item in task.items:
                    yield ImportTask.item_task(item)
                yield ImportTask.progress_sentinel(task.toppath, task.paths)

            ipl = pipeline.Pipeline([emitter(task), item_lookup(session), item_query(session)])
            task = pipeline.multiple(ipl.pull())
            continue

        # As albums: group items by albums and create task for each album
        if task.choice_flag is action.ALBUMS:

            def emitter(task):
                yield task

            ipl = pipeline.Pipeline(
                [emitter(task), group_albums(session), initial_lookup(session), user_query(session)]
            )
            task = pipeline.multiple(ipl.pull())
            continue

        # Check for duplicates if we have a match (or ASIS).
        if task.choice_flag in (action.ASIS, action.APPLY):
            ident = task.chosen_ident()
            # The "recent" set keeps track of identifiers for recently
            # imported albums -- those that haven't reached the database
            # yet.
            if ident in recent or _duplicate_check(session.lib, task):
                session.resolve_duplicate(task)
                session.log_choice(task, True)
            recent.add(ident)
Example #6
0
    def manipulate_files(self, move=False, copy=False, write=False,
                         link=False, session=None):
        items = self.imported_items()
        # Save the original paths of all items for deletion and pruning
        # in the next step (finalization).
        self.old_paths = [item.path for item in items]
        for item in items:
            if move or copy or link:
                # In copy and link modes, treat re-imports specially:
                # move in-library files. (Out-of-library files are
                # copied/moved as usual).
                old_path = item.path
                if (copy or link) and self.replaced_items[item] and \
                   session.lib.directory in util.ancestry(old_path):
                    item.move()
                    # We moved the item, so remove the
                    # now-nonexistent file from old_paths.
                    self.old_paths.remove(old_path)
                else:
                    # A normal import. Just copy files and keep track of
                    # old paths.
                    item.move(copy, link)

            if write and self.apply:
                item.try_write()

        with session.lib.transaction():
            for item in self.imported_items():
                item.store()

        plugins.send('import_task_files', session=session, task=self)
Example #7
0
 def test_listener_level2(self):
     self.config['verbose'] = 2
     with helper.capture_log() as logs:
         plugins.send('dummy_event')
     self.assertIn(u'dummy: warning listener', logs)
     self.assertIn(u'dummy: info listener', logs)
     self.assertIn(u'dummy: debug listener', logs)
Example #8
0
def _raw_main(args, lib=None):
    """A helper function for `main` without top-level exception
    handling.
    """
    parser = SubcommandsOptionParser()
    parser.add_option('-l', '--library', dest='library',
                      help='library database file to use')
    parser.add_option('-d', '--directory', dest='directory',
                      help="destination music directory")
    parser.add_option('-v', '--verbose', dest='verbose', action='store_true',
                      help='print debugging information')
    parser.add_option('-c', '--config', dest='config',
                      help='path to configuration file')
    parser.add_option('-h', '--help', dest='help', action='store_true',
                      help='how this help message and exit')
    parser.add_option('--version', dest='version', action='store_true',
                      help=optparse.SUPPRESS_HELP)

    options, subargs = parser.parse_global_options(args)
    subcommands, plugins, lib = _setup(options, lib)
    parser.add_subcommand(*subcommands)

    subcommand, suboptions, subargs = parser.parse_subcommand(subargs)
    subcommand.func(lib, suboptions, subargs)

    plugins.send('cli_exit', lib=lib)
Example #9
0
def _setup(options, lib=None):
    """Prepare and global state and updates it with command line options.

    Returns a list of subcommands, a list of plugins, and a library instance.
    """
    # Configure the MusicBrainz API.
    mb.configure()

    config = _configure(options)

    plugins = _load_plugins(config)

    # Temporary: Migrate from 1.0-style configuration.
    from beets.ui import migrate
    migrate.automigrate()

    # Get the default subcommands.
    from beets.ui.commands import default_commands

    subcommands = list(default_commands)
    subcommands.append(migrate.migrate_cmd)
    subcommands.extend(plugins.commands())

    if lib is None:
        lib = _open_library(config)
        plugins.send("library_opened", lib=lib)

    return subcommands, plugins, lib
Example #10
0
    def _run_importer(self):
        """
        Create an instance of the plugin, run the importer, and
        remove/unregister the plugin instance so a new instance can
        be created when this method is run again.
        This is a convenience method that can be called to setup, exercise
        and teardown the system under test after setting any config options
        and before assertions are made regarding changes to the filesystem.
        """
        # Setup
        # Create an instance of the plugin
        plugins.find_plugins()

        # Exercise
        # Run the importer
        self.importer.run()
        # Fake the occurence of the cli_exit event
        plugins.send('cli_exit', lib=self.lib)

        # Teardown
        if plugins._instances:
            classes = list(plugins._classes)

            # Unregister listners
            for event in classes[0].listeners:
                del classes[0].listeners[event][0]

            # Delete the plugin instance so a new one gets created for each test
            del plugins._instances[classes[0]]

        log.debug("--- library structure")
        self._list_files(self.lib_dir)
Example #11
0
def _raw_main(args, lib=None):
    """A helper function for `main` without top-level exception
    handling.
    """
    parser = SubcommandsOptionParser()
    parser.add_option('-l', '--library', dest='library',
                      help='library database file to use')
    parser.add_option('-d', '--directory', dest='directory',
                      help="destination music directory")
    parser.add_option('-v', '--verbose', dest='verbose', action='store_true',
                      help='print debugging information')
    parser.add_option('-c', '--config', dest='config',
                      help='path to configuration file')
    parser.add_option('-h', '--help', dest='help', action='store_true',
                      help='how this help message and exit')
    parser.add_option('--version', dest='version', action='store_true',
                      help=optparse.SUPPRESS_HELP)

    options, subargs = parser.parse_global_options(args)

    # Special case for the `config --edit` command: bypass _setup so
    # that an invalid configuration does not prevent the editor from
    # starting.
    if subargs[0] == 'config' and ('-e' in subargs or '--edit' in subargs):
        from beets.ui.commands import config_edit
        return config_edit()

    subcommands, plugins, lib = _setup(options, lib)
    parser.add_subcommand(*subcommands)

    subcommand, suboptions, subargs = parser.parse_subcommand(subargs)
    subcommand.func(lib, suboptions, subargs)

    plugins.send('cli_exit', lib=lib)
Example #12
0
    def set_art(self, path, copy=True):
        """Sets the album's cover art to the image at the given path.
        The image is copied (or moved) into place, replacing any
        existing art.

        Sends an 'art_set' event with `self` as the sole argument.
        """
        path = bytestring_path(path)
        oldart = self.artpath
        artdest = self.art_destination(path)

        if oldart and samefile(path, oldart):
            # Art already set.
            return
        elif samefile(path, artdest):
            # Art already in place.
            self.artpath = path
            return

        # Normal operation.
        if oldart == artdest:
            util.remove(oldart)
        artdest = util.unique_path(artdest)
        if copy:
            util.copy(path, artdest)
        else:
            util.move(path, artdest)
        self.artpath = artdest

        plugins.send('art_set', album=self)
Example #13
0
 def save(self, event=True):
     """Writes the library to disk (completing an sqlite
     transaction).
     """
     self.conn.commit()
     if event:
         plugins.send('save', lib=self)
Example #14
0
def _raw_main(args, lib=None):
    """A helper function for `main` without top-level exception
    handling.
    """
    subcommand, suboptions, subargs = _configure(args)

    if lib is None:
        # Open library file.
        dbpath = config["library"].as_filename()
        try:
            lib = library.Library(dbpath, config["directory"].as_filename(), get_path_formats(), get_replacements())
        except sqlite3.OperationalError:
            raise UserError(u"database file {0} could not be opened".format(util.displayable_path(dbpath)))
        plugins.send("library_opened", lib=lib)

    log.debug(
        u"data directory: {0}\n"
        u"library database: {1}\n"
        u"library directory: {2}".format(
            util.displayable_path(config.config_dir()),
            util.displayable_path(lib.path),
            util.displayable_path(lib.directory),
        )
    )

    # Configure the MusicBrainz API.
    mb.configure()

    # Invoke the subcommand.
    subcommand.func(lib, suboptions, subargs)
    plugins.send("cli_exit", lib=lib)
Example #15
0
    def write(self, path=None):
        """Write the item's metadata to a media file.

        ``path`` defaults to the item's path property.

        Can raise either a `ReadError` or a `WriteError`.
        """
        if path is None:
            path = self.path
        else:
            path = normpath(path)
        try:
            f = MediaFile(syspath(path))
        except (OSError, IOError) as exc:
            raise ReadError(self.path, exc)

        plugins.send('write', item=self, path=path)

        for key in ITEM_KEYS_WRITABLE:
            setattr(f, key, self[key])
        try:
            f.save(id3v23=beets.config['id3v23'].get(bool))
        except (OSError, IOError, MutagenError) as exc:
            raise WriteError(self.path, exc)

        # The file has a new mtime.
        self.mtime = self.current_mtime()
        plugins.send('after_write', item=self)
Example #16
0
def _raw_main(args, lib=None):
    """A helper function for `main` without top-level exception
    handling.
    """
    parser = SubcommandsOptionParser()
    parser.add_format_option(flags=("--format-item",), target=library.Item)
    parser.add_format_option(flags=("--format-album",), target=library.Album)
    parser.add_option("-l", "--library", dest="library", help="library database file to use")
    parser.add_option("-d", "--directory", dest="directory", help="destination music directory")
    parser.add_option("-v", "--verbose", dest="verbose", action="count", help="print debugging information")
    parser.add_option("-c", "--config", dest="config", help="path to configuration file")
    parser.add_option("-h", "--help", dest="help", action="store_true", help="how this help message and exit")
    parser.add_option("--version", dest="version", action="store_true", help=optparse.SUPPRESS_HELP)

    options, subargs = parser.parse_global_options(args)

    # Special case for the `config --edit` command: bypass _setup so
    # that an invalid configuration does not prevent the editor from
    # starting.
    if subargs and subargs[0] == "config" and ("-e" in subargs or "--edit" in subargs):
        from beets.ui.commands import config_edit

        return config_edit()

    subcommands, plugins, lib = _setup(options, lib)
    parser.add_subcommand(*subcommands)

    subcommand, suboptions, subargs = parser.parse_subcommand(subargs)
    subcommand.func(lib, suboptions, subargs)

    plugins.send("cli_exit", lib=lib)
Example #17
0
    def write(self, path=None):
        """Write the item's metadata to a media file.

        All fields in `_media_fields` are written to disk according to
        the values on this object.

        Can raise either a `ReadError` or a `WriteError`.
        """
        if path is None:
            path = self.path
        else:
            path = normpath(path)

        tags = dict(self)
        plugins.send('write', item=self, path=path, tags=tags)

        try:
            mediafile = MediaFile(syspath(path),
                                  id3v23=beets.config['id3v23'].get(bool))
        except (OSError, IOError, UnreadableFileError) as exc:
            raise ReadError(self.path, exc)

        mediafile.update(tags)
        try:
            mediafile.save()
        except (OSError, IOError, MutagenError) as exc:
            raise WriteError(self.path, exc)

        # The file has a new mtime.
        if path == self.path:
            self.mtime = self.current_mtime()
        plugins.send('after_write', item=self, path=path)
Example #18
0
def user_query(session):
    """A coroutine for interfacing with the user about the tagging
    process. lib is the Library to import into and logfile may be
    a file-like object for logging the import process. The coroutine
    accepts and yields ImportTask objects.
    """
    recent = set()
    task = None
    while True:
        task = yield task
        if task.sentinel:
            continue

        # Ask the user for a choice.
        choice = session.choose_match(task)
        task.set_choice(choice)
        session.log_choice(task)
        plugins.send('import_task_choice', session=session, task=task)

        # As-tracks: transition to singleton workflow.
        if choice is action.TRACKS:
            # Set up a little pipeline for dealing with the singletons.
            def emitter(task):
                for item in task.items:
                    yield ImportTask.item_task(item)
                yield ImportTask.progress_sentinel(task.toppath, task.paths)

            ipl = pipeline.Pipeline([
                emitter(task),
                item_lookup(session),
                item_query(session),
            ])
            task = pipeline.multiple(ipl.pull())
            continue

        # As albums: group items by albums and create task for each album
        if choice is action.ALBUMS:
            def emitter(task):
                yield task

            ipl = pipeline.Pipeline([
                emitter(task),
                group_albums(session),
                initial_lookup(session),
                user_query(session)
            ])
            task = pipeline.multiple(ipl.pull())
            continue

        # Check for duplicates if we have a match (or ASIS).
        if task.choice_flag in (action.ASIS, action.APPLY):
            ident = task.chosen_ident()
            # The "recent" set keeps track of identifiers for recently
            # imported albums -- those that haven't reached the database
            # yet.
            if ident in recent or _duplicate_check(session.lib, task):
                session.resolve_duplicate(task)
                session.log_choice(task, True)
            recent.add(ident)
Example #19
0
def convert_item(dest_dir, keep_new, path_formats, command, ext):
    while True:
        item = yield
        dest = item.destination(basedir=dest_dir, path_formats=path_formats)

        # When keeping the new file in the library, we first move the
        # current (pristine) file to the destination. We'll then copy it
        # back to its old path or transcode it to a new path.
        if keep_new:
            original = dest
            converted = replace_ext(item.path, ext)
        else:
            original = item.path
            dest = replace_ext(dest, ext)
            converted = dest

        # Ensure that only one thread tries to create directories at a
        # time. (The existence check is not atomic with the directory
        # creation inside this function.)
        with _fs_lock:
            util.mkdirall(dest)

        if os.path.exists(util.syspath(dest)):
            log.info(u'Skipping {0} (target file exists)'.format(
                util.displayable_path(item.path)
            ))
            continue

        if keep_new:
            log.info(u'Moving to {0}'.
                     format(util.displayable_path(original)))
            util.move(item.path, original)

        if not should_transcode(item):
            # No transcoding necessary.
            log.info(u'Copying {0}'.format(util.displayable_path(item.path)))
            util.copy(original, converted)
        else:
            try:
                encode(command, original, converted)
            except subprocess.CalledProcessError:
                continue

        # Write tags from the database to the converted file.
        item.write(path=converted)

        if keep_new:
            # If we're keeping the transcoded file, read it again (after
            # writing) to get new bitrate, duration, etc.
            item.path = converted
            item.read()
            item.store()  # Store new path and audio data.

        if config['convert']['embed']:
            album = item.get_album()
            if album and album.artpath:
                embed_item(item, album.artpath, itempath=converted)

        plugins.send('after_convert', item=item, dest=dest, keepnew=keep_new)
Example #20
0
def tracks_for_id(track_id):
    """Get a list of tracks for an ID."""
    candidates = [track_for_mbid(track_id)]
    plugin_tracks = plugins.track_for_id(track_id)
    for t in plugin_tracks:
        plugins.send('trackinfo_received', info=t)
    candidates.extend(plugin_tracks)
    return filter(None, candidates)
Example #21
0
def albums_for_id(album_id):
    """Get a list of albums for an ID."""
    candidates = [album_for_mbid(album_id)]
    plugin_albums = plugins.album_for_id(album_id)
    for a in plugin_albums:
        plugins.send('albuminfo_received', info=a)
    candidates.extend(plugin_albums)
    return filter(None, candidates)
Example #22
0
def import_files(lib, paths, copy, write, autot, logpath, art, threaded,
                 color, delete, quiet, resume, quiet_fallback):
    """Import the files in the given list of paths, tagging each leaf
    directory as an album. If copy, then the files are copied into
    the library folder. If write, then new metadata is written to the
    files themselves. If not autot, then just import the files
    without attempting to tag. If logpath is provided, then untaggable
    albums will be logged there. If art, then attempt to download
    cover art for each album. If threaded, then accelerate autotagging
    imports by running them in multiple threads. If color, then
    ANSI-colorize some terminal output. If delete, then old files are
    deleted when they are copied. If quiet, then the user is
    never prompted for input; instead, the tagger just skips anything
    it is not confident about. resume indicates whether interrupted
    imports can be resumed and is either a boolean or None.
    quiet_fallback should be either CHOICE_ASIS or CHOICE_SKIP and
    indicates what should happen in quiet mode when the recommendation
    is not strong.
    """
    # Open the log.
    if logpath:
        logfile = open(logpath, 'w')
    else:
        logfile = None

    # Never ask for input in quiet mode.
    if resume is None and quiet:
        resume = False
    
    # Perform the import.
    if autot:
        # Autotag. Set up the pipeline.
        pl = pipeline.Pipeline([
            read_albums(paths, resume),
            initial_lookup(),
            user_query(lib, logfile, color, quiet, quiet_fallback),
            apply_choices(lib, copy, write, art, delete, resume is not False),
        ])

        # Run the pipeline.
        try:
            if threaded:
                pl.run_parallel(QUEUE_SIZE)
            else:
                pl.run_sequential()
        except ImportAbort:
            # User aborted operation. Silently stop.
            pass
    else:
        # Simple import without autotagging. Always sequential.
        simple_import(lib, paths, copy, delete, resume)
    
    # If we were logging, close the file.
    if logfile:
        logfile.close()

    # Emit event.
    plugins.send('import', lib=lib, paths=paths)
Example #23
0
def manipulate_files(session):
    """A coroutine (pipeline stage) that performs necessary file
    manipulations *after* items have been added to the library.
    """
    task = None
    while True:
        task = yield task
        if task.should_skip():
            continue

        # Remove duplicate files marked for deletion.
        if task.remove_duplicates:
            for duplicate_path in task.duplicate_paths:
                log.debug(u'deleting replaced duplicate %s' %
                          util.displayable_path(duplicate_path))
                util.remove(duplicate_path)
                util.prune_dirs(os.path.dirname(duplicate_path),
                                session.lib.directory)

        # Move/copy/write files.
        items = task.imported_items()
        # Save the original paths of all items for deletion and pruning
        # in the next step (finalization).
        task.old_paths = [item.path for item in items]
        for item in items:
            if config['import']['move']:
                # Just move the file.
                item.move(False)
            elif config['import']['copy']:
                # If it's a reimport, move in-library files and copy
                # out-of-library files. Otherwise, copy and keep track
                # of the old path.
                old_path = item.path
                if task.replaced_items[item]:
                    # This is a reimport. Move in-library files and copy
                    # out-of-library files.
                    if session.lib.directory in util.ancestry(old_path):
                        item.move(False)
                        # We moved the item, so remove the
                        # now-nonexistent file from old_paths.
                        task.old_paths.remove(old_path)
                    else:
                        item.move(True)
                else:
                    # A normal import. Just copy files and keep track of
                    # old paths.
                    item.move(True)

            if config['import']['write'] and task.should_write_tags():
                item.try_write()

        # Save new paths.
        with session.lib.transaction():
            for item in items:
                item.store()

        # Plugin event.
        plugins.send('import_task_files', session=session, task=task)
Example #24
0
def albums_for_id(album_id):
    """Get a list of albums for an ID."""
    a = album_for_mbid(album_id)
    if a:
        yield a
    for a in plugins.album_for_id(album_id):
        if a:
            plugins.send(u'albuminfo_received', info=a)
            yield a
Example #25
0
def tracks_for_id(track_id):
    """Get a list of tracks for an ID."""
    t = track_for_mbid(track_id)
    if t:
        yield t
    for t in plugins.track_for_id(track_id):
        if t:
            plugins.send(u'trackinfo_received', info=t)
            yield t
Example #26
0
def track_for_mbid(recording_id):
    """Get a TrackInfo object for a MusicBrainz recording ID. Return None
    if the ID is not found.
    """
    try:
        track = mb.track_for_id(recording_id)
        if track:
            plugins.send('trackinfo_received', info=track)
        return track
    except mb.MusicBrainzAPIError as exc:
        exc.log(log)
Example #27
0
def album_for_mbid(release_id):
    """Get an AlbumInfo object for a MusicBrainz release ID. Return None
    if the ID is not found.
    """
    try:
        album = mb.album_for_id(release_id)
        if album:
            plugins.send('albuminfo_received', info=album)
        return album
    except mb.MusicBrainzAPIError as exc:
        exc.log(log)
Example #28
0
    def write(self):
        """Writes the item's metadata to the associated file.
        """
        f = MediaFile(syspath(self.path))
        plugins.send('write', item=self, mf=f)
        for key in ITEM_KEYS_WRITABLE:
            setattr(f, key, getattr(self, key))
        f.save()

        # The file has a new mtime.
        self.mtime = self.current_mtime()
Example #29
0
def user_query(session):
    """A coroutine for interfacing with the user about the tagging
    process. lib is the Library to import into and logfile may be
    a file-like object for logging the import process. The coroutine
    accepts and yields ImportTask objects.
    """
    recent = set()
    task = None
    while True:
        task = yield task
        if task.sentinel:
            continue

        # Ask the user for a choice.
        choice = session.choose_match(task)
        task.set_choice(choice)
        session.log_choice(task)
        plugins.send('import_task_choice', session=session, task=task)

        # As-tracks: transition to singleton workflow.
        if choice is action.TRACKS:
            # Set up a little pipeline for dealing with the singletons.
            item_tasks = []
            def emitter():
                for item in task.items:
                    yield ImportTask.item_task(item)
                yield ImportTask.progress_sentinel(task.toppath, task.paths)
            def collector():
                while True:
                    item_task = yield
                    item_tasks.append(item_task)
            ipl = pipeline.Pipeline((emitter(), item_lookup(session),
                                     item_query(session), collector()))
            ipl.run_sequential()
            task = pipeline.multiple(item_tasks)
            continue

        # Check for duplicates if we have a match (or ASIS).
        if task.choice_flag in (action.ASIS, action.APPLY):
            ident = task.chosen_ident()
            # The "recent" set keeps track of identifiers for recently
            # imported albums -- those that haven't reached the database
            # yet.
            task.duplicates = _duplicate_check(session.lib, task)
            if ident in recent:
                # TODO: Somehow manage duplicate hooks for recents
                pass
            plugins.send('import_task_duplicate', session=session, task=task)

            if task.duplicates:
                session.resolve_duplicate(task)
                session.log_choice(task, True)
            recent.add(ident)
Example #30
0
def item_lookup(session):
    """A coroutine used to perform the initial MusicBrainz lookup for
    an item task.
    """
    task = None
    while True:
        task = yield task
        if task.sentinel:
            continue

        plugins.send('import_task_start', session=session, task=task)

        task.set_item_candidates(*autotag.tag_item(task.item))