files: video previews are made on the Transcode device too

The setting's device makes every video preview: on the file's own machine
when it is there (the hub tells it by header), else on the device's machine,
which reads the file through the hub by a one-file token, and the preview is
kept in the file's machine's own cache (PUT /api/files/thumb, refused if the
file changed). Images and covers stay on the file's machine's CPU. If the
device's machine can't, the file's makes it as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Mitchell-Johnson
2026-10-23 16:07:00 -07:00
co-authored by Claude Opus 5.5
parent 1464e1f7a0
commit d5c16540a1
7 changed files with 335 additions and 12 deletions
+14
View File
@@ -155,3 +155,17 @@ func TestPieceArgs(t *testing.T) {
t.Error("no sound means -an")
}
}
func TestCapsOnly(t *testing.T) {
c := gridCaps()
if got := c.Only("/dev/dri/renderD129"); len(got.GPUs) != 1 || got.GPUs[0].Name != "Intel Arc A380" || len(c.GPUs) != 2 {
t.Fatalf("one card: %+v", got.GPUs)
}
if len(c.Only(CPU).GPUs) != 0 || len(c.Only("/dev/dri/renderD200").GPUs) != 0 || len(c.Only("").GPUs) != 2 {
t.Fatal("cpu, a device not here, any")
}
// previews then go to that card, or the CPU
if g := c.Only("/dev/dri/renderD129").pick("hevc"); g == nil || g.Device != "/dev/dri/renderD129" {
t.Fatalf("pick: %+v", g)
}
}
+24 -1
View File
@@ -62,6 +62,24 @@ type Caps struct {
type Opts struct {
Size int // the long edge in pixels; 256 when zero
Timeout time.Duration // for the whole job, ffprobe and every ffmpeg attempt; 30 s when zero
Kind string // image | video | audio; from the name when empty (a URL has none)
}
// Only is the caps with one device left to decode on: a render node, CPU for none, or ""
// for all of them — the Transcode device setting, which previews follow too. A device this
// machine doesn't have leaves the CPU.
func (c Caps) Only(device string) Caps {
if device == "" {
return c
}
out := c
out.GPUs = nil
for _, g := range c.GPUs {
if g.Device == device {
out.GPUs = append(out.GPUs, g)
}
}
return out
}
func (o Opts) defaults() Opts {
@@ -81,6 +99,8 @@ type Result struct {
Codec string `json:"codec"`
Width int `json:"width"`
Height int `json:"height"`
// Failure is why the file can't be previewed, when another machine tried (no JPEG then).
Failure string `json:"-"`
}
// ErrUnsupported: not an image or a video by its name.
@@ -356,7 +376,10 @@ func (a attempt) engine() string {
// Make produces the preview of one file.
func Make(ctx context.Context, c Caps, path string, o Opts) (Result, error) {
kind := Kind(path)
kind := o.Kind
if kind == "" {
kind = Kind(path)
}
if kind == "" {
return Result{}, ErrUnsupported
}