add JPG preview generation for pages and update routing logic
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -1 +1,3 @@
|
||||
vendor
|
||||
vendor
|
||||
pages/*.html
|
||||
web/previews/*.jpg
|
||||
2
pages/.gitignore
vendored
2
pages/.gitignore
vendored
@@ -1,2 +0,0 @@
|
||||
*
|
||||
!.gitignore
|
||||
@@ -4,6 +4,100 @@ declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
/**
|
||||
* Render a JPG preview for a generated page using a local headless browser.
|
||||
*/
|
||||
function renderPagePreview(string $seed, string $htmlFilePath): void
|
||||
{
|
||||
$previewDir = __DIR__ . '/previews';
|
||||
if (! is_dir($previewDir)) {
|
||||
mkdir($previewDir, 0775, true);
|
||||
}
|
||||
|
||||
$realHtmlPath = realpath($htmlFilePath);
|
||||
if ($realHtmlPath === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$pageUrl = 'file://' . $realHtmlPath;
|
||||
$previewPngPath = $previewDir . '/' . $seed . '.png';
|
||||
$previewJpgPath = $previewDir . '/' . $seed . '.jpg';
|
||||
|
||||
$browserCandidates = [
|
||||
'/usr/bin/google-chrome',
|
||||
'/usr/bin/google-chrome-stable',
|
||||
'/usr/bin/chromium',
|
||||
'/usr/bin/chromium-browser',
|
||||
'google-chrome',
|
||||
'google-chrome-stable',
|
||||
'chromium',
|
||||
'chromium-browser',
|
||||
];
|
||||
$browserBinary = null;
|
||||
|
||||
foreach ($browserCandidates as $candidate) {
|
||||
$candidatePath = null;
|
||||
|
||||
if (str_starts_with($candidate, '/')) {
|
||||
if (! is_executable($candidate)) {
|
||||
continue;
|
||||
}
|
||||
$candidatePath = $candidate;
|
||||
} else {
|
||||
$pathOut = [];
|
||||
$pathExitCode = 1;
|
||||
exec('command -v ' . escapeshellarg($candidate) . ' 2>/dev/null', $pathOut, $pathExitCode);
|
||||
if ($pathExitCode !== 0 || ! isset($pathOut[0]) || $pathOut[0] === '') {
|
||||
continue;
|
||||
}
|
||||
$candidatePath = $pathOut[0];
|
||||
}
|
||||
|
||||
$versionOut = [];
|
||||
$versionExitCode = 1;
|
||||
exec(escapeshellarg($candidatePath) . ' --version 2>/dev/null', $versionOut, $versionExitCode);
|
||||
if ($versionExitCode === 0) {
|
||||
$browserBinary = $candidatePath;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($browserBinary === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$command = sprintf(
|
||||
'%s --headless --disable-gpu --no-sandbox --disable-dev-shm-usage --hide-scrollbars --window-size=1440,900 --screenshot=%s --virtual-time-budget=8000 --run-all-compositor-stages-before-draw %s 2>/dev/null',
|
||||
escapeshellarg($browserBinary),
|
||||
escapeshellarg($previewPngPath),
|
||||
escapeshellarg($pageUrl)
|
||||
);
|
||||
|
||||
$output = [];
|
||||
$exitCode = 1;
|
||||
exec($command, $output, $exitCode);
|
||||
|
||||
if ($exitCode !== 0 || ! file_exists($previewPngPath)) {
|
||||
@unlink($previewPngPath);
|
||||
return;
|
||||
}
|
||||
|
||||
if (function_exists('imagecreatefrompng') && function_exists('imagejpeg')) {
|
||||
$image = @imagecreatefrompng($previewPngPath);
|
||||
if ($image !== false) {
|
||||
imagejpeg($image, $previewJpgPath, 85);
|
||||
imagedestroy($image);
|
||||
}
|
||||
}
|
||||
|
||||
if (file_exists($previewJpgPath)) {
|
||||
@unlink($previewPngPath);
|
||||
} else {
|
||||
// Keep PNG as fallback if JPG conversion is unavailable.
|
||||
@rename($previewPngPath, $previewJpgPath);
|
||||
}
|
||||
}
|
||||
|
||||
header('Content-Type: text/html; charset=UTF-8');
|
||||
|
||||
$startedAt = microtime(true);
|
||||
@@ -138,7 +232,9 @@ try {
|
||||
|
||||
//echo $html;
|
||||
|
||||
file_put_contents(__DIR__ . '/../pages/' . $seed . '.html', $html);
|
||||
$htmlPath = __DIR__ . '/../pages/' . $seed . '.html';
|
||||
file_put_contents($htmlPath, $html);
|
||||
renderPagePreview($seed, $htmlPath);
|
||||
|
||||
} catch (Throwable $e) {
|
||||
http_response_code(500);
|
||||
|
||||
@@ -1,25 +1,87 @@
|
||||
<?php
|
||||
$url = array_filter(explode('/',ltrim(parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH),'/')));
|
||||
|
||||
if (count($url) === 1 && str_starts_with($url[0], 'seed-')) {
|
||||
|
||||
$file = __DIR__ . '/../pages/' . $url[0] . '.html';
|
||||
if (file_exists($file)) {
|
||||
header('Content-Type: text/html; charset=UTF-8');
|
||||
echo file_get_contents($file);
|
||||
exit;
|
||||
}
|
||||
else {
|
||||
http_response_code(404);
|
||||
echo 'Page not found';
|
||||
exit;
|
||||
}
|
||||
}
|
||||
else if($url[0] === 'generate') {
|
||||
include_once __DIR__ . '/generator.php';
|
||||
declare(strict_types=1);
|
||||
|
||||
$path = (string) parse_url($_SERVER['REQUEST_URI'] ?? '/', PHP_URL_PATH);
|
||||
$segments = array_values(array_filter(explode('/', ltrim($path, '/'))));
|
||||
$firstSegment = $segments[0] ?? '';
|
||||
|
||||
if (count($segments) === 1 && str_starts_with($firstSegment, 'seed-')) {
|
||||
$file = __DIR__ . '/../pages/' . $firstSegment . '.html';
|
||||
if (file_exists($file)) {
|
||||
header('Content-Type: text/html; charset=UTF-8');
|
||||
echo file_get_contents($file);
|
||||
exit;
|
||||
}
|
||||
|
||||
http_response_code(404);
|
||||
echo 'Page not found';
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($firstSegment === 'generate') {
|
||||
include_once __DIR__ . '/generator.php';
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($firstSegment === 'list') {
|
||||
header('Content-Type: text/html; charset=UTF-8');
|
||||
|
||||
$pageFiles = glob(__DIR__ . '/../pages/seed-*.html') ?: [];
|
||||
usort($pageFiles, static fn (string $a, string $b): int => filemtime($b) <=> filemtime($a));
|
||||
|
||||
echo '<!doctype html><html lang="en"><head><meta charset="UTF-8">';
|
||||
echo '<meta name="viewport" content="width=device-width, initial-scale=1.0">';
|
||||
echo '<title>Generated Seed Pages</title>';
|
||||
echo '<script src="https://cdn.tailwindcss.com"></script></head>';
|
||||
echo '<body class="min-h-screen bg-zinc-950 text-zinc-100 p-6 md:p-10">';
|
||||
echo '<div class="max-w-7xl mx-auto">';
|
||||
echo '<div class="flex flex-wrap items-center justify-between gap-4 mb-8">';
|
||||
echo '<h1 class="text-3xl md:text-4xl font-bold">Generated Seeds</h1>';
|
||||
echo '<a href="/" class="px-4 py-2 rounded-md bg-emerald-600 hover:bg-emerald-500 transition text-white font-medium">Generate New</a>';
|
||||
echo '</div>';
|
||||
|
||||
if ($pageFiles === []) {
|
||||
echo '<p class="text-zinc-300">No generated seed pages found yet.</p>';
|
||||
} else {
|
||||
echo '<div class="grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-3 gap-6">';
|
||||
|
||||
foreach ($pageFiles as $pageFile) {
|
||||
$seed = basename($pageFile, '.html');
|
||||
$previewWebPath = '/previews/' . $seed . '.jpg';
|
||||
$previewFsPath = __DIR__ . '/previews/' . $seed . '.jpg';
|
||||
$hasPreview = file_exists($previewFsPath);
|
||||
|
||||
$title = $seed;
|
||||
$rawHtml = file_get_contents($pageFile);
|
||||
if ($rawHtml !== false && preg_match('/<title>(.*?)<\/title>/is', $rawHtml, $matches) === 1) {
|
||||
$title = trim(strip_tags($matches[1]));
|
||||
}
|
||||
|
||||
$updated = date('Y-m-d H:i:s', filemtime($pageFile));
|
||||
|
||||
echo '<a href="/' . htmlspecialchars($seed, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8') . '" class="block rounded-xl overflow-hidden border border-zinc-800 bg-zinc-900/70 hover:border-emerald-500 transition">';
|
||||
echo '<div class="aspect-[16/10] bg-zinc-800">';
|
||||
if ($hasPreview) {
|
||||
echo '<img loading="lazy" src="' . htmlspecialchars($previewWebPath, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8') . '" alt="Preview for ' . htmlspecialchars($seed, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8') . '" class="w-full h-full object-cover">';
|
||||
} else {
|
||||
echo '<div class="w-full h-full grid place-items-center text-zinc-400 text-sm">Preview not available yet</div>';
|
||||
}
|
||||
echo '</div>';
|
||||
echo '<div class="p-4">';
|
||||
echo '<h2 class="text-lg font-semibold mb-1 line-clamp-2">' . htmlspecialchars($title, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8') . '</h2>';
|
||||
echo '<p class="text-xs text-zinc-400 mb-1">' . htmlspecialchars($seed, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8') . '</p>';
|
||||
echo '<p class="text-xs text-zinc-500">Updated: ' . htmlspecialchars($updated, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8') . '</p>';
|
||||
echo '</div></a>';
|
||||
}
|
||||
|
||||
echo '</div>';
|
||||
}
|
||||
|
||||
echo '</div></body></html>';
|
||||
exit;
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user