fix: stop HDMI capture when logged out (#841)

* fix: stop HDMI capture when logged out

* feat: stop HDMI capture when viewers are idle

* fix: keep HDMI capture active for consumers
This commit is contained in:
watermeko
2026-07-30 17:29:07 +08:00
committed by GitHub
parent 004fd59093
commit b587b9f912
56 changed files with 1150 additions and 124 deletions

View File

@@ -85,6 +85,11 @@ export function setHdmiState(enabled: boolean) {
return disableHdmi();
}
// set HDMI idle timeout in minutes
export function setHdmiIdleTimeout(minutes: number) {
return http.post('/api/vm/hdmi/timeout', { minutes });
}
// get SSH state
export function getSSHState() {
return http.get('/api/vm/ssh');

View File

@@ -369,7 +369,10 @@ const ca = {
tip: 'Desactiva-ho si no és necessari'
},
hdmi: {
description: 'Activa la sortida HDMI'
description: 'Activa la sortida HDMI',
idleTimeoutTitle: "Temps d'espera d'inactivitat de captura",
idleTimeoutDescription: "Atura la captura HDMI després de no detectar espectadors actius durant",
minutes: 'min'
},
autostart: {
title: "Configuració dels scripts d'inici automàtic",

View File

@@ -372,7 +372,10 @@ const cz = {
tip: 'Vypnutí, pokud to není potřeba'
},
hdmi: {
description: 'Povolit výstup HDMI/monitor'
description: 'Povolit výstup HDMI/monitor',
idleTimeoutTitle: 'Časový limit nečinnosti snímání',
idleTimeoutDescription: 'Zastavit snímání HDMI po době bez aktivních diváků',
minutes: 'min'
},
autostart: {
title: 'Nastavení automatického spuštění skriptů',

View File

@@ -369,7 +369,10 @@ const da = {
tip: 'Slukker den, hvis den ikke er nødvendig'
},
hdmi: {
description: 'Aktiver HDMI/monitor output'
description: 'Aktiver HDMI/monitor output',
idleTimeoutTitle: 'Timeout for inaktiv optagelse',
idleTimeoutDescription: 'Stop HDMI-optagelse efter en periode uden aktive seere på',
minutes: 'min'
},
autostart: {
title: 'Indstillinger for autostart scripts',

View File

@@ -375,7 +375,10 @@ const de = {
tip: 'Deaktivieren Sie den Dienst, wenn Sie ihn nicht benötigen'
},
hdmi: {
description: 'HDMI/Monitor-Ausgabe aktivieren'
description: 'HDMI/Monitor-Ausgabe aktivieren',
idleTimeoutTitle: 'Zeitlimit für inaktive Aufnahme',
idleTimeoutDescription: 'HDMI-Aufnahme stoppen, wenn keine aktiven Zuschauer vorhanden sind für',
minutes: 'Min.'
},
autostart: {
title: 'Autostart-Skripteinstellungen',

View File

@@ -368,7 +368,10 @@ const en = {
tip: "Turning it off if it's not needed"
},
hdmi: {
description: 'Enable HDMI/monitor output'
description: 'Enable HDMI/monitor output',
idleTimeoutTitle: 'Capture idle timeout',
idleTimeoutDescription: 'Stop HDMI capture after there are no active viewers for',
minutes: 'min'
},
autostart: {
title: 'Autostart Scripts Settings',

View File

@@ -372,7 +372,10 @@ const es = {
tip: 'Desactívalo si no es necesario'
},
hdmi: {
description: 'Habilitar salida HDMI/monitor'
description: 'Habilitar salida HDMI/monitor',
idleTimeoutTitle: 'Tiempo de espera de captura inactiva',
idleTimeoutDescription: 'Detener la captura HDMI después de no haber espectadores activos durante',
minutes: 'min'
},
autostart: {
title: 'Configuración de scripts de inicio automático',

View File

@@ -374,7 +374,10 @@ const fr = {
tip: "L'éteindre si ce n'est pas nécessaire"
},
hdmi: {
description: 'Activer HDMI/sortie moniteur'
description: 'Activer HDMI/sortie moniteur',
idleTimeoutTitle: "Délai d'inactivité de la capture",
idleTimeoutDescription: "Arrêter la capture HDMI lorsqu'il n'y a aucun spectateur actif pendant",
minutes: 'min'
},
autostart: {
title: 'Paramètres des scripts de démarrage automatique',

View File

@@ -373,7 +373,10 @@ const hu = {
tip: 'Kikapcsolás, ha nincs rá szükség'
},
hdmi: {
description: 'HDMI/monitor kimenet engedélyezése'
description: 'HDMI/monitor kimenet engedélyezése',
idleTimeoutTitle: 'Inaktív rögzítés időkorlátja',
idleTimeoutDescription: 'A HDMI-rögzítés leállítása, ha nincs aktív néző ennyi ideig:',
minutes: 'perc'
},
autostart: {
title: 'Automatikus indítási parancsfájlok beállításai',

View File

@@ -371,7 +371,10 @@ const id = {
tip: 'Mematikan jika tidak diperlukan'
},
hdmi: {
description: 'Aktifkan keluaran HDMI/monitor'
description: 'Aktifkan keluaran HDMI/monitor',
idleTimeoutTitle: 'Batas waktu tangkapan tidak aktif',
idleTimeoutDescription: 'Hentikan tangkapan HDMI setelah tidak ada penonton aktif selama',
minutes: 'mnt'
},
autostart: {
title: 'Pengaturan Skrip Mulai Otomatis',

View File

@@ -373,7 +373,10 @@ const it = {
tip: 'Spegnerlo se non è necessario'
},
hdmi: {
description: 'Abilita HDMI/monitora uscita'
description: 'Abilita HDMI/monitora uscita',
idleTimeoutTitle: 'Timeout cattura inattiva',
idleTimeoutDescription: 'Interrompi la cattura HDMI dopo che non ci sono visualizzatori attivi per',
minutes: 'min'
},
autostart: {
title: 'Impostazioni script di avvio automatico',

View File

@@ -372,7 +372,10 @@ const ja = {
tip: 'この機能を使用していない場合は、オフにすることをお勧めします'
},
hdmi: {
description: 'HDMI/モニター 出力機能を有効にする'
description: 'HDMI/モニター 出力機能を有効にする',
idleTimeoutTitle: 'キャプチャのアイドルタイムアウト',
idleTimeoutDescription: 'アクティブな閲覧者がいない状態が次の時間続いたら HDMI キャプチャを停止',
minutes: '分'
},
autostart: {
title: '自動起動スクリプト設定',

View File

@@ -367,7 +367,10 @@ const ko = {
tip: '사용하지 않는 경우 끄는 것이 좋습니다'
},
hdmi: {
description: 'HDMI/모니터 출력 활성화'
description: 'HDMI/모니터 출력 활성화',
idleTimeoutTitle: '캡처 유휴 시간 제한',
idleTimeoutDescription: '활성 시청자가 없는 상태가 다음 시간 동안 지속되면 HDMI 캡처 중지',
minutes: '분'
},
autostart: {
title: '자동 시작 스크립트 설정',

View File

@@ -370,7 +370,10 @@ const nb = {
tip: 'Slå den av hvis den ikke er nødvendig'
},
hdmi: {
description: 'Aktiver HDMI/skjermutgang'
description: 'Aktiver HDMI/skjermutgang',
idleTimeoutTitle: 'Tidsavbrudd for inaktivt opptak',
idleTimeoutDescription: 'Stopp HDMI-opptak etter at det ikke har vært aktive seere i',
minutes: 'min'
},
autostart: {
title: 'Autostart skriptinnstillinger',

View File

@@ -373,7 +373,10 @@ const nl = {
tip: 'Schakel het uit als het niet nodig is'
},
hdmi: {
description: 'Schakel HDMI/monitoruitgang in'
description: 'Schakel HDMI/monitoruitgang in',
idleTimeoutTitle: 'Time-out voor inactieve opname',
idleTimeoutDescription: 'HDMI-opname stoppen nadat er gedurende deze tijd geen actieve kijkers zijn:',
minutes: 'min'
},
autostart: {
title: 'Instellingen voor automatisch starten van scripts',

View File

@@ -372,7 +372,10 @@ const pl = {
tip: 'Wyłączanie, jeśli nie jest potrzebne'
},
hdmi: {
description: 'Włącz HDMI/wyjście monitora'
description: 'Włącz HDMI/wyjście monitora',
idleTimeoutTitle: 'Limit czasu bezczynności przechwytywania',
idleTimeoutDescription: 'Zatrzymaj przechwytywanie HDMI po czasie bez aktywnych widzów:',
minutes: 'min'
},
autostart: {
title: 'Ustawienia skryptów autostartu',

View File

@@ -371,7 +371,10 @@ const pt_br = {
tip: 'Desligue se não for necessário'
},
hdmi: {
description: 'Habilitar saída HDMI/monitor'
description: 'Habilitar saída HDMI/monitor',
idleTimeoutTitle: 'Tempo limite de captura inativa',
idleTimeoutDescription: 'Parar a captura HDMI após não haver visualizadores ativos por',
minutes: 'min'
},
autostart: {
title: 'Configurações de scripts de inicialização automática',

View File

@@ -372,7 +372,10 @@ const ru = {
tip: 'Выключите, если нет необходимости'
},
hdmi: {
description: 'Включить HDMI/выход монитора'
description: 'Включить HDMI/выход монитора',
idleTimeoutTitle: 'Тайм-аут неактивного захвата',
idleTimeoutDescription: 'Остановить захват HDMI, если активных зрителей нет в течение',
minutes: 'мин'
},
autostart: {
title: 'Настройки сценариев автозапуска',

View File

@@ -368,7 +368,10 @@ const se = {
tip: 'Stäng av om det inte behövs'
},
hdmi: {
description: 'Aktivera HDMI/monitorutgång'
description: 'Aktivera HDMI/monitorutgång',
idleTimeoutTitle: 'Tidsgräns för inaktiv inspelning',
idleTimeoutDescription: 'Stoppa HDMI-inspelning efter att det inte har funnits aktiva tittare i',
minutes: 'min'
},
autostart: {
title: 'Autostart skriptinställningar',

View File

@@ -364,7 +364,10 @@ const th = {
tip: 'ปิดเครื่องหากไม่จำเป็น'
},
hdmi: {
description: 'เปิดใช้งาน HDMI/เอาต์พุตมอนิเตอร์'
description: 'เปิดใช้งาน HDMI/เอาต์พุตมอนิเตอร์',
idleTimeoutTitle: 'หมดเวลาการจับภาพเมื่อไม่มีการใช้งาน',
idleTimeoutDescription: 'หยุดการจับภาพ HDMI เมื่อไม่มีผู้ชมที่ใช้งานอยู่เป็นเวลา',
minutes: 'นาที'
},
autostart: {
title: 'การตั้งค่าสคริปต์เริ่มอัตโนมัติ',

View File

@@ -370,7 +370,10 @@ const tr = {
tip: 'Kullanmıyorsanız devre dışı bırakabilirsiniz'
},
hdmi: {
description: 'HDMI/Momitör çıktısını aktifleştir'
description: 'HDMI/Momitör çıktısını aktifleştir',
idleTimeoutTitle: 'Etkin olmayan yakalama zaman aşımı',
idleTimeoutDescription: 'Etkin görüntüleyici olmadığında HDMI yakalamayı şu süre sonunda durdur:',
minutes: 'dk'
},
autostart: {
title: 'Otomatik Başlatılan Komut Dosyaları Ayarları',

View File

@@ -371,7 +371,10 @@ const uk = {
tip: 'Вимкнути, якщо це не потрібно'
},
hdmi: {
description: 'Увімкнути вихід HDMI/monitor'
description: 'Увімкнути вихід HDMI/monitor',
idleTimeoutTitle: 'Час очікування неактивного захоплення',
idleTimeoutDescription: 'Зупинити захоплення HDMI, якщо активних глядачів немає протягом',
minutes: 'хв'
},
autostart: {
title: 'Налаштування сценаріїв автозапуску',

View File

@@ -368,7 +368,10 @@ const vi = {
tip: 'Tắt đi nếu không cần thiết'
},
hdmi: {
description: 'Kích hoạt HDMI/đầu ra màn hình'
description: 'Kích hoạt HDMI/đầu ra màn hình',
idleTimeoutTitle: 'Thời gian chờ khi không hoạt động',
idleTimeoutDescription: 'Dừng việc ghi hình HDMI sau khi không có người xem hoạt động trong',
minutes: 'phút'
},
autostart: {
title: 'Cài đặt tập lệnh tự khởi động',

View File

@@ -359,7 +359,10 @@ const zh = {
tip: '如果您未使用此功能,建议将其关闭'
},
hdmi: {
description: '启用 HDMI/显示器 输出功能'
description: '启用 HDMI/显示器 输出功能',
idleTimeoutTitle: '无观看者自动停止采集',
idleTimeoutDescription: '没有活跃观看者后停止 HDMI 采集0 表示永不停止',
minutes: '分钟'
},
autostart: {
title: '自动启动脚本设置',

View File

@@ -359,7 +359,10 @@ const zh_tw = {
tip: '若無需求,建議關閉此功能'
},
hdmi: {
description: '啟用 HDMI/螢幕 輸出'
description: '啟用 HDMI/螢幕 輸出',
idleTimeoutTitle: '擷取閒置逾時',
idleTimeoutDescription: '沒有活躍觀看者時,在指定時間後停止 HDMI 擷取',
minutes: '分鐘'
},
autostart: {
title: '啟動時指令碼設定',

View File

@@ -1,5 +1,5 @@
import { useEffect, useState } from 'react';
import { Switch } from 'antd';
import { InputNumber, Switch } from 'antd';
import { useAtom } from 'jotai';
import { useTranslation } from 'react-i18next';
@@ -13,6 +13,9 @@ export const Hdmi = () => {
const [isPcie, setIsPcie] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const [idleTimeout, setIdleTimeout] = useState(0);
const [idleTimeoutInput, setIdleTimeoutInput] = useState<number | null>(0);
const [isIdleTimeoutLoading, setIsIdleTimeoutLoading] = useState(false);
useEffect(() => {
getHardware();
@@ -34,11 +37,40 @@ export const Hdmi = () => {
const rsp = await api.getHdmiState();
if (rsp.code === 0) {
setIsHdmiEnabled(rsp.data.enabled);
const timeout = rsp.data.idleTimeout ?? 0;
setIdleTimeout(timeout);
setIdleTimeoutInput(timeout);
}
setIsLoading(false);
}
function updateIdleTimeout() {
if (
isIdleTimeoutLoading ||
idleTimeoutInput === null ||
idleTimeoutInput < 0 ||
idleTimeoutInput === idleTimeout
) {
return;
}
setIsIdleTimeoutLoading(true);
api
.setHdmiIdleTimeout(idleTimeoutInput)
.then((rsp) => {
if (rsp.code !== 0) {
setIdleTimeoutInput(idleTimeout);
return;
}
setIdleTimeout(idleTimeoutInput);
})
.finally(() => {
setIsIdleTimeoutLoading(false);
});
}
async function setHdmiState() {
if (isLoading) return;
setIsLoading(true);
@@ -60,16 +92,40 @@ export const Hdmi = () => {
return (
<>
{isPcie && (
<div className="flex items-center justify-between">
<div className="flex flex-col space-y-1">
<span>HDMI</span>
<div className="flex flex-col space-y-4">
<div className="flex items-center justify-between">
<div className="flex flex-col space-y-1">
<span>HDMI</span>
<span className="text-xs text-neutral-500">
{t('settings.device.hdmi.description')}
</span>
<span className="text-xs text-neutral-500">
{t('settings.device.hdmi.description')}
</span>
</div>
<Switch checked={isHdmiEnabled} loading={isLoading} onChange={setHdmiState} />
</div>
<Switch checked={isHdmiEnabled} loading={isLoading} onChange={setHdmiState} />
<div className="flex items-center justify-between">
<div className="flex flex-col space-y-1">
<span>{t('settings.device.hdmi.idleTimeoutTitle')}</span>
<span className="text-xs text-neutral-500">
{t('settings.device.hdmi.idleTimeoutDescription')}
</span>
</div>
<InputNumber
style={{ width: 150 }}
min={0}
max={10080}
precision={0}
value={idleTimeoutInput}
addonAfter={t('settings.device.hdmi.minutes')}
disabled={isIdleTimeoutLoading}
onChange={setIdleTimeoutInput}
onBlur={updateIdleTimeout}
onPressEnter={updateIdleTimeout}
/>
</div>
</div>
)}
</>

View File

@@ -135,12 +135,16 @@ export function createPicoclawSidebarSessionActions(options: PicoclawSidebarSess
}
]);
const sent = sendChatMessage(content, {
const task = sendChatMessage(content, {
id,
maxSteps: config.maxSteps,
maxRuntimeMs: config.maxRuntimeMs
});
return sent !== null;
if (task === null) {
setMessages((current) => current.filter((message) => message.id !== id));
return false;
}
return true;
}
async function handleReconnectGateway() {