Example #1
0
    def _check_cmd(self):
        if not self.cmd:
            raise StreamError("`cmd' attribute not set")

        cmd = compat_which(self.cmd)

        if not cmd:
            raise StreamError("Unable to find `{0}' command".format(self.cmd))

        return cmd
Example #2
0
    def open(self):
        process = self.spawn(self.parameters, self.arguments)

        # Wait 0.5 seconds to see if program exited prematurely
        time.sleep(0.5)

        if not process.poll() is None:
            if hasattr(self.stderr, "name"):
                raise StreamError(("Error while executing subprocess, "
                                   "error output logged to: {0}").format(self.stderr.name))
            else:
                raise StreamError("Error while executing subprocess")

        return StreamProcessIO(self.session, process, process.stdout, timeout=self.timeout)
Example #3
0
    def spawn(self,
              parameters=None,
              arguments=None,
              stderr=None,
              timeout=None,
              short_option_prefix="-",
              long_option_prefix="--"):
        """
        Spawn the process defined in `cmd`

        parameters is converted to options the short and long option prefixes
        if a list is given as the value, the parameter is repeated with each
        value

        If timeout is set the spawn will block until the process returns or
        the timeout expires.

        :param parameters: optional parameters
        :param arguments: positional arguments
        :param stderr: where to redirect stderr to
        :param timeout: timeout for short lived process
        :param long_option_prefix: option prefix, default -
        :param short_option_prefix: long option prefix, default --
        :return: spawned process
        """
        stderr = stderr or self.stderr
        cmd = self.bake(self._check_cmd(), parameters, arguments,
                        short_option_prefix, long_option_prefix)
        self.logger.debug("Spawning command: {0}",
                          subprocess.list2cmdline(cmd))

        try:
            process = subprocess.Popen(cmd,
                                       stderr=stderr,
                                       stdout=subprocess.PIPE)
        except (OSError, IOError) as err:
            raise StreamError("Failed to start process: {0} ({1})".format(
                self._check_cmd(), str(err)))

        if timeout:
            elapsed = 0
            while elapsed < timeout and not process.poll():
                time.sleep(0.25)
                elapsed += 0.25

            # kill after the timeout has expired and the process still hasn't ended
            if not process.poll():
                try:
                    self.logger.debug(
                        "Process timeout expired ({0}s), killing process".
                        format(timeout))
                    process.kill()
                except Exception:
                    pass

            process.wait()

        return process
Example #4
0
    def open(self):
        if self.session.options.get("rtmp-proxy"):
            if not self._supports_param("socks"):
                raise StreamError(
                    "Installed rtmpdump does not support --socks argument")

            self.parameters["socks"] = self.session.options.get("rtmp-proxy")

        if "jtv" in self.parameters and not self._supports_param("jtv"):
            raise StreamError(
                "Installed rtmpdump does not support --jtv argument")

        if "weeb" in self.parameters and not self._supports_param("weeb"):
            raise StreamError(
                "Installed rtmpdump does not support --weeb argument")

        if self.redirect:
            self._check_redirect()

        self.parameters["flv"] = "-"

        return StreamProcess.open(self)
Example #5
0
    def _supports_param(self, param, timeout=5.0):
        try:
            rtmpdump = self.spawn(dict(help=True),
                                  timeout=timeout,
                                  stderr=subprocess.PIPE)
        except StreamError as err:
            raise StreamError(
                "Error while checking rtmpdump compatibility: {0}".format(
                    err.message))

        for line in rtmpdump.stderr.readlines():
            m = re.match(r"^--(\w+)", str(line, "ascii"))

            if not m:
                continue

            if m.group(1) == param:
                return True

        return False
Example #6
0
    def _create_video_clip(self, chunks, start_offset, stop_offset):
        playlist_duration = stop_offset - start_offset
        playlist_offset = 0
        playlist_streams = []
        playlist_tags = []

        for chunk in chunks:
            chunk_url = chunk["url"]
            chunk_length = chunk["length"]
            chunk_start = playlist_offset
            chunk_stop = chunk_start + chunk_length
            chunk_stream = HTTPStream(self.session, chunk_url)

            if chunk_start <= start_offset <= chunk_stop:
                try:
                    headers = extract_flv_header_tags(chunk_stream)
                except IOError as err:
                    raise StreamError("Error while parsing FLV: {0}", err)

                if not headers.metadata:
                    raise StreamError(
                        "Missing metadata tag in the first chunk")

                metadata = headers.metadata.data.value
                keyframes = metadata.get("keyframes")

                if not keyframes:
                    if chunk["upkeep"] == "fail":
                        raise StreamError(
                            "Unable to seek into muted chunk, try another timestamp"
                        )
                    else:
                        raise StreamError(
                            "Missing keyframes info in the first chunk")

                keyframe_offset = None
                keyframe_offsets = keyframes.get("filepositions")
                keyframe_times = [
                    playlist_offset + t for t in keyframes.get("times")
                ]
                for time, offset in zip(keyframe_times, keyframe_offsets):
                    if time > start_offset:
                        break

                    keyframe_offset = offset

                if keyframe_offset is None:
                    raise StreamError("Unable to find a keyframe to seek to "
                                      "in the first chunk")

                chunk_headers = dict(
                    Range="bytes={0}-".format(int(keyframe_offset)))
                chunk_stream = HTTPStream(self.session,
                                          chunk_url,
                                          headers=chunk_headers)
                playlist_streams.append(chunk_stream)
                for tag in headers:
                    playlist_tags.append(tag)
            elif start_offset <= chunk_start < stop_offset:
                playlist_streams.append(chunk_stream)

            playlist_offset += chunk_length

        return FLVPlaylist(self.session,
                           playlist_streams,
                           tags=playlist_tags,
                           duration=playlist_duration)