示例#1
0
        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 path in reversed(rm_paths):
                is_dir = os.path.isdir(path) and not os.path.islink(path)
                self._engine.LOG.debug("Deleting '%s%s'" % (path, '/' if is_dir else ''))
                if not dry_run:
                    try:
                        (os.rmdir if is_dir else os.remove)(path)
                    except OSError, 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" % (path, '/' if is_dir else ''))
                        else:
                            raise
示例#2
0
        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
示例#3
0
            item = None # Make sure there's no accidental stale reference
            
            if not source_items[path]:
                self.LOG.warn("No download item found for %s, skipping!" % (pretty_path(path),))
                continue

            if len(source_items[path]) > 1:
                self.LOG.warn("Can't handle multi-item moving yet, skipping %s!" % (pretty_path(path),))
                continue
            
            if os.path.islink(path):
                self.LOG.warn("Won't move symlinks, skipping %s!" % (pretty_path(path),))
                continue

            for item in source_items[path]:
                if os.path.islink(item.path) and os.path.realpath(item.path) != os.readlink(item.path):
                    self.LOG.warn("Can't handle multi-hop symlinks yet, skipping %s!" % (pretty_path(path),))
                    continue

                if not item.is_complete:
                    if self.options.force_incomplete:
                        self.LOG.warn("Moving incomplete item '%s'!" % (item.name,))
                    else:
                        self.LOG.warn("Won't move incomplete item '%s'!" % (item.name,))
                        continue

                moved_count += 1
                dst = target
                if os.path.isdir(dst):
                    dst = os.path.join(dst, os.path.basename(path))
                self.LOG.info("Moving to %s..." % (pretty_path(dst),))
示例#4
0
    def mainloop(self):
        """ The main loop.
        """
        # Print usage if not enough args
        if len(self.args) < 2:
            self.parser.print_help()
            self.parser.exit()

        # TODO: Add mode to move tied metafiles, without losing the tie

        # Target handling
        target = self.args[-1]
        if "//" in target.rstrip('/'):
            # Create parts of target path
            existing, _ = target.split("//", 1)
            if not os.path.isdir(existing):
                self.fatal("Path before '//' MUST exists in %s" %
                           (pretty_path(target), ))

            # Possibly create the rest
            target = target.replace("//", "/")
            if not os.path.exists(target):
                self.guarded(os.makedirs, target)

        # Preparation
        # TODO: Handle cases where target is the original download path correctly!
        #       i.e.   rtmv foo/ foo   AND   rtmv foo/ .   (in the download dir)
        proxy = config.engine.open()
        download_path = os.path.realpath(
            os.path.expanduser(
                proxy.directory.default(xmlrpc.NOHASH).rstrip(os.sep)))
        target = self.resolve_slashed(target)
        source_paths = [self.resolve_slashed(i) for i in self.args[:-1]]
        source_realpaths = [os.path.realpath(i) for i in source_paths]
        source_items = defaultdict(list)  # map of source path to item
        items = list(config.engine.items(prefetch=self.PREFETCH_FIELDS))

        # Validate source paths and find matching items
        for item in items:
            if not item.path:
                continue

            realpath = None
            try:
                realpath = os.path.realpath(item.path)
            except (EnvironmentError, UnicodeError) as exc:
                self.LOG.warning("Cannot realpath %r (%s)" % (item.path, exc))

            # Look if item matches a source path
            # TODO: Handle download items nested into each other!
            try:
                path_idx = source_realpaths.index(realpath
                                                  or fmt.to_utf8(item.path))
            except ValueError:
                continue

            if realpath:
                self.LOG.debug('Item path %s resolved to %s' %
                               (pretty_path(item.path), pretty_path(realpath)))
            self.LOG.debug(
                'Found "%s" for %s' %
                (fmt.to_utf8(item.name), pretty_path(source_paths[path_idx])))
            source_items[source_paths[path_idx]].append(item)

        ##for path in source_paths: print path, "==>"; print "  " + "\n  ".join(i.path for i in source_items[path])

        if not os.path.isdir(target) and len(source_paths) > 1:
            self.fatal(
                "Can't move multiple files to %s which is no directory!" %
                (pretty_path(target), ))

        # Actually move the data
        moved_count = 0
        for path in source_paths:
            item = None  # Make sure there's no accidental stale reference

            if not source_items[path]:
                self.LOG.warn("No download item found for %s, skipping!" %
                              (pretty_path(path), ))
                continue

            if len(source_items[path]) > 1:
                self.LOG.warn(
                    "Can't handle multi-item moving yet, skipping %s!" %
                    (pretty_path(path), ))
                continue

            if os.path.islink(path):
                self.LOG.warn("Won't move symlinks, skipping %s!" %
                              (pretty_path(path), ))
                continue

            for item in source_items[path]:
                if os.path.islink(item.path) and os.path.realpath(
                        item.path) != os.readlink(item.path):
                    self.LOG.warn(
                        "Can't handle multi-hop symlinks yet, skipping %s!" %
                        (pretty_path(path), ))
                    continue

                if not item.is_complete:
                    if self.options.force_incomplete:
                        self.LOG.warn("Moving incomplete item '%s'!" %
                                      (item.name, ))
                    else:
                        self.LOG.warn("Won't move incomplete item '%s'!" %
                                      (item.name, ))
                        continue

                moved_count += 1
                dst = target
                if os.path.isdir(dst):
                    dst = os.path.join(dst, os.path.basename(path))
                self.LOG.info("Moving to %s..." % (pretty_path(dst), ))

                # Pause torrent?
                # was_active = item.is_active and not self.options.dry_run
                # if was_active: item.pause()

                # TODO: move across devices
                # TODO: move using "d.directory.set" instead of symlinks
                if os.path.islink(item.path):
                    if os.path.abspath(dst) == os.path.abspath(
                            item.path.rstrip(os.sep)):
                        # Moving back to original place
                        self.LOG.debug("Unlinking %s" %
                                       (pretty_path(item.path), ))
                        self.guarded(os.remove, item.path)
                        self.guarded(os.rename, path, dst)
                    else:
                        # Moving to another place
                        self.LOG.debug("Re-linking %s" %
                                       (pretty_path(item.path), ))
                        self.guarded(os.rename, path, dst)
                        self.guarded(os.remove, item.path)
                        self.guarded(os.symlink, os.path.abspath(dst),
                                     item.path)
                else:
                    # Moving download initially
                    self.LOG.debug("Symlinking %s" %
                                   (pretty_path(item.path), ))
                    src1, src2 = os.path.join(download_path,
                                              os.path.basename(
                                                  item.path)), fmt.to_unicode(
                                                      os.path.realpath(path))
                    assert src1 == src2, 'Item path %r should match %r!' % (
                        src1, src2)
                    self.guarded(os.rename, item.path, dst)
                    self.guarded(os.symlink, os.path.abspath(dst), item.path)

                # Resume torrent?
                # if was_active: sitem.resume()

        # Print stats
        self.LOG.debug("XMLRPC stats: %s" % proxy)
        self.LOG.log(
            logging.DEBUG if self.options.cron else logging.INFO,
            "Moved %d path%s (skipped %d)" %
            (moved_count, "" if moved_count == 1 else "s",
             len(source_paths) - moved_count))