Files
DemonGatanjieuandClaude Opus 5.5 0c1287ddc2 Voice pages: less on screen, plainer words
Voice-over's side panel shows who reads, speed, the language (one line
while detected) and the tone with a listen button; the comparison, the
numbers and the links fold under More. GPT-SoVITS Settings opens with one
status line and the tone to use; the language rules, base files, places
and version are folded and open by themselves when they need the user.
Plainer words throughout: tone, Steady / Natural / Lively, As the
character, Always read like this, Own settings, base files.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 02:19:32 +08:00

140 lines
5.8 KiB
JavaScript

import { t } from './interface_settings.js';
import { anomalousAlert, anomalousConfirm } from './ui_dialog.js';
import { button, el } from './usage_view.js';
/**
* "Anomalous_TTS version" in GPT-SoVITS Settings: the installed version (local), its
* published releases only after "Check for updates" (network), and an update to the
* newest stable one, or back to where the last update came from; then the restart
* question (ComfyUI Manager's reboot when there is one). The backend only acts on a git
* clone of the official repository (api/tts_version.py).
*/
async function request(url, options = {}) {
try {
const resp = await fetch(url, options);
return await resp.json().catch(() => ({ success: false, error: `HTTP ${resp.status}` }));
} catch (e) {
return { success: false, code: 'offline', error: e.message };
}
}
const post = (url, body = {}) => request(url, {
method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body),
});
function errorText(result) {
const key = `versionError_${result.code}`;
const text = t(key, { error: result.error || '' });
const message = text === key ? t('versionErrorGeneric', { error: result.error || result.code || '' }) : text;
return result.files?.length ? `${message}\n\n${result.files.join('\n')}` : message;
}
function versionOf(state) {
return state.tag || state.base_tag || state.label;
}
async function askRestart(message) {
const restart = await anomalousConfirm(`${message}\n\n${t('versionRestartQuestion')}`, t('ttsVersionTitle'), {
okLabel: t('versionRestartNow'), cancelLabel: t('versionRestartLater'),
});
if (!restart) return '';
// Manager's reboot exits the server, so a dropped connection means it is restarting.
const reboot = await post('/api/manager/reboot');
if (reboot.code === 'offline' || reboot.success !== false) return t('versionRestarting');
await anomalousAlert(t('versionRestartManual'));
return '';
}
/** The settings group; `local` false (another device) leaves the buttons out. */
export function renderTtsVersion(local) {
const group = el('div', 'anomalous-tts-setup-group');
const row = el('div', 'anomalous-tts-setup-row');
const value = el('span', 'anomalous-tts-setup-path', '…');
const note = el('span', 'anomalous-tts-setup-meta', '');
const actions = el('span', 'anomalous-tts-version-actions');
row.append(value, note, actions);
const hint = el('div', 'anomalous-tts-hint', t('ttsVersionHint'));
group.append(row, hint); // its title is the fold's (ui_tts_setup.js)
let busy = false;
const run = async (label, work) => {
if (busy) return;
busy = true;
actions.querySelectorAll('button').forEach(btn => { btn.disabled = true; });
note.textContent = label;
try { await work(); } finally { busy = false; }
};
const show = (state, listing = null) => {
value.textContent = versionOf(state);
note.textContent = '';
actions.replaceChildren();
if (!local) return;
const latest = listing?.releases.find(item => item.latest);
if (!listing) {
actions.append(button('anomalous-tts-add', t('ttsVersionCheck'), () => run(t('ttsVersionChecking'), check)));
} else if (!latest || latest.current || latest.direction !== 'newer') {
note.textContent = t('ttsVersionUpToDate');
} else {
note.textContent = t('ttsVersionNew', { tag: latest.tag });
actions.append(button('anomalous-tts-add', t('ttsVersionUpdate', { tag: latest.tag }),
() => run(t('versionSwitching', { tag: latest.tag }), () => update(state, latest))));
if (latest.url) {
const notes = el('a', 'anomalous-tts-link', t('ttsVersionNotes'));
notes.href = latest.url;
notes.target = '_blank';
notes.rel = 'noopener noreferrer';
actions.append(notes);
}
}
if (state.previous?.label) {
actions.append(button('anomalous-tts-link', t('ttsVersionUndo', { tag: state.previous.label }),
() => run(t('versionSwitching', { tag: state.previous.label }), () => undo(state))));
}
};
const check = async () => {
const listing = await request('/anomalous/tts_version/releases?refresh=1');
if (!listing.success) {
await anomalousAlert(errorText(listing));
return load();
}
show(listing.state, listing);
};
const finish = async (result, tag) => {
if (!result.success) {
await anomalousAlert(errorText(result));
return load();
}
show(result.state);
note.textContent = await askRestart(t('ttsVersionUpdated', { tag })) || t('versionPendingRestart');
};
const update = async (state, release) => {
if (!await anomalousConfirm(t('ttsVersionConfirm', { tag: release.tag, from: versionOf(state) }), t('ttsVersionTitle'))) return show(state);
await finish(await post('/anomalous/tts_version/update', { tag: release.tag }), release.tag);
};
const undo = async (state) => {
const tag = state.previous.label;
if (!await anomalousConfirm(t('versionConfirmUndo', { label: tag }), t('ttsVersionTitle'))) return show(state);
await finish(await post('/anomalous/tts_version/undo'), tag);
};
const load = async () => {
const data = await request('/anomalous/tts_version');
if (!data.success || !data.found) {
value.textContent = t('ttsVersionUnknown');
note.textContent = '';
actions.replaceChildren();
hint.textContent = t('ttsVersionNoGit');
return;
}
show(data.state);
};
load();
return group;
}