Files
DemonGatanjieu-Anomalous_Mo…/web/modules/ui_tts_version.js
T
DemonGatanjieuandClaude Opus 5.5 2e62703923 Update Anomalous_TTS from GPT-SoVITS Settings
The version manager takes an optional Checkout, so the companion node pack is
handled like the plugin itself: api/tts_version.py finds it among the
custom_nodes folders by its node class and accepts only a git clone of the
official repository; releases are fetched only on "Check for updates", an
update checks out the newest stable tag and can be undone. A checkout without
the release tags (cloned before it, then pulled) is matched by ls-remote.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 13:08:10 +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(el('div', 'anomalous-tts-section', t('ttsVersionTitle')), row, hint);
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;
}