2024-04-17 14:27:30 +02:00
|
|
|
<?php
|
|
|
|
|
|
|
|
namespace App\Services;
|
|
|
|
|
|
|
|
use Exception;
|
|
|
|
use Facebook\WebDriver\Chrome\ChromeOptions;
|
|
|
|
use Facebook\WebDriver\Exception\Internal\UnexpectedResponseException;
|
|
|
|
use Facebook\WebDriver\Exception\UnknownErrorException;
|
|
|
|
use Facebook\WebDriver\Exception\UnrecognizedExceptionException;
|
|
|
|
use Facebook\WebDriver\Remote\DesiredCapabilities;
|
|
|
|
use Facebook\WebDriver\Remote\RemoteWebDriver;
|
|
|
|
use Facebook\WebDriver\WebDriverBy;
|
|
|
|
use Illuminate\Support\Str;
|
2024-04-17 14:49:46 +02:00
|
|
|
use Intervention\Image\Laravel\Facades\Image;
|
2024-04-17 14:27:30 +02:00
|
|
|
use Laravel\Dusk\Browser as DuskBrowser;
|
|
|
|
use Laravel\Dusk\Chrome\SupportsChrome;
|
|
|
|
use Laravel\Dusk\Concerns\ProvidesBrowser;
|
|
|
|
|
|
|
|
class Browser
|
|
|
|
{
|
|
|
|
use ProvidesBrowser;
|
|
|
|
use SupportsChrome;
|
|
|
|
|
|
|
|
private $driver;
|
|
|
|
|
|
|
|
private $tries = 0;
|
|
|
|
|
|
|
|
private $screenshot = null;
|
|
|
|
|
|
|
|
private $title = null;
|
|
|
|
|
|
|
|
private $description = null;
|
|
|
|
|
2024-04-20 23:27:47 +02:00
|
|
|
public function __construct(public string $url, public ?string $optionsSet = 'normal')
|
2024-04-17 14:27:30 +02:00
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
public function name()
|
|
|
|
{
|
|
|
|
return Str::slug($this->url);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function dataname()
|
|
|
|
{
|
|
|
|
return Str::slug('data_' . $this->url);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2024-04-17 14:49:46 +02:00
|
|
|
* Return screenshot content as a jpg resource
|
2024-04-17 14:27:30 +02:00
|
|
|
*/
|
|
|
|
public function getScreenshot()
|
|
|
|
{
|
|
|
|
return $this->screenshot;
|
|
|
|
}
|
|
|
|
|
2024-04-17 14:49:46 +02:00
|
|
|
/**
|
|
|
|
* Return page title
|
|
|
|
*/
|
2024-04-17 14:27:30 +02:00
|
|
|
public function getTitle()
|
|
|
|
{
|
|
|
|
return $this->title;
|
|
|
|
}
|
|
|
|
|
2024-04-17 14:49:46 +02:00
|
|
|
/**
|
|
|
|
* Return page description, if any is found
|
|
|
|
*/
|
2024-04-17 14:27:30 +02:00
|
|
|
public function getDescription()
|
|
|
|
{
|
|
|
|
return $this->description;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Navigate to specified URL
|
|
|
|
*/
|
|
|
|
public function go()
|
|
|
|
{
|
|
|
|
$shouldRetry = false;
|
|
|
|
|
|
|
|
DuskBrowser::$storeScreenshotsAt = storage_path('app/screenshots');
|
|
|
|
DuskBrowser::$storeConsoleLogAt = storage_path('logs/dusk');
|
|
|
|
DuskBrowser::$storeSourceAt = storage_path('app/sources');
|
|
|
|
|
|
|
|
$this->closeAll();
|
|
|
|
|
|
|
|
try {
|
|
|
|
$this->browse(fn (DuskBrowser $browser) => $this->handleNavigation($browser));
|
|
|
|
} catch (UnknownErrorException $ex) {
|
|
|
|
// This could happen when SSL certificat is expired
|
|
|
|
} catch (UnexpectedResponseException $ex) {
|
|
|
|
dump($ex->getMessage());
|
|
|
|
} catch (UnrecognizedExceptionException $ex) {
|
|
|
|
// This could happen on - some - YouTube links. Using the "no-gpu"
|
|
|
|
// preset fixes the problem
|
|
|
|
$shouldRetry = true;
|
|
|
|
|
|
|
|
$this->optionsSet = 'no-gpu';
|
|
|
|
} finally {
|
|
|
|
$this->closeAll();
|
|
|
|
}
|
|
|
|
|
|
|
|
$this->tries++;
|
|
|
|
|
|
|
|
if ($shouldRetry) {
|
|
|
|
if ($this->tries === 3) {
|
|
|
|
throw new Exception(sprintf('Maximum tries reach for url %s', $this->url));
|
|
|
|
}
|
|
|
|
|
|
|
|
unset($this->driver);
|
|
|
|
|
|
|
|
return $this->go();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-20 23:27:47 +02:00
|
|
|
/**
|
|
|
|
* Create the RemoteWebDriver instance.
|
|
|
|
*/
|
|
|
|
protected function driver(): RemoteWebDriver
|
|
|
|
{
|
|
|
|
if (!isset($this->driver)) {
|
|
|
|
$options = $this->getDriverOptions();
|
|
|
|
|
|
|
|
$this->driver = RemoteWebDriver::create(
|
|
|
|
$_ENV['DUSK_DRIVER_URL'],
|
|
|
|
DesiredCapabilities::chrome()->setCapability(
|
|
|
|
ChromeOptions::CAPABILITY, $options
|
|
|
|
)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
return $this->driver;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return driver options
|
|
|
|
*/
|
|
|
|
protected function getDriverOptions()
|
|
|
|
{
|
|
|
|
$options = (new ChromeOptions)
|
|
|
|
->addArguments(
|
|
|
|
collect(config(sprintf('browser.%s', $this->optionsSet)))
|
|
|
|
->all()
|
|
|
|
);
|
|
|
|
|
|
|
|
return $options;
|
|
|
|
}
|
|
|
|
|
2024-04-17 14:27:30 +02:00
|
|
|
private function handleNavigation(DuskBrowser $browser)
|
|
|
|
{
|
|
|
|
$browser->resize(1920, 1080)
|
|
|
|
->visit($this->url)
|
|
|
|
->pause(1000);
|
|
|
|
|
2024-04-17 14:49:46 +02:00
|
|
|
$this->screenshot = $this->takeScreenshot($browser);
|
2024-04-17 14:27:30 +02:00
|
|
|
$this->title = $browser->driver->getTitle();
|
|
|
|
$this->description = $this->findDescription($browser);
|
|
|
|
|
|
|
|
$browser->quit();
|
|
|
|
}
|
|
|
|
|
|
|
|
private function findDescription(DuskBrowser $browser)
|
|
|
|
{
|
|
|
|
$description = null;
|
|
|
|
|
|
|
|
try {
|
|
|
|
$descriptionMeta = $browser->driver->findElement(WebDriverBy::xpath("//meta[@name='description']"));
|
|
|
|
$description = $descriptionMeta->getAttribute('content');
|
|
|
|
} finally {
|
|
|
|
}
|
|
|
|
|
|
|
|
return $description;
|
|
|
|
}
|
2024-04-17 14:49:46 +02:00
|
|
|
|
|
|
|
private function takeScreenshot(DuskBrowser $browser)
|
|
|
|
{
|
|
|
|
$contents = $browser->driver->takeScreenshot();
|
|
|
|
$image = Image::read($contents);
|
|
|
|
|
|
|
|
$this->screenshot = $image->toJpeg();
|
|
|
|
|
|
|
|
return $this->screenshot;
|
|
|
|
}
|
2024-04-17 14:27:30 +02:00
|
|
|
}
|