feat: make hover thumbnails optional (--no-thumbnails)

Thumbnails stay on by default. --no-thumbnails skips building the preview sprite, so the server does no extra work and the rest of the player still works. The frontend already shows no preview when the sprite is unavailable.
This commit is contained in:
Shaku-Med 2026-06-19 00:14:34 -04:00
parent 41f430d46d
commit c230c6b7d7
3 changed files with 21 additions and 0 deletions

View file

@ -149,6 +149,9 @@ video the first time you hover, in a single quick ffmpeg pass, and keeps it in
memory so nothing is written to disk. If you already have your own sprite, point
the `/scrub` route at it instead.
Hover previews are on by default. If you would rather not build them at all, start
the server with `--no-thumbnails` and the rest of the player keeps working.
### 4. Run directly in Terminal (Standalone)
If you prefer to bypass the web interface, you can render the video directly inside an ANSI-supported terminal (zero-flicker, true color):
```bash

View file

@ -310,6 +310,9 @@ async def scrub_meta(v: int | None = None):
loop) the first time it's asked for, then reuses it from memory."""
from fastapi import Response
import json as _json
# Thumbnails are on by default; --no-thumbnails turns the whole thing off.
if not getattr(app.state, "thumbnails", True):
return Response(content='{"available": false}', media_type="application/json")
video_path = _scrub_video_path(v)
if not video_path or not os.path.exists(video_path):
return Response(content='{"available": false}', media_type="application/json")
@ -757,6 +760,12 @@ if __name__ == "__main__":
help="Adaptive-codec colour fidelity (lossless = bit-exact; lower = "
"smaller stream via lossy temporal delta). Chars always exact."
)
playback.add_argument(
"--no-thumbnails",
action="store_true", default=False,
help="Turn off the hover thumbnails on the seek bar (skips building the "
"preview sprite). The rest of the player still works."
)
# ── Server ──
srv = parser.add_argument_group('\033[33mServer\033[0m')
@ -784,6 +793,7 @@ if __name__ == "__main__":
app.state.loop = args.loop
app.state.tolerance = {"lossless": 0, "high": 4, "balanced": 8, "low": 16}[args.quality]
app.state.debug = args.debug
app.state.thumbnails = not args.no_thumbnails
global_default_cols = args.cols if args.cols is not None else (450 if args.pixel else 200)
app.state.cols = global_default_cols
app.state.rows = args.rows

View file

@ -78,6 +78,14 @@ class ScrubTests(unittest.TestCase):
finally:
ss.app.state.queue = [_entry(self.video)]
def test_thumbnails_can_be_disabled(self):
ss.app.state.thumbnails = False
try:
body = json.loads(asyncio.run(ss.scrub_meta(0)).body)
self.assertFalse(body["available"])
finally:
ss.app.state.thumbnails = True
def test_sprite_404_before_it_is_built(self):
from fastapi import HTTPException
ss._scrub_cache.clear()