Persist ACE-Step XL-SFT quality defaults

This commit is contained in:
Mikei386
2026-09-08 16:37:36 +02:00
parent eeebbd06eb
commit 56c382f71f
6 changed files with 868 additions and 0 deletions
@@ -0,0 +1,164 @@
/**
* User preferences persistence – SAVE side only.
*
* Listens for user changes on Gradio UI controls and persists the current
* values to browser localStorage. Restoration is handled on the Python side
* via ``gr.Blocks.load()`` so Gradio's own Svelte reactivity updates every
* component correctly.
*
* Storage schema:
* key = "acestep.ui.user_preferences"
* value = JSON { _version: 2, audio_format: "flac", … }
*/
(() => {
const STORAGE_KEY = "acestep.ui.user_preferences";
const SCHEMA_VERSION = 2;
const DEBOUNCE_MS = 500;
/**
* Map of preference key → { elemId, type }.
* elemId : the HTML elem_id set in Gradio
* type : "dropdown" | "slider" | "checkbox" | "number"
*/
const PREFS = {
audio_format: { elemId: "acestep-audio-format", type: "dropdown" },
mp3_bitrate: { elemId: "acestep-mp3-bitrate", type: "dropdown" },
mp3_sample_rate: { elemId: "acestep-mp3-sample-rate", type: "dropdown" },
score_scale: { elemId: "acestep-score-scale", type: "slider" },
enable_normalization:{ elemId: "acestep-enable-normalization", type: "checkbox" },
normalization_db: { elemId: "acestep-normalization-db", type: "slider" },
fade_in_duration: { elemId: "acestep-fade-in-duration", type: "slider" },
fade_out_duration: { elemId: "acestep-fade-out-duration", type: "slider" },
latent_shift: { elemId: "acestep-latent-shift", type: "slider" },
latent_rescale: { elemId: "acestep-latent-rescale", type: "slider" },
lm_batch_chunk_size: { elemId: "acestep-lm-batch-chunk-size", type: "number" },
};
let saveTimer = null;
const wiredElements = new WeakSet();
// ── Storage helpers ──────────────────────────────────────────────
const saveAll = (prefs) => {
try {
window.localStorage.setItem(STORAGE_KEY, JSON.stringify(prefs));
} catch (_e) {
// Private browsing or quota exceeded – silently ignore.
}
};
// ── DOM helpers ──────────────────────────────────────────────────
const findInput = (elemId, type) => {
const wrapper = document.getElementById(elemId);
if (!wrapper) return null;
if (type === "dropdown") {
return wrapper.querySelector("input");
}
if (type === "slider") {
return wrapper.querySelector("input[type='range']")
|| wrapper.querySelector("input[type='number']");
}
if (type === "checkbox") {
return wrapper.querySelector("input[type='checkbox']");
}
if (type === "number") {
return wrapper.querySelector("input[type='number']");
}
return null;
};
const readValue = (key) => {
const spec = PREFS[key];
if (!spec) return undefined;
const el = findInput(spec.elemId, spec.type);
if (!el) return undefined;
if (spec.type === "checkbox") return el.checked;
if (spec.type === "slider" || spec.type === "number") {
const v = Number(el.value);
return Number.isFinite(v) ? v : undefined;
}
return el.value || undefined;
};
// ── Save (debounced) ─────────────────────────────────────────────
const scheduleSave = () => {
if (saveTimer !== null) {
clearTimeout(saveTimer);
}
saveTimer = setTimeout(() => {
saveTimer = null;
const prefs = { _version: SCHEMA_VERSION };
for (const key of Object.keys(PREFS)) {
const v = readValue(key);
if (v !== undefined) {
prefs[key] = v;
}
}
saveAll(prefs);
}, DEBOUNCE_MS);
};
// ── Wire listeners (re-entrant – safe to call on re-renders) ─────
const wireListeners = () => {
for (const key of Object.keys(PREFS)) {
const spec = PREFS[key];
const el = findInput(spec.elemId, spec.type);
if (!el || wiredElements.has(el)) continue;
wiredElements.add(el);
el.addEventListener("input", scheduleSave, { passive: true });
el.addEventListener("change", scheduleSave, { passive: true });
}
};
// ── MutationObserver – re-wire after Gradio re-renders ───────────
const startObserver = () => {
const target = document.getElementById("acestep-audio-format")
|| document.body;
const root = target.closest(".gradio-container") || document.body;
let rafPending = false;
new MutationObserver(() => {
if (rafPending) return;
rafPending = true;
requestAnimationFrame(() => {
rafPending = false;
wireListeners();
});
}).observe(root, { childList: true, subtree: true });
};
// ── Boot ─────────────────────────────────────────────────────────
const BOOT_POLL_MS = 200;
const BOOT_TIMEOUT_MS = 10000;
const boot = () => {
const started = Date.now();
const poll = () => {
const probe = document.getElementById(
PREFS.audio_format.elemId
);
if (!probe) {
if (Date.now() - started < BOOT_TIMEOUT_MS) {
setTimeout(poll, BOOT_POLL_MS);
}
return;
}
wireListeners();
startObserver();
};
poll();
};
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", boot, { once: true });
} else {
boot();
}
})();