def _process_snapshot(self, snapshot_path, pixfmt="yuv420p"):
		hflip  = self._settings.global_get_boolean(["webcam", "flipH"])
		vflip  = self._settings.global_get_boolean(["webcam", "flipV"])
		rotate = self._settings.global_get_boolean(["webcam", "rotate90"])
		ffmpeg = self._settings.global_get(["webcam", "ffmpeg"])
		
		if not ffmpeg or not os.access(ffmpeg, os.X_OK) or (not vflip and not hflip and not rotate):
			return

		ffmpeg_command = [ffmpeg, "-y", "-i", snapshot_path]

		rotate_params = ["format={}".format(pixfmt)] # workaround for foosel/OctoPrint#1317
		if rotate:
			rotate_params.append("transpose=2") # 90 degrees counter clockwise
		if hflip:
			rotate_params.append("hflip") 		# horizontal flip
		if vflip:
			rotate_params.append("vflip")		# vertical flip

		ffmpeg_command += ["-vf", sarge.shell_quote(",".join(rotate_params)), snapshot_path]
		self._logger.info("Running: {}".format(" ".join(ffmpeg_command)))

		p = sarge.run(ffmpeg_command, stdout=sarge.Capture(), stderr=sarge.Capture())
		if p.returncode == 0:
			self._logger.info("Rotated/flipped image with ffmpeg")
		else:
			self._logger.warn("Failed to rotate/flip image with ffmpeg, "
			                  "got return code {}: {}, {}".format(p.returncode,
			                                                      p.stdout.text,
			                                                      p.stderr.text))
Beispiel #2
0
    def _render(self, template_filename):
        render_config = self.render_config(template_filename)
        if self.badge_instance:
            url = reverse(
                "main:instance-filename",
                kwargs={
                    "slug": self.badge_template.slug,
                    "pk": self.badge_instance.pk,
                    "filename": template_filename,
                },
            )
        else:
            url = reverse(
                "main:preview-filename",
                kwargs={
                    "slug": self.badge_template.slug,
                    "filename": template_filename,
                },
            )

        file_format = render_config.get("format", "png")
        if file_format == "pdf":
            f = tempfile.NamedTemporaryFile(delete=False)
            tmppdf = f.name
            f.close()

            cmd = (
                "node {chrome_pdf_bin} "
                "--paper-width 8.27 --paper-height 11.69"
                "--no-margins --landscape --include-background --url {url} --pdf {tmppdf}"
                .format(
                    chrome_pdf_bin=settings.CHROME_PDF_BIN,
                    tmppdf=tmppdf,
                    url="{}{}".format(settings.RENDER_PREFIX_URL, url),
                ))
            sarge.run(cmd, stdout=sarge.Capture())

            f = open(tmppdf, "rb")
            image = f.read()
            f.close()
            os.unlink(f.name)

        else:
            cmd = ("{capture_bin} {url} --width={width} --height={height} "
                   "--type={type} --element={element} --no-default-background".
                   format(
                       width=render_config.get("screen_width", 1000),
                       height=render_config.get("screen_height", 1000),
                       type=file_format,
                       element=sarge.shell_quote(
                           render_config.get("element", ".screenshot")),
                       url="{}{}".format(settings.RENDER_PREFIX_URL, url),
                       capture_bin=settings.CAPTURE_BIN,
                   ))
            p = sarge.run(cmd, stdout=sarge.Capture())

            image = p.stdout.bytes

        mime = magic.from_buffer(image, mime=True)
        return image, mime
Beispiel #3
0
 def test_quote(self):
     self.assertEqual(shell_quote(""), "''")
     self.assertEqual(shell_quote("a"), "a")
     self.assertEqual(shell_quote("*"), "'*'")
     self.assertEqual(shell_quote("foo"), "foo")
     self.assertEqual(shell_quote("'*.py'"), "''\\''*.py'\\'''")
     self.assertEqual(shell_quote("'a'; rm -f b; true 'c'"), "''\\''a'\\''; rm -f b; true '\\''c'\\'''")
     self.assertEqual(shell_quote("*.py"), "'*.py'")
     self.assertEqual(shell_quote("'*.py"), "''\\''*.py'")
Beispiel #4
0
    def _create_ffmpeg_command_string(cls,
                                      ffmpeg,
                                      fps,
                                      bitrate,
                                      threads,
                                      input,
                                      output,
                                      hflip=False,
                                      vflip=False,
                                      rotate=False,
                                      watermark=None,
                                      pixfmt="yuv420p"):
        """
		Create ffmpeg command string based on input parameters.

		Arguments:
		    ffmpeg (str): Path to ffmpeg
		    fps (int): Frames per second for output
		    bitrate (str): Bitrate of output
		    threads (int): Number of threads to use for rendering
		    input (str): Absolute path to input files including file mask
		    output (str): Absolute path to output file
		    hflip (bool): Perform horizontal flip on input material.
		    vflip (bool): Perform vertical flip on input material.
		    rotate (bool): Perform 90° CCW rotation on input material.
		    watermark (str): Path to watermark to apply to lower left corner.
		    pixfmt (str): Pixel format to use for output. Default of yuv420p should usually fit the bill.

		Returns:
		    (str): Prepared command string to render `input` to `output` using ffmpeg.
		"""

        ### See unit tests in test/timelapse/test_timelapse_renderjob.py

        logger = logging.getLogger(__name__)

        command = [
            ffmpeg, '-framerate',
            str(fps), '-loglevel', 'error', '-i', '"{}"'.format(input),
            '-vcodec', 'mpeg2video', '-threads',
            str(threads), '-r', "25", '-y', '-b',
            str(bitrate), '-f', 'vob'
        ]

        filter_string = cls._create_filter_string(hflip=hflip,
                                                  vflip=vflip,
                                                  rotate=rotate,
                                                  watermark=watermark)

        if filter_string is not None:
            logger.debug(
                "Applying videofilter chain: {}".format(filter_string))
            command.extend(["-vf", sarge.shell_quote(filter_string)])

        # finalize command with output file
        logger.debug("Rendering movie to {}".format(output))
        command.append('"{}"'.format(output))

        return " ".join(command)
Beispiel #5
0
 def test_quote(self):
     self.assertEqual(shell_quote(''), "''")
     self.assertEqual(shell_quote('a'), 'a')
     self.assertEqual(shell_quote('*'), "'*'")
     self.assertEqual(shell_quote('foo'), 'foo')
     self.assertEqual(shell_quote("'*.py'"), "''\\''*.py'\\'''")
     self.assertEqual(shell_quote("'a'; rm -f b; true 'c'"),
                      "''\\''a'\\''; rm -f b; true '\\''c'\\'''")
     self.assertEqual(shell_quote("*.py"), "'*.py'")
     self.assertEqual(shell_quote("'*.py"), "''\\''*.py'")
Beispiel #6
0
 def test_quote(self):
     self.assertEqual(shell_quote(''), "''")
     self.assertEqual(shell_quote('a'), 'a')
     self.assertEqual(shell_quote('*'), "'*'")
     self.assertEqual(shell_quote('foo'), 'foo')
     self.assertEqual(shell_quote("'*.py'"), "''\\''*.py'\\'''")
     self.assertEqual(shell_quote("'a'; rm -f b; true 'c'"),
                                  "''\\''a'\\''; rm -f b; true '\\''c'\\'''")
     self.assertEqual(shell_quote("*.py"), "'*.py'")
     self.assertEqual(shell_quote("'*.py"), "''\\''*.py'")
Beispiel #7
0
def shell_quote(s):
    if platform.system() == "Windows":
        assert isinstance(s, str)
        if not s:
            result = '""'
        elif '"' not in s:
            result = s
        else:
            escaped = s.replace('"', r"\"")
            result = f'"{escaped}"'
        return result
    else:
        return sarge.shell_quote(s)
Beispiel #8
0
    def _install_plugin(cls,
                        plugin,
                        force_user=False,
                        pip_command=None,
                        pip_args=None,
                        on_log=None):
        if pip_args is None:
            pip_args = []

        if on_log is None:
            on_log = logging.getLogger(__name__).info

        # prepare pip caller
        def log(prefix, *lines):
            for line in lines:
                on_log("{} {}".format(prefix, line.rstrip()))

        def log_call(*lines):
            log(">", *lines)

        def log_stdout(*lines):
            log("<", *lines)

        def log_stderr(*lines):
            log("!", *lines)

        if cls._pip_caller is None:
            cls._pip_caller = create_pip_caller(command=pip_command,
                                                force_user=force_user)

        cls._pip_caller.on_log_call = log_call
        cls._pip_caller.on_log_stdout = log_stdout
        cls._pip_caller.on_log_stderr = log_stderr

        # install plugin
        pip = [
            "install",
            sarge.shell_quote(plugin["archive"]), "--no-cache-dir"
        ]

        if plugin.get("follow_dependency_links"):
            pip.append("--process-dependency-links")

        if force_user:
            pip.append("--user")

        if pip_args:
            pip += pip_args

        cls._pip_caller.execute(*pip)
Beispiel #9
0
	def _create_ffmpeg_command_string(cls, ffmpeg, fps, bitrate, threads, input, output, hflip=False, vflip=False,
	                                  rotate=False, watermark=None):
		"""
		Create ffmpeg command string based on input parameters.

		Examples:

		    >>> TimelapseRenderJob._create_ffmpeg_command_string("/path/to/ffmpeg", 25, "10000k", 1, "/path/to/input/files_%d.jpg", "/path/to/output.mpg")
		    '/path/to/ffmpeg -framerate 25 -loglevel error -i "/path/to/input/files_%d.jpg" -vcodec mpeg2video -threads 1 -pix_fmt yuv420p -r 25 -y -b 10000k -f vob "/path/to/output.mpg"'
		    >>> TimelapseRenderJob._create_ffmpeg_command_string("/path/to/ffmpeg", 25, "10000k", 1, "/path/to/input/files_%d.jpg", "/path/to/output.mpg", hflip=True)
		    '/path/to/ffmpeg -framerate 25 -loglevel error -i "/path/to/input/files_%d.jpg" -vcodec mpeg2video -threads 1 -pix_fmt yuv420p -r 25 -y -b 10000k -f vob -vf \\'[in] hflip [out]\\' "/path/to/output.mpg"'

		Arguments:
		    ffmpeg (str): Path to ffmpeg
		    fps (int): Frames per second for output
		    bitrate (str): Bitrate of output
		    threads (int): Number of threads to use for rendering
		    input (str): Absolute path to input files including file mask
		    output (str): Absolute path to output file
		    hflip (bool): Perform horizontal flip on input material.
		    vflip (bool): Perform vertical flip on input material.
		    rotate (bool): Perform 90° CCW rotation on input material.
		    watermark (str): Path to watermark to apply to lower left corner.

		Returns:
		    (str): Prepared command string to render `input` to `output` using ffmpeg.
		"""

		logger = logging.getLogger(__name__)

		command = [
			ffmpeg, '-framerate', str(fps), '-loglevel', 'error', '-i', '"{}"'.format(input), '-vcodec', 'mpeg2video',
			'-threads', str(threads), '-pix_fmt', 'yuv420p', '-r', str(fps), '-y', '-b', str(bitrate),
			'-f', 'vob']

		filter_string = cls._create_filter_string(hflip=hflip,
		                                          vflip=vflip,
		                                          rotate=rotate,
		                                          watermark=watermark)

		if filter_string is not None:
			logger.debug("Applying videofilter chain: {}".format(filter_string))
			command.extend(["-vf", sarge.shell_quote(filter_string)])

		# finalize command with output file
		logger.debug("Rendering movie to {}".format(output))
		command.append('"{}"'.format(output))

		return " ".join(command)
Beispiel #10
0
	def _create_ffmpeg_command_string(cls, ffmpeg, fps, bitrate, threads, input, output, hflip=False, vflip=False,
	                                  rotate=False, watermark=None):
		"""
		Create ffmpeg command string based on input parameters.

		Examples:

		    >>> TimelapseRenderJob._create_ffmpeg_command_string("/path/to/ffmpeg", 25, "10000k", 1, "/path/to/input/files_%d.jpg", "/path/to/output.mpg")
		    '/path/to/ffmpeg -framerate 25 -loglevel error -i "/path/to/input/files_%d.jpg" -vcodec mpeg2video -threads 1 -pix_fmt yuv420p -r 25 -y -b 10000k -f vob "/path/to/output.mpg"'
		    >>> TimelapseRenderJob._create_ffmpeg_command_string("/path/to/ffmpeg", 25, "10000k", 1, "/path/to/input/files_%d.jpg", "/path/to/output.mpg", hflip=True)
		    '/path/to/ffmpeg -framerate 25 -loglevel error -i "/path/to/input/files_%d.jpg" -vcodec mpeg2video -threads 1 -pix_fmt yuv420p -r 25 -y -b 10000k -f vob -vf \\'[in] hflip [out]\\' "/path/to/output.mpg"'

		Arguments:
		    ffmpeg (str): Path to ffmpeg
		    fps (int): Frames per second for output
		    bitrate (str): Bitrate of output
		    threads (int): Number of threads to use for rendering
		    input (str): Absolute path to input files including file mask
		    output (str): Absolute path to output file
		    hflip (bool): Perform horizontal flip on input material.
		    vflip (bool): Perform vertical flip on input material.
		    rotate (bool): Perform 90° CCW rotation on input material.
		    watermark (str): Path to watermark to apply to lower left corner.

		Returns:
		    (str): Prepared command string to render `input` to `output` using ffmpeg.
		"""

		logger = logging.getLogger(__name__)

		command = [
			ffmpeg, '-framerate', str(fps), '-loglevel', 'error', '-i', '"{}"'.format(input), '-vcodec', 'mpeg2video',
			'-threads', str(threads), '-pix_fmt', 'yuv420p', '-r', str(fps), '-y', '-b', str(bitrate),
			'-f', 'vob']

		filter_string = cls._create_filter_string(hflip=hflip,
		                                          vflip=vflip,
		                                          rotate=rotate,
		                                          watermark=watermark)

		if filter_string is not None:
			logger.debug("Applying videofilter chain: {}".format(filter_string))
			command.extend(["-vf", sarge.shell_quote(filter_string)])

		# finalize command with output file
		logger.debug("Rendering movie to {}".format(output))
		command.append('"{}"'.format(output))

		return " ".join(command)
Beispiel #11
0
    def test_quote_with_shell(self):
        from subprocess import PIPE, Popen

        if os.name != 'posix':
            raise unittest.SkipTest('This test works only on POSIX')

        workdir = tempfile.mkdtemp()
        try:
            s = "'\\\"; touch %s/foo #'" % workdir
            cmd = 'echo %s' % shell_quote(s)
            p = Popen(cmd, shell=True, stdout=PIPE, stderr=PIPE)
            p.communicate()
            self.assertEqual(p.returncode, 0)
            files = os.listdir(workdir)
            self.assertEqual(files, [])
            fn = "'ab?'"
            cmd = 'touch %s/%s' % (workdir, shell_quote(fn))
            p = Popen(cmd, shell=True, stdout=PIPE, stderr=PIPE)
            p.communicate()
            self.assertEqual(p.returncode, 0)
            files = os.listdir(workdir)
            self.assertEqual(files, ["'ab?'"])
        finally:
            shutil.rmtree(workdir)
Beispiel #12
0
    def test_quote_with_shell(self):
        from subprocess import PIPE, Popen

        if os.name != 'posix':  #pragma: no cover
            raise unittest.SkipTest('This test works only on POSIX')

        workdir = tempfile.mkdtemp()
        try:
            s = "'\\\"; touch %s/foo #'" % workdir
            cmd = 'echo %s' % shell_quote(s)
            p = Popen(cmd, shell=True, stdout=PIPE, stderr=PIPE)
            p.communicate()
            self.assertEqual(p.returncode, 0)
            files = os.listdir(workdir)
            self.assertEqual(files, [])
            fn = "'ab?'"
            cmd = 'touch %s/%s' % (workdir, shell_quote(fn))
            p = Popen(cmd, shell=True, stdout=PIPE, stderr=PIPE)
            p.communicate()
            self.assertEqual(p.returncode, 0)
            files = os.listdir(workdir)
            self.assertEqual(files, ["'ab?'"])
        finally:
            shutil.rmtree(workdir)
Beispiel #13
0
	def _create_ffmpeg_command_string(cls, ffmpeg, fps, bitrate, threads, input, output, hflip=False, vflip=False,
	                                  rotate=False, watermark=None, pixfmt="yuv420p"):
		"""
		Create ffmpeg command string based on input parameters.

		Arguments:
		    ffmpeg (str): Path to ffmpeg
		    fps (int): Frames per second for output
		    bitrate (str): Bitrate of output
		    threads (int): Number of threads to use for rendering
		    input (str): Absolute path to input files including file mask
		    output (str): Absolute path to output file
		    hflip (bool): Perform horizontal flip on input material.
		    vflip (bool): Perform vertical flip on input material.
		    rotate (bool): Perform 90° CCW rotation on input material.
		    watermark (str): Path to watermark to apply to lower left corner.
		    pixfmt (str): Pixel format to use for output. Default of yuv420p should usually fit the bill.

		Returns:
		    (str): Prepared command string to render `input` to `output` using ffmpeg.
		"""

		### See unit tests in test/timelapse/test_timelapse_renderjob.py

		logger = logging.getLogger(__name__)

		command = [
			ffmpeg, '-framerate', str(fps), '-loglevel', 'error', '-i', '"{}"'.format(input), '-vcodec', 'mpeg2video',
			'-threads', str(threads), '-r', "25", '-y', '-b', str(bitrate),
			'-f', 'vob']

		filter_string = cls._create_filter_string(hflip=hflip,
		                                          vflip=vflip,
		                                          rotate=rotate,
		                                          watermark=watermark)

		if filter_string is not None:
			logger.debug("Applying videofilter chain: {}".format(filter_string))
			command.extend(["-vf", sarge.shell_quote(filter_string)])

		# finalize command with output file
		logger.debug("Rendering movie to {}".format(output))
		command.append('"{}"'.format(output))

		return " ".join(command)
Beispiel #14
0
    def _create_ffmpeg_command_string(self,
                                      input_file_format,
                                      output_file,
                                      watermark=None,
                                      pix_fmt="yuv420p"):
        """
        Create ffmpeg command string based on input parameters.
        Arguments:
            input_file_format (str): Absolute path to input files including file mask
            output_file (str): Absolute path to output file
            watermark (str): Path to watermark to apply to lower left corner.
            pix_fmt (str): Pixel format to use for output. Default of yuv420p should usually fit the bill.
        Returns:
            (str): Prepared command string to render `input` to `output` using ffmpeg.
        """

        logger = logging.getLogger(__name__)

        v_codec = self._get_vcodec_from_output_format(
            self._rendering.output_format)

        command = [
            self._ffmpeg, '-framerate',
            str(self._fps), '-loglevel', 'error', '-i',
            '"{}"'.format(input_file_format)
        ]
        command.extend([
            '-threads',
            str(self._threads), '-r', "25", '-y', '-b',
            str(self._rendering.bitrate), '-vcodec', v_codec
        ])

        filter_string = self._create_filter_string(watermark=watermark,
                                                   pix_fmt=pix_fmt)

        if filter_string is not None:
            logger.debug(
                "Applying video filter chain: {}".format(filter_string))
            command.extend(["-vf", sarge.shell_quote(filter_string)])

        # finalize command with output file
        logger.debug("Rendering movie to {}".format(output_file))
        command.append('"{}"'.format(output_file))

        return " ".join(command)
Beispiel #15
0
def sfdx(
    command,
    username=None,
    log_note=None,
    access_token=None,
    args=None,
    env=None,
    capture_output=True,
    check_return=False,
):
    """Call an sfdx command and capture its output.

    Be sure to quote user input that is part of the command using `sarge.shell_format`.

    Returns a `sarge` Command instance with returncode, stdout, stderr
    """
    command = "sfdx {}".format(command)
    if args is not None:
        for arg in args:
            command += " " + sarge.shell_quote(arg)
    if username:
        command += sarge.shell_format(" -u {0}", username)
    if log_note:
        logger.info("{} with command: {}".format(log_note, command))
    # Avoid logging access token
    if access_token:
        command += sarge.shell_format(" -u {0}", access_token)
    p = sarge.Command(
        command,
        stdout=sarge.Capture(buffer_size=-1) if capture_output else None,
        stderr=sarge.Capture(buffer_size=-1) if capture_output else None,
        shell=True,
        env=env,
    )
    p.run()
    if capture_output:
        p.stdout_text = io.TextIOWrapper(p.stdout,
                                         encoding=sys.stdout.encoding)
        p.stderr_text = io.TextIOWrapper(p.stderr,
                                         encoding=sys.stdout.encoding)
    if check_return and p.returncode:
        raise Exception(f"Command exited with return code {p.returncode}")
    return p
Beispiel #16
0
	def _install_plugin(cls, plugin, force_user=False, pip_args=None, on_log=None):
		if pip_args is None:
			pip_args = []

		if on_log is None:
			on_log = logging.getLogger(__name__).info

		# prepare pip caller
		def log(prefix, *lines):
			for line in lines:
				on_log(u"{} {}".format(prefix, line.rstrip()))

		def log_call(*lines):
			log(u">", *lines)

		def log_stdout(*lines):
			log(u"<", *lines)

		def log_stderr(*lines):
			log(u"!", *lines)

		if cls._pip_caller is None:
			cls._pip_caller = LocalPipCaller(force_user=force_user)

		cls._pip_caller.on_log_call = log_call
		cls._pip_caller.on_log_stdout = log_stdout
		cls._pip_caller.on_log_stderr = log_stderr

		# install plugin
		pip = ["install", sarge.shell_quote(plugin["archive"]), '--no-cache-dir']

		if plugin.get("follow_dependency_links"):
			pip.append("--process-dependency-links")

		if force_user:
			pip.append("--user")

		if pip_args:
			pip += pip_args

		cls._pip_caller.execute(*pip)
	def command_install(self, url, force=False):
		# TODO need to solve issue of users externally modifying plugin folder, which could lead to more than
		# one plugin being found after installation of a package
		all_plugins_before = self._plugin_manager.plugins

		pip_args = ["install", sarge.shell_quote(url)]
		try:
			self._call_pip(pip_args)
		except:
			self._logger.exception("Could not install plugin from %s" % url)
			return make_response("Could not install plugin from url, see the log for more details", 500)
		else:
			if force:
				pip_args += ["--ignore-installed", "--force-reinstall", "--no-deps"]
				try:
					self._call_pip(pip_args)
				except:
					self._logger.exception("Could not install plugin from %s" % url)
					return make_response("Could not install plugin from url, see the log for more details", 500)

		self._plugin_manager.reload_plugins()
		all_plugins_after = self._plugin_manager.plugins

		new_plugins = set(all_plugins_after.keys()) - set(all_plugins_before.keys())
		if len(new_plugins) == 0 or len(new_plugins) > 1:
			# no new plugin or more than one new plugin? Something must have gone wrong...
			return make_response("Installed plugin, but could not find it afterwards", 500)

		new_plugin_key = new_plugins.pop()
		new_plugin = all_plugins_after[new_plugin_key]

		needs_restart = new_plugin.implementation and isinstance(new_plugin.implementation, octoprint.plugin.core.RestartNeedingPlugin)
		needs_refresh = new_plugin.implementation and isinstance(new_plugin.implementation, octoprint.plugin.ReloadNeedingPlugin)

		self._plugin_manager.log_all_plugins()

		result = dict(result=True, url=url, needs_restart=needs_restart, needs_refresh=needs_refresh, plugin=self._to_external_representation(new_plugin))
		self._send_result_notification("install", result)
		return jsonify(result)
Beispiel #18
0
	def command_install(self, url=None, path=None, force=False, reinstall=None, dependency_links=False):
		if url is not None:
			pip_args = ["install", sarge.shell_quote(url)]
		elif path is not None:
			pip_args = ["install", sarge.shell_quote(path)]
		else:
			raise ValueError("Either URL or path must be provided")

		if dependency_links or self._settings.get_boolean(["dependency_links"]):
			pip_args.append("--process-dependency-links")

		all_plugins_before = self._plugin_manager.find_plugins()

		success_string = "Successfully installed"
		failure_string = "Could not install"
		try:
			returncode, stdout, stderr = self._call_pip(pip_args)
		except:
			self._logger.exception("Could not install plugin from %s" % url)
			return make_response("Could not install plugin from URL, see the log for more details", 500)
		else:
			if force:
				pip_args += ["--ignore-installed", "--force-reinstall", "--no-deps"]
				try:
					returncode, stdout, stderr = self._call_pip(pip_args)
				except:
					self._logger.exception("Could not install plugin from %s" % url)
					return make_response("Could not install plugin from URL, see the log for more details", 500)

		try:
			result_line = filter(lambda x: x.startswith(success_string) or x.startswith(failure_string), stdout)[-1]
		except IndexError:
			result = dict(result=False, reason="Could not parse output from pip")
			self._send_result_notification("install", result)
			return jsonify(result)

		# The final output of a pip install command looks something like this:
		#
		#   Successfully installed OctoPrint-Plugin-1.0 Dependency-One-0.1 Dependency-Two-9.3
		#
		# or this:
		#
		#   Successfully installed OctoPrint-Plugin Dependency-One Dependency-Two
		#   Cleaning up...
		#
		# So we'll need to fetch the "Successfully installed" line, strip the "Successfully" part, then split by whitespace
		# and strip to get all installed packages.
		#
		# We then need to iterate over all known plugins and see if either the package name or the package name plus
		# version number matches one of our installed packages. If it does, that's our installed plugin.
		#
		# Known issue: This might return the wrong plugin if more than one plugin was installed through this
		# command (e.g. due to pulling in another plugin as dependency). It should be safe for now though to
		# consider this a rare corner case. Once it becomes a real problem we'll just extend the plugin manager
		# so that it can report on more than one installed plugin.

		result_line = result_line.strip()
		if not result_line.startswith(success_string):
			result = dict(result=False, reason="Pip did not report successful installation")
			self._send_result_notification("install", result)
			return jsonify(result)

		installed = [x.strip() for x in result_line[len(success_string):].split(" ")]
		all_plugins_after = self._plugin_manager.find_plugins(existing=dict(), ignore_uninstalled=False)

		for key, plugin in list(all_plugins_after.items()):
			if plugin.origin is None or plugin.origin.type != "entry_point":
				continue

			package_name = plugin.origin.package_name
			package_version = plugin.origin.package_version
			versioned_package = "{package_name}-{package_version}".format(**locals())

			if package_name in installed or versioned_package in installed:
				# exact match, we are done here
				new_plugin_key = key
				new_plugin = plugin
				break

			else:
				# it might still be a version that got stripped by python's package resources, e.g. 1.4.5a0 => 1.4.5a
				found = False

				for inst in installed:
					if inst.startswith(versioned_package):
						found = True
						break

				if found:
					new_plugin_key = key
					new_plugin = plugin
					break
		else:
			self._logger.warn("The plugin was installed successfully, but couldn't be found afterwards to initialize properly during runtime. Please restart OctoPrint.")
			result = dict(result=True, url=url, needs_restart=True, needs_refresh=True, was_reinstalled=False, plugin="unknown")
			self._send_result_notification("install", result)
			return jsonify(result)

		self._plugin_manager.reload_plugins()
		needs_restart = self._plugin_manager.is_restart_needing_plugin(new_plugin) or new_plugin_key in all_plugins_before or reinstall is not None
		needs_refresh = new_plugin.implementation and isinstance(new_plugin.implementation, octoprint.plugin.ReloadNeedingPlugin)

		is_reinstall = self._plugin_manager.is_plugin_marked(new_plugin_key, "uninstalled")
		self._plugin_manager.mark_plugin(new_plugin_key,
		                                 uninstalled=False,
		                                 installed=not is_reinstall and needs_restart)

		self._plugin_manager.log_all_plugins()

		result = dict(result=True, url=url, needs_restart=needs_restart, needs_refresh=needs_refresh, was_reinstalled=new_plugin_key in all_plugins_before or reinstall is not None, plugin=self._to_external_representation(new_plugin))
		self._send_result_notification("install", result)
		return jsonify(result)
Beispiel #19
0
    def _create_ffmpeg_command_string(cls,
                                      ffmpeg,
                                      fps,
                                      bitrate,
                                      threads,
                                      input_file,
                                      output_file,
                                      output_format='vob',
                                      h_flip=False,
                                      v_flip=False,
                                      rotate=False,
                                      watermark=None,
                                      pix_fmt="yuv420p",
                                      v_codec="mpeg2video"):
        """
        Create ffmpeg command string based on input parameters.
        Arguments:
            ffmpeg (str): Path to ffmpeg
            fps (int): Frames per second for output
            bitrate (str): Bitrate of output
            threads (int): Number of threads to use for rendering
            input_file (str): Absolute path to input files including file mask
            output_file (str): Absolute path to output file
            h_flip (bool): Perform horizontal flip on input material.
            v_flip (bool): Perform vertical flip on input material.
            rotate (bool): Perform 90° CCW rotation on input material.
            watermark (str): Path to watermark to apply to lower left corner.
            pix_fmt (str): Pixel format to use for output. Default of yuv420p should usually fit the bill.
        Returns:
            (str): Prepared command string to render `input` to `output` using ffmpeg.
        """

        # See unit tests in test/timelapse/test_timelapse_renderjob.py

        logger = logging.getLogger(__name__)
        ffmpeg = ffmpeg.strip()

        if sys.platform == "win32" and not (ffmpeg.startswith('"')
                                            and ffmpeg.endswith('"')):
            ffmpeg = "\"{0}\"".format(ffmpeg)
        command = [
            ffmpeg, '-framerate',
            str(fps), '-loglevel', 'error', '-i', '"{}"'.format(input_file)
        ]
        # Umm yea, something about codecs and GIFS.
        # See https://stackoverflow.com/a/47502141.
        if output_format != 'GIF':
            command.extend(['-vcodec', v_codec])
        command.extend([
            '-threads',
            str(threads), '-r', "25", '-y', '-b',
            str(bitrate), '-f',
            str(output_format)
        ])

        filter_string = cls._create_filter_string(hflip=h_flip,
                                                  vflip=v_flip,
                                                  rotate=rotate,
                                                  watermark=watermark,
                                                  pix_fmt=pix_fmt)

        if filter_string is not None:
            logger.debug(
                "Applying video filter chain: {}".format(filter_string))
            command.extend(["-vf", sarge.shell_quote(filter_string)])

        # finalize command with output file
        logger.debug("Rendering movie to {}".format(output_file))
        command.append('"{}"'.format(output_file))

        return " ".join(command)
Beispiel #20
0
	def command_install(self, url=None, path=None, force=False, reinstall=None, dependency_links=False):
		if url is not None:
			pip_args = ["install", sarge.shell_quote(url)]
		elif path is not None:
			pip_args = ["install", path]
		else:
			raise ValueError("Either url or path must be provided")

		if dependency_links or self._settings.get_boolean(["dependency_links"]):
			pip_args.append("--process-dependency-links")

		all_plugins_before = self._plugin_manager.find_plugins()

		success_string = "Successfully installed"
		failure_string = "Could not install"
		try:
			returncode, stdout, stderr = self._call_pip(pip_args)
		except:
			self._logger.exception("Could not install plugin from %s" % url)
			return make_response("Could not install plugin from url, see the log for more details", 500)
		else:
			if force:
				pip_args += ["--ignore-installed", "--force-reinstall", "--no-deps"]
				try:
					returncode, stdout, stderr = self._call_pip(pip_args)
				except:
					self._logger.exception("Could not install plugin from %s" % url)
					return make_response("Could not install plugin from url, see the log for more details", 500)

		try:
			result_line = filter(lambda x: x.startswith(success_string) or x.startswith(failure_string), stdout)[-1]
		except IndexError:
			result = dict(result=False, reason="Could not parse output from pip")
			self._send_result_notification("install", result)
			return jsonify(result)

		# The final output of a pip install command looks something like this:
		#
		#   Successfully installed OctoPrint-Plugin-1.0 Dependency-One-0.1 Dependency-Two-9.3
		#
		# or this:
		#
		#   Successfully installed OctoPrint-Plugin Dependency-One Dependency-Two
		#   Cleaning up...
		#
		# So we'll need to fetch the "Successfully installed" line, strip the "Successfully" part, then split by whitespace
		# and strip to get all installed packages.
		#
		# We then need to iterate over all known plugins and see if either the package name or the package name plus
		# version number matches one of our installed packages. If it does, that's our installed plugin.
		#
		# Known issue: This might return the wrong plugin if more than one plugin was installed through this
		# command (e.g. due to pulling in another plugin as dependency). It should be safe for now though to
		# consider this a rare corner case. Once it becomes a real problem we'll just extend the plugin manager
		# so that it can report on more than one installed plugin.

		result_line = result_line.strip()
		if not result_line.startswith(success_string):
			result = dict(result=False, reason="Pip did not report successful installation")
			self._send_result_notification("install", result)
			return jsonify(result)

		installed = map(lambda x: x.strip(), result_line[len(success_string):].split(" "))
		all_plugins_after = self._plugin_manager.find_plugins(existing=dict(), ignore_uninstalled=False)

		for key, plugin in all_plugins_after.items():
			if plugin.origin is None or plugin.origin.type != "entry_point":
				continue

			package_name = plugin.origin.package_name
			package_version = plugin.origin.package_version
			versioned_package = "{package_name}-{package_version}".format(**locals())

			if package_name in installed or versioned_package in installed:
				# exact match, we are done here
				new_plugin_key = key
				new_plugin = plugin
				break

			else:
				# it might still be a version that got stripped by python's package resources, e.g. 1.4.5a0 => 1.4.5a
				found = False

				for inst in installed:
					if inst.startswith(versioned_package):
						found = True
						break

				if found:
					new_plugin_key = key
					new_plugin = plugin
					break
		else:
			self._logger.warn("The plugin was installed successfully, but couldn't be found afterwards to initialize properly during runtime. Please restart OctoPrint.")
			result = dict(result=True, url=url, needs_restart=True, needs_refresh=True, was_reinstalled=False, plugin="unknown")
			self._send_result_notification("install", result)
			return jsonify(result)

		self._plugin_manager.mark_plugin(new_plugin_key, uninstalled=False)
		self._plugin_manager.reload_plugins()

		needs_restart = self._plugin_manager.is_restart_needing_plugin(new_plugin) or new_plugin_key in all_plugins_before or reinstall is not None
		needs_refresh = new_plugin.implementation and isinstance(new_plugin.implementation, octoprint.plugin.ReloadNeedingPlugin)

		self._plugin_manager.log_all_plugins()

		result = dict(result=True, url=url, needs_restart=needs_restart, needs_refresh=needs_refresh, was_reinstalled=new_plugin_key in all_plugins_before or reinstall is not None, plugin=self._to_external_representation(new_plugin))
		self._send_result_notification("install", result)
		return jsonify(result)
Beispiel #21
0
	def _create_movie(self, success=True):
		ffmpeg = settings().get(["webcam", "ffmpeg"])
		bitrate = settings().get(["webcam", "bitrate"])
		if ffmpeg is None or bitrate is None:
			self._logger.warn("Cannot create movie, path to ffmpeg or desired bitrate is unset")
			return

		input = os.path.join(self._capture_dir, "tmp_%05d.jpg")
		if success:
			output = os.path.join(self._movie_dir, "%s_%s.mpg" % (os.path.splitext(self._gcode_file)[0], time.strftime("%Y%m%d%H%M%S")))
		else:
			output = os.path.join(self._movie_dir, "%s_%s-failed.mpg" % (os.path.splitext(self._gcode_file)[0], time.strftime("%Y%m%d%H%M%S")))

		# prepare ffmpeg command
		command = [
			ffmpeg, '-framerate', str(self._fps), '-loglevel', 'error', '-i', input, '-vcodec', 'mpeg2video', '-threads', str(self._ffmpeg_threads), '-pix_fmt', 'yuv420p', '-r', str(self._fps), '-y', '-b', bitrate,
			'-f', 'vob']

		filters = []

		# flip video if configured
		if settings().getBoolean(["webcam", "flipH"]):
			filters.append('hflip')
		if settings().getBoolean(["webcam", "flipV"]):
			filters.append('vflip')
		if settings().getBoolean(["webcam", "rotate90"]):
			filters.append('transpose=2')

		# add watermark if configured
		watermarkFilter = None
		if settings().getBoolean(["webcam", "watermark"]):
			watermark = os.path.join(os.path.dirname(__file__), "static", "img", "watermark.png")
			if sys.platform == "win32":
				# Because ffmpeg hiccups on windows' drive letters and backslashes we have to give the watermark
				# path a special treatment. Yeah, I couldn't believe it either...
				watermark = watermark.replace("\\", "/").replace(":", "\\\\:")

			watermarkFilter = "movie=%s [wm]; [%%(inputName)s][wm] overlay=10:main_h-overlay_h-10" % watermark

		filterstring = None
		if len(filters) > 0:
			if watermarkFilter is not None:
				filterstring = "[in] %s [postprocessed]; %s [out]" % (",".join(filters), watermarkFilter % {"inputName": "postprocessed"})
			else:
				filterstring = "[in] %s [out]" % ",".join(filters)
		elif watermarkFilter is not None:
			filterstring = watermarkFilter % {"inputName": "in"} + " [out]"

		if filterstring is not None:
			self._logger.debug("Applying videofilter chain: %s" % filterstring)
			command.extend(["-vf", sarge.shell_quote(filterstring)])

		# finalize command with output file
		self._logger.debug("Rendering movie to %s" % output)
		command.append("\"" + output + "\"")
		eventManager().fire(Events.MOVIE_RENDERING, {"gcode": self._gcode_file, "movie": output, "movie_basename": os.path.basename(output)})

		command_str = " ".join(command)
		self._logger.debug("Executing command: %s" % command_str)

		try:
			p = sarge.run(command_str, stderr=sarge.Capture())
			if p.returncode == 0:
				eventManager().fire(Events.MOVIE_DONE, {"gcode": self._gcode_file, "movie": output, "movie_basename": os.path.basename(output)})
			else:
				returncode = p.returncode
				stderr_text = p.stderr.text
				self._logger.warn("Could not render movie, got return code %r: %s" % (returncode, stderr_text))
				eventManager().fire(Events.MOVIE_FAILED, {"gcode": self._gcode_file, "movie": output, "movie_basename": os.path.basename(output), "returncode": returncode, "error": stderr_text})
		except:
			self._logger.exception("Could not render movie due to unknown error")
			eventManager().fire(Events.MOVIE_FAILED, {"gcode": self._gcode_file, "movie": output, "movie_basename": os.path.basename(output), "returncode": 255, "error": "Unknown error"})
Beispiel #22
0
    def _create_ffmpeg_command_string(
        cls,
        commandline,
        ffmpeg,
        fps,
        bitrate,
        threads,
        input,
        output,
        videocodec,
        hflip=False,
        vflip=False,
        rotate=False,
        watermark=None,
        pixfmt="yuv420p",
    ):
        """
        Create ffmpeg command string based on input parameters.

        Arguments:
            commandline (str): Command line template to use
            ffmpeg (str): Path to ffmpeg
            fps (int): Frames per second for output
            bitrate (str): Bitrate of output
            threads (int): Number of threads to use for rendering
            videocodec (str): Videocodec to be used for encoding
            input (str): Absolute path to input files including file mask
            output (str): Absolute path to output file
            hflip (bool): Perform horizontal flip on input material.
            vflip (bool): Perform vertical flip on input material.
            rotate (bool): Perform 90° CCW rotation on input material.
            watermark (str): Path to watermark to apply to lower left corner.
            pixfmt (str): Pixel format to use for output. Default of yuv420p should usually fit the bill.

        Returns:
            (str): Prepared command string to render `input` to `output` using ffmpeg.
        """

        ### See unit tests in test/timelapse/test_timelapse_renderjob.py

        logger = logging.getLogger(__name__)

        ### Not all players can handle non-mpeg2 in VOB format
        if not videocodec:
            videocodec = "libx264"

        if videocodec == "mpeg2video":
            containerformat = "vob"
        else:
            containerformat = "mp4"

        filter_string = cls._create_filter_string(
            hflip=hflip, vflip=vflip, rotate=rotate, watermark=watermark
        )
        placeholders = {
            "ffmpeg": ffmpeg,
            "fps": str(fps if fps else "25"),
            "input": input,
            "output": output,
            "videocodec": videocodec,
            "threads": str(threads if threads else "1"),
            "bitrate": str(bitrate if bitrate else "10000k"),
            "containerformat": containerformat,
            "filters": ("-vf " + sarge.shell_quote(filter_string))
            if filter_string
            else "",
        }

        logger.debug("Rendering movie to {}".format(output))
        return commandline.format(**placeholders)
Beispiel #23
0
	def command_install(self, url=None, path=None, force=False, reinstall=None, dependency_links=False):
		if url is not None:
			source = url
			source_type = "url"
			already_installed_check = lambda line: url in line

		elif path is not None:
			path = os.path.abspath(path)
			path_url = "file://" + path
			if os.sep != "/":
				# windows gets special handling
				path = path.replace(os.sep, "/").lower()
				path_url = "file:///" + path

			source = path
			source_type = "path"
			already_installed_check = lambda line: path_url in line.lower() # lower case in case of windows

		else:
			raise ValueError("Either URL or path must be provided")

		self._logger.info("Installing plugin from {}".format(source))
		pip_args = ["install", sarge.shell_quote(source)]

		if dependency_links or self._settings.get_boolean(["dependency_links"]):
			pip_args.append("--process-dependency-links")

		all_plugins_before = self._plugin_manager.find_plugins(existing=dict())

		already_installed_string = "Requirement already satisfied (use --upgrade to upgrade)"
		success_string = "Successfully installed"
		failure_string = "Could not install"

		try:
			returncode, stdout, stderr = self._call_pip(pip_args)

			# pip's output for a package that is already installed looks something like any of these:
			#
			#   Requirement already satisfied (use --upgrade to upgrade): OctoPrint-Plugin==1.0 from \
			#     https://example.com/foobar.zip in <lib>
			#   Requirement already satisfied (use --upgrade to upgrade): OctoPrint-Plugin in <lib>
			#   Requirement already satisfied (use --upgrade to upgrade): OctoPrint-Plugin==1.0 from \
			#     file:///tmp/foobar.zip in <lib>
			#   Requirement already satisfied (use --upgrade to upgrade): OctoPrint-Plugin==1.0 from \
			#     file:///C:/Temp/foobar.zip in <lib>
			#
			# If we detect any of these matching what we just tried to install, we'll need to trigger a second
			# install with reinstall flags.

			if not force and any(map(lambda x: x.strip().startswith(already_installed_string) and already_installed_check(x),
			                         stdout)):
				self._logger.info("Plugin to be installed from {} was already installed, forcing a reinstall".format(source))
				self._log_message("Looks like the plugin was already installed. Forcing a reinstall.")
				force = True
		except:
			self._logger.exception("Could not install plugin from %s" % url)
			return make_response("Could not install plugin from URL, see the log for more details", 500)
		else:
			if force:
				# We don't use --upgrade here because that will also happily update all our dependencies - we'd rather
				# do that in a controlled manner
				pip_args += ["--ignore-installed", "--force-reinstall", "--no-deps"]
				try:
					returncode, stdout, stderr = self._call_pip(pip_args)
				except:
					self._logger.exception("Could not install plugin from {}".format(source))
					return make_response("Could not install plugin from source {}, see the log for more details"
					                     .format(source), 500)

		try:
			result_line = filter(lambda x: x.startswith(success_string) or x.startswith(failure_string),
			                     stdout)[-1]
		except IndexError:
			self._logger.error("Installing the plugin from {} failed, could not parse output from pip. "
			                   "See plugin_pluginmanager_console.log for generated output".format(source))
			result = dict(result=False,
			              source=source,
			              source_type=source_type,
			              reason="Could not parse output from pip, see plugin_pluginmanager_console.log "
			                     "for generated output")
			self._send_result_notification("install", result)
			return jsonify(result)

		# The final output of a pip install command looks something like this:
		#
		#   Successfully installed OctoPrint-Plugin-1.0 Dependency-One-0.1 Dependency-Two-9.3
		#
		# or this:
		#
		#   Successfully installed OctoPrint-Plugin Dependency-One Dependency-Two
		#   Cleaning up...
		#
		# So we'll need to fetch the "Successfully installed" line, strip the "Successfully" part, then split
		# by whitespace and strip to get all installed packages.
		#
		# We then need to iterate over all known plugins and see if either the package name or the package name plus
		# version number matches one of our installed packages. If it does, that's our installed plugin.
		#
		# Known issue: This might return the wrong plugin if more than one plugin was installed through this
		# command (e.g. due to pulling in another plugin as dependency). It should be safe for now though to
		# consider this a rare corner case. Once it becomes a real problem we'll just extend the plugin manager
		# so that it can report on more than one installed plugin.

		result_line = result_line.strip()
		if not result_line.startswith(success_string):
			self._logger.error("Installing the plugin from {} failed, pip did not report successful installation"
			                   .format(source))
			result = dict(result=False,
			              source=source,
			              source_type=source_type,
			              reason="Pip did not report successful installation")
			self._send_result_notification("install", result)
			return jsonify(result)

		installed = map(lambda x: x.strip(), result_line[len(success_string):].split(" "))
		all_plugins_after = self._plugin_manager.find_plugins(existing=dict(), ignore_uninstalled=False)

		new_plugin = self._find_installed_plugin(installed, plugins=all_plugins_after)
		if new_plugin is None:
			self._logger.warn("The plugin was installed successfully, but couldn't be found afterwards to "
			                  "initialize properly during runtime. Please restart OctoPrint.")
			result = dict(result=True,
			              source=source,
			              source_type=source_type,
			              needs_restart=True,
			              needs_refresh=True,
			              was_reinstalled=False,
			              plugin="unknown")
			self._send_result_notification("install", result)
			return jsonify(result)

		self._plugin_manager.reload_plugins()
		needs_restart = self._plugin_manager.is_restart_needing_plugin(new_plugin) \
		                or new_plugin.key in all_plugins_before \
		                or reinstall is not None
		needs_refresh = new_plugin.implementation \
		                and isinstance(new_plugin.implementation, octoprint.plugin.ReloadNeedingPlugin)

		is_reinstall = self._plugin_manager.is_plugin_marked(new_plugin.key, "uninstalled")
		self._plugin_manager.mark_plugin(new_plugin.key,
		                                 uninstalled=False,
		                                 installed=not is_reinstall and needs_restart)

		self._plugin_manager.log_all_plugins()

		self._logger.info("The plugin was installed successfully: {}, version {}".format(new_plugin.name, new_plugin.version))
		result = dict(result=True,
		              source=source,
		              source_type=source_type,
		              needs_restart=needs_restart,
		              needs_refresh=needs_refresh,
		              was_reinstalled=new_plugin.key in all_plugins_before or reinstall is not None,
		              plugin=self._to_external_plugin(new_plugin))
		self._send_result_notification("install", result)
		return jsonify(result)
Beispiel #24
0
	def command_install(self, url=None, path=None, force=False, reinstall=None, dependency_links=False):
		if url is not None:
			if not any(map(lambda scheme: url.startswith(scheme + "://"), self.URL_SCHEMES)):
				raise ValueError("Invalid URL to pip install from")

			source = url
			source_type = "url"
			already_installed_check = lambda line: url in line

		elif path is not None:
			path = os.path.abspath(path)
			path_url = "file://" + path
			if os.sep != "/":
				# windows gets special handling
				path = path.replace(os.sep, "/").lower()
				path_url = "file:///" + path

			source = path
			source_type = "path"
			already_installed_check = lambda line: path_url in line.lower() # lower case in case of windows

		else:
			raise ValueError("Either URL or path must be provided")

		self._logger.info("Installing plugin from {}".format(source))
		pip_args = ["install", sarge.shell_quote(source)]

		if dependency_links or self._settings.get_boolean(["dependency_links"]):
			pip_args.append("--process-dependency-links")

		all_plugins_before = self._plugin_manager.find_plugins(existing=dict())

		already_installed_string = "Requirement already satisfied (use --upgrade to upgrade)"
		success_string = "Successfully installed"
		failure_string = "Could not install"

		try:
			returncode, stdout, stderr = self._call_pip(pip_args)

			# pip's output for a package that is already installed looks something like any of these:
			#
			#   Requirement already satisfied (use --upgrade to upgrade): OctoPrint-Plugin==1.0 from \
			#     https://example.com/foobar.zip in <lib>
			#   Requirement already satisfied (use --upgrade to upgrade): OctoPrint-Plugin in <lib>
			#   Requirement already satisfied (use --upgrade to upgrade): OctoPrint-Plugin==1.0 from \
			#     file:///tmp/foobar.zip in <lib>
			#   Requirement already satisfied (use --upgrade to upgrade): OctoPrint-Plugin==1.0 from \
			#     file:///C:/Temp/foobar.zip in <lib>
			#
			# If we detect any of these matching what we just tried to install, we'll need to trigger a second
			# install with reinstall flags.

			if not force and any(map(lambda x: x.strip().startswith(already_installed_string) and already_installed_check(x),
			                         stdout)):
				self._logger.info("Plugin to be installed from {} was already installed, forcing a reinstall".format(source))
				self._log_message("Looks like the plugin was already installed. Forcing a reinstall.")
				force = True
		except:
			self._logger.exception("Could not install plugin from %s" % url)
			return make_response("Could not install plugin from URL, see the log for more details", 500)
		else:
			if force:
				# We don't use --upgrade here because that will also happily update all our dependencies - we'd rather
				# do that in a controlled manner
				pip_args += ["--ignore-installed", "--force-reinstall", "--no-deps"]
				try:
					returncode, stdout, stderr = self._call_pip(pip_args)
				except:
					self._logger.exception("Could not install plugin from {}".format(source))
					return make_response("Could not install plugin from source {}, see the log for more details"
					                     .format(source), 500)

		try:
			result_line = filter(lambda x: x.startswith(success_string) or x.startswith(failure_string),
			                     stdout)[-1]
		except IndexError:
			self._logger.error("Installing the plugin from {} failed, could not parse output from pip. "
			                   "See plugin_pluginmanager_console.log for generated output".format(source))
			result = dict(result=False,
			              source=source,
			              source_type=source_type,
			              reason="Could not parse output from pip, see plugin_pluginmanager_console.log "
			                     "for generated output")
			self._send_result_notification("install", result)
			return jsonify(result)

		# The final output of a pip install command looks something like this:
		#
		#   Successfully installed OctoPrint-Plugin-1.0 Dependency-One-0.1 Dependency-Two-9.3
		#
		# or this:
		#
		#   Successfully installed OctoPrint-Plugin Dependency-One Dependency-Two
		#   Cleaning up...
		#
		# So we'll need to fetch the "Successfully installed" line, strip the "Successfully" part, then split
		# by whitespace and strip to get all installed packages.
		#
		# We then need to iterate over all known plugins and see if either the package name or the package name plus
		# version number matches one of our installed packages. If it does, that's our installed plugin.
		#
		# Known issue: This might return the wrong plugin if more than one plugin was installed through this
		# command (e.g. due to pulling in another plugin as dependency). It should be safe for now though to
		# consider this a rare corner case. Once it becomes a real problem we'll just extend the plugin manager
		# so that it can report on more than one installed plugin.

		result_line = result_line.strip()
		if not result_line.startswith(success_string):
			self._logger.error("Installing the plugin from {} failed, pip did not report successful installation"
			                   .format(source))
			result = dict(result=False,
			              source=source,
			              source_type=source_type,
			              reason="Pip did not report successful installation")
			self._send_result_notification("install", result)
			return jsonify(result)

		installed = map(lambda x: x.strip(), result_line[len(success_string):].split(" "))
		all_plugins_after = self._plugin_manager.find_plugins(existing=dict(), ignore_uninstalled=False)

		new_plugin = self._find_installed_plugin(installed, plugins=all_plugins_after)
		if new_plugin is None:
			self._logger.warn("The plugin was installed successfully, but couldn't be found afterwards to "
			                  "initialize properly during runtime. Please restart OctoPrint.")
			result = dict(result=True,
			              source=source,
			              source_type=source_type,
			              needs_restart=True,
			              needs_refresh=True,
			              needs_reconnect=True,
			              was_reinstalled=False,
			              plugin="unknown")
			self._send_result_notification("install", result)
			return jsonify(result)

		self._plugin_manager.reload_plugins()
		needs_restart = self._plugin_manager.is_restart_needing_plugin(new_plugin) \
		                or new_plugin.key in all_plugins_before \
		                or reinstall is not None
		needs_refresh = new_plugin.implementation \
		                and isinstance(new_plugin.implementation, octoprint.plugin.ReloadNeedingPlugin)
		needs_reconnect = self._plugin_manager.has_any_of_hooks(new_plugin, self._reconnect_hooks) and self._printer.is_operational()

		is_reinstall = self._plugin_manager.is_plugin_marked(new_plugin.key, "uninstalled")
		self._plugin_manager.mark_plugin(new_plugin.key,
		                                 uninstalled=False,
		                                 installed=not is_reinstall and needs_restart)

		self._plugin_manager.log_all_plugins()

		self._logger.info("The plugin was installed successfully: {}, version {}".format(new_plugin.name, new_plugin.version))
		result = dict(result=True,
		              source=source,
		              source_type=source_type,
		              needs_restart=needs_restart,
		              needs_refresh=needs_refresh,
		              needs_reconnect=needs_reconnect,
		              was_reinstalled=new_plugin.key in all_plugins_before or reinstall is not None,
		              plugin=self._to_external_plugin(new_plugin))
		self._send_result_notification("install", result)
		return jsonify(result)
Beispiel #25
0
    def _createMovie(self, success=True):
        ffmpeg = settings().get(["webcam", "ffmpeg"])
        bitrate = settings().get(["webcam", "bitrate"])
        if ffmpeg is None or bitrate is None:
            self._logger.warn(
                "Cannot create movie, path to ffmpeg or desired bitrate is unset"
            )
            return

        input = os.path.join(self._captureDir, "tmp_%05d.jpg")
        if success:
            output = os.path.join(
                self._movieDir, "%s_%s.mpg" % (os.path.splitext(
                    self._gcodeFile)[0], time.strftime("%Y%m%d%H%M%S")))
        else:
            output = os.path.join(
                self._movieDir, "%s_%s-failed.mpg" % (os.path.splitext(
                    self._gcodeFile)[0], time.strftime("%Y%m%d%H%M%S")))

        # prepare ffmpeg command
        command = [
            ffmpeg, '-loglevel', 'error', '-i', input, '-vcodec', 'mpeg2video',
            '-pix_fmt', 'yuv420p', '-r',
            str(self._fps), '-y', '-b:v', bitrate, '-f', 'vob'
        ]

        filters = []

        # flip video if configured
        if settings().getBoolean(["webcam", "flipH"]):
            filters.append('hflip')
        if settings().getBoolean(["webcam", "flipV"]):
            filters.append('vflip')

        # add watermark if configured
        watermarkFilter = None
        if settings().getBoolean(["webcam", "watermark"]):
            watermark = os.path.join(os.path.dirname(__file__), "static",
                                     "img", "watermark.png")
            if sys.platform == "win32":
                # Because ffmpeg hiccups on windows' drive letters and backslashes we have to give the watermark
                # path a special treatment. Yeah, I couldn't believe it either...
                watermark = watermark.replace("\\", "/").replace(":", "\\\\:")

            watermarkFilter = "movie=%s [wm]; [%%(inputName)s][wm] overlay=10:main_h-overlay_h-10" % watermark

        filterstring = None
        if len(filters) > 0:
            if watermarkFilter is not None:
                filterstring = "[in] %s [postprocessed]; %s [out]" % (
                    ",".join(filters), watermarkFilter % {
                        "inputName": "postprocessed"
                    })
            else:
                filterstring = "[in] %s [out]" % ",".join(filters)
        elif watermarkFilter is not None:
            filterstring = watermarkFilter % {"inputName": "in"} + " [out]"

        if filterstring is not None:
            self._logger.debug("Applying videofilter chain: %s" % filterstring)
            command.extend(["-vf", sarge.shell_quote(filterstring)])

        # finalize command with output file
        self._logger.debug("Rendering movie to %s" % output)
        command.append(output)
        eventManager().fire(
            Events.MOVIE_RENDERING, {
                "gcode": self._gcodeFile,
                "movie": output,
                "movie_basename": os.path.basename(output)
            })

        command_str = " ".join(command)
        self._logger.debug("Executing command: %s" % command_str)

        p = sarge.run(command_str, stderr=sarge.Capture())
        if p.returncode == 0:
            eventManager().fire(
                Events.MOVIE_DONE, {
                    "gcode": self._gcodeFile,
                    "movie": output,
                    "movie_basename": os.path.basename(output)
                })
        else:
            returncode = p.returncode
            stderr_text = p.stderr.text
            self._logger.warn(
                "Could not render movie, got return code %r: %s" %
                (returncode, stderr_text))
            eventManager().fire(
                Events.MOVIE_FAILED, {
                    "gcode": self._gcodeFile,
                    "movie": output,
                    "movie_basename": os.path.basename(output),
                    "returncode": returncode,
                    "error": stderr_text
                })