Exemplo n.º 1
0
 def prune(self, filename):
     """Prune any empty directories above the given file. If this
     task has no `toppath` or the file path provided is not within
     the `toppath`, then this function has no effect. Similarly, if
     the file still exists, no pruning is performed, so it's safe to
     call when the file in question may not have been removed.
     """
     if self.toppath and not os.path.exists(filename):
         util.prune_dirs(os.path.dirname(filename), self.toppath)
Exemplo n.º 2
0
    def move(self,
             item,
             copy=False,
             in_album=False,
             basedir=None,
             with_album=True):
        """Move the item to its designated location within the library
        directory (provided by destination()). Subdirectories are
        created as needed. If the operation succeeds, the item's path
        field is updated to reflect the new location.
        
        If copy is True, moving the file is copied rather than moved.
        
        If in_album is True, then the track is treated as part of an
        album even if it does not yet have an album_id associated with
        it. (This allows items to be moved before they are added to the
        database, a performance optimization.)

        basedir overrides the library base directory for the
        destination.

        If the item is in an album, the album is given an opportunity to
        move its art. (This can be disabled by passing
        with_album=False.)
        
        The item is stored to the database if it is in the database, so
        any dirty fields prior to the move() call will be written as a
        side effect. You probably want to call save() to commit the DB
        transaction.
        """
        dest = self.destination(item, in_album=in_album, basedir=basedir)

        # Create necessary ancestry for the move.
        util.mkdirall(dest)

        # Perform the move and store the change.
        old_path = item.path
        item.move(dest, copy)
        if item.id is not None:
            self.store(item)

        # If this item is in an album, move its art.
        if with_album:
            album = self.get_album(item)
            if album:
                album.move_art(copy)

        # Prune vacated directory.
        if not copy:
            util.prune_dirs(os.path.dirname(old_path), self.directory)
Exemplo n.º 3
0
    def move(self, item, copy=False, in_album=False, basedir=None,
             with_album=True):
        """Move the item to its designated location within the library
        directory (provided by destination()). Subdirectories are
        created as needed. If the operation succeeds, the item's path
        field is updated to reflect the new location.
        
        If copy is True, moving the file is copied rather than moved.
        
        If in_album is True, then the track is treated as part of an
        album even if it does not yet have an album_id associated with
        it. (This allows items to be moved before they are added to the
        database, a performance optimization.)

        basedir overrides the library base directory for the
        destination.

        If the item is in an album, the album is given an opportunity to
        move its art. (This can be disabled by passing
        with_album=False.)
        
        The item is stored to the database if it is in the database, so
        any dirty fields prior to the move() call will be written as a
        side effect. You probably want to call save() to commit the DB
        transaction.
        """
        dest = self.destination(item, in_album=in_album, basedir=basedir)
        
        # Create necessary ancestry for the move.
        util.mkdirall(dest)
        
        # Perform the move and store the change.
        old_path = item.path
        item.move(dest, copy)
        if item.id is not None:
            self.store(item)

        # If this item is in an album, move its art.
        if with_album:
            album = self.get_album(item)
            if album:
                album.move_art(copy)

        # Prune vacated directory.
        if not copy:
            util.prune_dirs(os.path.dirname(old_path), self.directory)
Exemplo n.º 4
0
def finalize(config):
    """A coroutine that finishes up importer tasks. In particular, the
    coroutine sends plugin events, deletes old files, and saves
    progress. This is a "terminal" coroutine (it yields None).
    """
    lib = _reopen_lib(config.lib)
    while True:
        task = yield
        if task.should_skip():
            if config.resume is not False:
                task.save_progress()
            if config.incremental:
                task.save_history()
            continue

        items = [i for i in task.items if i] if task.is_album else [task.item]

        # Announce that we've added an album.
        if task.is_album:
            album = lib.get_album(task.album_id)
            plugins.send('album_imported', lib=lib, album=album, config=config)
        else:
            for item in items:
                plugins.send('item_imported',
                             lib=lib,
                             item=item,
                             config=config)

        # Finally, delete old files.
        if config.copy and config.delete:
            new_paths = [os.path.realpath(item.path) for item in items]
            for old_path in task.old_paths:
                # Only delete files that were actually copied.
                if old_path not in new_paths:
                    os.remove(syspath(old_path))
                    # Clean up directory if it is emptied.
                    if task.toppath:
                        util.prune_dirs(os.path.dirname(old_path),
                                        task.toppath)

        # Update progress.
        if config.resume is not False:
            task.save_progress()
        if config.incremental:
            task.save_history()
Exemplo n.º 5
0
def finalize(config):
    """A coroutine that finishes up importer tasks. In particular, the
    coroutine sends plugin events, deletes old files, and saves
    progress. This is a "terminal" coroutine (it yields None).
    """
    lib = _reopen_lib(config.lib)
    while True:
        task = yield
        if task.should_skip():
            if config.resume is not False:
                task.save_progress()
            if config.incremental:
                task.save_history()
            continue

        items = [i for i in task.items if i] if task.is_album else [task.item]

        # Announce that we've added an album.
        if task.is_album:
            album = lib.get_album(task.album_id)
            plugins.send('album_imported', lib=lib, album=album, config=config)
        else:
            for item in items:
                plugins.send('item_imported', lib=lib, item=item, config=config)

        # Finally, delete old files.
        if config.copy and config.delete:
            new_paths = [os.path.realpath(item.path) for item in items]
            for old_path in task.old_paths:
                # Only delete files that were actually copied.
                if old_path not in new_paths:
                    os.remove(syspath(old_path))
                    # Clean up directory if it is emptied.
                    if task.toppath:
                        util.prune_dirs(os.path.dirname(old_path),
                                        task.toppath)

        # Update progress.
        if config.resume is not False:
            task.save_progress()
        if config.incremental:
            task.save_history()
Exemplo n.º 6
0
    def remove(self, item, delete=False, with_album=True):
        """Removes this item. If delete, then the associated file is
        removed from disk. If with_album, then the item's album (if any)
        is removed if it the item was the last in the album.
        """
        album = self.get_album(item) if with_album else None

        self.conn.execute("DELETE FROM items WHERE id=?", (item.id,))

        if album:
            item_iter = album.items()
            try:
                item_iter.next()
            except StopIteration:
                # Album is empty.
                album.remove(delete, False)

        if delete:
            util.soft_remove(item.path)
            util.prune_dirs(os.path.dirname(item.path), self.directory)
Exemplo n.º 7
0
    def remove(self, item, delete=False, with_album=True):
        """Removes this item. If delete, then the associated file is
        removed from disk. If with_album, then the item's album (if any)
        is removed if it the item was the last in the album.
        """
        album = self.get_album(item) if with_album else None

        self.conn.execute('DELETE FROM items WHERE id=?', (item.id, ))

        if album:
            item_iter = album.items()
            try:
                item_iter.next()
            except StopIteration:
                # Album is empty.
                album.remove(delete, False)

        if delete:
            util.soft_remove(item.path)
            util.prune_dirs(os.path.dirname(item.path), self.directory)
Exemplo n.º 8
0
    def move_art(self, copy=False):
        """Move or copy any existing album art so that it remains in the
        same directory as the items.
        """
        old_art = self.artpath
        if not old_art:
            return

        new_art = self.art_destination(old_art)
        if new_art == old_art:
            return

        log.debug("moving album art %s to %s" % (old_art, new_art))
        if copy:
            util.copy(old_art, new_art)
        else:
            util.move(old_art, new_art)
        self.artpath = new_art

        # Prune old path when moving.
        if not copy:
            util.prune_dirs(os.path.dirname(old_art), self._library.directory)
Exemplo n.º 9
0
    def move_art(self, copy=False):
        """Move or copy any existing album art so that it remains in the
        same directory as the items.
        """
        old_art = self.artpath
        if not old_art:
            return

        new_art = self.art_destination(old_art)
        if new_art == old_art:
            return

        log.debug('moving album art %s to %s' % (old_art, new_art))
        if copy:
            util.copy(old_art, new_art)
        else:
            util.move(old_art, new_art)
        self.artpath = new_art

        # Prune old path when moving.
        if not copy:
            util.prune_dirs(os.path.dirname(old_art), self._library.directory)
Exemplo n.º 10
0
def apply_choices(config):
    """A coroutine for applying changes to albums and singletons during
    the autotag process.
    """
    task = None
    while True:
        task = yield task
        if task.should_skip():
            continue

        items = task.imported_items()
        # Clear IDs in case the items are being re-tagged.
        for item in items:
            item.id = None
            item.album_id = None

        # Change metadata.
        if task.should_write_tags():
            if task.is_album:
                autotag.apply_metadata(
                    task.match.info, task.match.mapping,
                    per_disc_numbering=config.per_disc_numbering
                )
            else:
                autotag.apply_item_metadata(task.item, task.match.info)
            plugins.send('import_task_apply', config=config, task=task)

        # Infer album-level fields.
        if task.is_album:
            _infer_album_fields(task)

        # Find existing item entries that these are replacing (for
        # re-imports). Old album structures are automatically cleaned up
        # when the last item is removed.
        task.replaced_items = defaultdict(list)
        for item in items:
            dup_items = config.lib.items(library.MatchQuery('path', item.path))
            for dup_item in dup_items:
                task.replaced_items[item].append(dup_item)
                log.debug('replacing item %i: %s' %
                          (dup_item.id, displayable_path(item.path)))
        log.debug('%i of %i items replaced' % (len(task.replaced_items),
                                               len(items)))

        # Find old items that should be replaced as part of a duplicate
        # resolution.
        duplicate_items = []
        if task.remove_duplicates:
            if task.is_album:
                for album in _duplicate_check(config.lib, task):
                    duplicate_items += album.items()
            else:
                duplicate_items = _item_duplicate_check(config.lib, task)
            log.debug('removing %i old duplicated items' %
                      len(duplicate_items))

            # Delete duplicate files that are located inside the library
            # directory.
            for duplicate_path in [i.path for i in duplicate_items]:
                if config.lib.directory in util.ancestry(duplicate_path):
                    log.debug(u'deleting replaced duplicate %s' %
                              util.displayable_path(duplicate_path))
                    util.remove(duplicate_path)
                    util.prune_dirs(os.path.dirname(duplicate_path),
                                    config.lib.directory)

        # Add items -- before path changes -- to the library. We add the
        # items now (rather than at the end) so that album structures
        # are in place before calls to destination().
        with config.lib.transaction():
            # Remove old items.
            for replaced in task.replaced_items.itervalues():
                for item in replaced:
                    config.lib.remove(item)
            for item in duplicate_items:
                config.lib.remove(item)

            # Add new ones.
            if task.is_album:
                # Add an album.
                album = config.lib.add_album(items)
                task.album_id = album.id
            else:
                # Add tracks.
                for item in items:
                    config.lib.add(item)
Exemplo n.º 11
0
def apply_choices(config):
    """A coroutine for applying changes to albums during the autotag
    process.
    """
    lib = _reopen_lib(config.lib)
    task = None
    while True:    
        task = yield task
        if task.should_skip():
            continue

        items = [i for i in task.items if i] if task.is_album else [task.item]
        # Clear IDs in case the items are being re-tagged.
        for item in items:
            item.id = None
            item.album_id = None

        # Change metadata.
        if task.should_write_tags():
            if task.is_album:
                autotag.apply_metadata(task.items, task.info)
            else:
                autotag.apply_item_metadata(task.item, task.info)

        # Infer album-level fields.
        if task.is_album:
            _infer_album_fields(task)

        # Find existing item entries that these are replacing (for
        # re-imports). Old album structures are automatically cleaned up
        # when the last item is removed.
        replaced_items = defaultdict(list)
        for item in items:
            dup_items = lib.items(library.MatchQuery('path', item.path))
            for dup_item in dup_items:
                replaced_items[item].append(dup_item)
                log.debug('replacing item %i: %s' %
                          (dup_item.id, displayable_path(item.path)))
        log.debug('%i of %i items replaced' % (len(replaced_items),
                                               len(items)))

        # Find old items that should be replaced as part of a duplicate
        # resolution.
        duplicate_items = []
        if task.remove_duplicates:
            if task.is_album:
                for album in _duplicate_check(lib, task):
                    duplicate_items += album.items()
            else:
                duplicate_items = _item_duplicate_check(lib, task)
            log.debug('removing %i old duplicated items' %
                      len(duplicate_items))

            # Delete duplicate files that are located inside the library
            # directory.
            for duplicate_path in [i.path for i in duplicate_items]:
                if lib.directory in util.ancestry(duplicate_path):
                    log.debug(u'deleting replaced duplicate %s' %
                              util.displayable_path(duplicate_path))
                    util.soft_remove(duplicate_path)
                    util.prune_dirs(os.path.dirname(duplicate_path),
                                    lib.directory)

        # Move/copy files.
        task.old_paths = [item.path for item in items]
        for item in items:
            if config.copy:
                # If we're replacing an item, then move rather than
                # copying.
                old_path = item.path
                do_copy = not bool(replaced_items[item])
                lib.move(item, do_copy, task.is_album)
                if not do_copy:
                    # If we moved the item, remove the now-nonexistent
                    # file from old_paths.
                    task.old_paths.remove(old_path)
            if config.write and task.should_write_tags():
                item.write()

        # Add items to library. We consolidate this at the end to avoid
        # locking while we do the copying and tag updates.
        try:
            # Remove old items.
            for replaced in replaced_items.itervalues():
                for item in replaced:
                    lib.remove(item)
            for item in duplicate_items:
                lib.remove(item)

            # Add new ones.
            if task.is_album:
                # Add an album.
                album = lib.add_album(items)
                task.album_id = album.id
            else:
                # Add tracks.
                for item in items:
                    lib.add(item)
        finally:
            lib.save()
Exemplo n.º 12
0
def apply_choices(config):
    """A coroutine for applying changes to albums during the autotag
    process.
    """
    lib = _reopen_lib(config.lib)
    task = None
    while True:
        task = yield task
        if task.should_skip():
            continue

        items = [i for i in task.items if i] if task.is_album else [task.item]
        # Clear IDs in case the items are being re-tagged.
        for item in items:
            item.id = None
            item.album_id = None

        # Change metadata.
        if task.should_write_tags():
            if task.is_album:
                autotag.apply_metadata(task.items, task.info)
            else:
                autotag.apply_item_metadata(task.item, task.info)

        # Infer album-level fields.
        if task.is_album:
            _infer_album_fields(task)

        # Find existing item entries that these are replacing (for
        # re-imports). Old album structures are automatically cleaned up
        # when the last item is removed.
        replaced_items = defaultdict(list)
        for item in items:
            dup_items = lib.items(library.MatchQuery('path', item.path))
            for dup_item in dup_items:
                replaced_items[item].append(dup_item)
                log.debug('replacing item %i: %s' %
                          (dup_item.id, displayable_path(item.path)))
        log.debug('%i of %i items replaced' %
                  (len(replaced_items), len(items)))

        # Find old items that should be replaced as part of a duplicate
        # resolution.
        duplicate_items = []
        if task.remove_duplicates:
            if task.is_album:
                for album in _duplicate_check(lib, task):
                    duplicate_items += album.items()
            else:
                duplicate_items = _item_duplicate_check(lib, task)
            log.debug('removing %i old duplicated items' %
                      len(duplicate_items))

            # Delete duplicate files that are located inside the library
            # directory.
            for duplicate_path in [i.path for i in duplicate_items]:
                if lib.directory in util.ancestry(duplicate_path):
                    log.debug(u'deleting replaced duplicate %s' %
                              util.displayable_path(duplicate_path))
                    util.soft_remove(duplicate_path)
                    util.prune_dirs(os.path.dirname(duplicate_path),
                                    lib.directory)

        # Move/copy files.
        task.old_paths = [item.path for item in items]
        for item in items:
            if config.copy:
                # If we're replacing an item, then move rather than
                # copying.
                old_path = item.path
                do_copy = not bool(replaced_items[item])
                lib.move(item, do_copy, task.is_album)
                if not do_copy:
                    # If we moved the item, remove the now-nonexistent
                    # file from old_paths.
                    task.old_paths.remove(old_path)
            if config.write and task.should_write_tags():
                item.write()

        # Add items to library. We consolidate this at the end to avoid
        # locking while we do the copying and tag updates.
        try:
            # Remove old items.
            for replaced in replaced_items.itervalues():
                for item in replaced:
                    lib.remove(item)
            for item in duplicate_items:
                lib.remove(item)

            # Add new ones.
            if task.is_album:
                # Add an album.
                album = lib.add_album(items)
                task.album_id = album.id
            else:
                # Add tracks.
                for item in items:
                    lib.add(item)
        finally:
            lib.save()