<?php
date_default_timezone_set('Europe/Copenhagen');
$config = require __DIR__ . "/config.php";
foreach (["videos_dir", "thumbs_dir", "uploads_dir"] as $d) if (!is_dir($config[$d])) mkdir($config[$d], 0775, true);
if (!is_dir(__DIR__ . "/cache")) mkdir(__DIR__ . "/cache", 0775, true);
function h($s){ return htmlspecialchars((string)$s, ENT_QUOTES, "UTF-8"); }
function nice_title($s){ return trim(ucwords(str_replace(['_','-'], ' ', $s))); }
function hidden_video_outputs($config){
    $hidden = [];
    foreach (glob($config['uploads_dir'].'/*', GLOB_ONLYDIR) ?: [] as $dir) {
        if (!is_file($dir . '/.mvlog-hidden')) continue;
        $state = is_file($dir . '/.movmaker-state.json') ? json_decode((string)file_get_contents($dir . '/.movmaker-state.json'), true) : [];
        if (is_array($state) && !empty($state['output'])) $hidden[basename((string)$state['output'])] = true;
    }
    return $hidden;
}
function video_metadata($path){
    $base = pathinfo($path, PATHINFO_FILENAME);
    $meta = ['title' => nice_title($base), 'date' => date('Y-m-d', filemtime($path)), 'sort_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['sort_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['sort_date'] = substr($lower['creation_time'], 0, 10);
            if (empty($lower['date'])) $meta['date'] = $meta['sort_date'];
        }
        foreach (['location','place','location-eng','com.apple.quicktime.location.name','com.apple.quicktime.location.iso6709'] as $k) {
            if (!empty($lower[$k])) { $meta['location'] = $lower[$k]; break; }
        }
        foreach (['description','synopsis'] as $k) {
            if (!empty($lower[$k])) { $meta['description'] = $lower[$k]; break; }
        }
    }
    return $meta;
}
function load_video_cache($videos){
    $cacheFile = __DIR__ . '/cache/videos.json';
    $cache = is_file($cacheFile) ? json_decode((string)file_get_contents($cacheFile), true) : [];
    if (!is_array($cache) || ($cache['version'] ?? 0) !== 2) $cache = [];
    $items = $cache['items'] ?? [];
    $newItems = [];
    $changed = false;

    foreach ($videos as $path) {
        $key = basename($path);
        $mtime = filemtime($path);
        $size = filesize($path);
        $cached = $items[$key] ?? null;
        if (!is_array($cached) || ($cached['mtime'] ?? null) !== $mtime || ($cached['size'] ?? null) !== $size) {
            $cached = ['mtime' => $mtime, 'size' => $size, 'metadata' => video_metadata($path)];
            $changed = true;
        }
        $newItems[$key] = $cached;
    }

    if (array_diff(array_keys($items), array_keys($newItems))) $changed = true;
    if ($changed) {
        file_put_contents($cacheFile, json_encode(['version' => 2, 'generated_at' => date('c'), 'items' => $newItems], JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT), LOCK_EX);
    }
    return $newItems;
}
$hiddenOutputs = hidden_video_outputs($config);
$videos = array_values(array_filter(glob($config['videos_dir'].'/*.{mp4,webm,mov,m4v}', GLOB_BRACE) ?: [], fn($v)=>empty($hiddenOutputs[basename($v)]) && !preg_match('/_preview\.(mp4|webm|mov|m4v)$/i', basename($v)) && !preg_match('/_preview\.(mp4|webm|mov|m4v)$/i', basename($v))));
$videoCache = load_video_cache($videos);
usort($videos, function($a, $b) use ($videoCache) {
    $am = $videoCache[basename($a)] ?? [];
    $bm = $videoCache[basename($b)] ?? [];
    $ad = strtotime($am['metadata']['sort_date'] ?? $am['metadata']['date'] ?? '') ?: filemtime($a);
    $bd = strtotime($bm['metadata']['sort_date'] ?? $bm['metadata']['date'] ?? '') ?: filemtime($b);
    return $bd <=> $ad;
});
$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="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="stylesheet" href="style.css"></head><body>
<header class="site-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</h1><p>Bubulescu.Org</p></a></div><button id="push-toggle" class="push-toggle" type="button" hidden aria-label="Notifications" title="Notifications"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 22a2.7 2.7 0 0 0 2.6-2h-5.2A2.7 2.7 0 0 0 12 22Zm7-6V11a7 7 0 0 0-5-6.7V3a2 2 0 0 0-4 0v1.3A7 7 0 0 0 5 11v5l-2 2v1h18v-1l-2-2Z"/></svg></button><a class="rss-link" href="feed.php" aria-label="RSS feed" title="RSS feed"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M6.2 17.8a2 2 0 1 1-4 0 2 2 0 0 1 4 0ZM2.2 8.7v3.1a10 10 0 0 1 10 10h3.1A13.1 13.1 0 0 0 2.2 8.7Zm0-5.5v3.1a15.5 15.5 0 0 1 15.5 15.5h3.1A18.6 18.6 0 0 0 2.2 3.2Z"/></svg></a></header><main>
<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); $m=$videoCache[$name]['metadata'] ?? video_metadata($v); ?>
<article class="video-row"><time class="created-at" datetime="<?=h(date('c', filemtime($v)))?>"><?=h(date('d.m.Y H:i', filemtime($v)))?></time><video controls controlsList="nodownload" oncontextmenu="return false" 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"><?=nl2br(h($m['description']), false)?></p><?php endif; ?></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>
</main><footer>Bubulescu.Org</footer><script>
(function(){
  const btn=document.getElementById('push-toggle');
  if(!btn || !('serviceWorker' in navigator) || !('PushManager' in window) || !('Notification' in window)) return;
  function b64uToUint8Array(s){ const pad='='.repeat((4-s.length%4)%4); const b64=(s+pad).replace(/-/g,'+').replace(/_/g,'/'); const raw=atob(b64); return Uint8Array.from([...raw].map(c=>c.charCodeAt(0))); }
  async function state(reg){ const sub=await reg.pushManager.getSubscription(); btn.classList.toggle('active', !!sub); btn.title=sub?'Notifications on':'Notifications'; btn.hidden=false; return sub; }
  navigator.serviceWorker.register('sw.js').then(async reg=>{
    const cfg=await fetch('push_config.php',{cache:'no-store'}).then(r=>r.json()).catch(()=>({enabled:false}));
    if(!cfg.enabled || !cfg.publicKey) return;
    await state(reg);
    btn.addEventListener('click', async ()=>{
      btn.disabled=true;
      try{
        let sub=await reg.pushManager.getSubscription();
        if(sub){
          return;
        }else{
          const perm=await Notification.requestPermission();
          if(perm!=='granted') return;
          sub=await reg.pushManager.subscribe({userVisibleOnly:true,applicationServerKey:b64uToUint8Array(cfg.publicKey)});
          await fetch('push_subscribe.php',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(sub)});
        }
        await state(reg);
      }catch(e){ alert(e.message || 'Could not update notifications'); }
      finally{ await state(reg); }
    });
  });
})();
</script></body></html>
