Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save divinity76/98329daf4899d7b6322d03b61e8b471d to your computer and use it in GitHub Desktop.
Save divinity76/98329daf4899d7b6322d03b61e8b471d to your computer and use it in GitHub Desktop.
<?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