files: the previews button says where video previews are made
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
d5c16540a1
commit
fac8a3a180
+31
-1
@@ -245,8 +245,38 @@ type transcodeMachine struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// getTranscode: GET /api/transcode — the setting, and what every machine can encode on,
|
// getTranscode: GET /api/transcode — the setting, and what every machine can encode on,
|
||||||
// asked of each now.
|
// asked of each now. ?only=selected asks only the chosen machine, for the device's name.
|
||||||
func (s *Server) getTranscode(w http.ResponseWriter, r *http.Request) {
|
func (s *Server) getTranscode(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if r.URL.Query().Get("only") == "selected" {
|
||||||
|
c := s.transcodeChoice()
|
||||||
|
out := map[string]any{"selected": c}
|
||||||
|
if c != nil {
|
||||||
|
name := c.Device
|
||||||
|
ctx, cancel := context.WithTimeout(r.Context(), 5*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
var devs []media.Device
|
||||||
|
if c.Node == "" {
|
||||||
|
caps, _ := s.thumbs.capsFor(ctx, false, false)
|
||||||
|
devs = media.Devices(caps)
|
||||||
|
} else if code, b, err := s.nodes.Do(ctx, c.Node, "GET", "/api/files/play-devices", nil, nil); err == nil && code == 200 {
|
||||||
|
var d struct{ Devices []media.Device }
|
||||||
|
_ = json.Unmarshal(b, &d)
|
||||||
|
devs = d.Devices
|
||||||
|
}
|
||||||
|
for _, d := range devs {
|
||||||
|
if d.ID == c.Device {
|
||||||
|
name = d.Name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
host := c.Node
|
||||||
|
if host == "" {
|
||||||
|
host = s.hostName()
|
||||||
|
}
|
||||||
|
out["host"], out["name"] = host, name
|
||||||
|
}
|
||||||
|
jsonOK(w, 200, out)
|
||||||
|
return
|
||||||
|
}
|
||||||
names := []string{""}
|
names := []string{""}
|
||||||
if s.nodes != nil {
|
if s.nodes != nil {
|
||||||
names = append(names, s.nodes.Names()...)
|
names = append(names, s.nodes.Names()...)
|
||||||
|
|||||||
+4
-1
@@ -307,6 +307,9 @@ export default function Files({ onOpenInTerminal }: { onOpenInTerminal?: (cmd: s
|
|||||||
const [thumbs, setThumbs] = useState<ThumbStatus | null>(null)
|
const [thumbs, setThumbs] = useState<ThumbStatus | null>(null)
|
||||||
const [listEl, setListEl] = useState<HTMLDivElement | null>(null)
|
const [listEl, setListEl] = useState<HTMLDivElement | null>(null)
|
||||||
const near = useMemo(() => (listEl && previews ? new Near(listEl) : null), [listEl, previews])
|
const near = useMemo(() => (listEl && previews ? new Near(listEl) : null), [listEl, previews])
|
||||||
|
// Video previews are made on the Transcode device (Settings), wherever it is.
|
||||||
|
const [tc, setTc] = useState<string>('')
|
||||||
|
useEffect(() => { api.transcodeSelected().then((t) => setTc(t.selected ? `videos on ${t.host}'s ${t.name} (Settings → Transcode device), images on this machine's CPU` : '')).catch(() => {}) }, [])
|
||||||
const readThumbs = useCallback((refresh = false) => { api.thumbStatus(sudo, refresh).then(setThumbs).catch((e) => setThumbs({ ok: false, engine: '', summary: '', gpus: [], error: e instanceof Error ? e.message : 'failed', user: '', cache: { path: '', entries: 0, bytes: 0 } })) }, [sudo])
|
const readThumbs = useCallback((refresh = false) => { api.thumbStatus(sudo, refresh).then(setThumbs).catch((e) => setThumbs({ ok: false, engine: '', summary: '', gpus: [], error: e instanceof Error ? e.message : 'failed', user: '', cache: { path: '', entries: 0, bytes: 0 } })) }, [sudo])
|
||||||
useEffect(() => { try { localStorage.setItem(PREVIEWS_KEY, previews ? '1' : '0') } catch { /* private mode */ } }, [previews])
|
useEffect(() => { try { localStorage.setItem(PREVIEWS_KEY, previews ? '1' : '0') } catch { /* private mode */ } }, [previews])
|
||||||
useEffect(() => { if (previews) readThumbs(); else setThumbs(null) }, [previews, readThumbs])
|
useEffect(() => { if (previews) readThumbs(); else setThumbs(null) }, [previews, readThumbs])
|
||||||
@@ -409,7 +412,7 @@ export default function Files({ onOpenInTerminal }: { onOpenInTerminal?: (cmd: s
|
|||||||
<div className="files-tools">
|
<div className="files-tools">
|
||||||
<button className={'btn' + (sudo ? ' on' : '')} title="browse as root" onClick={() => setSudo(!sudo)}>{sudo ? 'root' : 'sudo'}</button>
|
<button className={'btn' + (sudo ? ' on' : '')} title="browse as root" onClick={() => setSudo(!sudo)}>{sudo ? 'root' : 'sudo'}</button>
|
||||||
<button className={'btn' + (hidden ? ' on' : '')} title="show hidden files" onClick={() => setHidden(!hidden)}>.hidden</button>
|
<button className={'btn' + (hidden ? ' on' : '')} title="show hidden files" onClick={() => setHidden(!hidden)}>.hidden</button>
|
||||||
<button className={'btn' + (previews ? ' on' : '')} title={'thumbnails for images and videos' + (thumbs ? ` — ${thumbs.summary}` + (thumbs.cache.entries ? ` · ${thumbs.cache.entries} kept (${fmtBytes(thumbs.cache.bytes)})` : '') : '')} onClick={() => setPreviews(!previews)}>previews</button>
|
<button className={'btn' + (previews ? ' on' : '')} title={'thumbnails for images and videos' + (thumbs ? ` — ${tc || thumbs.summary}` + (thumbs.cache.entries ? ` · ${thumbs.cache.entries} kept (${fmtBytes(thumbs.cache.bytes)})` : '') : '')} onClick={() => setPreviews(!previews)}>previews</button>
|
||||||
<select className="btn" value={sort} onChange={(e) => setSort(e.target.value as Sort)}><option value="name">name</option><option value="size">size</option><option value="mtime">modified</option></select>
|
<select className="btn" value={sort} onChange={(e) => setSort(e.target.value as Sort)}><option value="name">name</option><option value="size">size</option><option value="mtime">modified</option></select>
|
||||||
<input className="filter" placeholder="filter" value={q} onChange={(e) => setQ(e.target.value)} />
|
<input className="filter" placeholder="filter" value={q} onChange={(e) => setQ(e.target.value)} />
|
||||||
<button className="btn" onClick={mkdir}>+ folder</button>
|
<button className="btn" onClick={mkdir}>+ folder</button>
|
||||||
|
|||||||
@@ -298,6 +298,7 @@ export const api = {
|
|||||||
playStatus: (node: string, id: string) => req<PlayStatus>('GET', nodeAPI(node, `/api/files/play/${id}`), undefined, true),
|
playStatus: (node: string, id: string) => req<PlayStatus>('GET', nodeAPI(node, `/api/files/play/${id}`), undefined, true),
|
||||||
playStop: (node: string, id: string) => req<void>('DELETE', nodeAPI(node, `/api/files/play/${id}`), undefined, true),
|
playStop: (node: string, id: string) => req<void>('DELETE', nodeAPI(node, `/api/files/play/${id}`), undefined, true),
|
||||||
transcode: () => req<TranscodeSetting>('GET', '/api/transcode', undefined, true),
|
transcode: () => req<TranscodeSetting>('GET', '/api/transcode', undefined, true),
|
||||||
|
transcodeSelected: () => req<{ selected: { node: string; device: string } | null; host?: string; name?: string }>('GET', '/api/transcode?only=selected', undefined, true),
|
||||||
setTranscode: (node: string, device: string) => req<TranscodeSetting>('PUT', '/api/transcode', device ? { node, device } : {}, true),
|
setTranscode: (node: string, device: string) => req<TranscodeSetting>('PUT', '/api/transcode', device ? { node, device } : {}, true),
|
||||||
fileReadURL: (path: string, sudo: boolean) => apiPath(`/api/files/read?${q(path, sudo)}`),
|
fileReadURL: (path: string, sudo: boolean) => apiPath(`/api/files/read?${q(path, sudo)}`),
|
||||||
fileDownloadURL: (path: string, sudo: boolean) => apiPath(`/api/files/download?${q(path, sudo)}`),
|
fileDownloadURL: (path: string, sudo: boolean) => apiPath(`/api/files/download?${q(path, sudo)}`),
|
||||||
|
|||||||
Reference in New Issue
Block a user