scriptedbrowser/scriptedbrowser/main.php
2024-08-29 20:09:46 +02:00

346 lines
8.8 KiB
PHP

<?php
/**
* SPDX-FileCopyrightText: 2024 Sascha Nitsch (grumpydeveloper) https://contentnation.net/@grumpydevelop
* SPDX-License-Identifier: GPL-3.0-or-later
*
* @author Sascha Nitsch (grumpydeveloper)
**/
namespace ScriptedBrowser;
/**
* Calculate bezier blending from 0...1
*
* @param float $t input value 0...1
* @return float
*/
function bezierBlend(float $t)
{
return $t * $t * (3.0 - 2.0 * $t);
}
/**
* main class
*/
class Main
{
/**
* control instance
*
* @var Control
*/
private $control;
/**
* display id
* @var int $display
*/
private $display;
/**
* recording process
* @var ?resource
*/
private $ffmpeg = null;
/**
* frame rate of recording
*
* @var int $frameRate
*/
private $frameRate = 30;
/**
* X mouse offset of browser to left edge
*
* @var int $mouseOffsetX
*/
private $mouseOffsetX = 0;
/**
* Y mouse offset of browser to top edge
*
* @var int $mouseOffsetY
*/
private $mouseOffsetY = 0;
/**
* mouse movement speed (pixel per second)
*
* @var int $mouseSpeed
*/
private $mouseSpeed = 500;
/**
* mouse position X
*
* @var int $mouseX
*/
private $mouseX = 0;
/**
* mouse position Y
*
* @var int $mouseY
*/
private $mouseY = 0;
/**
* typing speed in characters/s
*
* @var int $typeSpeed
*/
private $typeSpeed = 5;
/**
* var storage
*
* @var array<string, string>
*/
private $vars = [];
/**
* constructor
*
* @param string $serverHost server host name
* @param int $serverPort server port
* @param int $display VNC display id
*/
public function __construct($serverHost, $serverPort, $display = 6)
{
require 'commands/base.php';
$this->display = $display;
$this->control = new Control($serverHost, $serverPort, $display);
// open vnc session
$this->control->openVNC();
// open xdotool
$this->control->openInput();
// open/connect to browser
$sessionFile = @fopen('.session', 'r');
$session = null;
if ($sessionFile !== false) {
$session = trim(fgets($sessionFile));
fclose($sessionFile);
if ($session == '') {
$session = null;
}
}
$newSession = $this->control->openGeckoDriver($session);
if ($newSession !== $session) {
$sessionFile = fopen('.session', 'w');
fwrite($sessionFile, $newSession."\n");
fclose($sessionFile);
}
$this->control->mousemove(959, 499);
$windowpos = $this->control->fullscreen();
$this->control->mousemove(960, 500);
$this->mouseX = 960;
$this->mouseY = 500;
$this->mouseOffsetX = $windowpos['x'];
$this->mouseOffsetY = $windowpos['y'];
}
/**
* get frame rate
*
* @return int frame rate
*/
public function getFrameRate()
{
return $this->frameRate;
}
/**
* get mouse offset
*
* @return array<int> X,Y offset
*/
public function getMouseOffset()
{
return [$this->mouseOffsetX, $this->mouseOffsetY];
}
/**
* get mouse speed
*
* @return int speed in pixel/s
*/
public function getMouseSpeed()
{
return $this->mouseSpeed;
}
/**
* get mouse X coordinate
*
* @return int X coordinate
*/
public function getMouseX()
{
return $this->mouseX;
}
/**
* get mouse X coordinate
*
* @return int X coordinate
*/
public function getMouseY()
{
return $this->mouseY;
}
/**
* get typeing speed
*
* @return int typing speed
*/
public function getTypeSpeed()
{
return $this->typeSpeed;
}
public function run(string $instructions) : void
{
$this->control->mousemove(960, 500);
$steps = file_get_contents($instructions);
$steps = preg_replace('/[\r\n]/', '', $steps);
$steps = preg_replace('!/\*.*\*/!', '', $steps);
$plans = json_decode($steps, true);
if ($plans === null) {
echo "error reading config\n";
return;
}
$plans = $plans["plans"];
foreach ($plans as $plan) {
echo "running " . $plan['id'] . "\n";
$video = $plan['video'];
$framerate = $plan['framerate'];
$this->mouseSpeed = $plan['mousespeed'];
$this->typeSpeed = $plan['typespeed'];
$starttime = 0;
$this->vars = $plan['vars'];
foreach ($plan['actions'] as $action) {
if (is_string($action)) {
$tokens = explode(' ', $action, 2);
$action = [
'name' => $tokens[0],
'parameter' => (sizeof($tokens) < 2) ? '' : $tokens[1]
];
}
$actionname = $action['name'];
if ($actionname[0] === '-') {
continue;
}
if ($starttime > 0) {
echo $actionname;
fflush(STDOUT);
}
switch ($actionname) {
case 'startrecording':
$this->ffmpeg = popen(
"ffmpeg -video_size 1920x1080 -framerate $framerate -f x11grab -i :" . $this->display . ".0 -c:v libx264 -preset ultrafast -qp 0 $video -y 2>>/dev/null > /dev/null",
"w"
);
$starttime = hrtime(true);
echo 'start recording';
break;
case 'stoprecording':
$this->stopRecording();
$starttime = 0;
echo "\n";
break;
default:
$functionName = '\\ScriptedBrowser\\Commands\\' . $actionname;
if (!function_exists($functionName)) {
$file = 'scriptedbrowser/commands/' . $actionname . '.php';
if (file_exists($file)) {
require_once($file);
}
}
if (function_exists($functionName)) {
if (!$functionName($this, $this->control, $action)) {
echo "\r" . $action['name'] . " ". $action['parameter'] . " failed\n";
$this->stopRecording();
return;
}
} else {
echo "unsup " . $action['name'] . " ". array_key_exists('parameter', $action) ? $action['parameter'] : ''. "\n";
$this->stopRecording();
return;
}
}
if ($starttime > 0) {
$now = hrtime(true);
$d = ($now - $starttime);
$sec = $d / 1000000000;
$frame = ($sec - floor($sec)) * $framerate;
printf(" -> %d:%.0f (%.3f)\n", $sec, $frame, $sec);
}
}
}
}
/**
* set mouse X position (internal state only)
*
* @param int $mouseX new mouse X position
* @return void
*/
public function setMouseX($mouseX)
{
$this->mouseX = $mouseX;
}
/**
* set mouse Y position (internal state only)
*
* @param int $mouseY new mouse Y position
* @return void
*/
public function setMouseY($mouseY)
{
$this->mouseY = $mouseY;
}
/**
* programmtically set var
*
* @param string $name name to set
* @param string $value value to set
* @return void
*/
public function setVar($name, $value)
{
$this->vars[$name] = $value;
}
/**
* stop recording
*
* @return void
*/
public function stopRecording()
{
if ($this->ffmpeg !== null) {
fwrite($this->ffmpeg, "q");
pclose($this->ffmpeg);
}
}
/**
* replace vars in input string
*
* @param string $input input string
* @return string input string with var replacements
*/
public function vars(string $input) : string
{
// find ${...}
while (preg_match("/\\$\\{([^\\}]+)\\}/", $input, $matches) == 1) {
$replacement = $this->vars[$matches[1]];
$input = str_replace($matches[0], $replacement, $input);
}
return $input;
}
}