diff --git a/src/matebot/sitegen.py b/src/matebot/sitegen.py
index 2cbff4f..9beb58c 100644
--- a/src/matebot/sitegen.py
+++ b/src/matebot/sitegen.py
@@ -11,6 +11,8 @@
import importlib.resources
import json
import logging
+import shutil
+import subprocess
from pathlib import Path
from .slog import Shot, SlogError, parse_slog
@@ -88,7 +90,21 @@ def _video_info(shots_dir: Path, out_dir: Path, sid: str) -> dict:
offset = float(json.loads((shots_dir / f"{sid}.video.json").read_text())["offset"])
except (OSError, ValueError, KeyError):
pass
- return {"video": f"video/{sid}.mp4", "video_offset": offset}
+ info = {"video": f"video/{sid}.mp4", "video_offset": offset}
+ # poster frame: phones don't decode a frame until play, showing black
+ poster = out_dir / "video" / f"{sid}.jpg"
+ stale = not poster.exists() or poster.stat().st_mtime < mp4.stat().st_mtime
+ if stale and shutil.which("ffmpeg"):
+ proc = subprocess.run(
+ ["ffmpeg", "-hide_banner", "-loglevel", "error", "-y", "-ss", "0.5",
+ "-i", str(mp4), "-frames:v", "1", "-vf", "scale=640:-2", str(poster)],
+ capture_output=True,
+ )
+ if proc.returncode != 0:
+ poster.unlink(missing_ok=True)
+ if poster.exists():
+ info["poster"] = f"video/{sid}.jpg"
+ return info
def generate(shots_dir: str | Path, out_dir: str | Path, *, title: str = "Shot Journal") -> int:
diff --git a/src/matebot/web/app.js b/src/matebot/web/app.js
index 01cc9b4..0dd456e 100644
--- a/src/matebot/web/app.js
+++ b/src/matebot/web/app.js
@@ -92,7 +92,7 @@ function renderDetail(id, shot, compareId, compare) {
if (shot.video) {
const holder = document.createElement("div");
holder.className = "chart-card video-card";
- holder.innerHTML = `
+ holder.innerHTML = `
`;
charts.appendChild(holder);
VIDEO = holder.querySelector("video");