Files
MajoorWaldi-ComfyUI-Majoor-…/js/app/settings/settingsScanning.js
T
Ewald 44c0c57f6e feat: Add asset management and real-time listener functionalities
- Implemented entry UI registration for the Assets Manager, including sidebar and bottom panel integration.
- Created real-time listeners for execution events, asset updates, and enrichment status.
- Developed execution runtime controller to manage job tracking, asset buffering, and indexing.
- Introduced lazy initialization for vector services to optimize resource usage.
- Added HTTP handlers for plugin management, enabling listing, enabling, and reloading of plugins.
2026-03-29 01:03:49 +01:00

350 lines
14 KiB
JavaScript

/**
* Settings section: Scanning (AutoScan, Watcher, RtHydrate, RatingTagsSync).
*/
import { APP_DEFAULTS } from "../config.js";
import {
toggleWatcher,
getWatcherSettings,
updateWatcherSettings,
setExecutionGroupingSettings,
} from "../../api/client.js";
import { comfyToast } from "../toast.js";
import { t } from "../i18n.js";
import { _safeNum } from "./settingsUtils.js";
import { saveMajoorSettings, applySettingsToConfig } from "./settingsCore.js";
const SETTINGS_PREFIX = "Majoor";
const SETTINGS_CATEGORY = "Majoor Assets Manager";
/**
* Register all Scanning-related settings.
*
* @param {Function} safeAddSetting - Wrapped addSetting function from the parent.
* @param {object} settings - Live settings object (mutated on change).
* @param {Function} notifyApplied - Callback(key) to schedule change notifications.
*/
export function registerScanningSettings(safeAddSetting, settings, notifyApplied) {
const cat = (section, label) => [SETTINGS_CATEGORY, section, label];
safeAddSetting({
id: `${SETTINGS_PREFIX}.ExecutionGrouping.Enabled`,
category: cat(t("cat.scanning"), "Execution grouping"),
name: "Execution job/stack grouping",
tooltip:
"Enable or disable all live job_id / stack_id tracking, grouping, and stack finalization logic.",
type: "boolean",
defaultValue: !!(
settings.executionGrouping?.enabled ?? APP_DEFAULTS.EXECUTION_GROUPING_ENABLED
),
onChange: async (value) => {
const previous = !!(
settings.executionGrouping?.enabled ?? APP_DEFAULTS.EXECUTION_GROUPING_ENABLED
);
const normalized = !!value;
settings.executionGrouping = settings.executionGrouping || {};
settings.executionGrouping.enabled = normalized;
saveMajoorSettings(settings);
applySettingsToConfig(settings);
notifyApplied("executionGrouping.enabled");
try {
const res = await setExecutionGroupingSettings(normalized);
if (!res?.ok) {
throw new Error(res?.error || "Failed to update execution grouping setting");
}
settings.executionGrouping.enabled = !!res?.data?.prefs?.enabled;
saveMajoorSettings(settings);
applySettingsToConfig(settings);
notifyApplied("executionGrouping.enabled");
} catch (error) {
settings.executionGrouping.enabled = previous;
saveMajoorSettings(settings);
applySettingsToConfig(settings);
notifyApplied("executionGrouping.enabled");
comfyToast(error?.message || "Failed to update execution grouping setting", "error");
}
},
});
safeAddSetting({
id: `${SETTINGS_PREFIX}.AutoScan.OnStartup`,
category: cat(t("cat.scanning"), t("setting.scan.startup.name").replace("Majoor: ", "")),
name: t("setting.scan.startup.name"),
tooltip: t("setting.scan.startup.desc"),
type: "boolean",
defaultValue: !!settings.autoScan?.onStartup,
onChange: (value) => {
settings.autoScan = settings.autoScan || {};
settings.autoScan.onStartup = !!value;
saveMajoorSettings(settings);
applySettingsToConfig(settings);
notifyApplied("autoScan.onStartup");
},
});
safeAddSetting({
id: `${SETTINGS_PREFIX}.Scan.FastMode`,
category: cat(t("cat.scanning"), "Scan mode"),
name: "Fast scan mode",
tooltip:
"Use fast scan mode for manual backfill scans (skip heavier metadata work during scan).",
type: "boolean",
defaultValue: !!(settings.scan?.fastMode ?? true),
onChange: (value) => {
settings.scan = settings.scan || {};
settings.scan.fastMode = !!value;
saveMajoorSettings(settings);
notifyApplied("scan.fastMode");
},
});
safeAddSetting({
id: `${SETTINGS_PREFIX}.RtHydrate.Concurrency`,
category: cat(t("cat.scanning"), "Hydration"),
name: "Hydrate Concurrency",
tooltip: "Maximum concurrent hydration requests for rating/tags.",
type: "number",
defaultValue: Number(
settings.rtHydrate?.concurrency || APP_DEFAULTS.RT_HYDRATE_CONCURRENCY || 5,
),
attrs: { min: 1, max: 20, step: 1 },
onChange: (value) => {
settings.rtHydrate = settings.rtHydrate || {};
settings.rtHydrate.concurrency = Math.max(
1,
Math.min(20, Math.round(_safeNum(value, APP_DEFAULTS.RT_HYDRATE_CONCURRENCY || 5))),
);
saveMajoorSettings(settings);
applySettingsToConfig(settings);
notifyApplied("rtHydrate.concurrency");
},
});
const _clampWatcherValue = (raw, fallback, min, max) => {
const parsed = Math.round(_safeNum(raw, fallback));
return Math.max(min, Math.min(max, parsed));
};
const applyWatcherSettingsFromBackend = (payload = {}) => {
const changed = [];
settings.watcher = settings.watcher || {};
if (typeof payload.debounce_ms === "number") {
const normalized = Math.max(50, Math.min(5000, Math.round(payload.debounce_ms)));
if (settings.watcher.debounceMs !== normalized) {
settings.watcher.debounceMs = normalized;
changed.push("watcher.debounceMs");
}
}
if (typeof payload.dedupe_ttl_ms === "number") {
const normalized = Math.max(100, Math.min(30000, Math.round(payload.dedupe_ttl_ms)));
if (settings.watcher.dedupeTtlMs !== normalized) {
settings.watcher.dedupeTtlMs = normalized;
changed.push("watcher.dedupeTtlMs");
}
}
if (!changed.length) {
return;
}
saveMajoorSettings(settings);
changed.forEach((key) => notifyApplied(key));
};
const syncWatcherRuntimeSettings = async () => {
try {
const res = await getWatcherSettings();
if (!res?.ok) {
return;
}
applyWatcherSettingsFromBackend(res.data || {});
} catch (e) {
console.debug?.(e);
}
};
safeAddSetting({
id: `${SETTINGS_PREFIX}.Watcher.Enabled`,
category: cat(t("cat.scanning"), t("setting.watcher.enabled.label", "Enable watcher")),
name: t("setting.watcher.name"),
tooltip: t("setting.watcher.desc") + " (env: MJR_ENABLE_WATCHER)",
type: "boolean",
defaultValue: !!settings.watcher?.enabled,
onChange: async (value) => {
settings.watcher = settings.watcher || {};
settings.watcher.enabled = !!value;
saveMajoorSettings(settings);
notifyApplied("watcher.enabled");
try {
const res = await toggleWatcher(!!value);
if (!res?.ok) {
settings.watcher.enabled = !value;
saveMajoorSettings(settings);
notifyApplied("watcher.enabled");
comfyToast(
res?.error || t("toast.failedToggleWatcher", "Failed to toggle watcher"),
"error",
);
}
} catch {
settings.watcher.enabled = !value;
saveMajoorSettings(settings);
notifyApplied("watcher.enabled");
}
},
});
safeAddSetting({
id: `${SETTINGS_PREFIX}.Watcher.DebounceDelay`,
category: cat(
t("cat.scanning"),
t("setting.watcher.debounce.label", "Watcher debounce delay"),
),
name: t("setting.watcher.debounce.name"),
tooltip: t("setting.watcher.debounce.desc") + " (env: MJR_WATCHER_DEBOUNCE_MS)",
type: "number",
defaultValue: settings.watcher?.debounceMs ?? APP_DEFAULTS.WATCHER_DEBOUNCE_MS,
attrs: { min: 50, max: 5000, step: 50 },
onChange: async (value) => {
const fallback = APP_DEFAULTS.WATCHER_DEBOUNCE_MS;
const clamped = _clampWatcherValue(value, fallback, 50, 5000);
const previous = settings.watcher?.debounceMs ?? fallback;
if (clamped === previous) return;
settings.watcher = settings.watcher || {};
settings.watcher.debounceMs = clamped;
saveMajoorSettings(settings);
try {
const res = await updateWatcherSettings({ debounce_ms: clamped });
if (!res?.ok) {
throw new Error(res?.error || t("setting.watcher.debounce.error"));
}
const backendValue = Math.round(Number(res?.data?.debounce_ms ?? clamped));
settings.watcher.debounceMs = backendValue;
saveMajoorSettings(settings);
notifyApplied("watcher.debounceMs");
} catch (error) {
settings.watcher.debounceMs = previous;
saveMajoorSettings(settings);
notifyApplied("watcher.debounceMs");
comfyToast(error?.message || t("setting.watcher.debounce.error"), "error");
}
},
});
safeAddSetting({
id: `${SETTINGS_PREFIX}.Watcher.DedupeWindow`,
category: cat(
t("cat.scanning"),
t("setting.watcher.dedupe.label", "Watcher dedupe window"),
),
name: t("setting.watcher.dedupe.name"),
tooltip: t("setting.watcher.dedupe.desc") + " (env: MJR_WATCHER_DEDUPE_TTL_MS)",
type: "number",
defaultValue: settings.watcher?.dedupeTtlMs ?? APP_DEFAULTS.WATCHER_DEDUPE_TTL_MS,
attrs: { min: 100, max: 30000, step: 100 },
onChange: async (value) => {
const fallback = APP_DEFAULTS.WATCHER_DEDUPE_TTL_MS;
const clamped = _clampWatcherValue(value, fallback, 100, 30000);
const previous = settings.watcher?.dedupeTtlMs ?? fallback;
if (clamped === previous) return;
settings.watcher = settings.watcher || {};
settings.watcher.dedupeTtlMs = clamped;
saveMajoorSettings(settings);
try {
const res = await updateWatcherSettings({ dedupe_ttl_ms: clamped });
if (!res?.ok) {
throw new Error(res?.error || t("setting.watcher.dedupe.error"));
}
const backendValue = Math.round(Number(res?.data?.dedupe_ttl_ms ?? clamped));
settings.watcher.dedupeTtlMs = backendValue;
saveMajoorSettings(settings);
notifyApplied("watcher.dedupeTtlMs");
} catch (error) {
settings.watcher.dedupeTtlMs = previous;
saveMajoorSettings(settings);
notifyApplied("watcher.dedupeTtlMs");
comfyToast(error?.message || t("setting.watcher.dedupe.error"), "error");
}
},
});
safeAddSetting({
id: `${SETTINGS_PREFIX}.Watcher.MaxPending`,
category: cat(t("cat.scanning"), "Watcher queue"),
name: "Watcher: max pending files",
tooltip: "Maximum number of pending watcher files kept in memory.",
type: "number",
defaultValue: Number(settings.watcher?.maxPending ?? 500),
attrs: { min: 10, max: 5000, step: 10 },
onChange: (value) => {
settings.watcher = settings.watcher || {};
settings.watcher.maxPending = Math.max(
10,
Math.min(5000, Math.round(_safeNum(value, 500))),
);
saveMajoorSettings(settings);
applySettingsToConfig(settings);
notifyApplied("watcher.maxPending");
},
});
safeAddSetting({
id: `${SETTINGS_PREFIX}.Watcher.MinSize`,
category: cat(t("cat.scanning"), "Watcher file size"),
name: "Watcher: min size (bytes)",
tooltip: "Minimum file size indexed by watcher.",
type: "number",
defaultValue: Number(settings.watcher?.minSize ?? 100),
attrs: { min: 0, max: 1000000, step: 100 },
onChange: (value) => {
settings.watcher = settings.watcher || {};
settings.watcher.minSize = Math.max(
0,
Math.min(1000000, Math.round(_safeNum(value, 100))),
);
saveMajoorSettings(settings);
applySettingsToConfig(settings);
notifyApplied("watcher.minSize");
},
});
safeAddSetting({
id: `${SETTINGS_PREFIX}.Watcher.MaxSize`,
category: cat(t("cat.scanning"), "Watcher file size"),
name: "Watcher: max size (bytes)",
tooltip: "Maximum file size indexed by watcher.",
type: "number",
defaultValue: Number(settings.watcher?.maxSize ?? 4294967296),
attrs: { min: 100000, max: 17179869184, step: 100000 },
onChange: (value) => {
settings.watcher = settings.watcher || {};
settings.watcher.maxSize = Math.max(
100000,
Math.min(17179869184, Math.round(_safeNum(value, 4294967296))),
);
saveMajoorSettings(settings);
applySettingsToConfig(settings);
notifyApplied("watcher.maxSize");
},
});
try {
syncWatcherRuntimeSettings().catch(() => {});
} catch (e) {
console.debug?.(e);
}
safeAddSetting({
id: `${SETTINGS_PREFIX}.RatingTagsSync.Enabled`,
category: cat(t("cat.scanning"), t("setting.sync.rating.name").replace("Majoor: ", "")),
name: t("setting.sync.rating.name"),
tooltip: t("setting.sync.rating.desc"),
type: "boolean",
defaultValue: !!settings.ratingTagsSync?.enabled,
onChange: (value) => {
settings.ratingTagsSync = settings.ratingTagsSync || {};
settings.ratingTagsSync.enabled = !!value;
saveMajoorSettings(settings);
notifyApplied("ratingTagsSync.enabled");
},
});
}