$input->getFilename(), 'uoutput' => $output->getFilename(), 'frames' => $input->getVideoDuration()->frames, 'seconds' => $input->getVideoDuration()->seconds, ]; $env['input'] = escapeshellarg($env['uinput']); $env['output'] = escapeshellarg($env['uoutput']); $script = $this->loadScript($preset); $script->execute($env); } private function loadScript(Preset $preset) { $script = new Script(); foreach ((array)$preset->get('set') as $k=>$v) { $script->set($k,$v); } foreach ((array)$preset->get('script') as $name=>$step) { $op = new Operation($name, $step); $script->addOperation($op); } return $script; } } class Script { /** @var array Variables */ protected $vars = []; public function set($k,$v) { $this->vars[$k] = $v; } public function addOperation(Operation $operation) { $operation->setScript($this); $this->operations[] = $operation; } public function execute(array $env) { $env = $this->buildEnvironment($env, $this->vars); //print_r($env); foreach ($this->operations as $operation) { //printf("Executing: %s\n", $operation->getInfo()); $operation->execute($env); } } private function buildEnvironment(array $env, array $vars) { $vars = array_merge($env, $vars); foreach ($vars as $k=>$var) { if (is_array($var)) { $esc = empty($var['escape'])?false:$var['escape']; $var = preg_replace_callback('/\{\%([a-z]+?)\}/i', function ($match) use (&$vars,$var) { $k = $match[1]; if (empty($vars[$k])) return null; return $vars[$k]; }, $var['value']); if ($esc) $var = escapeshellarg($var); $vars[$k] = $var; } } return $vars; } public function parseVariable($value, array $env) { return preg_replace_callback('/\{\%([a-z]+?)\}/i', function ($match) use (&$env) { $k = $match[1]; if (empty($env[$k])) return null; return $env[$k]; }, $value); } } class Operation { protected $name; protected $info; protected $parser; protected $command; protected $script; public function __construct($name, array $data) { $_ = function($a,$k,$d=null) { return empty($a[$k])?$d:$a[$k]; }; $this->name = $name; $this->info = $_($data,'info'); $this->command = $_($data,'exec'); $this->parser = new Parser($_($data,'parse')); } public function getName() { return $this->name; } public function getInfo() { return $this->info; } public function setScript(Script $script) { $this->script = $script; } public function execute(array $env) { $cmdl = $this->script->parseVariable($this->command, $env); //printf(" cmd: %s\n", $this->command); //printf(" eval: %s\n", $cmdl); $descr = [ 0 => [ 'pipe', 'r' ], 1 => [ 'pipe', 'w' ], 2 => [ 'pipe', 'w' ] ]; echo "Exec: ".$cmdl."\n"; $proc = proc_open($cmdl, $descr, $pipes); $tot = (int)$env['frames']-1; printf("\r%s [%s]", $this->info, $this->name); $this->parser->parse($pipes, function ($status) use ($tot) { $curr = (int)$status['frame']; $pc = min(100,100/$tot*$curr); printf("\r%s [%s]: %.1f%%", $this->info, $this->name, $pc); }); proc_close($proc); printf("\n"); } } class Parser { const STDOUT = 1; const STDERR = 2; /** @var array Resources */ protected $streams = []; /** @var string The expression to match (preg match) */ protected $expression; /** @var int The stream to watch */ protected $source; /** @var array Index to name mappings */ protected $names = []; public function __construct(array $parser=null) { $_ = function($a,$k,$d=null) { return empty($a[$k])?$d:$a[$k]; }; $this->expression = $_($parser,'regex'); $this->source = $_($parser,'from'); foreach ((array)$parser as $k=>$v) { if (is_numeric($v)) { $this->names[$k] = $v; } } } public function parse(array $streams, callable $callback) { $this->streams = $streams; $match = [ 'stdout' => 1, 'stderr' => 2 ]; $source = $this->source?:'stdout'; foreach ($this->streams as $stream) stream_set_blocking($stream, false); while (!feof($this->streams[1])) { $stdout = fread($this->streams[1],8192); $stderr = fread($this->streams[2],8192); //($stdout) && printf("OUT: <<%s>>\n", $stdout); //($stderr) && printf("ERR: <<%s>>\n", $stderr); if (($stdout && $this->expression) && ($source=='stdout')) if (preg_match($this->expression, $stdout, $match)) call_user_func($callback, $this->parseNames($match)); if (($stderr && $this->expression) && ($source=='stderr')) if (preg_match($this->expression, $stderr, $match)) call_user_func($callback, $this->parseNames($match)); usleep(10000); } } protected function parseNames(array $match) { $ret = []; foreach ($this->names as $k=>$index) { $ret[$k] = $match[$index]; } return $ret; } } return new ExecutorPlugin();