Ttitles cleanup and logo positioning
This commit is contained in:
parent
a87d8d8920
commit
6343ebf654
3 changed files with 94 additions and 16 deletions
24
index.php
24
index.php
|
|
@ -57,6 +57,17 @@ function map_hidden_outputs($config){
|
||||||
return $hidden;
|
return $hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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['thumbs_dir'] . '/' . $base . '.' . $ext;
|
||||||
|
if (is_file($file)) return $config['public_thumbs'] . '/' . rawurlencode($base . '.' . $ext) . '?v=' . filemtime($file);
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
function video_metadata($path){
|
function video_metadata($path){
|
||||||
$base = pathinfo($path, PATHINFO_FILENAME);
|
$base = pathinfo($path, PATHINFO_FILENAME);
|
||||||
$meta = [
|
$meta = [
|
||||||
|
|
@ -246,20 +257,12 @@ $headerTitles = [
|
||||||
'Many Voices of Life',
|
'Many Voices of Life',
|
||||||
'Many Views of Life',
|
'Many Views of Life',
|
||||||
'Modern Vagabond Log',
|
'Modern Vagabond Log',
|
||||||
'Motorcycle Wanderlust Log',
|
|
||||||
'Mapped Wanderings Log',
|
|
||||||
'Miles Beyond Reason',
|
|
||||||
'Miles Beyond Reality',
|
|
||||||
'Motion, Velocity & Lunacy',
|
'Motion, Velocity & Lunacy',
|
||||||
'Miles, Wind & Luck',
|
|
||||||
'Maps, Villages & Legends',
|
'Maps, Villages & Legends',
|
||||||
'Multi-Vision Log',
|
|
||||||
'Multi-Vista Log',
|
|
||||||
'Mutable View Log',
|
'Mutable View Log',
|
||||||
'Mistakes, Victories & Lessons',
|
'Mistakes, Victories & Lessons',
|
||||||
'Mostly Verified Legends',
|
'Mostly Verified Legends',
|
||||||
'Mostly Vague Logistics',
|
'Mostly Vague Logistics',
|
||||||
'Marginally Viable Leadership',
|
|
||||||
'Mileage Versus Luck',
|
'Mileage Versus Luck',
|
||||||
'Mechanical Violence Log',
|
'Mechanical Violence Log',
|
||||||
'Moderately Violent Leisure',
|
'Moderately Violent Leisure',
|
||||||
|
|
@ -272,7 +275,7 @@ $siteHeaderTitle = $headerTitles[array_rand($headerTitles)];
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||||
<title><?=h($siteHeaderTitle)?></title>
|
<title>MVL</title>
|
||||||
<link rel="alternate" type="application/rss+xml" title="MVLog RSS" href="feed.php">
|
<link rel="alternate" type="application/rss+xml" title="MVLog RSS" href="feed.php">
|
||||||
<link rel="icon" type="image/png" href="assets/img/moto_travel.png">
|
<link rel="icon" type="image/png" href="assets/img/moto_travel.png">
|
||||||
<link rel="stylesheet" href="style.css">
|
<link rel="stylesheet" href="style.css">
|
||||||
|
|
@ -338,6 +341,7 @@ $siteHeaderTitle = $headerTitles[array_rand($headerTitles)];
|
||||||
$name = basename($v);
|
$name = basename($v);
|
||||||
$url = $config['public_videos'].'/'.rawurlencode($name);
|
$url = $config['public_videos'].'/'.rawurlencode($name);
|
||||||
$m = $videoCache[$name]['metadata'] ?? video_metadata($v);
|
$m = $videoCache[$name]['metadata'] ?? video_metadata($v);
|
||||||
|
$posterUrl = video_thumb_public_url($config, $name);
|
||||||
$dateFilterUrl = query_url(['q' => 'date=' . (string)($m['date'] ?? '')]);
|
$dateFilterUrl = query_url(['q' => 'date=' . (string)($m['date'] ?? '')]);
|
||||||
$locFilterUrl = query_url(['q' => 'location=' . (string)($m['location'] ?? '')]);
|
$locFilterUrl = query_url(['q' => 'location=' . (string)($m['location'] ?? '')]);
|
||||||
?>
|
?>
|
||||||
|
|
@ -352,7 +356,7 @@ $siteHeaderTitle = $headerTitles[array_rand($headerTitles)];
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="video-wrapper">
|
<div class="video-wrapper">
|
||||||
<video controls controlsList="nodownload" oncontextmenu="return false" preload="metadata" src="<?=h($url)?>"></video>
|
<video controls controlsList="nodownload" oncontextmenu="return false" preload="metadata" src="<?=h($url)?>"<?= $posterUrl !== '' ? ' poster="'.h($posterUrl).'"' : '' ?>></video>
|
||||||
<?php
|
<?php
|
||||||
$map = pathinfo($name, PATHINFO_FILENAME).".png";
|
$map = pathinfo($name, PATHINFO_FILENAME).".png";
|
||||||
$hideMap = !empty($hiddenMapOutputs[$name]);
|
$hideMap = !empty($hiddenMapOutputs[$name]);
|
||||||
|
|
|
||||||
82
new.php
82
new.php
|
|
@ -567,6 +567,20 @@ function set_input_dir_map_hidden($dir, $hidden){
|
||||||
if ($hidden) { file_put_contents($path, "hide-map\n"); chmod($path, 0664); }
|
if ($hidden) { file_put_contents($path, "hide-map\n"); chmod($path, 0664); }
|
||||||
elseif (is_file($path)) unlink($path);
|
elseif (is_file($path)) unlink($path);
|
||||||
}
|
}
|
||||||
|
function is_image_media_file($name){
|
||||||
|
$ext = strtolower(pathinfo((string)$name, PATHINFO_EXTENSION));
|
||||||
|
return in_array($ext, ['jpg','jpeg','png','webp','gif'], true);
|
||||||
|
}
|
||||||
|
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['thumbs_dir'] . '/' . $base . '.' . $ext;
|
||||||
|
if (is_file($file)) return $config['public_thumbs'] . '/' . rawurlencode($base . '.' . $ext) . '?v=' . filemtime($file);
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
}
|
||||||
function ensure_state_article_id($dir){
|
function ensure_state_article_id($dir){
|
||||||
$articleId = ensure_article_id($dir);
|
$articleId = ensure_article_id($dir);
|
||||||
$stateFile = $dir . '/.movmaker-state.json';
|
$stateFile = $dir . '/.movmaker-state.json';
|
||||||
|
|
@ -831,6 +845,48 @@ try {
|
||||||
}
|
}
|
||||||
header('Location: new.php?tab=edit&msg=' . rawurlencode($message));
|
header('Location: new.php?tab=edit&msg=' . rawurlencode($message));
|
||||||
exit;
|
exit;
|
||||||
|
} elseif ($action === 'set_video_thumb') {
|
||||||
|
$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 = basename((string)($_POST['file'] ?? ''));
|
||||||
|
if ($file === '' || !is_image_media_file($file) || !is_file($dir . '/' . $file)) throw new RuntimeException('Invalid image file.');
|
||||||
|
$state = input_dir_state($dir);
|
||||||
|
$outputs = [];
|
||||||
|
foreach (['output', 'preview_output'] as $key) {
|
||||||
|
$name = basename((string)($state[$key] ?? ''));
|
||||||
|
if ($name !== '' && is_file($config['videos_dir'] . '/' . $name)) $outputs[] = $name;
|
||||||
|
}
|
||||||
|
$outputs = array_values(array_unique($outputs));
|
||||||
|
if (!$outputs) throw new RuntimeException('No rendered video found for this input directory yet.');
|
||||||
|
|
||||||
|
$src = $dir . '/' . $file;
|
||||||
|
$ext = strtolower(pathinfo($file, PATHINFO_EXTENSION));
|
||||||
|
$written = [];
|
||||||
|
foreach ($outputs as $out) {
|
||||||
|
$base = pathinfo($out, PATHINFO_FILENAME);
|
||||||
|
foreach (['jpg','jpeg','png','webp','gif'] as $oldExt) {
|
||||||
|
$old = $config['thumbs_dir'] . '/' . $base . '.' . $oldExt;
|
||||||
|
if (is_file($old)) @unlink($old);
|
||||||
|
}
|
||||||
|
$target = $config['thumbs_dir'] . '/' . $base . '.' . $ext;
|
||||||
|
if (!@copy($src, $target)) throw new RuntimeException('Failed to write thumbnail.');
|
||||||
|
@chmod($target, 0664);
|
||||||
|
$written[] = basename($target);
|
||||||
|
}
|
||||||
|
|
||||||
|
$message = 'Thumbnail set from ' . $file . ' for ' . count($written) . ' video(s).';
|
||||||
|
if (!empty($_POST['ajax'])) {
|
||||||
|
ajax_json([
|
||||||
|
'ok' => true,
|
||||||
|
'message' => $message,
|
||||||
|
'dir' => basename($dir),
|
||||||
|
'id' => $articleId,
|
||||||
|
'written' => $written,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
header('Location: new.php?tab=edit&id=' . rawurlencode((string)$articleId) . '&edit=' . rawurlencode(basename($dir)) . '&msg=' . rawurlencode($message));
|
||||||
|
exit;
|
||||||
} elseif ($action === 'update') {
|
} elseif ($action === 'update') {
|
||||||
$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);
|
||||||
|
|
@ -984,13 +1040,13 @@ $editDir = null; $editStatus = ''; $editRunning = false; $editData = ['title'=>'
|
||||||
if ($editName !== '' || $editId !== '') { try { $editDir = resolve_input_dir_from_request($config['uploads_dir'], $editId, $editName, $articleIndex); $editStatus = input_dir_status($editDir); $editRunning = input_dir_running($editDir); if (!$editRunning) { $editData = read_data($editDir); $editFiles = media_sort_files($editDir, list_files($editDir)); } } catch (Throwable $e) { $err = $e->getMessage(); } }
|
if ($editName !== '' || $editId !== '') { try { $editDir = resolve_input_dir_from_request($config['uploads_dir'], $editId, $editName, $articleIndex); $editStatus = input_dir_status($editDir); $editRunning = input_dir_running($editDir); if (!$editRunning) { $editData = read_data($editDir); $editFiles = media_sort_files($editDir, list_files($editDir)); } } catch (Throwable $e) { $err = $e->getMessage(); } }
|
||||||
$runningJobs = active_worker_jobs($config);
|
$runningJobs = active_worker_jobs($config);
|
||||||
?>
|
?>
|
||||||
<!doctype html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Admin - <?=h($config['site_name'])?></title><link rel="icon" type="image/png" href="assets/img/moto_travel.png"><link rel="stylesheet" href="style.css"><style>.admin-search-form{display:flex;flex-wrap:nowrap;align-items:center;gap:.55rem;margin:0 0 .6rem;overflow-x:auto;padding-bottom:2px}.admin-search-wrap{position:relative;min-width:180px;flex:1 1 auto}.admin-search-wrap input{margin:0;padding-right:2.15rem}.admin-search-clear{position:absolute;right:.45rem;top:50%;transform:translateY(-50%);display:inline-flex;align-items:center;justify-content:center;width:1.5rem;height:1.5rem;border-radius:50%;background:#C46A3A;color:#111315;text-decoration:none;font-size:1rem;line-height:1;font-weight:700;box-shadow:0 1px 4px #0007;transition:opacity .15s ease,transform .15s ease}.admin-search-clear:hover{background:#d97b48;color:#111315;transform:translateY(-50%) scale(1.05)}.admin-search-clear.is-empty{opacity:.38;pointer-events:none}.admin-search-submit{display:inline-grid;place-items:center;width:2.35rem;height:2.35rem;padding:0;border-radius:.5rem;background:#C46A3A;color:#111315;flex:0 0 auto}.admin-search-submit .icon{font-size:1.02rem;line-height:1;transform:translateY(.01em)}.admin-search-filters{display:flex;flex-wrap:nowrap;gap:.7rem;align-items:center;flex:0 0 auto;white-space:nowrap}.admin-filter-item{display:inline-flex;align-items:center;gap:.35rem;margin:0;white-space:nowrap;font-size:.92rem}.admin-filter-item input[type=checkbox]{width:.9rem;height:.9rem;margin:0}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.description-teaser{margin:.2rem 0 .45rem;color:#eadfca}.description-quote{margin:.2rem 0 .45rem;font-style:italic;font-size:.82rem;color:#d4d7dd}</style></head><body>
|
<!doctype html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>MVL Admin</title><link rel="icon" type="image/png" href="assets/img/moto_travel.png"><link rel="stylesheet" href="style.css"><style>.admin-search-form{display:flex;flex-wrap:nowrap;align-items:center;gap:.55rem;margin:0 0 .6rem;overflow-x:auto;padding-bottom:2px}.admin-search-wrap{position:relative;min-width:180px;flex:1 1 auto}.admin-search-wrap input{margin:0;padding-right:2.15rem}.admin-search-clear{position:absolute;right:.45rem;top:50%;transform:translateY(-50%);display:inline-flex;align-items:center;justify-content:center;width:1.5rem;height:1.5rem;border-radius:50%;background:#C46A3A;color:#111315;text-decoration:none;font-size:1rem;line-height:1;font-weight:700;box-shadow:0 1px 4px #0007;transition:opacity .15s ease,transform .15s ease}.admin-search-clear:hover{background:#d97b48;color:#111315;transform:translateY(-50%) scale(1.05)}.admin-search-clear.is-empty{opacity:.38;pointer-events:none}.admin-search-submit{display:inline-grid;place-items:center;width:2.35rem;height:2.35rem;padding:0;border-radius:.5rem;background:#C46A3A;color:#111315;flex:0 0 auto}.admin-search-submit .icon{font-size:1.02rem;line-height:1;transform:translateY(.01em)}.admin-search-filters{display:flex;flex-wrap:nowrap;gap:.7rem;align-items:center;flex:0 0 auto;white-space:nowrap}.admin-filter-item{display:inline-flex;align-items:center;gap:.35rem;margin:0;white-space:nowrap;font-size:.92rem}.admin-filter-item input[type=checkbox]{width:.9rem;height:.9rem;margin:0}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.description-teaser{margin:.2rem 0 .45rem;color:#eadfca}.description-quote{margin:.2rem 0 .45rem;font-style:italic;font-size:.82rem;color:#d4d7dd}</style></head><body>
|
||||||
<header class="site-header admin-header"><div class="brand-wrap"><a class="header-logo" href="index.php" aria-label="MVLog home"><img src="assets/img/moto_travel.png" alt=""></a><a class="brand" href="index.php"><h1>MVLog <span class="admin-word">Admin</span></h1><p>journal of an<br>unreliable narrator.</p></a></div></header>
|
<header class="site-header admin-header"><div class="brand-wrap"><a class="header-logo" href="index.php" aria-label="MVLog home"><img src="assets/img/moto_travel.png" alt=""></a><a class="brand" href="index.php"><h1 style="font-size:1.15rem">MVLog <span class="admin-word">Admin</span></h1><p>journal of an<br>unreliable narrator.</p></a></div></header>
|
||||||
<main>
|
<main>
|
||||||
<?php if($err): ?><div class="err"><?=h($err)?></div><?php endif; ?>
|
<?php if($err): ?><div class="err"><?=h($err)?></div><?php endif; ?>
|
||||||
|
|
||||||
<?php if($tab==='videos'): ?><section><h2>Videos without input dir</h2><?php if(!$orphanVideos): ?><p>No orphan videos.</p><?php endif; ?><div class="admin-list"><?php foreach($orphanVideosPage as $v): $vn=basename($v); ?><form method="post" class="admin-item" onsubmit="return confirm('Delete this generated video?') js-ajax-form"><div><strong><?=h($vn)?></strong><br><span><?=h(round(filesize($v)/1048576,1))?> MB</span></div><input type="hidden" name="action" value="delete_video"><input type="hidden" name="video" value="<?=h($vn)?>"><button type="submit">Delete video</button></form><?php endforeach; ?></div><?=page_links('videos',$videoPage,$videoPages)?></section><?php endif; ?>
|
<?php if($tab==='videos'): ?><section><h2>Videos without input dir</h2><?php if(!$orphanVideos): ?><p>No orphan videos.</p><?php endif; ?><div class="admin-list"><?php foreach($orphanVideosPage as $v): $vn=basename($v); ?><form method="post" class="admin-item" onsubmit="return confirm('Delete this generated video?') js-ajax-form"><div><strong><?=h($vn)?></strong><br><span><?=h(round(filesize($v)/1048576,1))?> MB</span></div><input type="hidden" name="action" value="delete_video"><input type="hidden" name="video" value="<?=h($vn)?>"><button type="submit">Delete video</button></form><?php endforeach; ?></div><?=page_links('videos',$videoPage,$videoPages)?></section><?php endif; ?>
|
||||||
<?php if($tab==='edit' && !$editDir): ?><section><form method="get" class="admin-search-form"><input type="hidden" name="tab" value="edit"><div class="admin-search-wrap"><input name="q" value="<?=h($rawKeyword)?>" placeholder="Search title/teaser/quote_da/description or use date=... / location=..." aria-label="Search input dirs"><a class="admin-search-clear <?=$searchEmpty ? 'is-empty' : ''?>" href="new.php?tab=edit" aria-label="Clear filter" title="Clear filter">×</a></div><div class="admin-search-filters"><label class="admin-filter-item" title="Preview"><input type="checkbox" name="f_preview" value="1" <?=$filterPreview?'checked':''?>><span>P</span></label><label class="admin-filter-item" title="Render"><input type="checkbox" name="f_render" value="1" <?=$filterRender?'checked':''?>><span>R</span></label><label class="admin-filter-item" title="Show"><input type="checkbox" name="f_shown" value="1" <?=$filterShown?'checked':''?>><span>S</span></label></div><button type="submit" class="admin-search-submit" aria-label="Filter"><span class="icon" aria-hidden="true">🔍</span><span class="sr-only">Filter</span></button></form><?php if(!$dirs): ?><p>No input directories yet.</p><?php endif; ?><div class="admin-list video-list"><?php foreach($dirsPage as $d): $runStatus=input_dir_status($d); $running=input_dir_running($d); $enabled=input_dir_enabled($d); $preview=input_dir_preview($d); $state=input_dir_state($d); $fullOutput=basename((string)($state['output'] ?? '')); $previewOutput=basename((string)($state['preview_output'] ?? '')); $hasFullVideo=$fullOutput !== '' && is_file($config['videos_dir'].'/'.$fullOutput); $hasPreviewVideo=$previewOutput !== '' && is_file($config['videos_dir'].'/'.$previewOutput); $displayOutput=($preview && $hasPreviewVideo) ? $previewOutput : ($hasFullVideo ? $fullOutput : ($hasPreviewVideo ? $previewOutput : '')); $hasVideo=$displayOutput !== ''; $displayVideoPath=$hasVideo ? $config['videos_dir'].'/'.$displayOutput : ''; $previewVideo=$hasVideo && $displayOutput === $previewOutput; $canShow=$hasFullVideo && !$preview; $visible=$canShow && input_dir_visible($d); $mapHidden=input_dir_map_hidden($d); $info=$dirInfo[basename($d)] ?? input_dir_info($d); $data=read_data($d); $meta=['title'=>(string)($data['title'] ?? ''),'teaser'=>(string)($data['teaser'] ?? ''),'date'=>(string)($data['date'] ?? ''),'location'=>(string)($data['place'] ?? ''),'quote_da'=>(string)($data['quote_da'] ?? ''),'description'=>(string)($data['description'] ?? '')]; $currentSignature=$info['signature'] ?? ''; $fullFingerprint=is_array($state)?(string)($state['fingerprint'] ?? ''):''; $previewFingerprint=is_array($state)?(string)($state['preview_fingerprint'] ?? ''):''; $editedSinceRender=$hasFullVideo && $currentSignature !== '' && $fullFingerprint !== '' && $currentSignature !== $fullFingerprint; $editedSincePreview=!$hasFullVideo && $previewVideo && $currentSignature !== '' && $previewFingerprint !== '' && $currentSignature !== $previewFingerprint; $staleLabel=$editedSinceRender ? 'Edited since render' : ($editedSincePreview ? 'Edited since preview' : ''); ?><article class="video-row admin-video-row<?= $visible ? ' shown' : '' ?>" data-job="<?=h(basename($d))?>" data-id="<?=h(read_article_id($d))?>"><div><?php if($hasVideo): ?><div class="video-wrapper"><video controls preload="metadata" src="<?=h($config['public_videos'].'/'.rawurlencode($displayOutput))?>"></video><?php $map_n = ($displayOutput !== '' ? pathinfo($displayOutput, PATHINFO_FILENAME) : basename($d)) . '.png'; if(is_file($config['videos_dir'].'/'.$map_n)): ?><a class="map-image-link" href="<?=h($config['public_videos'].'/'.rawurlencode($map_n))?>"><img class="map-image" src="<?=h($config['public_videos'].'/'.rawurlencode($map_n))?>" alt="Map"></a><?php endif; ?></div><?php else: ?><div class="video-placeholder"><img src="assets/img/moto_travel.png" alt=""><span>No video yet</span></div><?php endif; ?></div><div class="video-info"><div class="admin-row-top"><div class="admin-switches">
|
<?php if($tab==='edit' && !$editDir): ?><section><form method="get" class="admin-search-form"><input type="hidden" name="tab" value="edit"><div class="admin-search-wrap"><input name="q" value="<?=h($rawKeyword)?>" placeholder="Search title/teaser/quote_da/description or use date=... / location=..." aria-label="Search input dirs"><a class="admin-search-clear <?=$searchEmpty ? 'is-empty' : ''?>" href="new.php?tab=edit" aria-label="Clear filter" title="Clear filter">×</a></div><div class="admin-search-filters"><label class="admin-filter-item" title="Preview"><input type="checkbox" name="f_preview" value="1" <?=$filterPreview?'checked':''?>><span>P</span></label><label class="admin-filter-item" title="Render"><input type="checkbox" name="f_render" value="1" <?=$filterRender?'checked':''?>><span>R</span></label><label class="admin-filter-item" title="Show"><input type="checkbox" name="f_shown" value="1" <?=$filterShown?'checked':''?>><span>S</span></label></div><button type="submit" class="admin-search-submit" aria-label="Filter"><span class="icon" aria-hidden="true">🔍</span><span class="sr-only">Filter</span></button></form><?php if(!$dirs): ?><p>No input directories yet.</p><?php endif; ?><div class="admin-list video-list"><?php foreach($dirsPage as $d): $runStatus=input_dir_status($d); $running=input_dir_running($d); $enabled=input_dir_enabled($d); $preview=input_dir_preview($d); $state=input_dir_state($d); $fullOutput=basename((string)($state['output'] ?? '')); $previewOutput=basename((string)($state['preview_output'] ?? '')); $hasFullVideo=$fullOutput !== '' && is_file($config['videos_dir'].'/'.$fullOutput); $hasPreviewVideo=$previewOutput !== '' && is_file($config['videos_dir'].'/'.$previewOutput); $displayOutput=($preview && $hasPreviewVideo) ? $previewOutput : ($hasFullVideo ? $fullOutput : ($hasPreviewVideo ? $previewOutput : '')); $hasVideo=$displayOutput !== ''; $displayVideoPath=$hasVideo ? $config['videos_dir'].'/'.$displayOutput : ''; $posterUrl=$hasVideo ? video_thumb_public_url($config, $displayOutput) : ''; $previewVideo=$hasVideo && $displayOutput === $previewOutput; $canShow=$hasFullVideo && !$preview; $visible=$canShow && input_dir_visible($d); $mapHidden=input_dir_map_hidden($d); $info=$dirInfo[basename($d)] ?? input_dir_info($d); $data=read_data($d); $meta=['title'=>(string)($data['title'] ?? ''),'teaser'=>(string)($data['teaser'] ?? ''),'date'=>(string)($data['date'] ?? ''),'location'=>(string)($data['place'] ?? ''),'quote_da'=>(string)($data['quote_da'] ?? ''),'description'=>(string)($data['description'] ?? '')]; $currentSignature=$info['signature'] ?? ''; $fullFingerprint=is_array($state)?(string)($state['fingerprint'] ?? ''):''; $previewFingerprint=is_array($state)?(string)($state['preview_fingerprint'] ?? ''):''; $editedSinceRender=$hasFullVideo && $currentSignature !== '' && $fullFingerprint !== '' && $currentSignature !== $fullFingerprint; $editedSincePreview=!$hasFullVideo && $previewVideo && $currentSignature !== '' && $previewFingerprint !== '' && $currentSignature !== $previewFingerprint; $staleLabel=$editedSinceRender ? 'Edited since render' : ($editedSincePreview ? 'Edited since preview' : ''); ?><article class="video-row admin-video-row<?= $visible ? ' shown' : '' ?>" data-job="<?=h(basename($d))?>" data-id="<?=h(read_article_id($d))?>"><div><?php if($hasVideo): ?><div class="video-wrapper"><video controls preload="metadata" src="<?=h($config['public_videos'].'/'.rawurlencode($displayOutput))?>"<?= $posterUrl !== '' ? ' poster="'.h($posterUrl).'"' : '' ?>></video><?php $map_n = ($displayOutput !== '' ? pathinfo($displayOutput, PATHINFO_FILENAME) : basename($d)) . '.png'; if(is_file($config['videos_dir'].'/'.$map_n)): ?><a class="map-image-link" href="<?=h($config['public_videos'].'/'.rawurlencode($map_n))?>"><img class="map-image" src="<?=h($config['public_videos'].'/'.rawurlencode($map_n))?>" alt="Map"></a><?php endif; ?></div><?php else: ?><div class="video-placeholder"><img src="assets/img/moto_travel.png" alt=""><span>No video yet</span></div><?php endif; ?></div><div class="video-info"><div class="admin-row-top"><div class="admin-switches">
|
||||||
<form method="post" class="inline-form switch-form" data-switch="preview">
|
<form method="post" class="inline-form switch-form" data-switch="preview">
|
||||||
<input type="hidden" name="action" value="set_preview">
|
<input type="hidden" name="action" value="set_preview">
|
||||||
<input type="hidden" name="dir" value="<?=h(basename($d))?>">
|
<input type="hidden" name="dir" value="<?=h(basename($d))?>">
|
||||||
|
|
@ -1034,7 +1090,7 @@ $runningJobs = active_worker_jobs($config);
|
||||||
<?php if($hasVideo): ?><time class="admin-time<?= $staleLabel ? ' admin-time-stale' : '' ?>" title="<?=h($staleLabel ?: 'Video created at')?>"><?=h(date('d.m.Y H:i', filemtime($displayVideoPath)))?></time><?php endif; ?>
|
<?php if($hasVideo): ?><time class="admin-time<?= $staleLabel ? ' admin-time-stale' : '' ?>" title="<?=h($staleLabel ?: 'Video created at')?>"><?=h(date('d.m.Y H:i', filemtime($displayVideoPath)))?></time><?php endif; ?>
|
||||||
</div><div class="admin-actions"><?php if($running): ?><span class="button disabled" title="Rendering now" data-edit-action="1">Rendering</span><?php else: ?><a class="button" href="?id=<?=rawurlencode(read_article_id($d))?>&edit=<?=rawurlencode(basename($d))?>" data-edit-action="1" data-edit-href="?id=<?=rawurlencode(read_article_id($d))?>&edit=<?=rawurlencode(basename($d))?>">Edit</a><?php endif; ?></div></div><h2><?=h($meta['title'] ?: ($info['title'] ?? basename($d)))?></h2><p class="meta"><?php if($meta['date']): ?><span><?=h($meta['date'])?></span><?php endif; ?><?php if($meta['location']): ?><span><?=h($meta['location'])?></span><?php endif; ?><?php if(!$hasVideo): ?><span>No video</span><?php endif; ?><?php if($previewVideo): ?><span>Preview video</span><?php endif; ?></p><?php if(!empty($meta['teaser'])): ?><p class="description-teaser"><?=h((string)$meta['teaser'])?></p><?php endif; ?><?php if($meta['description']): ?><p class="description"><?=nl2br(h($meta['description']), false)?></p><?php endif; ?><?php if(!empty($meta['quote_da'])): ?><p class="description-quote">“<?=h(trim((string)$meta['quote_da'], "\"“”"))?>”</p><?php endif; ?><p class="details"><?=h(basename($d))?> · <?=h($info['file_count'] ?? count(list_files($d)))?> files</p></div></article><?php endforeach; ?></div><?=page_links('edit',$editPage,$editPages)?></section><?php endif; ?>
|
</div><div class="admin-actions"><?php if($running): ?><span class="button disabled" title="Rendering now" data-edit-action="1">Rendering</span><?php else: ?><a class="button" href="?id=<?=rawurlencode(read_article_id($d))?>&edit=<?=rawurlencode(basename($d))?>" data-edit-action="1" data-edit-href="?id=<?=rawurlencode(read_article_id($d))?>&edit=<?=rawurlencode(basename($d))?>">Edit</a><?php endif; ?></div></div><h2><?=h($meta['title'] ?: ($info['title'] ?? basename($d)))?></h2><p class="meta"><?php if($meta['date']): ?><span><?=h($meta['date'])?></span><?php endif; ?><?php if($meta['location']): ?><span><?=h($meta['location'])?></span><?php endif; ?><?php if(!$hasVideo): ?><span>No video</span><?php endif; ?><?php if($previewVideo): ?><span>Preview video</span><?php endif; ?></p><?php if(!empty($meta['teaser'])): ?><p class="description-teaser"><?=h((string)$meta['teaser'])?></p><?php endif; ?><?php if($meta['description']): ?><p class="description"><?=nl2br(h($meta['description']), false)?></p><?php endif; ?><?php if(!empty($meta['quote_da'])): ?><p class="description-quote">“<?=h(trim((string)$meta['quote_da'], "\"“”"))?>”</p><?php endif; ?><p class="details"><?=h(basename($d))?> · <?=h($info['file_count'] ?? count(list_files($d)))?> files</p></div></article><?php endforeach; ?></div><?=page_links('edit',$editPage,$editPages)?></section><?php endif; ?>
|
||||||
<?php if($tab==='edit' && $editDir && $editRunning): ?><section><h2>Edit input dir</h2><p><code>in-dir/<?=h(basename($editDir))?></code></p><div class="err">This input directory is <?=h($editStatus)?>. Editing is disabled until the job completes.</div></section><?php endif; ?>
|
<?php if($tab==='edit' && $editDir && $editRunning): ?><section><h2>Edit input dir</h2><p><code>in-dir/<?=h(basename($editDir))?></code></p><div class="err">This input directory is <?=h($editStatus)?>. Editing is disabled until the job completes.</div></section><?php endif; ?>
|
||||||
<?php if($tab==='edit' && $editDir && !$editRunning): ?><section><h2>Edit input dir</h2><p><code>in-dir/<?=h(basename($editDir))?></code></p><form method="post" enctype="multipart/form-data" id="edit-form"><input type="hidden" name="action" value="update"><input type="hidden" name="dir" value="<?=h(basename($editDir))?>"><input type="hidden" name="id" value="<?=h(ensure_article_id($editDir))?>"><label>Title<input name="title" value="<?=h($editData['title'])?>"></label><label>Teaser<input name="teaser" value="<?=h($editData['teaser'])?>" maxlength="240"></label><label>Danish quote<input name="quote_da" value="<?=h($editData['quote_da'])?>" maxlength="280"></label><label>Date<input name="date" value="<?=h($editData['date'])?>"></label><label>Place<input name="place" value="<?=h($editData['place'])?>"></label><label>Description<textarea name="description" maxlength="5000" data-counter="description-counter-edit"><?=h($editData['description'])?></textarea><small id="description-counter-edit" class="counter"></small></label><label>Add images / videos<input type="file" name="media[]" multiple accept="image/*,video/*"></label><label>Add audio<input type="file" name="audio[]" multiple accept="audio/*"></label><h3>Files and captions</h3><div class="file-list"><?php foreach($editFiles as $f): $ext=strtolower(pathinfo($f, PATHINFO_EXTENSION)); $fileUrl='in-dir/'.rawurlencode(basename($editDir)).'/'.rawurlencode($f); ?><div class="caption-row"><div class="preview"><?php if(in_array($ext,['jpg','jpeg','png','webp','gif'])): ?><img src="<?=h($fileUrl)?>" alt=""><?php elseif(in_array($ext,['mp4','mov','m4v','webm'])): ?><video src="<?=h($fileUrl)?>" controls preload="metadata"></video><a class="download-link" href="<?=h($fileUrl)?>" download>Download</a><?php else: ?><span><?=h(strtoupper($ext ?: 'FILE'))?></span><?php endif; ?></div><div class="caption-fields"><strong><?=h($f)?></strong><?php if(in_array($ext,['jpg','jpeg','png','webp','gif','mp4','mov','m4v','webm'])): ?><input type="hidden" name="caption_files[]" value="<?=h($f)?>"><textarea name="captions[]" placeholder="Optional caption for this file"><?=h($editData['captions'][$f] ?? '')?></textarea><?php if(in_array($ext,['mp4','mov','m4v','webm'])): ?><label class="checkbox-label"><input type="checkbox" name="use_audio_files[]" value="<?=h($f)?>" <?=!empty($editData['video_audio'][$f])?'checked':''?>> <span>Use video file audio</span></label><?php endif; ?><?php else: ?><small>No caption for audio files</small><?php endif; ?></div><button type="button" onclick="deleteFile(<?=h(json_encode($f))?>)">Remove</button></div><?php endforeach; ?></div><button type="submit">Save changes</button></form><form method="post" id="delete-file-form" style="display:none"><input type="hidden" name="action" value="delete_file"><input type="hidden" name="dir" value="<?=h(basename($editDir))?>"><input type="hidden" name="id" value="<?=h(ensure_article_id($editDir))?>"><input type="hidden" name="file" id="delete-file-name" value=""></form><form method="post" onsubmit="return confirm(\'Delete this input directory\?\')" id="delete-dir-form""><input type="hidden" name="action" value="delete_dir"><input type="hidden" name="dir" value="<?=h(basename($editDir))?>"><input type="hidden" name="id" value="<?=h(ensure_article_id($editDir))?>"><label class="checkbox-label"><input type="checkbox" name="delete_output" value="1"> <span>Also delete generated video, if any</span></label><button class="danger" type="submit">Delete input dir</button></form></section><?php endif; ?>
|
<?php if($tab==='edit' && $editDir && !$editRunning): ?><section><h2>Edit input dir</h2><p><code>in-dir/<?=h(basename($editDir))?></code></p><form method="post" enctype="multipart/form-data" id="edit-form"><input type="hidden" name="action" value="update"><input type="hidden" name="dir" value="<?=h(basename($editDir))?>"><input type="hidden" name="id" value="<?=h(ensure_article_id($editDir))?>"><label>Title<input name="title" value="<?=h($editData['title'])?>"></label><label>Teaser<input name="teaser" value="<?=h($editData['teaser'])?>" maxlength="240"></label><label>Danish quote<input name="quote_da" value="<?=h($editData['quote_da'])?>" maxlength="280"></label><label>Date<input name="date" value="<?=h($editData['date'])?>"></label><label>Place<input name="place" value="<?=h($editData['place'])?>"></label><label>Description<textarea name="description" maxlength="5000" data-counter="description-counter-edit"><?=h($editData['description'])?></textarea><small id="description-counter-edit" class="counter"></small></label><label>Add images / videos<input type="file" name="media[]" multiple accept="image/*,video/*"></label><label>Add audio<input type="file" name="audio[]" multiple accept="audio/*"></label><h3>Files and captions</h3><div class="file-list"><?php foreach($editFiles as $f): $ext=strtolower(pathinfo($f, PATHINFO_EXTENSION)); $fileUrl='in-dir/'.rawurlencode(basename($editDir)).'/'.rawurlencode($f); ?><div class="caption-row"><div class="preview"><?php if(in_array($ext,['jpg','jpeg','png','webp','gif'])): ?><img src="<?=h($fileUrl)?>" alt=""><?php elseif(in_array($ext,['mp4','mov','m4v','webm'])): ?><video src="<?=h($fileUrl)?>" controls preload="metadata"></video><a class="download-link" href="<?=h($fileUrl)?>" download>Download</a><?php else: ?><span><?=h(strtoupper($ext ?: 'FILE'))?></span><?php endif; ?></div><div class="caption-fields"><strong><?=h($f)?></strong><?php if(in_array($ext,['jpg','jpeg','png','webp','gif','mp4','mov','m4v','webm'])): ?><input type="hidden" name="caption_files[]" value="<?=h($f)?>"><textarea name="captions[]" placeholder="Optional caption for this file"><?=h($editData['captions'][$f] ?? '')?></textarea><?php if(in_array($ext,['mp4','mov','m4v','webm'])): ?><label class="checkbox-label"><input type="checkbox" name="use_audio_files[]" value="<?=h($f)?>" <?=!empty($editData['video_audio'][$f])?'checked':''?>> <span>Use video file audio</span></label><?php endif; ?><?php else: ?><small>No caption for audio files</small><?php endif; ?></div><div class="file-actions" style="display:flex;gap:.45rem;flex-wrap:wrap;align-self:start"><?php if(in_array($ext,['jpg','jpeg','png','webp','gif'])): ?><button type="button" onclick="setThumb(<?=h(json_encode($f))?>)">Set as thumb</button><?php endif; ?><button type="button" onclick="deleteFile(<?=h(json_encode($f))?>)">Remove</button></div></div><?php endforeach; ?></div><button type="submit">Save changes</button></form><form method="post" id="delete-file-form" style="display:none"><input type="hidden" name="action" value="delete_file"><input type="hidden" name="dir" value="<?=h(basename($editDir))?>"><input type="hidden" name="id" value="<?=h(ensure_article_id($editDir))?>"><input type="hidden" name="file" id="delete-file-name" value=""></form><form method="post" onsubmit="return confirm(\'Delete this input directory\?\')" id="delete-dir-form""><input type="hidden" name="action" value="delete_dir"><input type="hidden" name="dir" value="<?=h(basename($editDir))?>"><input type="hidden" name="id" value="<?=h(ensure_article_id($editDir))?>"><label class="checkbox-label"><input type="checkbox" name="delete_output" value="1"> <span>Also delete generated video, if any</span></label><button class="danger" type="submit">Delete input dir</button></form></section><?php endif; ?>
|
||||||
<?php if($tab==='new'): ?><section><h2>Add new movie input</h2><form method="post" enctype="multipart/form-data" id="new-form"><input type="hidden" name="action" value="create"><label>Title*<input name="title" required></label><label>Teaser<input name="teaser" maxlength="240"></label><label>Danish quote<input name="quote_da" maxlength="280"></label><label>Date<input name="date" placeholder="2026-05-25"></label><label>Place<input name="place"></label><label>Description<textarea name="description" maxlength="5000" data-counter="description-counter-new"></textarea><small id="description-counter-new" class="counter"></small></label><label>Images / videos*<input type="file" name="media[]" multiple required accept="image/*,video/*"></label><label>Audio (optional)<input type="file" name="audio[]" multiple accept="audio/*"></label><button type="submit">Create input-dir</button></form></section><?php endif; ?>
|
<?php if($tab==='new'): ?><section><h2>Add new movie input</h2><form method="post" enctype="multipart/form-data" id="new-form"><input type="hidden" name="action" value="create"><label>Title*<input name="title" required></label><label>Teaser<input name="teaser" maxlength="240"></label><label>Danish quote<input name="quote_da" maxlength="280"></label><label>Date<input name="date" placeholder="2026-05-25"></label><label>Place<input name="place"></label><label>Description<textarea name="description" maxlength="5000" data-counter="description-counter-new"></textarea><small id="description-counter-new" class="counter"></small></label><label>Images / videos*<input type="file" name="media[]" multiple required accept="image/*,video/*"></label><label>Audio (optional)<input type="file" name="audio[]" multiple accept="audio/*"></label><button type="submit">Create input-dir</button></form></section><?php endif; ?>
|
||||||
<script>
|
<script>
|
||||||
function deleteFile(name){
|
function deleteFile(name){
|
||||||
|
|
@ -1072,6 +1128,24 @@ function deleteFile(name){
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setThumb(name){
|
||||||
|
var ctx = getEditDirContext();
|
||||||
|
if(!ctx.dir){ showToast('Internal error: missing input dir', false); return; }
|
||||||
|
var body = 'action=set_video_thumb&dir=' + encodeURIComponent(ctx.dir) + '&id=' + encodeURIComponent(ctx.id) + '&file=' + encodeURIComponent(name) + '&ajax=1';
|
||||||
|
fetch('new.php', {
|
||||||
|
method: 'POST',
|
||||||
|
credentials: 'same-origin',
|
||||||
|
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||||
|
body: body
|
||||||
|
}).then(function(r){ return r.json().catch(function(){ return { ok:false, message:'Invalid server response' }; }); })
|
||||||
|
.then(function(json){
|
||||||
|
if(!json || !json.ok) throw new Error((json && (json.error || json.message)) ? (json.error || json.message) : 'Set thumb failed');
|
||||||
|
showToast(json.message || ('Thumbnail set from ' + name), true);
|
||||||
|
}).catch(function(err){
|
||||||
|
showToast(err.message || String(err), false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function getEditDirContext(){
|
function getEditDirContext(){
|
||||||
var form = document.getElementById('delete-file-form');
|
var form = document.getElementById('delete-file-form');
|
||||||
if(!form) return {dir:'', id:''};
|
if(!form) return {dir:'', id:''};
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
Loading…
Add table
Add a link
Reference in a new issue