Example #1
0
    def cull(self, file_filter=None, attrs=None):
        """ Delete ALL data files and remove torrent from client.

            @param file_filter: Optional callable for selecting a subset of all files.
                The callable gets a file item as described for RtorrentItem._get_files
                and must return True for items eligible for deletion.
            @param attrs: Optional list of additional attributes to fetch for a filter.
        """
        dry_run = 0  # set to 1 for testing

        def remove_with_links(path):
            "Remove a path including any symlink chains leading to it."
            rm_paths = []
            while os.path.islink(path):
                target = os.readlink(path)
                rm_paths.append(path)
                path = target

            if os.path.exists(path):
                rm_paths.append(path)
            else:
                self._engine.LOG.debug(
                    "Real path '%s' doesn't exist,"
                    " but %d symlink(s) leading to it will be deleted..." %
                    (path, len(rm_paths)))

            # Remove the link chain, starting at the real path
            # (this prevents losing the chain when there's permission problems)
            for rm_path in reversed(rm_paths):
                is_dir = os.path.isdir(rm_path) and not os.path.islink(rm_path)
                self._engine.LOG.debug("Deleting '%s%s'" %
                                       (rm_path, '/' if is_dir else ''))
                if not dry_run:
                    try:
                        (os.rmdir if is_dir else os.remove)(rm_path)
                    except OSError as exc:
                        if exc.errno == errno.ENOENT:
                            # Seems this disappeared somehow inbetween (race condition)
                            self._engine.LOG.info(
                                "Path '%s%s' disappeared before it could be deleted"
                                % (rm_path, '/' if is_dir else ''))
                        else:
                            raise

            return rm_paths

        # Assemble doomed files and directories
        files, dirs = set(), set()
        base_path = os.path.expanduser(self.directory)
        item_files = list(self._get_files(attrs=attrs))

        if not self.directory:
            raise error.EngineError(
                "Directory for item #%s is empty,"
                " you might want to add a filter 'directory=!'" %
                (self._fields["hash"], ))
        if not os.path.isabs(base_path):
            raise error.EngineError(
                "Directory '%s' for item #%s is not absolute, which is a bad idea;"
                " fix your .rtorrent.rc, and use 'directory.default.set = /...'"
                % (
                    self.directory,
                    self._fields["hash"],
                ))
        if self.fetch("=is_multi_file") and os.path.isdir(self.directory):
            dirs.add(self.directory)

        for item_file in item_files:
            if file_filter and not file_filter(item_file):
                continue
            #print repr(item_file)
            path = os.path.join(base_path, item_file.path)
            files.add(path)
            if '/' in item_file.path:
                dirs.add(os.path.dirname(path))

        # Delete selected files
        if not dry_run:
            self.stop()
        for path in sorted(files):
            ##self._engine.LOG.debug("Deleting file '%s'" % (path,))
            remove_with_links(path)

        # Prune empty directories (longer paths first)
        doomed = files | dirs
        for path in sorted(dirs, reverse=True):
            residue = set(os.listdir(path) if os.path.exists(path) else [])
            ignorable = set(i for i in residue if any(
                fnmatch.fnmatch(i, pat) for pat in config.waif_pattern_list)
                            #or os.path.join(path, i) in doomed
                            )
            ##print "---", residue - ignorable
            if residue and residue != ignorable:
                self._engine.LOG.info(
                    "Keeping non-empty directory '%s' with %d %s%s!" % (
                        path,
                        len(residue),
                        "entry" if len(residue) == 1 else "entries",
                        (" (%d ignorable)" %
                         len(ignorable)) if ignorable else "",
                    ))
            else:
                ##print "---", ignorable
                for waif in ignorable:  # - doomed:
                    waif = os.path.join(path, waif)
                    self._engine.LOG.debug("Deleting waif '%s'" % (waif, ))
                    if not dry_run:
                        try:
                            os.remove(waif)
                        except EnvironmentError as exc:
                            self._engine.LOG.warn(
                                "Problem deleting waif '%s' (%s)" %
                                (waif, exc))

                ##self._engine.LOG.debug("Deleting empty directory '%s'" % (path,))
                doomed.update(remove_with_links(path))

        # Delete item from engine
        if not dry_run:
            self.delete()
Example #2
0
     path = os.path.join(base_path, item_file.path)
     files.add(path)
     if '/' in item_file.path:
         dirs.add(os.path.dirname(path))
 
 # Delete selected files
 if not dry_run:
     self.stop()
 for path in sorted(files):
     ##self._engine.LOG.debug("Deleting file '%s'" % (path,))
     remove_with_links(path)
 
 # Prune empty directories (longer paths first)
 doomed = files | dirs
 for path in sorted(dirs, reverse=True):
     residue = set(os.listdir(path) if os.path.exists(path) else [])
     ignorable = set(i for i in residue 
         if any(fnmatch.fnmatch(i, pat) for pat in config.waif_pattern_list)
         #or os.path.join(path, i) in doomed 
     )
     ##print "---", residue - ignorable
     if residue and residue != ignorable:
         self._engine.LOG.info("Keeping non-empty directory '%s' with %d %s%s!" % (
             path, len(residue),
             "entry" if len(residue) == 1 else "entries",
             (" (%d ignorable)" % len(ignorable)) if ignorable else "",
         ))
     else:
         ##print "---", ignorable
         for waif in ignorable:# - doomed:
             waif = os.path.join(path, waif)
Example #3
0
            path = os.path.join(base_path, item_file.path)
            files.add(path)
            if '/' in item_file.path:
                dirs.add(os.path.dirname(path))

        # Delete selected files
        if not dry_run:
            self.stop()
        for path in sorted(files):
            ##self._engine.LOG.debug("Deleting file '%s'" % (path,))
            remove_with_links(path)

        # Prune empty directories (longer paths first)
        doomed = files | dirs
        for path in sorted(dirs, reverse=True):
            residue = set(os.listdir(path) if os.path.exists(path) else [])
            ignorable = set(i for i in residue if any(
                fnmatch.fnmatch(i, pat) for pat in config.waif_pattern_list)
                            #or os.path.join(path, i) in doomed
                            )
            ##print "---", residue - ignorable
            if residue and residue != ignorable:
                self._engine.LOG.info(
                    "Keeping non-empty directory '%s' with %d %s%s!" % (
                        path,
                        len(residue),
                        "entry" if len(residue) == 1 else "entries",
                        (" (%d ignorable)" %
                         len(ignorable)) if ignorable else "",
                    ))
            else: