Exemplo n.º 1
0
 def stop(self):
     """Stop the subproces."""
     if self._process and self._process.is_alive():
         logger.debug("Terminating process: %s", self._process.name)
         stop_command = ("stop", None)
         self.pipe.send(stop_command)
         self._process.join()
Exemplo n.º 2
0
def subprocess(pipe, reuse):  # type: (mp.connection, bool) -> None
    try:
        # Wait till we receive
        # commands from the executer
        while True:
            # Stop the subprocess
            # This is required when reusing the subprocess
            command, data = pipe.recv(
            )  # type: (str, Tuple[Addon, List[str], LocalRepo, urlparse.SplitResult])
            if command == "stop":
                break

            # Execute the addon
            elif command == "execute":
                addon, deps, cached, url = data
                # Patch sys.argv to emulate what is expected
                urllist = [url.scheme, url.netloc, url.path, "", ""]
                sys.argv = (urlparse.urlunsplit(urllist), 1,
                            "?{}".format(url.query))

                try:
                    # Create tesseract to handle kodi module interactions
                    xbmc.session = tesseract = Tesseract(addon,
                                                         deps,
                                                         cached,
                                                         pipe=pipe)
                    tesseract.data.path = url.geturl()

                    # Execute the addon
                    module = addon.entrypoint[1]
                    runpy.run_module(module,
                                     run_name="__main__",
                                     alter_sys=False)

                # Addon must have directly raised an error
                except Exception as e:
                    logger.debug(e, exc_info=True)
                    pipe.send((False, False))

                else:
                    # Send back the results from the addon
                    resp = (tesseract.data.succeeded, tesseract.data)
                    pipe.send(resp)

            # If this subprocess will not be reused then
            # break from loop to end the process
            if reuse is False:
                break
    except KeyboardInterrupt:
        pipe.send((False, False))

    except Exception as e:
        logger.error(e, exc_info=True)
        pipe.send((False, False))
Exemplo n.º 3
0
    def download(self, dep):  # type: (Union[str, Dependency, Addon]) -> Addon
        if isinstance(dep, str) and dep in self.db:
            repo, addon = self.db[dep]

        elif isinstance(dep, (Dependency, Addon)) and dep.id in self.db:
            repo, addon = self.db[dep.id]

            # Warn user if we are downloading an older
            # version than what is required
            if addon.version < dep.version:
                warnings.warn(
                    "required version is greater than whats available: {} < {}"
                    .format(addon.version, dep.version), RuntimeWarning)
        else:
            raise KeyError("{} not found on remote repo".format(dep))

        filename = u"{}-{}.zip".format(addon.id, addon.version)
        filepath = os.path.join(PACKAGE_DIR, filename)
        if os.path.exists(filepath):
            logger.debug("Using cached package: '{}'".format(filename))
        else:
            logger.info("Downloading: '{}'".format(filename))
            # Remove old zipfiles before download, if any
            self.cleanup(addon.id)

            # Request the addon zipfile from server
            url_part = "{0}/{1}".format(addon.id, filename)
            url = "{}/{}".format(repo, url_part)
            resp = self.session.get(url)

            # Read and save contents of zipfile to package directory
            try:
                with open(filepath, "wb") as stream:
                    for chunk in resp.iter_content(decode_unicode=False):
                        stream.write(chunk)

            except (OSError, IOError) as e:
                self.cleanup(addon.id)
                raise e

            finally:
                resp.close()

        # Remove the old addon directory if exists
        addon_dir = os.path.join(CACHE_DIR, addon.id)
        if os.path.exists(addon_dir):
            shutil.rmtree(addon_dir)

        self.extract_zip(filepath)
        self.cached[addon.id] = addon
        addon.path = addon_dir
        return addon
Exemplo n.º 4
0
 def _find_addons(*paths):  # type: (str) -> Iterator[Tuple[str, Addon]]
     """
     Search givin paths for kodi addons.
     Returning a tuple consisting of addon id and Addon object.
     """
     for addons_dir in paths:
         if os.path.exists(addons_dir):
             for filename in os.listdir(addons_dir):
                 path = os.path.join(addons_dir, filename, "addon.xml")
                 if os.path.exists(path):
                     addon = Addon.from_file(path)
                     logger.debug("Addon: %s v%s",
                                  os.path.join(addons_dir, filename),
                                  addon.version)
                     yield addon.id, addon
Exemplo n.º 5
0
    def process(self):  # type: () -> mp.Process
        if self._process and self._process.is_alive():
            logger.debug("Reuseing subprocess: %s", self._process.name)
            return self._process
        else:
            # Create the new process that will execute the addon
            process = mp.Process(target=subprocess,
                                 args=[self.sub_pipe, self.reuse])
            process.start()

            logger.debug("Spawned new subprocess: %s", process.name)

            if self.reuse:
                # Save the process for later use
                self._process = process
            return process
Exemplo n.º 6
0
    def update(self):
        """Check if any cached addon need updating."""
        logger.debug("Checking for updates...")
        for addon in self.cached.values():
            if addon.id not in self:
                # We have a cached addon that no longer exists in the repo
                warnings.warn(
                    "Cached Addon '{}' no longer available on kodi repo".
                    format(addon.id))

            elif addon.version < self.db[addon.id][1].version:
                self.download(addon)

        # Create update-check file
        # with current timestamp
        timestamp = time.time()
        with open(self.check_file, "w", encoding="utf8") as stream:
            json.dump(timestamp, stream)
Exemplo n.º 7
0
    def __init__(self, cmdargs, cached, display=None):
        # type: (argparse.Namespace, LocalRepo, Type[displays.BaseDisplay]) -> None

        self.parent_stack = []  # type: List[KodiData]
        self.cached = cached
        self.args = cmdargs

        # Use custom display object if one is given, else
        # Use the pretty terminal display if possible, otherwise use the basic non tty display
        display = display if display else displays.CMDisplay
        self.display = display(cached, cmdargs)

        # The process manager
        self.pm = PManager(cached, self.display.input)

        # Reverse the list of preselection for faster access
        self.preselect = list(map(int, cmdargs.preselect))
        self.preselect.reverse()

        # Log the arguments pass to program
        logger.debug("Command-Line Arguments: %s", vars(cmdargs))