Files: previews — a toggle that shows a thumbnail per image and video, made by ffmpeg on the machine's GPU where it has one, cached under the data dir

A previews button on the Files tab turns the icon column into a 256 px thumbnail for
every image and video row, loaded as rows scroll near. internal/thumbs probes what the
machine can do rather than assuming it: ffmpeg and ffprobe, every /dev/dri/renderD*
opened and exercised, vainfo's decode profiles per node where it exists. A video goes to
the first GPU that lists its codec, with ffmpeg's own software fallback behind it and a
CPU retry behind that; images decode on the CPU, JPEGs at a reduced DCT size. Previews
are kept under <data dir>/thumbs/ keyed by path, size and mtime, with undecodable files
remembered too. Root mode makes them through the same root helper the file reads use.
Where ffmpeg is missing the tab offers to install it; where a render node exists but is
closed to the daemon's user, it offers a drop-in adding the group and a restart.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Mitchell-Johnson
2026-10-11 15:20:27 -07:00
co-authored by Claude Fable 5.1
parent e7034cd478
commit f8c9479e93
13 changed files with 1483 additions and 16 deletions
+15 -10
View File
@@ -24,19 +24,21 @@ import (
"time"
"hamlab/internal/priv"
"hamlab/internal/thumbs"
)
type Entry struct {
Name string `json:"name"`
Type string `json:"type"` // dir | file | link | other
Size int64 `json:"size"`
Mode string `json:"mode"` // "-rw-r--r--"
Perm string `json:"perm"` // "0644"
Mtime int64 `json:"mtime"`
User string `json:"user"`
Group string `json:"group"`
Target string `json:"target,omitempty"` // link target
Broken bool `json:"broken,omitempty"` // dangling link
Name string `json:"name"`
Type string `json:"type"` // dir | file | link | other
Size int64 `json:"size"`
Mode string `json:"mode"` // "-rw-r--r--"
Perm string `json:"perm"` // "0644"
Mtime int64 `json:"mtime"`
User string `json:"user"`
Group string `json:"group"`
Target string `json:"target,omitempty"` // link target
Broken bool `json:"broken,omitempty"` // dangling link
Preview string `json:"preview,omitempty"` // "image" | "video": the Files tab can show a thumbnail of it
}
type Listing struct {
@@ -146,6 +148,9 @@ func entryOf(dir, name string, lst os.FileInfo) Entry {
default:
e.Type = "other"
}
if e.Type == "file" || (e.Type == "link" && !e.Broken) {
e.Preview = thumbs.Kind(name)
}
return e
}