Simplify index keyword filter UI and unify q-based push target URLs

This commit is contained in:
hbrain 2026-05-31 15:11:49 +02:00
parent a52e0a8bb3
commit 7f45193a6f
2 changed files with 65 additions and 52 deletions

110
index.php
View file

@ -125,40 +125,52 @@ usort($videos, function($a, $b) use ($videoCache) {
return $bd <=> $ad;
});
$filters = [
'f_title' => trim((string)($_GET['f_title'] ?? '')),
'f_date' => trim((string)($_GET['f_date'] ?? '')),
'f_location' => trim((string)($_GET['f_location'] ?? '')),
];
$titleExact = !empty($_GET['f_exact']) && (string)$_GET['f_exact'] === '1';
$rawKeyword = trim((string)($_GET['q'] ?? ''));
// Backward compatibility with old push URL format (?job=...)
$legacyJob = trim((string)($_GET['job'] ?? ''));
if ($filters['f_title'] === '' && $legacyJob !== '') {
// Backward compatibility with older filter/query formats.
if ($rawKeyword === '') {
$legacyTitle = trim((string)($_GET['f_title'] ?? ''));
$legacyDate = trim((string)($_GET['f_date'] ?? ''));
$legacyLocation = trim((string)($_GET['f_location'] ?? ''));
$legacyJob = trim((string)($_GET['job'] ?? ''));
if ($legacyTitle !== '') $rawKeyword = $legacyTitle;
elseif ($legacyDate !== '') $rawKeyword = 'date=' . $legacyDate;
elseif ($legacyLocation !== '') $rawKeyword = 'location=' . $legacyLocation;
elseif ($legacyJob !== '') {
$legacy = preg_replace('/^\d{8}[_-]/', '', $legacyJob);
$filters['f_title'] = nice_title($legacy ?: $legacyJob);
$rawKeyword = nice_title($legacy ?: $legacyJob);
}
}
$videos = array_values(array_filter($videos, function($v) use ($videoCache, $filters) {
$keywordType = 'general';
$keywordValue = $rawKeyword;
if ($rawKeyword !== '' && preg_match('/^\s*(date|location|place)\s*=\s*(.+)\s*$/i', $rawKeyword, $m)) {
$keywordType = lower_text((string)$m[1]);
if ($keywordType === 'place') $keywordType = 'location';
$keywordValue = trim((string)$m[2]);
}
$videos = array_values(array_filter($videos, function($v) use ($videoCache, $keywordType, $keywordValue) {
if ($keywordValue === '') return true;
$name = basename($v);
$m = $videoCache[$name]['metadata'] ?? video_metadata($v);
$title = (string)($m['title'] ?? '');
$description = (string)($m['description'] ?? '');
$date = (string)($m['date'] ?? '');
$sortDate = (string)($m['sort_date'] ?? '');
$location = (string)($m['location'] ?? '');
if ($filters['f_title'] !== '') {
if ($titleExact) {
if (lower_text(trim($title)) !== lower_text(trim($filters['f_title']))) return false;
} else {
if (stripos($title, $filters['f_title']) === false) return false;
if ($keywordType === 'date') {
return stripos($date, $keywordValue) !== false || stripos($sortDate, $keywordValue) !== false;
}
if ($keywordType === 'location') {
return stripos($location, $keywordValue) !== false;
}
if ($filters['f_date'] !== '' && stripos($date, $filters['f_date']) === false && stripos($sortDate, $filters['f_date']) === false) return false;
if ($filters['f_location'] !== '' && stripos($location, $filters['f_location']) === false) return false;
return true;
return stripos($title, $keywordValue) !== false || stripos($description, $keywordValue) !== false;
}));
$page = max(1, (int)($_GET['page'] ?? 1));
@ -168,9 +180,8 @@ $pages = max(1, (int)ceil($total / $per));
$page = min($page, $pages);
$slice = array_slice($videos, ($page - 1) * $per, $per);
$activeFilterCount = count(keep_non_empty($filters));
$baseParams = keep_non_empty($filters);
if ($titleExact && ($filters['f_title'] ?? '') !== '') $baseParams['f_exact'] = '1';
$activeFilterCount = ($keywordValue !== '') ? 1 : 0;
$baseParams = ($rawKeyword !== '') ? ['q' => $rawKeyword] : [];
?>
<!doctype html>
<html>
@ -182,17 +193,21 @@ if ($titleExact && ($filters['f_title'] ?? '') !== '') $baseParams['f_exact'] =
<link rel="icon" type="image/png" href="assets/img/moto_travel.png">
<link rel="stylesheet" href="style.css">
<style>
.filter-form{display:grid;grid-template-columns:1.4fr 1fr 1fr auto auto;gap:.55rem;align-items:end;margin:0 0 1rem}
.filter-form label{margin:0;font-size:.8rem;color:#A0A4AB;font-weight:600}
.filter-form input{margin-top:.2rem}
.filter-clear-x{display:inline-flex;align-items:center;justify-content:center;width:2.1rem;height:2.1rem;border-radius:50%;background:#C46A3A;border:1px solid #C46A3A;color:#111315;text-decoration:none;font-size:0;position:relative;box-shadow:0 1px 4px #0008}
.filter-clear-x::before{content:'✕';font-size:1rem;line-height:1;font-weight:700;transform:translateY(-.02em)}
.filter-clear-x:hover{background:#d97b48;border-color:#d97b48;color:#111315}
.filter-clear-x:focus-visible{outline:2px solid #3BA7A0;outline-offset:2px}
.filter-form{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:.55rem;align-items:center;margin:0 0 .4rem}
.filter-input-wrap{position:relative;min-width:0}
.filter-input-wrap input{margin:0;padding-right:2.15rem}
.filter-clear-inside{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}
.filter-clear-inside:hover{background:#d97b48;color:#111315;transform:translateY(-50%) scale(1.05)}
.filter-clear-inside.is-empty{opacity:.38;pointer-events:none}
.filter-submit{display:inline-grid;place-items:center;width:2.35rem;height:2.35rem;padding:0;border-radius:.5rem;background:#C46A3A;color:#111315}
.filter-submit .icon{font-size:1.02rem;line-height:1;transform:translateY(.01em)}
.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}
.filter-summary{color:#A0A4AB;font-size:.9rem;margin:0 0 .8rem}
.video-row-head{grid-column:1 / -1;margin:0 0 .15rem}
.video-row-title{font-size:1.35rem;line-height:1.2;margin:.1rem 0 .45rem;color:#F3F4F6}
.video-row-head .meta{margin:0 0 .45rem}
.meta a{color:inherit;text-decoration:none}
.meta a:hover{text-decoration:underline}
@media (max-width:900px){.filter-form{grid-template-columns:1fr}}
</style>
</head>
<body>
@ -200,17 +215,14 @@ if ($titleExact && ($filters['f_title'] ?? '') !== '') $baseParams['f_exact'] =
<main>
<section id="videos">
<form method="get" class="filter-form">
<label>Title
<input name="f_title" value="<?=h($filters['f_title'])?>" placeholder="Filter by title">
</label>
<label>Date
<input name="f_date" value="<?=h($filters['f_date'])?>" placeholder="YYYY-MM-DD or month/year">
</label>
<label>Location
<input name="f_location" value="<?=h($filters['f_location'])?>" placeholder="Filter by location">
</label>
<a class="filter-clear-x" href="index.php" title="Clear filters" aria-label="Clear filters">&times;</a>
<button type="submit">Filter</button>
<div class="filter-input-wrap">
<input name="q" value="<?=h($rawKeyword)?>" placeholder="Search title/description or use date=... / location=..." aria-label="Search articles">
<a class="filter-clear-inside <?=$rawKeyword === '' ? 'is-empty' : ''?>" href="index.php" aria-label="Clear filter" title="Clear filter">×</a>
</div>
<button type="submit" class="filter-submit" aria-label="Filter">
<span class="icon" aria-hidden="true">🔍</span>
<span class="sr-only">Filter</span>
</button>
</form>
<p class="filter-summary">
@ -225,11 +237,18 @@ if ($titleExact && ($filters['f_title'] ?? '') !== '') $baseParams['f_exact'] =
$name = basename($v);
$url = $config['public_videos'].'/'.rawurlencode($name);
$m = $videoCache[$name]['metadata'] ?? video_metadata($v);
$dateFilterUrl = query_url(array_merge($baseParams, ['f_date' => (string)($m['date'] ?? '')]));
$locFilterUrl = query_url(array_merge($baseParams, ['f_location' => (string)($m['location'] ?? '')]));
$dateFilterUrl = query_url(['q' => 'date=' . (string)($m['date'] ?? '')]);
$locFilterUrl = query_url(['q' => 'location=' . (string)($m['location'] ?? '')]);
?>
<article class="video-row">
<time class="created-at" datetime="<?=h(date('c', filemtime($v)))?>"><?=h(date('d.m.Y H:i', filemtime($v)))?></time>
<div class="video-row-head">
<h2 class="video-row-title"><?=h($m['title'])?></h2>
<p class="meta">
<span><a href="<?=h($dateFilterUrl)?>"><?=h($m['date'])?></a></span>
<?php if(!empty($m['location'])): ?><span><a href="<?=h($locFilterUrl)?>"><?=h($m['location'])?></a></span><?php endif; ?>
</p>
</div>
<div class="video-wrapper">
<video controls controlsList="nodownload" oncontextmenu="return false" preload="metadata" src="<?=h($url)?>"></video>
<?php
@ -240,11 +259,6 @@ if ($titleExact && ($filters['f_title'] ?? '') !== '') $baseParams['f_exact'] =
<?php endif; ?>
</div>
<div class="video-info">
<h2><?=h($m['title'])?></h2>
<p class="meta">
<span><a href="<?=h($dateFilterUrl)?>"><?=h($m['date'])?></a></span>
<?php if(!empty($m['location'])): ?><span><a href="<?=h($locFilterUrl)?>"><?=h($m['location'])?></a></span><?php endif; ?>
</p>
<?php if(!empty($m['description'])): ?><p class="description"><?=nl2br(h($m['description']), false)?></p><?php endif; ?>
</div>
</article>

View file

@ -151,8 +151,7 @@ function try_send_show_notification($job, $jobdir, $mvlog_root = null, $logfile
if ($articleTitle === '') $articleTitle = mvlog_nice_title((string)$job);
$filterUrl = 'https://bubulescu.org/mvlog/?' . http_build_query([
'f_title' => $articleTitle,
'f_exact' => '1',
'q' => $articleTitle,
]);
$payload = json_encode([