297 lines
No EOL
11 KiB
PHP
297 lines
No EOL
11 KiB
PHP
<?php
|
|
|
|
namespace App\Soshot;
|
|
|
|
use App\DataBase\DataBase;
|
|
use App\Utils\ResizeToDemande;
|
|
use HeadlessChromium\Browser;
|
|
use HeadlessChromium\BrowserFactory;
|
|
use HeadlessChromium\Page;
|
|
use HeadlessChromium\Exception\BrowserConnectionFailed;
|
|
use HeadlessChromium\Exception\OperationTimedOut;
|
|
use Noodlehaus\Config;
|
|
|
|
if (!function_exists('n_print')) {
|
|
function n_print($data, $name = '') {
|
|
print_r($data, 1);
|
|
}
|
|
}
|
|
|
|
class MakeThumb {
|
|
|
|
private $queuePath = __DIR__ . '/../../cache/queue/';
|
|
private $thumbPath = __DIR__ . '/../../cache/img/';
|
|
private $fileSocket = __DIR__ . '/../../cache/tmp/chrome-php-socket';
|
|
private $fileList = [];
|
|
private $maxGenPerBatch = 2;
|
|
private $demandes = [];
|
|
private $open = false;
|
|
private $conf;
|
|
private $db;
|
|
private $chromePath = '';
|
|
private $tmpDir = '/tmp/chrome_soshot';
|
|
|
|
/**
|
|
* Creates a new instance of the class.
|
|
*
|
|
* This method loads the default and user configuration files using the `Config`
|
|
* class. It sets the maximum number of generations per batch, the Chrome path, and
|
|
* the queue path. It then retrieves the list of JSON files in the queue directory
|
|
* and decodes their contents to populate the `$demandes` property. Finally, it
|
|
* initializes the `DataBase` object and assigns it to the `$db` property.
|
|
*
|
|
* @return void
|
|
*/
|
|
function __construct() {
|
|
|
|
$this->conf = (object)Config::load([__DIR__ . '/../../datas/config.default.json', '?' . __DIR__ . '/../../datas/config.json'])->all();
|
|
$this->maxGenPerBatch = $this->conf->maxGenPerBatch;
|
|
|
|
$this->chromePath = $this->conf->chromePath;
|
|
|
|
foreach (array_slice(glob($this->queuePath . "*.json"), 0, $this->maxGenPerBatch) as $filename) {
|
|
$this->fileList[] = $filename;
|
|
}
|
|
|
|
foreach ($this->fileList as $demande) {
|
|
$this->demandes[] = json_decode(file_get_contents($demande));
|
|
}
|
|
$this->db = new DataBase();
|
|
}
|
|
|
|
/**
|
|
* Processes the demands in the queue.
|
|
*
|
|
* This method iterates over the list of demands in the `$demandes` property.
|
|
* For each demand, it sets the parameters for the `DataBase` object, writes the HMAC
|
|
* to a temporary file, and checks if the demand is already complete.
|
|
*
|
|
* If the demand is complete and not a PDF, it resizes the image to the demanded size using the
|
|
* `ResizeToDemande` class.
|
|
*
|
|
* If the demand is not complete, it generates the complete image using the `makeComplete` method.
|
|
*
|
|
* In either case, it deletes the demand from the queue using the `deleteQueue` method and
|
|
* updates the database using the `addUpdate` method.
|
|
*
|
|
* Finally, it sets the `$open` property to `true` to indicate that the queue is open.
|
|
* If the `$open` property is `true`, it generates a complete
|
|
* image for all demands in the queue using the `makeComplete` method.
|
|
*
|
|
* @return void
|
|
*/
|
|
public function compute() {
|
|
foreach ($this->demandes as $demande) {
|
|
$this->db->setParams($demande);
|
|
file_put_contents('/tmp/soshot_queue', $demande->hmac);
|
|
|
|
// todo verif permit type
|
|
|
|
if ($this->testComplete($demande->complete) && $demande->type !== 'pdf') {
|
|
if (ResizeToDemande::makeDemande($demande)) {
|
|
$this->deleteQueue($demande->hmac);
|
|
$this->db->addUpdate(1, $demande->type);
|
|
}
|
|
} else {
|
|
$this->makeComplete($demande);
|
|
if ($demande->type !== 'pdf') {
|
|
if (!ResizeToDemande::makeDemande($demande)) {
|
|
// todo log
|
|
}
|
|
}
|
|
$this->deleteQueue($demande->hmac);
|
|
$this->db->addUpdate(1, $demande->type);
|
|
|
|
$completeDemande = (object)[
|
|
'hmac' => $demande->hmac,
|
|
'url' => $demande->url,
|
|
'type' => 'complete'
|
|
];
|
|
$complete = new DataBase($completeDemande);
|
|
$complete->addUpdate(1, $demande->type);
|
|
}
|
|
$this->open = true;
|
|
|
|
if ($this->open === true) {
|
|
$this->makeComplete(null, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Deletes a demand from the queue.
|
|
*
|
|
* This method takes an HMAC as a parameter and deletes the corresponding JSON file
|
|
* from the queue directory using the `unlink` function.
|
|
*
|
|
* @param string $hmac The HMAC of the demand to delete.
|
|
*
|
|
* @return void
|
|
*/
|
|
private function deleteQueue(string $hmac) {
|
|
if (file_exists($this->queuePath . $hmac . '.json')) {
|
|
unlink($this->queuePath . $hmac . '.json');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Checks if a file exists.
|
|
*
|
|
* This method takes a file path as a parameter and checks if the file exists using
|
|
* the `file_exists` function. It returns `true` if the file exists, and `false`
|
|
* otherwise.
|
|
*
|
|
* @param string $complete The file path to check.
|
|
*
|
|
* @return bool `true` if the file exists, `false` otherwise.
|
|
*/
|
|
private function testComplete(string $complete): bool {
|
|
if (file_exists($complete)) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Generates a complete image or PDF for a demand.
|
|
*
|
|
* This method takes a demand object as a parameter and generates a complete image
|
|
* or PDF for the demand.
|
|
*
|
|
* If the `$close` parameter is set to `true`, it closes the browser and deletes the socket file.
|
|
* The method first checks if a socket file exists and connects to an existing browser if it does.
|
|
*
|
|
* If not, it creates a new browser instance using the `BrowserFactory` class.
|
|
*
|
|
* It then navigates to the URL specified in the demand and takes a screenshot of the page.
|
|
*
|
|
* If the demand type is 'pdf' or the `alwaysMakePdf` configuration option is set to `true`, it also
|
|
* generates a PDF of the page.
|
|
*
|
|
* Finally, it saves the screenshot and PDF to the appropriate file paths and
|
|
* updates the database using the `addUpdate` method.
|
|
*
|
|
* If an `OperationTimedOut` exception is thrown, it logs the error, closes the browser,
|
|
* deletes the socket file, copies an error image to the file path, and updates the
|
|
* database with an error status.
|
|
*
|
|
* @param object|null $demande The demand object containing the URL, HMAC, file path, and type of the demand.
|
|
* @param bool $close Whether to close the browser and delete the socket file after
|
|
* generating the complete image or PDF. Defaults to `false`.
|
|
*
|
|
* @return void
|
|
*/
|
|
private function makeComplete(object|null $demande, bool $close = false) {
|
|
if (file_exists($this->fileSocket)) {
|
|
$socket = \file_get_contents($this->fileSocket);
|
|
try {
|
|
$browser = BrowserFactory::connectToBrowser($socket);
|
|
} catch (BrowserConnectionFailed $e) {
|
|
$browser = $this->openBrowser();
|
|
}
|
|
} else {
|
|
$factory = new BrowserFactory($this->chromePath);
|
|
$browser = $factory->createBrowser([
|
|
'userAgent' => 'Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)',
|
|
'keepAlive' => true,
|
|
'headless' => true,
|
|
'windowSize' => [1920, 1080],
|
|
'userDataDir' => $this->tmpDir,
|
|
'customFlags' => [
|
|
'--disable-dev-shm-usage',
|
|
'--disable-gpu'
|
|
],
|
|
'noSandbox' => true,
|
|
//'connectionDelay' => 0.8, // add 0.8 second of delay between each instruction sent to chrome,
|
|
//'debugLogger' => 'php://stdout',
|
|
]);
|
|
\file_put_contents($this->fileSocket, $browser->getSocketUri(), LOCK_EX);
|
|
}
|
|
|
|
if ($close === true) {
|
|
$socket = \file_get_contents($this->fileSocket);
|
|
$browser = BrowserFactory::connectToBrowser($socket);
|
|
$browser->close();
|
|
unlink($this->fileSocket);
|
|
return true;
|
|
}
|
|
|
|
$dir = $this->thumbPath . substr($demande->hmac, 0, 4) . '/';
|
|
|
|
if (!is_dir($dir)) {
|
|
mkdir($dir);
|
|
}
|
|
|
|
try {
|
|
$page = $browser->createPage();
|
|
$page->navigate($demande->url)->waitForNavigation(Page::LOAD, 15000);
|
|
sleep(4);
|
|
|
|
$page->screenshot([
|
|
'captureBeyondViewport' => true,
|
|
'clip' => $page->getFullPageClip(),
|
|
'format' => $this->conf->fileFormat,
|
|
])->saveToFile($demande->complete);
|
|
|
|
if ($demande->type === 'pdf' || $this->conf->alwaysMakePdf === true) {
|
|
if ($this->conf->alwaysMakePdf === true) {
|
|
$pdfFile = str_replace($demande->type, 'pdf', $demande->filePath);
|
|
$pdfFile = str_replace($this->conf->fileFormat, 'pdf', $pdfFile);
|
|
} else {
|
|
$pdfFile = $demande->filePath;
|
|
}
|
|
|
|
$page->pdf([
|
|
'printBackground' => true,
|
|
'displayHeaderFooter' => true,
|
|
'paperWidth' => 8.268,
|
|
'paperHeight' => 11.693,
|
|
'scale' => 1
|
|
])->saveToFile($pdfFile);
|
|
|
|
if ($this->conf->alwaysMakePdf === true) {
|
|
$this->db->addUpdate(1, 'pdf');
|
|
}
|
|
}
|
|
$this->db->addUpdate(1, 'complete');
|
|
} catch (OperationTimedOut $e) {
|
|
// todo log
|
|
$socket = \file_get_contents($this->fileSocket);
|
|
$browser = BrowserFactory::connectToBrowser($socket);
|
|
$browser->close();
|
|
unlink($this->fileSocket);
|
|
copy(__DIR__ . '/../../src/images/error_thumb.png', $demande->filePath);
|
|
$this->db->addUpdate(2, $demande->type);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Creates and opens a new browser instance.
|
|
*
|
|
* This method creates a new browser instance using the `BrowserFactory` class with
|
|
* the specified configuration options. It then saves the socket URI to a file and
|
|
* returns the browser instance.
|
|
*
|
|
* @return Browser The created browser instance.
|
|
*/
|
|
private function openBrowser(): Browser {
|
|
$factory = new BrowserFactory($this->chromePath);
|
|
|
|
// Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)
|
|
$browser = $factory->createBrowser([
|
|
'userAgent' => 'Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)',
|
|
'keepAlive' => true,
|
|
'headless' => true,
|
|
'windowSize' => [1920, 1080],
|
|
'userDataDir' => $this->tmpDir,
|
|
'customFlags' => [
|
|
'--disable-dev-shm-usage',
|
|
'--disable-gpu'
|
|
],
|
|
'noSandbox' => true,
|
|
]);
|
|
|
|
\file_put_contents($this->fileSocket, $browser->getSocketUri(), LOCK_EX);
|
|
return $browser;
|
|
}
|
|
} |