Added autoloader to plugin loader, executor improvements

This commit is contained in:
2017-02-11 23:37:35 +01:00
parent 0ca1da06e7
commit a3f8728d9d
10 changed files with 474 additions and 181 deletions

View File

@ -0,0 +1,38 @@
<?php
namespace VfxApply\Plugin\Executor;
use VfxApply\Plugin;
use VfxApply\Input;
use VfxApply\Output;
use VfxApply\Preset;
class Helper
{
protected $body;
protected $script;
public function __construct($body)
{
$this->body = $body;
}
public function setScript(Script $script)
{
$this->script = $script;
}
public function call(array $env)
{
$body = "<?php ".preg_replace_callback('/(\{\{\%([a-z]+)\}\})/', function ($m) use ($env) {
return $env[$m[2]];
}, $this->body);
$tmpname = tempnam(null, "vfxhelper");
file_put_contents($tmpname, $body);
passthru("php {$tmpname}");
unlink($tmpname);
}
}

View File

@ -0,0 +1,87 @@
<?php
namespace VfxApply\Plugin\Executor;
use VfxApply\Plugin;
use VfxApply\Input;
use VfxApply\Output;
use VfxApply\Preset;
class Operation
{
protected $name;
protected $info;
protected $parser;
protected $command;
protected $helper;
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->helper = $_($data,'call');
$this->parser = new Parser($_($data,'parse'), $_($data,'extract'));
}
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, $dialog)
{
if ($this->helper) {
$helper = $this->script->getHelper($this->helper);
$helper->call($env);
}
if (!$this->command) {
return;
}
$cmdl = $this->script->parseVariable($this->command, $env);
//printf(" cmd: %s\n", $this->command);
//printf(" eval: %s\n", $cmdl);
$this->parser->prepareExtractors($this->script, $env);
$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, $dialog) {
$curr = (int)$status['frame'];
$pc = min(100,100/$tot*$curr);
$dialog->setProgress($pc);
$out = sprintf("%s [%s]: %.1f%%", $this->info, $this->name, $pc);
$dialog->setText($out);
echo "\r{$out}";
});
proc_close($proc);
printf("\n");
}
}

105
plugins/executor/Parser.php Normal file
View File

@ -0,0 +1,105 @@
<?php
namespace VfxApply\Plugin\Executor;
use VfxApply\Plugin;
use VfxApply\Input;
use VfxApply\Output;
use VfxApply\Preset;
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 = [];
protected $extractors = [];
public function __construct(array $parser=null, $extractors=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;
}
}
if (is_array($extractors))
$this->extractors = $extractors;
}
public function prepareExtractors(Script $script, array $env)
{
foreach ($this->extractors as $i=>$extractor) {
if (array_key_exists('write',$extractor)) {
$write = $this->extractors[$i]['write'];
$write = $script->parseVariable($write, $env);
$this->extractors[$i]['write'] = trim($write,"'");
}
}
}
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);
$this->parseExtractors($stdout, $stderr);
//($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 parseExtractors($stdout, $stderr)
{
foreach ($this->extractors as $extractor) {
switch ($extractor['from']) {
case 'stdout': $buf = $stdout; break;
case 'stderr': $buf = $stderr; break;
}
if ($buf == "") continue;
$lines = explode("\n", $buf);
foreach ($lines as $line) {
if (preg_match($extractor['regex'], $line)) {
$fo = fopen($extractor['write'], "a+");
fwrite($fo, trim($line)."\n");
fclose($fo);
}
}
}
}
protected function parseNames(array $match)
{
$ret = [];
foreach ($this->names as $k=>$index) {
$ret[$k] = $match[$index];
}
return $ret;
}
}

View File

@ -64,3 +64,34 @@ To parse the output, add a `parse` key to the command block.
from: <- stream for comparison (stdout or stderr)
frame: <- index of frame number, or leave out
fps: <- index of frames per second, or leave out
### Extracting output
While you can only have a single parser, you can have multiple extractors.
An extractor matches a pattern on a line, and then writes it to the destination
file.
extract:
# Write everything matching '[Parsed_blackframe' to a file
- { from: 'stderr', regex: '/^\[Parsed_blackframe/', write:'{%scenes}' }
## Helpers
Helpers can be defined in the preset. These are essentially embedded scripts
written in PHP that have access to the environment of the preset.
Helpers are not executed, but rather called:
...
split:
info: Splitting video
call: scenesplitter
cleanup:
exec: "rm {%scenes}"
helpers:
scenesplitter: |
/*
* This is the PHP script
*
...

View File

@ -0,0 +1,84 @@
<?php
namespace VfxApply\Plugin\Executor;
use VfxApply\Plugin;
use VfxApply\Input;
use VfxApply\Output;
use VfxApply\Preset;
class Script
{
/** @var array Variables */
protected $vars = [];
protected $plugin;
public function setPlugin($plugin)
{
$this->plugin = $plugin;
}
public function set($k,$v)
{
$this->vars[$k] = $v;
}
public function addOperation(Operation $operation)
{
$operation->setScript($this);
$this->operations[] = $operation;
}
public function addHelper($name, Helper $helper)
{
$helper->setScript($this);
$this->helpers[$name] = $helper;
}
public function getHelper($name)
{
return $this->helpers[$name];
}
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());
$dialog = $this->plugin->createDialog(DIALOG_PROGRESS, $operation->getInfo());
$dialog->show();
$operation->execute($env, $dialog);
unset($dialog);
}
}
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);
}
}

View File

@ -27,6 +27,7 @@ class ExecutorPlugin extends Plugin
$env['output'] = escapeshellarg($env['uoutput']);
$script = $this->loadScript($preset);
$script->setPlugin($this);
$script->execute($env);
}
@ -41,187 +42,14 @@ class ExecutorPlugin extends Plugin
$op = new Operation($name, $step);
$script->addOperation($op);
}
foreach ((array)$preset->get('helpers') as $name=>$helper) {
$hs = new Helper($helper);
$script->addHelper($name, $hs);
}
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();