251 lines
9.0 KiB
PHP
251 lines
9.0 KiB
PHP
<?php
|
|
|
|
namespace App\Jobs\Tasks;
|
|
|
|
use App\Helpers\FirstParty\OSSUploader\OSSUploader;
|
|
use App\Helpers\ThirdParty\DFS\SettingSerpLiveAdvanced;
|
|
use App\Jobs\PublishIndexPostJob;
|
|
use App\Models\NewsSerpResult;
|
|
use DFSClientV3\DFSClient;
|
|
use Exception;
|
|
use Illuminate\Support\Facades\Http;
|
|
use Image;
|
|
|
|
class GenerateArticleFeaturedImageTask
|
|
{
|
|
public static function handle($post)
|
|
{
|
|
$keyword = $post->main_keyword;
|
|
$title = $post->short_title;
|
|
$article_type = $post->type;
|
|
$country_iso = 'US';
|
|
$country_name = get_country_name_by_iso($country_iso);
|
|
|
|
$images = [];
|
|
|
|
$client = new DFSClient(
|
|
config('dataforseo.login'),
|
|
config('dataforseo.password'),
|
|
config('dataforseo.timeout'),
|
|
config('dataforseo.api_version'),
|
|
config('dataforseo.url'),
|
|
);
|
|
|
|
// You will receive SERP data specific to the indicated keyword, search engine, and location parameters
|
|
$serp_model = new SettingSerpLiveAdvanced();
|
|
|
|
$serp_model->setSe('google');
|
|
$serp_model->setSeType('images');
|
|
$serp_model->setKeyword($keyword);
|
|
$serp_model->setLocationName($country_name);
|
|
$serp_model->setDepth(100);
|
|
$serp_model->setLanguageCode('en');
|
|
$serp_res = $serp_model->getAsJson();
|
|
|
|
// try {
|
|
$serp_obj = json_decode($serp_res, false, 512, JSON_THROW_ON_ERROR);
|
|
|
|
//dd($serp_obj);
|
|
|
|
if ($serp_obj?->status_code == 20000) {
|
|
$json_file_name = config('platform.dataset.news.images_serp.file_prefix').str_slug($keyword).'-'.epoch_now_timestamp().'.json';
|
|
|
|
$upload_status = OSSUploader::uploadJson(
|
|
config('platform.dataset.news.images_serp.driver'),
|
|
config('platform.dataset.news.images_serp.path'),
|
|
$json_file_name,
|
|
$serp_obj);
|
|
|
|
if ($upload_status) {
|
|
$news_serp_result = new NewsSerpResult();
|
|
$news_serp_result->serp_provider = 'dfs';
|
|
$news_serp_result->serp_se = 'google';
|
|
$news_serp_result->serp_se_type = 'images';
|
|
$news_serp_result->serp_keyword = $keyword;
|
|
$news_serp_result->serp_country_iso = strtoupper($country_iso);
|
|
$news_serp_result->serp_cost = $serp_obj?->cost;
|
|
$news_serp_result->result_count = $serp_obj?->tasks[0]?->result[0]?->items_count;
|
|
$news_serp_result->filename = $json_file_name;
|
|
$news_serp_result->status = 'initial';
|
|
|
|
if ($news_serp_result->save()) {
|
|
|
|
$serp_items = $serp_obj?->tasks[0]?->result[0]?->items;
|
|
|
|
//dd($serp_items);
|
|
|
|
foreach ($serp_items as $item) {
|
|
if ($item->type == 'images_search') {
|
|
//dd($item);
|
|
$images[] = $item->source_url;
|
|
|
|
if (count($images) > 20) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
//return $news_serp_result;
|
|
} else {
|
|
throw new Exception('Uploading failed', 1);
|
|
}
|
|
} else {
|
|
throw new Exception('Data failed', 1);
|
|
}
|
|
// } catch (Exception $e) {
|
|
// return null;
|
|
// }
|
|
|
|
$numImagesInCanvas = 2;
|
|
|
|
if ($numImagesInCanvas > count($images)) {
|
|
$numImagesInCanvas = count($images);
|
|
}
|
|
|
|
$canvasWidth = 720;
|
|
$canvasHeight = 405;
|
|
|
|
$canvas = Image::canvas($canvasWidth, $canvasHeight);
|
|
|
|
// Add Images
|
|
$imageWidth = $canvasWidth / $numImagesInCanvas;
|
|
|
|
// Process and place each image
|
|
$xOffset = 0; // Horizontal offset to place each image
|
|
for ($i = 0; $i < count($images); $i++) {
|
|
$url = $images[$i];
|
|
|
|
try {
|
|
$imageResponse = Http::timeout(300)->withHeaders([
|
|
'User-Agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36',
|
|
])->get($url);
|
|
|
|
$imageContent = $imageResponse->body();
|
|
|
|
$image = Image::make($imageContent)
|
|
->resize(null, $canvasHeight, function ($constraint) {
|
|
$constraint->aspectRatio();
|
|
})
|
|
->resizeCanvas($imageWidth, $canvasHeight, 'center', false, [255, 255, 255, 0]);
|
|
//->blur(6)
|
|
|
|
$canvas->insert($image, 'top-left', $xOffset, 0);
|
|
$xOffset += $imageWidth;
|
|
} catch (Exception $e) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
///
|
|
|
|
$fontSize = 28;
|
|
$articleTypeFontSize = 24;
|
|
$padding = 15;
|
|
|
|
$fontPath = resource_path('fonts/Inter/Inter-Black.ttf');
|
|
|
|
// Split title into words and reconstruct lines
|
|
$words = explode(' ', $title);
|
|
$lines = [''];
|
|
$currentLineIndex = 0;
|
|
|
|
foreach ($words as $word) {
|
|
$potentialLine = $lines[$currentLineIndex] ? $lines[$currentLineIndex].' '.$word : $word;
|
|
|
|
$box = imagettfbbox($fontSize, 0, $fontPath, $potentialLine);
|
|
$textWidth = abs($box[2] - $box[0]);
|
|
|
|
if ($textWidth < $canvasWidth * 0.9) {
|
|
$lines[$currentLineIndex] = $potentialLine;
|
|
} else {
|
|
$currentLineIndex++;
|
|
$lines[$currentLineIndex] = $word;
|
|
}
|
|
}
|
|
|
|
// Calculate the dimensions of the article_type text
|
|
$articleTypeBox = imagettfbbox($articleTypeFontSize, 0, $fontPath, $article_type);
|
|
$articleTypeWidth = abs($articleTypeBox[2] - $articleTypeBox[0]);
|
|
$articleTypeHeight = abs($articleTypeBox[7] - $articleTypeBox[1]);
|
|
|
|
// Define the start Y position for the article type overlay
|
|
$articleOverlayStartY = $canvasHeight - ($fontSize * count($lines)) - ($articleTypeHeight + 4 * $padding);
|
|
|
|
// Create the blue overlay for the article type
|
|
$overlayWidth = $articleTypeWidth + 2 * $padding;
|
|
$canvas->rectangle(20, $articleOverlayStartY, 10 + $overlayWidth, $articleOverlayStartY + $articleTypeHeight + 2 * $padding, function ($draw) {
|
|
$draw->background([255, 255, 255, 0.8]);
|
|
});
|
|
|
|
// Overlay the article_type text within its overlay, centered horizontally and vertically
|
|
$textStartX = 20 + ($overlayWidth - $articleTypeWidth) / 2; // Center the text horizontally
|
|
$canvas->text(strtoupper($article_type), $textStartX, $articleOverlayStartY + ($articleTypeHeight + 2 * $padding) / 2, function ($font) use ($articleTypeFontSize, $fontPath) {
|
|
$font->file($fontPath);
|
|
$font->size($articleTypeFontSize);
|
|
$font->color('#0000FF');
|
|
$font->align('left');
|
|
$font->valign('middle'); // This ensures the text is vertically centered within the overlay
|
|
});
|
|
|
|
// Create the blue overlay for the title
|
|
$titleOverlayStartY = $articleOverlayStartY + $articleTypeHeight + 2 * $padding;
|
|
|
|
$canvas->rectangle(0, $titleOverlayStartY, $canvasWidth, $canvasHeight, function ($draw) {
|
|
$draw->background([0, 0, 255, 0.5]);
|
|
});
|
|
|
|
// Draw each line for the title
|
|
$yPosition = $titleOverlayStartY + $padding;
|
|
foreach ($lines as $line) {
|
|
$canvas->text($line, $canvasWidth / 2, $yPosition, function ($font) use ($fontSize, $fontPath) {
|
|
$font->file($fontPath);
|
|
$font->size($fontSize);
|
|
$font->color('#FFFFFF');
|
|
$font->align('center');
|
|
$font->valign('top');
|
|
});
|
|
$yPosition += $fontSize + $padding;
|
|
}
|
|
|
|
$epoch_now_timestamp = epoch_now_timestamp();
|
|
|
|
$filename = $post->slug.'-'.$epoch_now_timestamp.'.jpg';
|
|
|
|
$ok = OSSUploader::uploadFile('r2', 'post_images/', $filename, (string) $canvas->stream('jpeg'));
|
|
|
|
// LQIP
|
|
// Clone the main image for LQIP version
|
|
$lqipImage = clone $canvas;
|
|
|
|
// Apply blur
|
|
$lqipImage->blur(5); // Adjust to the desired blur level
|
|
|
|
// Pixelate the image
|
|
$lqipImage->pixelate(30); // Adjust this value based on the desired pixelation level
|
|
|
|
// Encode the image to a low-quality JPG
|
|
$lqipImage->encode('webp', 5);
|
|
|
|
// LQIP filename
|
|
$lqip_filename = $post->slug.'-'.$epoch_now_timestamp.'_lqip.webp';
|
|
|
|
// Upload the LQIP version using OSSUploader
|
|
$lqip_ok = OSSUploader::uploadFile('r2', 'post_images/', $lqip_filename, (string) $lqipImage->stream('webp'));
|
|
|
|
if ($ok && $lqip_ok) {
|
|
|
|
$post->featured_image = 'post_images/'.$filename;
|
|
$post->status = 'publish';
|
|
$post->save();
|
|
|
|
PublishIndexPostJob::dispatch($post)->onQueue('default')->onConnection('default');
|
|
|
|
return $post;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
}
|