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()
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)
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: