Improve video metadata layout
This commit is contained in:
parent
6ff3e3c369
commit
dd0804a2ab
5 changed files with 43 additions and 4 deletions
BIN
assets/fonts/BebasNeue-Regular.ttf
Normal file
BIN
assets/fonts/BebasNeue-Regular.ttf
Normal file
Binary file not shown.
BIN
assets/fonts/IBMPlexSans-SemiBold.ttf
Normal file
BIN
assets/fonts/IBMPlexSans-SemiBold.ttf
Normal file
Binary file not shown.
BIN
assets/fonts/NotoSans-Bold.ttf
Normal file
BIN
assets/fonts/NotoSans-Bold.ttf
Normal file
Binary file not shown.
45
index.php
45
index.php
|
|
@ -2,14 +2,53 @@
|
||||||
$config = require __DIR__ . "/config.php";
|
$config = require __DIR__ . "/config.php";
|
||||||
foreach (["videos_dir", "thumbs_dir", "uploads_dir"] as $d) if (!is_dir($config[$d])) mkdir($config[$d], 0775, true);
|
foreach (["videos_dir", "thumbs_dir", "uploads_dir"] as $d) if (!is_dir($config[$d])) mkdir($config[$d], 0775, true);
|
||||||
function h($s){ return htmlspecialchars((string)$s, ENT_QUOTES, "UTF-8"); }
|
function h($s){ return htmlspecialchars((string)$s, ENT_QUOTES, "UTF-8"); }
|
||||||
|
function nice_title($s){ return trim(ucwords(str_replace(['_','-'], ' ', $s))); }
|
||||||
|
function video_metadata($path){
|
||||||
|
$base = pathinfo($path, PATHINFO_FILENAME);
|
||||||
|
$meta = ['title' => nice_title($base), 'date' => date('Y-m-d', filemtime($path)), 'location' => '', 'description' => ''];
|
||||||
|
|
||||||
|
if (preg_match('/^(\d{4})(\d{2})(\d{2})[_-](.+)$/', $base, $m)) {
|
||||||
|
$meta['date'] = "$m[1]-$m[2]-$m[3]";
|
||||||
|
$meta['title'] = nice_title($m[4]);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ([dirname($path).'/'.$base.'.txt', dirname($path).'/'.$base.'.data.txt'] as $sidecar) {
|
||||||
|
if (!is_file($sidecar)) continue;
|
||||||
|
foreach (file($sidecar, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) as $line) {
|
||||||
|
if (!str_contains($line, ':')) continue;
|
||||||
|
[$k, $v] = array_map('trim', explode(':', $line, 2));
|
||||||
|
$k = strtolower($k);
|
||||||
|
if (in_array($k, ['title','date','location','place','description'], true)) $meta[$k === 'place' ? 'location' : $k] = $v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$ffprobe = trim((string)shell_exec('command -v ffprobe 2>/dev/null'));
|
||||||
|
if ($ffprobe !== '') {
|
||||||
|
$json = shell_exec(escapeshellarg($ffprobe).' -v quiet -print_format json -show_entries format_tags '.escapeshellarg($path).' 2>/dev/null');
|
||||||
|
$data = json_decode((string)$json, true);
|
||||||
|
$tags = $data['format']['tags'] ?? [];
|
||||||
|
$lower = [];
|
||||||
|
foreach ($tags as $k => $v) $lower[strtolower($k)] = $v;
|
||||||
|
if (!empty($lower['title'])) $meta['title'] = $lower['title'];
|
||||||
|
if (!empty($lower['date'])) $meta['date'] = $lower['date'];
|
||||||
|
if (!empty($lower['creation_time'])) $meta['date'] = substr($lower['creation_time'], 0, 10);
|
||||||
|
foreach (['location','place','location-eng','com.apple.quicktime.location.iso6709'] as $k) {
|
||||||
|
if (!empty($lower[$k])) { $meta['location'] = $lower[$k]; break; }
|
||||||
|
}
|
||||||
|
foreach (['description','comment','synopsis'] as $k) {
|
||||||
|
if (!empty($lower[$k])) { $meta['description'] = $lower[$k]; break; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $meta;
|
||||||
|
}
|
||||||
$videos = glob($config['videos_dir'].'/*.{mp4,webm,mov,m4v}', GLOB_BRACE) ?: [];
|
$videos = glob($config['videos_dir'].'/*.{mp4,webm,mov,m4v}', GLOB_BRACE) ?: [];
|
||||||
usort($videos, fn($a,$b)=>filemtime($b)<=>filemtime($a));
|
usort($videos, fn($a,$b)=>filemtime($b)<=>filemtime($a));
|
||||||
$page=max(1,(int)($_GET['page']??1)); $per=$config['items_per_page']; $total=count($videos); $pages=max(1,(int)ceil($total/$per)); $page=min($page,$pages); $slice=array_slice($videos,($page-1)*$per,$per);
|
$page=max(1,(int)($_GET['page']??1)); $per=$config['items_per_page']; $total=count($videos); $pages=max(1,(int)ceil($total/$per)); $page=min($page,$pages); $slice=array_slice($videos,($page-1)*$per,$per);
|
||||||
?>
|
?>
|
||||||
<!doctype html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>MVLog</title><link rel="stylesheet" href="style.css"></head><body>
|
<!doctype html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>MVLog</title><link rel="stylesheet" href="style.css"></head><body>
|
||||||
<header><h1>MVLog</h1></header><main>
|
<header><h1>MVLog</h1></header><main>
|
||||||
<section id="videos"><h2>Videos</h2><?php if(!$slice): ?><p>No videos yet.</p><?php endif; ?><div class="grid">
|
<section id="videos"><?php if(!$slice): ?><p>No videos yet.</p><?php endif; ?><div class="video-list">
|
||||||
<?php foreach($slice as $v): $name=basename($v); $url=$config['public_videos'].'/'.rawurlencode($name); $size=filesize($v); ?>
|
<?php foreach($slice as $v): $name=basename($v); $url=$config['public_videos'].'/'.rawurlencode($name); $size=filesize($v); $m=video_metadata($v); ?>
|
||||||
<article class="card"><video controls preload="metadata" src="<?=h($url)?>"></video><h3><?=h(pathinfo($name,PATHINFO_FILENAME))?></h3><p><?=h(date('Y-m-d H:i',filemtime($v)))?> · <?=h(round($size/1048576,1))?> MB</p><a href="<?=h($url)?>" download>Download</a></article>
|
<article class="video-row"><video controls preload="metadata" src="<?=h($url)?>"></video><div class="video-info"><h2><?=h($m['title'])?></h2><p class="meta"><span><?=h($m['date'])?></span><?php if($m['location']): ?><span><?=h($m['location'])?></span><?php endif; ?></p><?php if($m['description']): ?><p class="description"><?=h($m['description'])?></p><?php endif; ?><p class="details"><?=h(round($size/1048576,1))?> MB</p></div></article>
|
||||||
<?php endforeach; ?></div><div class="pages"><?php for($i=1;$i<=$pages;$i++): ?><a class="<?=$i===$page?'active':''?>" href="?page=<?=$i?>"><?=$i?></a><?php endfor; ?></div></section>
|
<?php endforeach; ?></div><div class="pages"><?php for($i=1;$i<=$pages;$i++): ?><a class="<?=$i===$page?'active':''?>" href="?page=<?=$i?>"><?=$i?></a><?php endfor; ?></div></section>
|
||||||
</main><footer>Bubulescu.Org</footer></body></html>
|
</main><footer>Bubulescu.Org</footer></body></html>
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
:root{font-family:system-ui,sans-serif;color:#17202a;background:#f6f7fb}body{margin:0}header{background:#101827;color:white;padding:1.2rem 2rem;text-align:center}a{color:#2563eb}.button,button{background:#2563eb;color:white;border:0;border-radius:.5rem;padding:.7rem 1rem;text-decoration:none;cursor:pointer}nav a{margin-left:1rem;color:white}main{max-width:1100px;margin:2rem auto;padding:0 1rem}.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(260px,1fr));gap:1rem}.card,form{background:white;border-radius:.8rem;padding:1rem;box-shadow:0 2px 10px #0001}.card video{width:100%;aspect-ratio:16/9;background:#111;border-radius:.5rem}label{display:block;margin:.8rem 0;font-weight:600}input,textarea{display:block;width:100%;box-sizing:border-box;margin-top:.3rem;padding:.65rem;border:1px solid #cbd5e1;border-radius:.45rem}textarea{min-height:6rem}.ok,.err{padding:1rem;border-radius:.5rem;margin-bottom:1rem}.ok{background:#dcfce7}.err{background:#fee2e2}.pages a{display:inline-block;margin:.2rem;padding:.45rem .7rem;background:white;border-radius:.4rem;text-decoration:none}.pages .active{background:#2563eb;color:white}footer{text-align:center;color:#667085;padding:2rem;font-size:.85rem}
|
@font-face{font-family:BebasNeue;src:url('assets/fonts/BebasNeue-Regular.ttf')}@font-face{font-family:IBMPlexCaption;src:url('assets/fonts/IBMPlexSans-SemiBold.ttf')}@font-face{font-family:NotoStamp;src:url('assets/fonts/NotoSans-Bold.ttf')}:root{font-family:IBMPlexCaption,system-ui,sans-serif;color:#17202a;background:#f6f7fb}body{margin:0}header{background:#101827;color:white;padding:1.2rem 2rem;text-align:center}header h1{font-family:BebasNeue,system-ui,sans-serif;font-size:3rem;letter-spacing:.06em;margin:0}a{color:#2563eb}.button,button{background:#2563eb;color:white;border:0;border-radius:.5rem;padding:.7rem 1rem;text-decoration:none;cursor:pointer}nav a{margin-left:1rem;color:white}main{max-width:1100px;margin:2rem auto;padding:0 1rem}.video-list{display:flex;flex-direction:column;gap:1rem}.video-row,form{background:white;border-radius:.8rem;padding:1rem;box-shadow:0 2px 10px #0001}.video-row{display:grid;grid-template-columns:minmax(280px,42%) 1fr;gap:1rem;align-items:start}.video-row video{width:100%;aspect-ratio:16/9;background:#111;border-radius:.5rem}.video-info h2{font-size:1.6rem;margin:.1rem 0 .5rem}.meta{display:flex;gap:.5rem;flex-wrap:wrap;color:#475569;margin:.25rem 0}.meta span{background:#eef2ff;border-radius:999px;padding:.25rem .55rem}.description{font-size:1rem;line-height:1.45;color:#334155;margin:.9rem 0}.details{color:#667085;font-size:.9rem}label{display:block;margin:.8rem 0;font-weight:600}input,textarea{display:block;width:100%;box-sizing:border-box;margin-top:.3rem;padding:.65rem;border:1px solid #cbd5e1;border-radius:.45rem}textarea{min-height:6rem}.ok,.err{padding:1rem;border-radius:.5rem;margin-bottom:1rem}.ok{background:#dcfce7}.err{background:#fee2e2}.pages a{display:inline-block;margin:.2rem;padding:.45rem .7rem;background:white;border-radius:.4rem;text-decoration:none}.pages .active{background:#2563eb;color:white}footer{font-family:NotoStamp,system-ui,sans-serif;text-align:center;color:#667085;padding:2rem;font-size:.8rem}@media(max-width:760px){.video-row{grid-template-columns:1fr}header h1{font-size:2.4rem}}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue