Example #1
0
    def _loop(self):
        executable = self.get_executable()
        args = self.get_parameters()
        self._start_logging(executable, args)

        args.insert(0, executable)

        logging.debug("Conversion: (%s)", " ".join(args))

        kwargs = {
            "bufsize": 1,
            "stdout": subprocess.PIPE,
            "stderr": subprocess.STDOUT,
            "stdin": subprocess.PIPE,
            "close_fds": True
        }
        try:
            self.process_handle = Popen(args, **kwargs)
            self.process_output(line_reader(self.process_handle.stdout))
            self.process_handle.wait()

        except OSError, ose:
            if ose.errno == errno.ENOENT:
                self.error = _("%(program)s does not exist.",
                               {"program": self.get_executable()})
            else:
                logging.exception("Exception in conversion loop: %s %s", args,
                                  kwargs)
Example #2
0
File: util.py Project: foxi/miro
def call_command(*args, **kwargs):
    """Call an external command.  If the command doesn't exit with
    status 0, or if it outputs to stderr, an exception will be raised.
    Returns stdout.

    :param ignore_stderr: boolean.  defaults to False.  If True and
        the command returns stderr, it's ignored rather than raising
        an exception.
    :param return_everything: boolean.  defaults to False.  If True,
        then this returns (retcode, stdout, stderr).  This implies
        ignore_stderr is True, so you don't need to explicitly state
        that, too.
    :param env: dict.  Environment to pass to subprocess.Popen
    """
    ignore_stderr = kwargs.pop("ignore_stderr", False)
    return_everything = kwargs.pop("return_everything", False)
    env = kwargs.pop("env", None)

    if kwargs:
        raise TypeError("extra keyword arguments: %s" % kwargs)

    pipe = Popen(args, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE, env=env)
    stdout, stderr = pipe.communicate()
    if return_everything:
        return (pipe.returncode, stdout, stderr)
    elif pipe.returncode != 0:
        raise OSError(
            "call_command with %s has return code %s\n" "stdout:%s\nstderr:%s" % (args, pipe.returncode, stdout, stderr)
        )
    elif stderr and not ignore_stderr:
        raise OSError("call_command with %s outputed error text:\n%s" % (args, stderr))
    else:
        return stdout
Example #3
0
File: util.py Project: kfatehi/miro
def call_command(*args, **kwargs):
    """Call an external command.  If the command doesn't exit with
    status 0, or if it outputs to stderr, an exception will be raised.
    Returns stdout.

    :param ignore_stderr: boolean.  defaults to False.  If True and
        the command returns stderr, it's ignored rather than raising
        an exception.
    :param return_everything: boolean.  defaults to False.  If True,
        then this returns (retcode, stdout, stderr).  This implies
        ignore_stderr is True, so you don't need to explicitly state
        that, too.
    """
    ignore_stderr = kwargs.pop('ignore_stderr', False)
    return_everything = kwargs.pop('return_everything', False)

    if kwargs:
        raise TypeError('extra keyword arguments: %s' % kwargs)

    pipe = Popen(args,
                 stdout=subprocess.PIPE,
                 stdin=subprocess.PIPE,
                 stderr=subprocess.PIPE)
    stdout, stderr = pipe.communicate()
    if return_everything:
        return (pipe.returncode, stdout, stderr)
    elif pipe.returncode != 0:
        raise OSError("call_command with %s has return code %s\n"
                      "stdout:%s\nstderr:%s" %
                      (args, pipe.returncode, stdout, stderr))
    elif stderr and not ignore_stderr:
        raise OSError("call_command with %s outputed error text:\n%s" %
                      (args, stderr))
    else:
        return stdout
Example #4
0
def diskutil(cmd, path_or_disk, use_plist=True):
    args = ['/usr/sbin/diskutil', cmd]
    if use_plist:
        args.append('-plist')
    if path_or_disk:
        args.append(path_or_disk)
    proc = Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    stdout, stderr = proc.communicate()
    if not use_plist:
        return stdout
    try:
        return plistlib.readPlistFromString(stdout)
    except:
        logging.warn('error parsing plist for command: %s\n%s' % (
            ' '.join(args), stdout))
Example #5
0
def launch_download_daemon(oldpid, env):
    kill_process(oldpid)

    # FIXME - wtf is going on here between os.environ, env and
    # environ?
    for key, value in env.items():
        os.environ[key] = value

    environ = os.environ.copy()
    environ['DEMOCRACY_DOWNLOADER_LOG'] = app.config.get(
        prefs.DOWNLOADER_LOG_PATHNAME)
    environ['MIRO_APP_VERSION'] = app.config.get(prefs.APP_VERSION)
    if hasattr(app, 'in_unit_tests'):
        environ['MIRO_IN_UNIT_TESTS'] = '1'
    environ.update(env)

    # start the downloader.  We use the subprocess module to turn off
    # the console.  One slightly awkward thing is that the current
    # process might not have a valid stdin/stdout/stderr, so we create
    # a pipe to it that we never actually use.

    # note that we use "Miro" instead of the app name here, so custom
    # versions will work

    downloader_path = (os.path.join(resources.app_root(),
                                   "Miro_Downloader.exe"),) 
    return Popen(downloader_path, stdout=subprocess.PIPE,
                 stderr=subprocess.PIPE,
                 stdin=subprocess.PIPE,
                 env=environ)
Example #6
0
    def _loop(self):
        executable = self.get_executable()
        args = self.get_parameters()
        self._start_logging(executable, args)

        args.insert(0, executable)

        logging.debug("Conversion: (%s)", " ".join(args))

        kwargs = {"bufsize": 1,
                  "stdout": subprocess.PIPE,
                  "stderr": subprocess.STDOUT,
                  "stdin": subprocess.PIPE,
                  "close_fds": True}
        try:
            self.process_handle = Popen(args, **kwargs)
            self.process_output(line_reader(self.process_handle.stdout))
            self.process_handle.wait()

        except OSError, ose:
            if ose.errno == errno.ENOENT:
                self.error = _("%(program)s does not exist.",
                               {"program": self.get_executable()})
            else:
                logging.exception("Exception in conversion loop: %s %s",
                                  args, kwargs)
Example #7
0
 def _start_subprocess(self):
     cmd_line, env = miro_helper_program_info()
     kwargs = {
               "stdout": subprocess.PIPE,
               "stdin": subprocess.PIPE,
               "stderr": open(os.devnull, 'wb'),
               "env": env,
               "close_fds": True
     }
     process = Popen(cmd_line, **kwargs)
     return process
Example #8
0
def _app_command_line():
    """Get the command line to lanch the Miro.app bundle. """
    exe = NSBundle.mainBundle().executablePath()

    os_info = os.uname()
    os_version = int(os_info[2].split('.')[0])
    if os_version < 9:
        return [exe]
    else:
        arch = Popen("/usr/bin/arch",
                     stdout=subprocess.PIPE).communicate()[0].strip()
        return ['/usr/bin/arch', '-%s' % arch, exe]
Example #9
0
def launch_download_daemon(oldpid, env):
    kill_process(oldpid)

    # FIXME - wtf is going on here between os.environ, env and
    # environ?
    for key, value in env.items():
        os.environ[key] = value

    environ = os.environ.copy()
    environ['DEMOCRACY_DOWNLOADER_LOG'] = app.config.get(
        prefs.DOWNLOADER_LOG_PATHNAME)
    environ['MIRO_APP_VERSION'] = app.config.get(prefs.APP_VERSION)
    if hasattr(app, 'in_unit_tests'):
        environ['MIRO_IN_UNIT_TESTS'] = '1'
    environ.update(env)

    # note that we use "Miro" instead of the app name here, so custom
    # versions will work

    downloader_path = (os.path.join(resources.app_root(),
                                    "Miro_Downloader.exe"), )
    return Popen(downloader_path, close_fds=True, env=environ)
Example #10
0
class ConversionTask(object):
    def __init__(self, converter_info, item_info, target_folder, create_item):
        self.item_info = item_info
        self.converter_info = converter_info
        self.input_path = item_info.filename
        self.final_output_path, self.temp_output_path = build_output_paths(
            item_info, target_folder, converter_info)
        self.create_item = create_item

        self.key = "%s->%s" % (self.input_path, self.final_output_path)
        self.thread = None
        self.duration = None
        self.progress = 0
        self.log_path = None
        self.log_file = None
        self.process_handle = None
        self.error = None
        self.start_time = time.time()

    def get_executable(self):
        raise NotImplementedError()

    def get_parameters(self):
        raise NotImplementedError()

    def get_output_size_guess(self):
        if self.item_info.duration and self.converter_info.bit_rate:
            return self.converter_info.bit_rate * self.item_info.duration / 8
        return self.item_info.size

    def get_display_name(self):
        return self.converter_info.displayname

    def run(self):
        logging.debug("temp_output_path: [%s] final_output_path: [%s]",
                      self.temp_output_path, self.final_output_path)

        self.progress = 0
        self.thread = threading.Thread(target=utils.thread_body,
                                       args=[self._loop],
                                       name="Conversion Task")
        self.thread.setDaemon(True)
        self.thread.start()

    def get_eta(self):
        """Calculates the eta for this conversion to be completed.

        :returns: None if progress is <= 0, otherwise returns number
            of seconds until this is complete
        """
        if self.progress <= 0:
            return None

        progress = self.progress * 100
        duration = time.time() - self.start_time
        time_per_percent = duration / progress
        return int(time_per_percent * (100 - progress))

    def is_pending(self):
        return self.thread is None

    def is_running(self):
        return self.thread is not None and self.thread.isAlive()

    def done_running(self):
        return self.thread is not None and not self.thread.isAlive()

    def is_finished(self):
        return self.done_running() and not self.is_failed()

    def is_failed(self):
        return (self.error or (self.process_handle is not None
                               and self.process_handle.returncode is not None
                               and self.process_handle.returncode != 0))

    def _loop(self):
        executable = self.get_executable()
        args = self.get_parameters()
        self._start_logging(executable, args)

        args.insert(0, executable)

        logging.debug("Conversion: (%s)", " ".join(args))

        kwargs = {
            "bufsize": 1,
            "stdout": subprocess.PIPE,
            "stderr": subprocess.STDOUT,
            "stdin": subprocess.PIPE,
            "close_fds": True
        }
        try:
            self.process_handle = Popen(args, **kwargs)
            self.process_output(line_reader(self.process_handle.stdout))
            self.process_handle.wait()

        except OSError, ose:
            if ose.errno == errno.ENOENT:
                self.error = _("%(program)s does not exist.",
                               {"program": self.get_executable()})
            else:
                logging.exception("Exception in conversion loop: %s %s", args,
                                  kwargs)

        finally:
Example #11
0
    def transcode(self):
        rc = True
        try:
            ffmpeg_exe = get_ffmpeg_executable_path()
            kwargs = {"stdin": open(os.devnull, 'rb'),
                      "stdout": subprocess.PIPE,
                      "stderr": open(os.devnull, 'wb'),
                      "close_fds": True}
            args = [ffmpeg_exe, "-i", self.media_file]
            if self.time_offset:
                logging.debug('transcode: start job @ %d' % self.time_offset)
                args += TranscodeObject.time_offset_args + [
                    str(self.time_offset)]
            video_needs_trancode = False
            if self.has_video:
                logging.debug('Video codec: %s', self.video_codec)
                logging.debug('Video size: %s', self.video_size)
                if video_can_copy(self.video_codec, self.video_size):
                    args += get_transcode_video_copy_options()
                else:
                    args += get_transcode_video_options()
                    video_needs_transcode = True
            if self.has_audio:
                logging.debug('Audio codec: %s', self.audio_codec)
                logging.debug('Audio sample rate: %s', self.audio_sample_rate)
                if (valid_av_combo(self.video_codec, self.audio_codec) and
                  audio_can_copy(self.audio_codec, self.audio_sample_rate)):
                    args += get_transcode_audio_copy_options()
                else:
                    args += get_transcode_audio_options()
            else:
               raise ValueError('no video or audio stream present')

            args += TranscodeObject.output_args
            logging.debug('Running command %s' % ' '.join(args))
            self.ffmpeg_handle = Popen(args, **kwargs)

            segmenter_exe = get_segmenter_executable_path()
            args = [segmenter_exe]
            address, port = self.sink.server_address
            args += TranscodeObject.segmenter_args + [str(port)]
            # Can't use close_fds here because we need to pass the fds to
            # the child
            kwargs = {"stdout": open(os.devnull, 'rb'),
                      "stdin": self.ffmpeg_handle.stdout,
                      "stderr": open(os.devnull, 'wb')}

            logging.debug('Running command %s' % ' '.join(args))
            self.segmenter_handle = Popen(args, **kwargs)
   
            self.sink_thread = threading.Thread(target=thread_body,
                                                args=[self.segmenter_consumer],
                                                name="Segmenter Consumer")
            self.sink_thread.daemon = True
            self.sink_thread.start()

        except StandardError:
            (typ, value, tb) = sys.exc_info()
            logging.error('ERROR: %s %s' % (str(typ), str(value)))
            rc = False
        self.transcode_gate.set()
        return rc
Example #12
0
def needs_transcode(media_file):
    """needs_transcode()

    Returns (False, None) if no need to transcode.
    Returns (True, info) if there is a need to transcode.

    where info is

    (duration, has_audio, has_video)

    The duration is transmitted for transcoding purposes because we need to
    build a m3u8 playlist and what we get out of the Miro database may be 
    unreliable (does not exist).

    May throw exception if ffmpeg not found.  Remember to catch."""
    ffmpeg_exe = get_ffmpeg_executable_path()
    kwargs = {"stdout": subprocess.PIPE,
              "stderr": subprocess.PIPE,
              "stdin": subprocess.PIPE,
              "close_fds": True}
    args = [ffmpeg_exe, "-i", media_file]
    handle = Popen(args, **kwargs)
    # XXX unbounded read here but should be okay, ffmpeg output is finite.
    # note that we need to read from stderr, since that's what ffmpeg spits 
    # out.
    text = handle.stderr.read()
    # Initial determination based on the file type - need to drill down
    # to see if the resolution, etc are within parameters.
    if container_regex.search(text):
        transcode = False
    else:
        transcode = True
    # "    Duration: XX:XX:XX.XX, ..."
    match = duration_regex.search(text)
    start, end = match.span()
    duration_start = text[end:]
    duration = duration_start[:duration_start.index(',')]
    # Convert to seconds.  We can't handle fractions of a second so 
    # skip over those bits.
    hours, minutes, seconds = duration.split(':')
    # Strip the fractional seconds.  Always round up, then we won't miss
    # any data.
    seconds = int(float(seconds) + 0.5)
    seconds += int(minutes) * 60
    seconds += int(hours) * 3600
    has_audio = has_audio_regex.search(text)
    has_video = has_video_regex.search(text)
    vcodec = acodec = size = sample_rate = None
    vcopy = acopy = False
    if has_video:
        vcodec, size = get_video_parameters(has_video)
        vcopy = video_can_copy(vcodec, size)
        if not vcopy:
            transcode = False
    if has_audio:
        acodec, sample_rate = get_audio_parameters(has_audio)
        acopy = audio_can_copy(acodec, sample_rate)
        if not acopy:
            transcode = False
    return (transcode, (seconds, has_audio, acodec, sample_rate,
                        has_video, vcodec, size))
Example #13
0
class ConversionTask(object):
    def __init__(self, converter_info, item_info, target_folder,
                 create_item):
        self.item_info = item_info
        self.converter_info = converter_info
        self.input_path = item_info.filename
        self.final_output_path, self.temp_output_path = build_output_paths(
            item_info, target_folder, converter_info)
        self.create_item = create_item

        self.key = "%s->%s" % (self.input_path, self.final_output_path)
        self.thread = None
        self.duration = None
        self.progress = 0
        self.log_path = None
        self.log_file = None
        self.process_handle = None
        self.error = None
        self.start_time = time.time()

    def get_executable(self):
        raise NotImplementedError()

    def get_parameters(self):
        raise NotImplementedError()

    def get_output_size_guess(self):
        if self.item_info.duration and self.converter_info.bit_rate:
            return self.converter_info.bit_rate * self.item_info.duration / 8
        return self.item_info.size

    def get_display_name(self):
        return self.converter_info.displayname

    def run(self):
        logging.debug("temp_output_path: [%s] final_output_path: [%s]",
                      self.temp_output_path, self.final_output_path)

        self.progress = 0
        self.thread = threading.Thread(target=utils.thread_body,
                                       args=[self._loop],
                                       name="Conversion Task")
        self.thread.setDaemon(True)
        self.thread.start()

    def get_eta(self):
        """Calculates the eta for this conversion to be completed.

        :returns: None if progress is <= 0, otherwise returns number
            of seconds until this is complete
        """
        if self.progress <= 0:
            return None

        progress = self.progress * 100
        duration = time.time() - self.start_time
        time_per_percent = duration / progress
        return int(time_per_percent * (100 - progress))

    def is_pending(self):
        return self.thread is None

    def is_running(self):
        return self.thread is not None and self.thread.isAlive()

    def done_running(self):
        return self.thread is not None and not self.thread.isAlive()

    def is_finished(self):
        return self.done_running() and not self.is_failed()

    def is_failed(self):
        return (self.error or
                (self.process_handle is not None and
                 self.process_handle.returncode is not None and
                 self.process_handle.returncode != 0))

    def _loop(self):
        executable = self.get_executable()
        args = self.get_parameters()
        self._start_logging(executable, args)

        args.insert(0, executable)

        logging.debug("Conversion: (%s)", " ".join(args))

        kwargs = {"bufsize": 1,
                  "stdout": subprocess.PIPE,
                  "stderr": subprocess.STDOUT,
                  "stdin": subprocess.PIPE,
                  "close_fds": True}
        try:
            self.process_handle = Popen(args, **kwargs)
            self.process_output(line_reader(self.process_handle.stdout))
            self.process_handle.wait()

        except OSError, ose:
            if ose.errno == errno.ENOENT:
                self.error = _("%(program)s does not exist.",
                               {"program": self.get_executable()})
            else:
                logging.exception("Exception in conversion loop: %s %s",
                                  args, kwargs)

        finally: