diff --git a/index.php b/index.php index 7b61a60..d7c6108 100644 --- a/index.php +++ b/index.php @@ -485,7 +485,6 @@ $siteHeaderTitle = $headerTitles[array_rand($headerTitles)]; -
" href="">
- diff --git a/new.php b/new.php index 9187e61..c6fc559 100644 --- a/new.php +++ b/new.php @@ -75,14 +75,14 @@ function apply_thumbnail_overlay($imagePath, $title, $teaser) { $text = $normalize($text); return $text === '' ? '' : wordwrap($text, $width, "\n", false); }; - $clampWrappedText = static function ($text, int $width, int $maxLines, bool $cutLongWords = false) use ($normalize): string { + $clampWrappedText = static function ($text, int $width, int $maxLines) use ($normalize): string { $text = $normalize($text); if ($text === '') return ''; $words = preg_split('/\s+/u', $text, -1, PREG_SPLIT_NO_EMPTY) ?: []; if (!$words) return ''; $candidate = implode(' ', $words); while (true) { - $wrapped = wordwrap($candidate, $width, "\n", $cutLongWords); + $wrapped = wordwrap($candidate, $width, "\n", false); $lineCount = substr_count($wrapped, "\n") + 1; if ($lineCount <= $maxLines || count($words) <= 1) return $wrapped; array_pop($words); @@ -94,8 +94,8 @@ function apply_thumbnail_overlay($imagePath, $title, $teaser) { $bestSize = 95; $bestWidth = 28; $bestLines = 1; - for ($size = 84; $size >= 54; $size -= 4) { - $width = max(16, min(46, (int)round(23 + (84 - $size) / 2.7))); + 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; @@ -108,14 +108,14 @@ function apply_thumbnail_overlay($imagePath, $title, $teaser) { }; [$titleText, $titleFontSize, $titleWrapWidth, $titleLines] = $fitTitleText($title); - $teaserText = $clampWrappedText($teaser, 34, 6, true); + $teaserText = $clampWrappedText($teaser, 38, 6); $teaserLines = max(1, substr_count($teaserText, "\n") + 1); - // Move the whole block a bit left and higher while keeping left-aligned text. - $left = max(14, (int)round($imgW * 0.046)); - $titleY = max(20, (int)round($imgH * 0.062)); + // 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(68, (int)round($imgH * 0.043)); - $teaserY = max((int)round($imgH * 0.235), $titleY + ($titleLines * $titleLineStep) + max(54, (int)round($imgH * 0.03))); + $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'; @@ -158,7 +158,7 @@ function apply_thumbnail_overlay($imagePath, $title, $teaser) { 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 70,82 %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 62,76 %s -composite -strip -interlace Plane -sampling-factor 4:2:0 -quality %d %s 2>&1', escapeshellarg($imagePath), $imgW, $imgH, @@ -813,13 +813,9 @@ function input_dir_output($dir){ return basename((string)($state['output'] ?? '')); } function regenerate_input_dir_maps($dir, $config){ - $logFile = '/var/log/mvlog_map.log'; $python = trim((string)shell_exec('command -v python3 2>/dev/null')); $script = __DIR__ . '/bin/gpsmap.py'; - 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; - } + if ($python === '' || !is_file($script)) return; $state = input_dir_state($dir); $targets = []; @@ -835,19 +831,14 @@ function regenerate_input_dir_maps($dir, $config){ } $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) { - 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'; $output = (string)shell_exec($cmd); if (is_file($outPng)) { @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 { - file_put_contents($logFile, '[mvlog] map regenerate failed for ' . basename((string)$dir) . ' target=' . basename((string)$outPng) . ' output=' . trim($output) . "\n", FILE_APPEND); + error_log('[mvlog] map regenerate failed for '.basename((string)$dir).' target='.basename((string)$outPng).' output='.trim($output)."\n", 3, '/var/log/mvlog_map.log'); } } } @@ -1108,15 +1099,11 @@ try { $dir = resolve_input_dir_from_request($config['uploads_dir'], $_POST['id'] ?? '', $_POST['dir'] ?? '', $articleIndex); $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.'); - 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); - save_uploads('media', $dir, $allowedMedia); - save_uploads('audio', $dir, $allowedAudio); - } finally { - // Always regenerate map image(s) whenever Save changes is pressed. - regenerate_input_dir_maps($dir, $config); - } + write_data($dir, $_POST); + save_uploads('media', $dir, $allowedMedia); + save_uploads('audio', $dir, $allowedAudio); + // Always regenerate map image(s) on Save changes. + 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]); header('Location: new.php?tab=edit'); exit; @@ -1381,11 +1368,9 @@ function setThumbButtonsDisabled(disabled){ buttons[i].classList.toggle('is-busy-target', disabled); } } -function showThumbBusy(label){ +function showThumbBusy(){ var overlay = ensureThumbBusyOverlay(); setThumbButtonsDisabled(true); - var text = overlay.querySelector('.label'); - if(text && label) text.textContent = label; overlay.classList.add('show'); } function hideThumbBusy(){ @@ -1395,7 +1380,7 @@ function hideThumbBusy(){ function setThumb(name){ var ctx = getEditDirContext(); if(!ctx.dir){ showToast('Internal error: missing input dir', false); return; } - showThumbBusy('Creating thumbnail…'); + showThumbBusy(); var body = 'action=set_video_thumb&dir=' + encodeURIComponent(ctx.dir) + '&id=' + encodeURIComponent(ctx.id) + '&file=' + encodeURIComponent(name) + '&ajax=1'; fetch('new.php', { method: 'POST', @@ -1632,7 +1617,7 @@ function handleAjaxFormSubmit(form, redirectTab) { data.set('ajax', '1'); const action = String(data.get('action') || ''); - const verb = action === 'update' ? 'Saving changes…' + const verb = action === 'update' ? 'Saving' : action === 'delete_video' ? 'Deleting' : action === 'delete_dir' ? 'Deleting' : action === 'delete_file' ? 'Deleting' @@ -1641,22 +1626,18 @@ function handleAjaxFormSubmit(form, redirectTab) { let timer = 0; let success = false; - const useBusyOverlay = action === 'update'; if (submit) { submit.dataset.label = submit.dataset.label || submit.textContent.trim() || 'Submit'; submit.disabled = true; submit.style.opacity = '0.78'; submit.style.cursor = 'wait'; - if (!useBusyOverlay) { - let dots = 0; - submit.textContent = verb; - timer = window.setInterval(function(){ - dots = (dots + 1) % 4; - submit.textContent = verb + '.'.repeat(dots); - }, 320); - } + let dots = 0; + submit.textContent = verb; + timer = window.setInterval(function(){ + dots = (dots + 1) % 4; + submit.textContent = verb + '.'.repeat(dots); + }, 320); } - if (useBusyOverlay) showThumbBusy('Saving changes…'); try{ const res = await fetch('new.php', { method: 'POST', body: data, credentials: 'same-origin', headers: { 'Accept': 'application/json' }}); @@ -1664,11 +1645,9 @@ function handleAjaxFormSubmit(form, redirectTab) { if(!res.ok || !json.ok) throw new Error(json.error || 'Operation failed'); success = true; showToast(json.message || 'Operation successful', true); - if (useBusyOverlay) setTimeout(hideThumbBusy, 120); setTimeout(() => { window.location.href = 'new.php?tab=' + (json.tab || redirectTab); }, 450); } catch(e) { showToast(e.message || 'Operation failed', false); - if (useBusyOverlay) setTimeout(hideThumbBusy, 120); } finally { if (timer) window.clearInterval(timer); if(submit && !success) { @@ -1677,7 +1656,6 @@ function handleAjaxFormSubmit(form, redirectTab) { submit.style.cursor = ''; submit.textContent = submit.dataset.label || 'Submit'; } - if (useBusyOverlay && !success) hideThumbBusy(); } }); }