Created
August 27, 2020 13:06
-
-
Save divinity76/98329daf4899d7b6322d03b61e8b471d to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
declare(strict_types=1); | |
init(); | |
function record():array{ | |
$autoit=new AutoIt(); | |
$stdin=fopen("php://stdin","rb"); | |
stream_set_blocking($stdin,false); | |
$image_binaries=[]; | |
$filename="recorded".bin2hex(random_bytes(4)).".png"; | |
for($i=0;;++$i){ | |
if(0){ | |
$im=imagegrabscreen(); | |
//imagepng($im,"recorded{$i}.png"); | |
imagepng($im,$filename); | |
$image_binaries[]=file_get_contents($filename); | |
imagedestroy($im); | |
}else{ | |
$image=$autoit->_ScreenCapture_Capture(); | |
$image_binaries[]=$image; | |
} | |
echo "."; | |
$read=WindowsAsyncReadStdin(); | |
var_dump($read); | |
if($read==="x"){ | |
break; | |
} | |
} | |
return $image_binaries; | |
} | |
function WindowsAsyncReadStdin():string{ | |
$ret=""; | |
$fp=fopen("php://stdin","rb"); | |
stream_set_blocking($fp,false); | |
$read=[STDIN]; | |
$unused=[]; | |
stream_select($read,$unused,$unused,0,0); | |
if(!empty($read)){ | |
$ret.=fread($fp,1); | |
} | |
fclose($fp); | |
return $ret; | |
} | |
$infile="steps.mht"; | |
// Content-Type: image | |
$content=file_get_contents($infile); | |
$imageBinaries=extractImageBinaries($content); | |
//$imageBinaries=record(); | |
if(0){ | |
foreach($imageBinaries as $key=>$imageBinary){ | |
file_put_contents("extracted{$key}.jpg",$imageBinary); | |
} | |
} | |
makeVideo($imageBinaries); | |
// ffmpeg -i - -ss 00:00:3 -s 650x390 -vframes 1 -c:v png -f image2pipe - | |
// ffmpeg -framerate 1 -pattern_type glob -i '*.png' -c:v libx264 -r 30 -pix_fmt yuv420p out.mp4 | |
// var_dump($imageBinaries); | |
function makeVideo(array $imageBinaries){ | |
$temp_folder="steps_temp_".bin2hex(random_bytes(4)); | |
mkdir($temp_folder); | |
foreach($imageBinaries as $key=>$imageBinary){ | |
file_put_contents($temp_folder. DIRECTORY_SEPARATOR . "extracted{$key}.jpg",$imageBinary); | |
} | |
if(0){ | |
$cmd=implode(" ",array( | |
"ffmpeg", | |
"-y", | |
"-framerate 1", | |
)); | |
foreach($imageBinaries as $key=>$imageBinary){ | |
$cmd.=" -i ".escapeshellarg($temp_folder. DIRECTORY_SEPARATOR . "extracted{$key}.jpg"); | |
} | |
$cmd.=" "; | |
$cmd.= "-c:v libx264 -r 30 -pix_fmt yuv420p out.mp4"; | |
} | |
if(0){ | |
$cmd="ffmpeg -y -r 1/5 "; | |
// -f concat -safe 0 | |
foreach($imageBinaries as $key=>$imageBinary){ | |
$cmd.=" -i ".escapeshellarg($temp_folder. DIRECTORY_SEPARATOR . "extracted{$key}.jpg"); | |
} | |
// -vf \"fps=25,format=yuv420p\" | |
$cmd.=" -c:v libx264 -framerate 1 -vf \"fps=1\" out.mp4"; | |
var_dump($cmd); | |
file_put_contents("cmd.txt",$cmd); | |
// -c:v libx264 -vf "fps=25,format=yuv420p" "e:\out.mp4" | |
} | |
if(1){ | |
// cat *.png | ffmpeg -f image2pipe -i - output.mp4 | |
$cmd="ffmpeg -y -f image2pipe -framerate 1 -i - -framerate 1 -crf 0 -filter:v \"crop=2560:1440:0:0\" output.mp4"; | |
echo "executing: (pipe stuff) {$cmd}\n"; | |
$proc=popen($cmd,"wb"); | |
foreach($imageBinaries as $key=>$imageBinary){ | |
fwrite_all($proc,$imageBinary); | |
} | |
pclose($proc); | |
} | |
if(0){ | |
echo "executing: {$cmd}\n"; | |
passthru($cmd); | |
} | |
if(0){echo "SLEEPING";sleep(99999);} | |
foreach($imageBinaries as $key=>$imageBinary){ | |
unlink($temp_folder. DIRECTORY_SEPARATOR . "extracted{$key}.jpg"); | |
} | |
rmdir($temp_folder); | |
} | |
function extractImageBinaries(string $content):array{ | |
$ret = array(); | |
while(false!==($next_pos=strpos($content,'Content-Type: image'))){ | |
$content=substr($content,$next_pos); | |
$crlf2="\r\n\r\n"; | |
$next_pos=strpos($content,$crlf2); | |
if(false===$next_pos){throw new \LogicException("rnrn not found!?");} | |
$content=substr($content,$next_pos+strlen($crlf2)); | |
$imageEnd=strpos($content,$crlf2); | |
if(false===$imageEnd){throw new \LogicException("unable to find imageEnd!");} | |
$image_base64=substr($content,0,$imageEnd); | |
$image_binary=base64_decode($image_base64,true); | |
if(!is_string($image_binary)){ | |
throw new \LogicException("extracted base64 is invalid!?"); | |
} | |
$ret[]=$image_binary; | |
} | |
return $ret; | |
} | |
function init(){ | |
error_reporting(E_ALL); | |
stream_set_blocking(STDIN,false); | |
} | |
function fwrite_all($handle, string $data): void | |
{ | |
$len = $original_len = strlen($data); | |
$written_total = 0; | |
while ($len > 0) { | |
$written_now = fwrite($handle, $data); | |
if ($written_now === $len) { | |
return; | |
} | |
if ($written_now <= 0) { | |
throw new \RuntimeException("could only write {$written_total}/{$original_len} bytes!"); | |
} | |
$written_total += $written_now; | |
$data = substr($data, $written_now); | |
$len -= $written_now; | |
assert($len > 0); | |
} | |
} | |
class AutoIt | |
{ | |
protected $srch; | |
protected $srcf; | |
protected $au3exe_path; | |
function __construct(string $au3exe_path = "autodetect") | |
{ | |
if (empty($au3exe_path) || $au3exe_path === "autodetect") { | |
$au3exe_path = $this->_LocateAu3Exe(false); | |
} | |
$au3exe_path = $this->_cygwinify_filepath($au3exe_path); | |
if (!is_executable($au3exe_path)) { | |
throw new \InvalidArgumentException("supplied au3exe_path is not executable!"); | |
} | |
$this->au3exe_path = $au3exe_path; | |
if (!($this->srch = tmpfile())) { | |
throw new \RuntimeException("tmpfile failed!"); | |
} | |
$this->srcf = $this->_uncygwinify_filepath(stream_get_meta_data($this->srch)['uri']); | |
} | |
public static function _uncygwinify_filepath(string $path) : string | |
{ | |
static $is_cygwin_cache = null; | |
if ($is_cygwin_cache === null) { | |
$is_cygwin_cache = (false !== stripos(PHP_OS, "cygwin")); | |
} | |
if ($is_cygwin_cache) { | |
return trim(shell_exec("cygpath -aw " . escapeshellarg($path))); | |
} else { | |
return $path; | |
} | |
} | |
public static function _cygwinify_filepath(string $path) : string | |
{ | |
static $is_cygwin_cache = null; | |
if ($is_cygwin_cache === null) { | |
$is_cygwin_cache = (false !== stripos(PHP_OS, "cygwin")); | |
} | |
if ($is_cygwin_cache) { | |
return trim(shell_exec("cygpath -a " . escapeshellarg($path))); | |
//return "/cygdrive/" . strtr($path, array(':' => '', '\\' => '/')); | |
} else { | |
return $path; | |
} | |
} | |
public static function _LocateAu3Exe(bool $force_refresh_cache = false) : string | |
{ | |
static $cache = null; | |
if (!$force_refresh_cache && $cache !== null) { | |
return $cache; | |
} | |
$paths = [ | |
'C:\Program Files (x86)\AutoIt3\AutoIt3_x64.exe', | |
'C:\Program Files (x86)\AutoIt3\AutoIt3.exe', | |
'C:\Program Files\AutoIt3\AutoIt3_x64.exe', | |
'C:\Program Files\AutoIt3\AutoIt3.exe' | |
]; | |
foreach ($paths as $path) { | |
if (is_executable($path)) { | |
$cache = $path; | |
return $cache; | |
} | |
} | |
throw new \RuntimeException("unable to find AutoIt3.exe! supply the path in the constructor!"); | |
} | |
function __destruct() | |
{ | |
fclose($this->srch); // thanks to tmpfile(), it's auto-deleted upon being fclose()'ed. | |
} | |
public static function quote_string(string $str) : string | |
{ | |
return '"' . str_replace('"', '""', $str) . '"'; | |
} | |
protected static function _my_shell_exec(string $cmd, string &$stdout = null, string &$stderr = null) : int | |
{ | |
$stdout = ""; | |
$stderr = ""; | |
// pipes would be faster but using tmpfile simplifies the code.. | |
$_stdout = tmpfile(); | |
$_stderr = tmpfile(); | |
try { | |
$descriptorspec = array( | |
0 => array("pipe", "rb"), // stdin | |
1 => $_stdout, | |
2 => $_stderr | |
); | |
$proc = proc_open($cmd, $descriptorspec, $pipes); | |
fclose($pipes[0]); | |
$ret = proc_close($proc); | |
// in theory a single stream_get_contents call with correct arguments would suffice, | |
// but in practice: https://bugs.php.net/bug.php?id=76268 | |
rewind($_stderr); | |
rewind($_stdout); | |
$stdout = stream_get_contents($_stdout); | |
$stderr = stream_get_contents($_stderr); | |
return $ret; | |
} | |
finally { | |
fclose($_stderr); | |
fclose($_stdout); | |
} | |
} | |
public function exec(string $code, string &$stdout = null, string &$stderr = null) : void | |
{ | |
$stdout = ""; | |
$stderr = ""; | |
fwrite($this->srch, $code); | |
try { | |
// use /ErrorStdOut ? maybe someday. | |
$cmd = escapeshellarg($this->au3exe_path) . " " . escapeshellarg($this->srcf); | |
$ret = $this->_my_shell_exec($cmd, $stdout, $stderr); | |
if ($ret !== 0) { | |
var_dump($code); | |
throw new \RuntimeException("AutoIt failed, returned error code {$ret}"); | |
} | |
} | |
finally { | |
rewind($this->srch); | |
ftruncate($this->srch, 0); | |
} | |
} | |
public function MouseMove(int $x, int $y, int $speed = 10) : void | |
{ | |
$this->exec("MouseMove({$x}, {$y}, {$speed});"); | |
} | |
public function MouseClick(string $button = "left", int $x = null, int $y = null, int $clicks = 1, int $speed = 10) : void | |
{ | |
if ($x === null) { | |
$x = "Default"; | |
} | |
if ($y === null) { | |
$y = "Default"; | |
} | |
$stdout = ""; | |
$this->exec("ConsoleWrite(MouseClick (" . $this->quote_string($button) . ",{$x},{$y},{$clicks},{$speed}));", $stdout); | |
if (trim($stdout) !== "1") { | |
throw new \RuntimeException("the button is not in the list or invalid parameter as x without y."); | |
} | |
} | |
/** | |
* _ScreenCapture_Capture | |
* take a screenshot. | |
* if filename is provided, image stored in filename and returns NULL, | |
* otherwise returns the raw image binary in .png-format. | |
* @return string|null | |
*/ | |
public function _ScreenCapture_Capture(string $sFileName = null, int $iLeft = 0, int $iTop = 0, int $iRight = -1, int $iBottom = -1, bool $bCursor = true) | |
{ | |
$myfh = null; | |
if (empty($sFileName)) { | |
$myfh = tmpfile(); | |
$sFileName = stream_get_meta_data($myfh)['uri'] . ".png"; | |
} elseif (empty(pathinfo($sFileName, PATHINFO_EXTENSION))) { | |
throw new \InvalidArgumentException("sFileName must either be NULL -or- have an extension (.jpg,.png,.bmp, etc) (AutoIt requirement.)"); | |
} | |
$code = | |
"#include <ScreenCapture.au3>\r\n" . | |
"_ScreenCapture_Capture ( " . $this->quote_string($this->_uncygwinify_filepath($sFileName)) . ", {$iLeft}, {$iTop},{$iRight},{$iBottom}," . ($bCursor ? "True" : "False") . " );\r\n" . | |
"ConsoleWrite(@error);"; | |
$this->exec($code, $stdout); | |
$ret = null; | |
if (!empty($myfh)) { | |
$ret = file_get_contents($sFileName); | |
fclose($myfh); | |
unlink($sFileName); | |
} | |
if (trim($stdout) !== "0") { | |
throw new \RuntimeException("AutoIt's @error was not 0 at exit, it was: " . var_export($stdout, true)); | |
} | |
return $ret; | |
} | |
/** | |
* Pauses execution of the script until the requested window is active. | |
* returns bool(true) on success, and bool(false) on timeout. | |
* https://www.autoitscript.com/autoit3/docs/functions/WinWaitActive.htm | |
* @param string $title | |
* @param string $text | |
* @param integer $timeout | |
* @return boolean | |
*/ | |
public function WinWaitActive(string $title, string $text = "", int $timeout = 0) : bool | |
{ | |
$this->exec("ConsoleWrite(WinWaitActive (" . $this->quote_string($title) . ", " . $this->quote_string($text) . ",{$timeout}));", $stdout); | |
return (trim($stdout) !== "0"); | |
} | |
/** | |
* Sends simulated keystrokes to the active window. | |
* https://www.autoitscript.com/autoit3/docs/functions/Send.htm | |
* | |
* @param string $keys | |
* @param boolean $raw | |
* @param integer $SendKeyDelay | |
* @param integer $SendKeyDownDelay | |
* @return void | |
*/ | |
public function Send(string $keys, bool $raw = false, int $SendKeyDelay = 5, int $SendKeyDownDelay = 5) : void | |
{ | |
$code = | |
"AutoItSetOption ( 'SendKeyDelay', {$SendKeyDelay});\r\n" . | |
"AutoItSetOption ( 'SendKeyDownDelay', {$SendKeyDownDelay});\r\n" . | |
"Send(" . $this->quote_string($keys) . "," . ((int)$raw) . ");"; | |
$this->exec($code); | |
} | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment