Use article IDs for permalinks and OG images

This commit is contained in:
hbrain 2026-06-02 09:50:00 +00:00
parent fbc4f1045a
commit 37e43b6bca
3 changed files with 168 additions and 59 deletions

130
new.php
View file

@ -62,9 +62,8 @@ function apply_thumbnail_overlay($imagePath, $title, $teaser) {
throw new RuntimeException('Thumbnail font file is missing.');
}
$dims = @getimagesize($imagePath);
$imgW = max(1, (int)($dims[0] ?? 1920));
$imgH = max(1, (int)($dims[1] ?? 1080));
$imgW = 1200;
$imgH = 630;
$normalize = static function ($text): string {
$text = trim((string)$text);
@ -92,11 +91,11 @@ function apply_thumbnail_overlay($imagePath, $title, $teaser) {
};
$fitTitleText = static function ($text) use ($wrapText): array {
$bestText = '';
$bestSize = 285;
$bestWidth = 18;
$bestSize = 95;
$bestWidth = 28;
$bestLines = 1;
for ($size = 320; $size >= 240; $size -= 5) {
$width = max(14, min(28, (int)round(22 + (285 - $size) / 8)));
for ($size = 94; $size >= 60; $size -= 4) {
$width = max(16, min(46, (int)round(24 + (94 - $size) / 2.7)));
$candidate = $wrapText($text, $width);
$lines = substr_count($candidate, "\n") + 1;
$bestText = $candidate;
@ -109,14 +108,14 @@ function apply_thumbnail_overlay($imagePath, $title, $teaser) {
};
[$titleText, $titleFontSize, $titleWrapWidth, $titleLines] = $fitTitleText($title);
$teaserText = $clampWrappedText($teaser, 34, 5);
$teaserText = $clampWrappedText($teaser, 38, 6);
$teaserLines = max(1, substr_count($teaserText, "\n") + 1);
// Move the whole block a bit right while keeping left-aligned text.
$left = max(18, (int)round($imgW * 0.055));
$titleY = max(24, (int)round($imgH * 0.06));
$titleLineStep = max(230, (int)round($imgH * 0.085));
$teaserY = max((int)round($imgH * 0.27), $titleY + ($titleLines * $titleLineStep) + max(140, (int)round($imgH * 0.06)));
$titleLineStep = max(68, (int)round($imgH * 0.043));
$teaserY = max((int)round($imgH * 0.25), $titleY + ($titleLines * $titleLineStep) + max(56, (int)round($imgH * 0.03)));
$overlayBase = tempnam(sys_get_temp_dir(), 'thumb_overlay_');
$outputPath = dirname($imagePath) . '/' . basename($imagePath) . '.tmp.' . uniqid('', true) . '.jpg';
@ -127,7 +126,7 @@ function apply_thumbnail_overlay($imagePath, $title, $teaser) {
@unlink($overlayBase);
$cmd1 = sprintf(
'/usr/bin/convert -size %dx%d xc:none -font %s -pointsize %d -fill "#F3F4F6" -stroke black -strokewidth 7 -interline-spacing 2 -gravity northwest -annotate +%d+%d %s -font %s -pointsize 220 -fill "#EADFC9" -stroke black -strokewidth 6 -interline-spacing -90 -gravity northwest -annotate +%d+%d %s %s 2>&1',
'/usr/bin/convert -size %dx%d xc:none -font %s -pointsize %d -fill "#F3F4F6" -stroke black -strokewidth 2 -interline-spacing 2 -gravity northwest -annotate +%d+%d %s -font %s -pointsize 58 -fill "#EADFC9" -stroke black -strokewidth 2 -interline-spacing -18 -gravity northwest -annotate +%d+%d %s %s 2>&1',
$imgW,
$imgH,
escapeshellarg($titleFontPath),
@ -150,25 +149,54 @@ function apply_thumbnail_overlay($imagePath, $title, $teaser) {
throw new RuntimeException('Failed to render thumbnail overlay: ' . trim((string)$out1));
}
$cmd2 = sprintf(
'/usr/bin/convert %s -modulate 56,78 %s -composite %s 2>&1',
escapeshellarg($imagePath),
escapeshellarg($overlayPath),
escapeshellarg($outputPath)
);
file_put_contents($logFile, "Composite CMD: " . $cmd2 . "\n", FILE_APPEND);
$out2 = shell_exec($cmd2);
file_put_contents($logFile, "Composite Output: " . $out2 . "\n", FILE_APPEND);
$maxBytes = 600 * 1024;
$qualities = [88, 84, 80, 76, 72, 68, 64, 60, 56, 52, 48];
$finalPath = '';
$finalSize = 0;
$finalOut = '';
if (!is_file($outputPath) || filesize($outputPath) < 100) {
@unlink($overlayPath);
@unlink($outputPath);
throw new RuntimeException('Failed to compose thumbnail overlay: ' . trim((string)$out2));
foreach ($qualities as $quality) {
$attemptPath = dirname($imagePath) . '/' . basename($imagePath) . '.tmp.' . uniqid('', true) . '.jpg';
$cmd2 = sprintf(
'/usr/bin/convert -filter Lanczos -define filter:blur=0.85 %s -resize %dx%d^ -gravity center -extent %dx%d -modulate 62,76 %s -composite -strip -interlace Plane -sampling-factor 4:2:0 -quality %d %s 2>&1',
escapeshellarg($imagePath),
$imgW,
$imgH,
$imgW,
$imgH,
escapeshellarg($overlayPath),
$quality,
escapeshellarg($attemptPath)
);
file_put_contents($logFile, "Composite CMD (q=$quality): " . $cmd2 . "\n", FILE_APPEND);
$out2 = shell_exec($cmd2);
file_put_contents($logFile, "Composite Output (q=$quality): " . $out2 . "\n", FILE_APPEND);
if (!is_file($attemptPath) || filesize($attemptPath) < 100) {
@unlink($attemptPath);
$finalOut = trim((string)$out2);
continue;
}
$finalPath = $attemptPath;
$finalSize = filesize($attemptPath);
$finalOut = trim((string)$out2);
if ($finalSize <= $maxBytes) break;
}
if (!@rename($outputPath, $imagePath)) {
if ($finalPath === '') {
@unlink($overlayPath);
@unlink($outputPath);
throw new RuntimeException('Failed to compose thumbnail overlay: ' . $finalOut);
}
if ($finalSize > $maxBytes) {
file_put_contents($logFile, "Thumbnail still above size limit: " . $finalSize . " bytes\n", FILE_APPEND);
}
if (!@rename($finalPath, $imagePath)) {
@unlink($overlayPath);
@unlink($finalPath);
throw new RuntimeException('Failed to replace thumbnail image.');
}
@ -700,13 +728,27 @@ function is_image_media_file($name){
$ext = strtolower(pathinfo((string)$name, PATHINFO_EXTENSION));
return in_array($ext, ['jpg','jpeg','png','webp','gif'], true);
}
function public_url_path($path){
$path = str_replace('\\', '/', trim((string)$path));
if ($path === '') return '';
$parts = array_values(array_filter(explode('/', $path), static fn($p) => $p !== ''));
return implode('/', array_map('rawurlencode', $parts));
}
function video_thumb_public_url($config, $videoName){
$videoName = basename((string)$videoName);
if ($videoName === '') return '';
$base = pathinfo($videoName, PATHINFO_FILENAME);
foreach (['jpg','jpeg','png','webp','gif'] as $ext) {
$file = $config['videos_dir'] . '/' . $base . '_thumb.' . $ext;
if (is_file($file)) return $config['public_videos'] . '/' . rawurlencode($base . '_thumb.' . $ext) . '?v=' . filemtime($file);
$info = video_thumb_source_info($config, $videoName);
$sourcePath = (string)($info['source_path'] ?? '');
if ($sourcePath !== '') {
$abs = str_starts_with($sourcePath, '/') ? $sourcePath : __DIR__ . '/' . ltrim($sourcePath, '/');
if (is_file($abs)) return public_url_path($sourcePath) . '?v=' . filemtime($abs);
}
$sourceFile = basename((string)($info['source_file'] ?? ''));
$inputDir = basename((string)($info['input_dir'] ?? ''));
if ($sourceFile !== '' && $inputDir !== '') {
$rel = 'in-dir/' . $inputDir . '/' . $sourceFile;
$abs = __DIR__ . '/' . $rel;
if (is_file($abs)) return public_url_path($rel) . '?v=' . filemtime($abs);
}
return '';
}
@ -1003,6 +1045,19 @@ try {
if (!$outputs) throw new RuntimeException('No rendered video found for this input directory yet.');
$src = $dir . '/' . $file;
$thumbName = $articleId . '_og.jpg';
$target = $config['videos_dir'] . '/' . $thumbName;
if (is_file($target)) @unlink($target);
if (!@copy($src, $target)) {
file_put_contents($logFile, "Failed to copy thumbnail.\n", FILE_APPEND); // LOG 2
throw new RuntimeException('Failed to write thumbnail.');
}
@chmod($target, 0664);
file_put_contents($logFile, "Thumbnail copied, now applying overlay...\n", FILE_APPEND); // LOG 3
// Apply text overlay after smart resizing/cropping to 1200x630
apply_thumbnail_overlay($target, (string)($meta['title'] ?? ''), (string)($meta['teaser'] ?? ''));
$written = [];
foreach ($outputs as $out) {
$base = pathinfo($out, PATHINFO_FILENAME);
@ -1011,25 +1066,13 @@ try {
$old = $config['thumbs_dir'] . '/' . $base . '.' . $oldExt;
if (is_file($old)) @unlink($old);
}
// Copy new thumbnail to videos_dir with _thumb.jpg suffix
$target = $config['videos_dir'] . '/' . $base . '_thumb.jpg';
if (!@copy($src, $target)) {
file_put_contents($logFile, "Failed to copy thumbnail.\n", FILE_APPEND); // LOG 2
throw new RuntimeException('Failed to write thumbnail.');
}
@chmod($target, 0664);
file_put_contents($logFile, "Thumbnail copied, now applying overlay...\n", FILE_APPEND); // LOG 3
// Apply text overlay
apply_thumbnail_overlay($target, (string)($meta['title'] ?? ''), (string)($meta['teaser'] ?? ''));
$thumbMeta = [
'article_id' => $articleId,
'input_dir' => basename($dir),
'source_file' => $file,
'source_path' => 'in-dir/' . basename($dir) . '/' . $file,
'thumb_file' => $thumbName,
'thumb_path' => 'out-dir/' . $thumbName,
'created_at' => gmdate('c'),
];
$thumbMetaPath = $config['videos_dir'] . '/' . $base . '_thumb.json';
@ -1037,7 +1080,6 @@ try {
throw new RuntimeException('Failed to write thumbnail metadata.');
}
@chmod($thumbMetaPath, 0664);
$written[] = basename($target);
}