Log and always regenerate MVLog maps on save
This commit is contained in:
parent
37e43b6bca
commit
6ba4050d9a
2 changed files with 50 additions and 26 deletions
|
|
@ -485,6 +485,7 @@ $siteHeaderTitle = $headerTitles[array_rand($headerTitles)];
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<?php if ($articleId === ''): ?>
|
||||||
<div class="pages">
|
<div class="pages">
|
||||||
<?php for($i=1;$i<=$pages;$i++):
|
<?php for($i=1;$i<=$pages;$i++):
|
||||||
$linkParams = $baseParams;
|
$linkParams = $baseParams;
|
||||||
|
|
@ -493,6 +494,7 @@ $siteHeaderTitle = $headerTitles[array_rand($headerTitles)];
|
||||||
<a class="<?=$i===$page?'active':''?>" href="<?=h(query_url($linkParams))?>"><?=$i?></a>
|
<a class="<?=$i===$page?'active':''?>" href="<?=h(query_url($linkParams))?>"><?=$i?></a>
|
||||||
<?php endfor; ?>
|
<?php endfor; ?>
|
||||||
</div>
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
<?php include __DIR__ . '/_footer.php'; ?>
|
<?php include __DIR__ . '/_footer.php'; ?>
|
||||||
|
|
|
||||||
54
new.php
54
new.php
|
|
@ -75,14 +75,14 @@ function apply_thumbnail_overlay($imagePath, $title, $teaser) {
|
||||||
$text = $normalize($text);
|
$text = $normalize($text);
|
||||||
return $text === '' ? '' : wordwrap($text, $width, "\n", false);
|
return $text === '' ? '' : wordwrap($text, $width, "\n", false);
|
||||||
};
|
};
|
||||||
$clampWrappedText = static function ($text, int $width, int $maxLines) use ($normalize): string {
|
$clampWrappedText = static function ($text, int $width, int $maxLines, bool $cutLongWords = false) use ($normalize): string {
|
||||||
$text = $normalize($text);
|
$text = $normalize($text);
|
||||||
if ($text === '') return '';
|
if ($text === '') return '';
|
||||||
$words = preg_split('/\s+/u', $text, -1, PREG_SPLIT_NO_EMPTY) ?: [];
|
$words = preg_split('/\s+/u', $text, -1, PREG_SPLIT_NO_EMPTY) ?: [];
|
||||||
if (!$words) return '';
|
if (!$words) return '';
|
||||||
$candidate = implode(' ', $words);
|
$candidate = implode(' ', $words);
|
||||||
while (true) {
|
while (true) {
|
||||||
$wrapped = wordwrap($candidate, $width, "\n", false);
|
$wrapped = wordwrap($candidate, $width, "\n", $cutLongWords);
|
||||||
$lineCount = substr_count($wrapped, "\n") + 1;
|
$lineCount = substr_count($wrapped, "\n") + 1;
|
||||||
if ($lineCount <= $maxLines || count($words) <= 1) return $wrapped;
|
if ($lineCount <= $maxLines || count($words) <= 1) return $wrapped;
|
||||||
array_pop($words);
|
array_pop($words);
|
||||||
|
|
@ -94,8 +94,8 @@ function apply_thumbnail_overlay($imagePath, $title, $teaser) {
|
||||||
$bestSize = 95;
|
$bestSize = 95;
|
||||||
$bestWidth = 28;
|
$bestWidth = 28;
|
||||||
$bestLines = 1;
|
$bestLines = 1;
|
||||||
for ($size = 94; $size >= 60; $size -= 4) {
|
for ($size = 84; $size >= 54; $size -= 4) {
|
||||||
$width = max(16, min(46, (int)round(24 + (94 - $size) / 2.7)));
|
$width = max(16, min(46, (int)round(23 + (84 - $size) / 2.7)));
|
||||||
$candidate = $wrapText($text, $width);
|
$candidate = $wrapText($text, $width);
|
||||||
$lines = substr_count($candidate, "\n") + 1;
|
$lines = substr_count($candidate, "\n") + 1;
|
||||||
$bestText = $candidate;
|
$bestText = $candidate;
|
||||||
|
|
@ -108,14 +108,14 @@ function apply_thumbnail_overlay($imagePath, $title, $teaser) {
|
||||||
};
|
};
|
||||||
|
|
||||||
[$titleText, $titleFontSize, $titleWrapWidth, $titleLines] = $fitTitleText($title);
|
[$titleText, $titleFontSize, $titleWrapWidth, $titleLines] = $fitTitleText($title);
|
||||||
$teaserText = $clampWrappedText($teaser, 38, 6);
|
$teaserText = $clampWrappedText($teaser, 34, 6, true);
|
||||||
$teaserLines = max(1, substr_count($teaserText, "\n") + 1);
|
$teaserLines = max(1, substr_count($teaserText, "\n") + 1);
|
||||||
|
|
||||||
// Move the whole block a bit right while keeping left-aligned text.
|
// Move the whole block a bit left and higher while keeping left-aligned text.
|
||||||
$left = max(18, (int)round($imgW * 0.055));
|
$left = max(14, (int)round($imgW * 0.046));
|
||||||
$titleY = max(24, (int)round($imgH * 0.06));
|
$titleY = max(20, (int)round($imgH * 0.062));
|
||||||
$titleLineStep = max(68, (int)round($imgH * 0.043));
|
$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)));
|
$teaserY = max((int)round($imgH * 0.235), $titleY + ($titleLines * $titleLineStep) + max(54, (int)round($imgH * 0.03)));
|
||||||
|
|
||||||
$overlayBase = tempnam(sys_get_temp_dir(), 'thumb_overlay_');
|
$overlayBase = tempnam(sys_get_temp_dir(), 'thumb_overlay_');
|
||||||
$outputPath = dirname($imagePath) . '/' . basename($imagePath) . '.tmp.' . uniqid('', true) . '.jpg';
|
$outputPath = dirname($imagePath) . '/' . basename($imagePath) . '.tmp.' . uniqid('', true) . '.jpg';
|
||||||
|
|
@ -158,7 +158,7 @@ function apply_thumbnail_overlay($imagePath, $title, $teaser) {
|
||||||
foreach ($qualities as $quality) {
|
foreach ($qualities as $quality) {
|
||||||
$attemptPath = dirname($imagePath) . '/' . basename($imagePath) . '.tmp.' . uniqid('', true) . '.jpg';
|
$attemptPath = dirname($imagePath) . '/' . basename($imagePath) . '.tmp.' . uniqid('', true) . '.jpg';
|
||||||
$cmd2 = sprintf(
|
$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',
|
'/usr/bin/convert -filter Lanczos -define filter:blur=0.85 %s -resize %dx%d^ -gravity center -extent %dx%d -modulate 70,82 %s -composite -strip -interlace Plane -sampling-factor 4:2:0 -quality %d %s 2>&1',
|
||||||
escapeshellarg($imagePath),
|
escapeshellarg($imagePath),
|
||||||
$imgW,
|
$imgW,
|
||||||
$imgH,
|
$imgH,
|
||||||
|
|
@ -813,9 +813,13 @@ function input_dir_output($dir){
|
||||||
return basename((string)($state['output'] ?? ''));
|
return basename((string)($state['output'] ?? ''));
|
||||||
}
|
}
|
||||||
function regenerate_input_dir_maps($dir, $config){
|
function regenerate_input_dir_maps($dir, $config){
|
||||||
|
$logFile = '/var/log/mvlog_map.log';
|
||||||
$python = trim((string)shell_exec('command -v python3 2>/dev/null'));
|
$python = trim((string)shell_exec('command -v python3 2>/dev/null'));
|
||||||
$script = __DIR__ . '/bin/gpsmap.py';
|
$script = __DIR__ . '/bin/gpsmap.py';
|
||||||
if ($python === '' || !is_file($script)) return;
|
if ($python === '' || !is_file($script)) {
|
||||||
|
file_put_contents($logFile, '[mvlog] map regenerate skipped for ' . basename((string)$dir) . ' (python=' . ($python !== '' ? 'yes' : 'no') . ', script=' . (is_file($script) ? 'yes' : 'no') . ")\n", FILE_APPEND);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
$state = input_dir_state($dir);
|
$state = input_dir_state($dir);
|
||||||
$targets = [];
|
$targets = [];
|
||||||
|
|
@ -831,14 +835,19 @@ function regenerate_input_dir_maps($dir, $config){
|
||||||
}
|
}
|
||||||
|
|
||||||
$targets = array_values(array_unique($targets));
|
$targets = array_values(array_unique($targets));
|
||||||
|
file_put_contents($logFile, '[mvlog] map regenerate requested for ' . basename((string)$dir) . ' targets=' . implode(', ', array_map('basename', $targets)) . "\n", FILE_APPEND);
|
||||||
|
|
||||||
foreach ($targets as $outPng) {
|
foreach ($targets as $outPng) {
|
||||||
|
file_put_contents($logFile, '[mvlog] map regenerate start for ' . basename((string)$dir) . ' target=' . basename((string)$outPng) . "\n", FILE_APPEND);
|
||||||
$cmd = escapeshellarg($python) . ' ' . escapeshellarg($script) . ' ' . escapeshellarg($dir) . ' ' . escapeshellarg($outPng) . ' 2>&1';
|
$cmd = escapeshellarg($python) . ' ' . escapeshellarg($script) . ' ' . escapeshellarg($dir) . ' ' . escapeshellarg($outPng) . ' 2>&1';
|
||||||
$output = (string)shell_exec($cmd);
|
$output = (string)shell_exec($cmd);
|
||||||
if (is_file($outPng)) {
|
if (is_file($outPng)) {
|
||||||
@chmod($outPng, 0664);
|
@chmod($outPng, 0664);
|
||||||
|
clearstatcache(true, $outPng);
|
||||||
|
$size = @filesize($outPng);
|
||||||
|
file_put_contents($logFile, '[mvlog] map regenerate done for ' . basename((string)$dir) . ' target=' . basename((string)$outPng) . ' bytes=' . ($size !== false ? $size : 'unknown') . "\n", FILE_APPEND);
|
||||||
} else {
|
} else {
|
||||||
error_log('[mvlog] map regenerate failed for '.basename((string)$dir).' target='.basename((string)$outPng).' output='.trim($output)."\n", 3, '/var/log/mvlog_map.log');
|
file_put_contents($logFile, '[mvlog] map regenerate failed for ' . basename((string)$dir) . ' target=' . basename((string)$outPng) . ' output=' . trim($output) . "\n", FILE_APPEND);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1099,11 +1108,15 @@ try {
|
||||||
$dir = resolve_input_dir_from_request($config['uploads_dir'], $_POST['id'] ?? '', $_POST['dir'] ?? '', $articleIndex);
|
$dir = resolve_input_dir_from_request($config['uploads_dir'], $_POST['id'] ?? '', $_POST['dir'] ?? '', $articleIndex);
|
||||||
$articleId = ensure_state_article_id($dir);
|
$articleId = ensure_state_article_id($dir);
|
||||||
if (input_dir_running($dir)) throw new RuntimeException('This input directory is being rendered. Editing is disabled until the job completes.');
|
if (input_dir_running($dir)) throw new RuntimeException('This input directory is being rendered. Editing is disabled until the job completes.');
|
||||||
|
file_put_contents('/var/log/mvlog_map.log', '[mvlog] save changes pressed for ' . basename($dir) . ' id=' . $articleId . "\n", FILE_APPEND);
|
||||||
|
try {
|
||||||
write_data($dir, $_POST);
|
write_data($dir, $_POST);
|
||||||
save_uploads('media', $dir, $allowedMedia);
|
save_uploads('media', $dir, $allowedMedia);
|
||||||
save_uploads('audio', $dir, $allowedAudio);
|
save_uploads('audio', $dir, $allowedAudio);
|
||||||
// Always regenerate map image(s) on Save changes.
|
} finally {
|
||||||
|
// Always regenerate map image(s) whenever Save changes is pressed.
|
||||||
regenerate_input_dir_maps($dir, $config);
|
regenerate_input_dir_maps($dir, $config);
|
||||||
|
}
|
||||||
if (!empty($_POST['ajax'])) ajax_json(['ok'=>true, 'message'=>'Updated input directory: in-dir/' . basename($dir), 'dir'=>basename($dir), 'id'=>$articleId]);
|
if (!empty($_POST['ajax'])) ajax_json(['ok'=>true, 'message'=>'Updated input directory: in-dir/' . basename($dir), 'dir'=>basename($dir), 'id'=>$articleId]);
|
||||||
header('Location: new.php?tab=edit');
|
header('Location: new.php?tab=edit');
|
||||||
exit;
|
exit;
|
||||||
|
|
@ -1368,9 +1381,11 @@ function setThumbButtonsDisabled(disabled){
|
||||||
buttons[i].classList.toggle('is-busy-target', disabled);
|
buttons[i].classList.toggle('is-busy-target', disabled);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function showThumbBusy(){
|
function showThumbBusy(label){
|
||||||
var overlay = ensureThumbBusyOverlay();
|
var overlay = ensureThumbBusyOverlay();
|
||||||
setThumbButtonsDisabled(true);
|
setThumbButtonsDisabled(true);
|
||||||
|
var text = overlay.querySelector('.label');
|
||||||
|
if(text && label) text.textContent = label;
|
||||||
overlay.classList.add('show');
|
overlay.classList.add('show');
|
||||||
}
|
}
|
||||||
function hideThumbBusy(){
|
function hideThumbBusy(){
|
||||||
|
|
@ -1380,7 +1395,7 @@ function hideThumbBusy(){
|
||||||
function setThumb(name){
|
function setThumb(name){
|
||||||
var ctx = getEditDirContext();
|
var ctx = getEditDirContext();
|
||||||
if(!ctx.dir){ showToast('Internal error: missing input dir', false); return; }
|
if(!ctx.dir){ showToast('Internal error: missing input dir', false); return; }
|
||||||
showThumbBusy();
|
showThumbBusy('Creating thumbnail…');
|
||||||
var body = 'action=set_video_thumb&dir=' + encodeURIComponent(ctx.dir) + '&id=' + encodeURIComponent(ctx.id) + '&file=' + encodeURIComponent(name) + '&ajax=1';
|
var body = 'action=set_video_thumb&dir=' + encodeURIComponent(ctx.dir) + '&id=' + encodeURIComponent(ctx.id) + '&file=' + encodeURIComponent(name) + '&ajax=1';
|
||||||
fetch('new.php', {
|
fetch('new.php', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
|
@ -1617,7 +1632,7 @@ function handleAjaxFormSubmit(form, redirectTab) {
|
||||||
data.set('ajax', '1');
|
data.set('ajax', '1');
|
||||||
|
|
||||||
const action = String(data.get('action') || '');
|
const action = String(data.get('action') || '');
|
||||||
const verb = action === 'update' ? 'Saving'
|
const verb = action === 'update' ? 'Saving changes…'
|
||||||
: action === 'delete_video' ? 'Deleting'
|
: action === 'delete_video' ? 'Deleting'
|
||||||
: action === 'delete_dir' ? 'Deleting'
|
: action === 'delete_dir' ? 'Deleting'
|
||||||
: action === 'delete_file' ? 'Deleting'
|
: action === 'delete_file' ? 'Deleting'
|
||||||
|
|
@ -1626,11 +1641,13 @@ function handleAjaxFormSubmit(form, redirectTab) {
|
||||||
|
|
||||||
let timer = 0;
|
let timer = 0;
|
||||||
let success = false;
|
let success = false;
|
||||||
|
const useBusyOverlay = action === 'update';
|
||||||
if (submit) {
|
if (submit) {
|
||||||
submit.dataset.label = submit.dataset.label || submit.textContent.trim() || 'Submit';
|
submit.dataset.label = submit.dataset.label || submit.textContent.trim() || 'Submit';
|
||||||
submit.disabled = true;
|
submit.disabled = true;
|
||||||
submit.style.opacity = '0.78';
|
submit.style.opacity = '0.78';
|
||||||
submit.style.cursor = 'wait';
|
submit.style.cursor = 'wait';
|
||||||
|
if (!useBusyOverlay) {
|
||||||
let dots = 0;
|
let dots = 0;
|
||||||
submit.textContent = verb;
|
submit.textContent = verb;
|
||||||
timer = window.setInterval(function(){
|
timer = window.setInterval(function(){
|
||||||
|
|
@ -1638,6 +1655,8 @@ function handleAjaxFormSubmit(form, redirectTab) {
|
||||||
submit.textContent = verb + '.'.repeat(dots);
|
submit.textContent = verb + '.'.repeat(dots);
|
||||||
}, 320);
|
}, 320);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if (useBusyOverlay) showThumbBusy('Saving changes…');
|
||||||
|
|
||||||
try{
|
try{
|
||||||
const res = await fetch('new.php', { method: 'POST', body: data, credentials: 'same-origin', headers: { 'Accept': 'application/json' }});
|
const res = await fetch('new.php', { method: 'POST', body: data, credentials: 'same-origin', headers: { 'Accept': 'application/json' }});
|
||||||
|
|
@ -1645,9 +1664,11 @@ function handleAjaxFormSubmit(form, redirectTab) {
|
||||||
if(!res.ok || !json.ok) throw new Error(json.error || 'Operation failed');
|
if(!res.ok || !json.ok) throw new Error(json.error || 'Operation failed');
|
||||||
success = true;
|
success = true;
|
||||||
showToast(json.message || 'Operation successful', true);
|
showToast(json.message || 'Operation successful', true);
|
||||||
|
if (useBusyOverlay) setTimeout(hideThumbBusy, 120);
|
||||||
setTimeout(() => { window.location.href = 'new.php?tab=' + (json.tab || redirectTab); }, 450);
|
setTimeout(() => { window.location.href = 'new.php?tab=' + (json.tab || redirectTab); }, 450);
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
showToast(e.message || 'Operation failed', false);
|
showToast(e.message || 'Operation failed', false);
|
||||||
|
if (useBusyOverlay) setTimeout(hideThumbBusy, 120);
|
||||||
} finally {
|
} finally {
|
||||||
if (timer) window.clearInterval(timer);
|
if (timer) window.clearInterval(timer);
|
||||||
if(submit && !success) {
|
if(submit && !success) {
|
||||||
|
|
@ -1656,6 +1677,7 @@ function handleAjaxFormSubmit(form, redirectTab) {
|
||||||
submit.style.cursor = '';
|
submit.style.cursor = '';
|
||||||
submit.textContent = submit.dataset.label || 'Submit';
|
submit.textContent = submit.dataset.label || 'Submit';
|
||||||
}
|
}
|
||||||
|
if (useBusyOverlay && !success) hideThumbBusy();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue