yoink talkinghead - goodbye extras dependency

73393a5d5eadc92814d9689091d13c24bc0e3d95

Wolfsblvt <wolfsblvt@gmail.com>

8 files changed, +6 -474Showing whitespace changes
public/locales/fr-fr.json+0 -1
@@ -1602,7 +1602,6 @@
16021602 "Character Expressions": "Expressions de personnages",
16031603 "Translate text to English before classification": "Traduire le texte en anglais avant de le classer",
16041604 "Show default images (emojis) if sprite missing": "Afficher les images par défaut (emojis) si le sprite est manquant",
1605- "Image Type - talkinghead (extras)": "Type d'image - talkinghead (extras)",
16061605 "Classifier API": "API de classification",
16071606 "Select the API for classifying expressions.": "Sélectionnez l'API pour classer les expressions.",
16081607 "Main API": "API principale",
public/locales/ko-kr.json+0 -1
@@ -1467,7 +1467,6 @@
14671467 "menu within": "내의 메뉴",
14681468 "Translate text to English before classification": "분류 전에 텍스트를 영어로 번역합니다.",
14691469 "Show default images (emojis) if sprite missing": "해당하는 스프라이트가 없으면 기본 이미지 (이모지들)을 표시합니다.",
1470- "Image Type - talkinghead (extras)": "이미지 유형 - 토킹 헤드 (부가 사항)",
14711470 "Classifier API": "분류를 위한 API",
14721471 "Select the API for classifying expressions.": "감정 이미지들을 분류할 API를 선택하세요.",
14731472 "Local": "로컬",
public/locales/zh-cn.json+0 -1
@@ -1349,7 +1349,6 @@
13491349 "Character Expressions": "角色表情",
13501350 "Translate text to English before classification": "分类之前将文本翻译成英文",
13511351 "Show default images (emojis) if sprite missing": "如果表情包缺失,则显示默认图像(表情符号)",
1352- "Image Type - talkinghead (extras)": "图像类型 - 说话头像(附加内容)",
13531352 "Classifier API": "分类器 API",
13541353 "Select the API for classifying expressions.": "选择用于对表达式进行分类的API。",
13551354 "Main API": "主要 API",
public/locales/zh-tw.json+0 -1
@@ -1653,7 +1653,6 @@
16531653 "HuggingFace Token": "HuggingFace 符元",
16541654 "Image Captioning": "圖片註解",
16551655 "Generate Caption": "產生圖片註解",
1656- "Image Type - talkinghead (extras)": "圖片類型 - talkinghead(額外選項)",
16571656 "Injection Position": "插入位置",
16581657 "Injection position. Relative (to other prompts in prompt manager) or In-chat @ Depth.": "插入位置(與提示詞管理器中的其他提示相比)或聊天中的深度位置。",
16591658 "Injection Template": "插入範本",
public/scripts/extensions/expressions/index.js+6 -437
@@ -38,11 +38,9 @@ export { MODULE_NAME };
3838const MODULE_NAME = 'expressions';
3939const UPDATE_INTERVAL = 2000;
4040const STREAMING_UPDATE_INTERVAL = 10000;
41-const TALKINGCHECK_UPDATE_INTERVAL = 500;
4241const DEFAULT_FALLBACK_EXPRESSION = 'joy';
4342const DEFAULT_LLM_PROMPT = 'Ignore previous instructions. Classify the emotion of the last message. Output just one word, e.g. "joy" or "anger". Choose only one of the following labels: {{labels}}';
4443const DEFAULT_EXPRESSIONS = [
45- 'talkinghead',
4644 'admiration',
4745 'amusement',
4846 'anger',
@@ -86,8 +84,6 @@ const NO_IMAGE_PLACEHOLDER = { title: 'No Image', type: 'failure', fileName: 'No
8684let expressionsList = null;
8785let lastCharacter = undefined;
8886let lastMessage = null;
89-let lastTalkingState = false;
90-let lastTalkingStateMessage = null; // last message as seen by `updateTalkingState` (tracked separately, different timer)
9187/** @type {{[characterKey: string]: Expression[]}} */
9288let spriteCache = {};
9389let inApiCall = false;
@@ -96,10 +92,6 @@ let lastServerResponseTime = 0;
9692/** @type {{[characterName: string]: string}} */
9793export let lastExpression = {};
9894
99-function isTalkingHeadEnabled() {
100- return extension_settings.expressions.talkinghead && extension_settings.expressions.api == EXPRESSION_API.extras;
101-}
102-
10395/**
10496 * Returns the fallback expression if explicitly chosen, otherwise the default one
10597 * @returns {string} expression name
@@ -108,18 +100,6 @@ function getFallbackExpression() {
108100 return extension_settings.expressions.fallback_expression ?? DEFAULT_FALLBACK_EXPRESSION;
109101}
110102
111-/**
112- * Toggles Talkinghead mode on/off.
113- *
114- * Implements the `/th` slash command, which is meant to be bound to a Quick Reply button
115- * as a quick way to switch Talkinghead on or off (e.g. to conserve GPU resources when AFK
116- * for a long time).
117- */
118-function toggleTalkingHeadCommand(_) {
119- setTalkingHeadState(!extension_settings.expressions.talkinghead);
120- return String(extension_settings.expressions.talkinghead);
121-}
122-
123103function isVisualNovelMode() {
124104 return Boolean(!isMobile() && power_user.waifuMode && getContext().groupId);
125105}
@@ -451,199 +431,9 @@ function onExpressionsShowDefaultInput() {
451431 }
452432}
453433
454-/**
455- * Stops animating Talkinghead.
456- */
457-async function unloadTalkingHead() {
458- if (!modules.includes('talkinghead')) {
459- console.debug('talkinghead module is disabled');
460- return;
461- }
462- console.debug('expressions: Stopping Talkinghead');
463-
464- try {
465- const url = new URL(getApiUrl());
466- url.pathname = '/api/talkinghead/unload';
467- const loadResponse = await doExtrasFetch(url);
468- if (!loadResponse.ok) {
469- throw new Error(loadResponse.statusText);
470- }
471- //console.log(`Response: ${loadResponseText}`);
472- } catch (error) {
473- //console.error(`Error unloading - ${error}`);
474- }
475-}
476-
477-/**
478- * Posts `talkinghead.png` of the current character to the talkinghead module in SillyTavern-extras, to start animating it.
479- */
480-async function loadTalkingHead() {
481- if (!modules.includes('talkinghead')) {
482- console.debug('talkinghead module is disabled');
483- return;
484- }
485- console.debug('expressions: Starting Talkinghead');
486-
487- const spriteFolderName = getSpriteFolderName();
488-
489- const talkingheadPath = `/characters/${encodeURIComponent(spriteFolderName)}/talkinghead.png`;
490- const emotionsSettingsPath = `/characters/${encodeURIComponent(spriteFolderName)}/_emotions.json`;
491- const animatorSettingsPath = `/characters/${encodeURIComponent(spriteFolderName)}/_animator.json`;
492-
493- try {
494- const spriteResponse = await fetch(talkingheadPath);
495-
496- if (!spriteResponse.ok) {
497- throw new Error(spriteResponse.statusText);
498- }
499-
500- const spriteBlob = await spriteResponse.blob();
501- const spriteFile = new File([spriteBlob], 'talkinghead.png', { type: 'image/png' });
502- const formData = new FormData();
503- formData.append('file', spriteFile);
504-
505- const url = new URL(getApiUrl());
506- url.pathname = '/api/talkinghead/load';
507-
508- const loadResponse = await doExtrasFetch(url, {
509- method: 'POST',
510- body: formData,
511- });
512-
513- if (!loadResponse.ok) {
514- throw new Error(loadResponse.statusText);
515- }
516-
517- const loadResponseText = await loadResponse.text();
518- console.log(`Load talkinghead response: ${loadResponseText}`);
519-
520- // Optional: per-character emotion templates
521- let emotionsSettings;
522- try {
523- const emotionsResponse = await fetch(emotionsSettingsPath);
524- if (emotionsResponse.ok) {
525- emotionsSettings = await emotionsResponse.json();
526- console.log(`Loaded ${emotionsSettingsPath}`);
527- } else {
528- throw new Error();
529- }
530- }
531- catch (error) {
532- emotionsSettings = {}; // blank -> use server defaults (to unload the previous character's customizations)
533- console.log(`No valid config at ${emotionsSettingsPath}, using server defaults`);
534- }
535- try {
536- const url = new URL(getApiUrl());
537- url.pathname = '/api/talkinghead/load_emotion_templates';
538- const apiResult = await doExtrasFetch(url, {
539- method: 'POST',
540- headers: {
541- 'Content-Type': 'application/json',
542- 'Bypass-Tunnel-Reminder': 'bypass',
543- },
544- body: JSON.stringify(emotionsSettings),
545- });
546-
547- if (!apiResult.ok) {
548- throw new Error(apiResult.statusText);
549- }
550- }
551- catch (error) {
552- // it's ok if not supported
553- console.log('Failed to send _emotions.json (backend too old?), ignoring');
554- }
555-
556- // Optional: per-character animator and postprocessor config
557- let animatorSettings;
558- try {
559- const animatorResponse = await fetch(animatorSettingsPath);
560- if (animatorResponse.ok) {
561- animatorSettings = await animatorResponse.json();
562- console.log(`Loaded ${animatorSettingsPath}`);
563- } else {
564- throw new Error();
565- }
566- }
567- catch (error) {
568- animatorSettings = {}; // blank -> use server defaults (to unload the previous character's customizations)
569- console.log(`No valid config at ${animatorSettingsPath}, using server defaults`);
570- }
571- try {
572- const url = new URL(getApiUrl());
573- url.pathname = '/api/talkinghead/load_animator_settings';
574- const apiResult = await doExtrasFetch(url, {
575- method: 'POST',
576- headers: {
577- 'Content-Type': 'application/json',
578- 'Bypass-Tunnel-Reminder': 'bypass',
579- },
580- body: JSON.stringify(animatorSettings),
581- });
582-
583- if (!apiResult.ok) {
584- throw new Error(apiResult.statusText);
585- }
586- }
587- catch (error) {
588- // it's ok if not supported
589- console.log('Failed to send _animator.json (backend too old?), ignoring');
590- }
591- } catch (error) {
592- console.error(`Error loading talkinghead image: ${talkingheadPath} - ${error}`);
593- }
594-}
595-
596-function handleImageChange() {
597- const imgElement = document.querySelector('img#expression-image.expression');
598-
599- if (!imgElement || !(imgElement instanceof HTMLImageElement)) {
600- console.log('Cannot find addExpressionImage()');
601- return;
602- }
603-
604- if (isTalkingHeadEnabled() && modules.includes('talkinghead')) {
605- const talkingheadResultFeedSrc = `${getApiUrl()}/api/talkinghead/result_feed`;
606- $('#expression-holder').css({ display: '' });
607- if (imgElement.src !== talkingheadResultFeedSrc) {
608- const expressionImageElement = document.querySelector('.expression_list_image');
609-
610- if (expressionImageElement && expressionImageElement instanceof HTMLImageElement) {
611- doExtrasFetch(expressionImageElement.src, {
612- method: 'HEAD',
613- })
614- .then(response => {
615- if (response.ok) {
616- imgElement.src = talkingheadResultFeedSrc;
617- }
618- })
619- .catch(error => {
620- console.error(error);
621- });
622- }
623- }
624- } else {
625- imgElement.src = ''; // remove in case char doesn't have expressions
626-
627- // When switching Talkinghead off, force-set the character to the last known expression, if any.
628- // This preserves the same expression Talkinghead had at the moment it was switched off.
629- const charName = getContext().name2;
630- const last = lastExpression[charName];
631- const targetExpression = last ? last : getFallbackExpression();
632- setExpression(charName, targetExpression, true);
633- }
634-}
635-
636434async function moduleWorker() {
637435 const context = getContext();
638436
639- // Hide and disable Talkinghead while not in extras
640- $('#image_type_block').toggle(extension_settings.expressions.api == EXPRESSION_API.extras);
641-
642- if (extension_settings.expressions.api != EXPRESSION_API.extras && extension_settings.expressions.talkinghead) {
643- $('#image_type_toggle').prop('checked', false);
644- setTalkingHeadState(false);
645- }
646-
647437 // non-characters not supported
648438 if (!context.groupId && context.characterId === undefined) {
649439 removeExpression();
@@ -773,91 +563,6 @@ async function moduleWorker() {
773563 }
774564}
775565
776-/**
777- * Starts/stops Talkinghead talking animation.
778- *
779- * Talking starts only when all the following conditions are met:
780- * - The LLM is currently streaming its output.
781- * - The AI's current last message is non-empty, and also not just '...' (as produced by a swipe).
782- * - The AI's current last message has changed from what we saw during the previous call.
783- *
784- * In all other cases, talking stops.
785- *
786- * A Talkinghead API call is made only when the talking state changes.
787- *
788- * Note that also the TTS system, if enabled, starts/stops the Talkinghead talking animation.
789- * See `talkingAnimation` in `SillyTavern/public/scripts/extensions/tts/index.js`.
790- */
791-async function updateTalkingState() {
792- // Don't bother if Talkinghead is disabled or not loaded.
793- if (!isTalkingHeadEnabled() || !modules.includes('talkinghead')) {
794- return;
795- }
796-
797- const context = getContext();
798- const currentLastMessage = getLastCharacterMessage();
799-
800- try {
801- // TODO: Not sure if we need also "&& !context.groupId" here - the classify check in `moduleWorker`
802- // (that similarly checks the streaming processor state) does that for some reason.
803- // Talkinghead isn't currently designed to work with groups.
804- const lastMessageChanged = !((lastCharacter === context.characterId || lastCharacter === context.groupId) && lastTalkingStateMessage === currentLastMessage.mes);
805- const url = new URL(getApiUrl());
806- let newTalkingState;
807- if (context.streamingProcessor && !context.streamingProcessor.isFinished &&
808- currentLastMessage.mes.length !== 0 && currentLastMessage.mes !== '...' && lastMessageChanged) {
809- url.pathname = '/api/talkinghead/start_talking';
810- newTalkingState = true;
811- } else {
812- url.pathname = '/api/talkinghead/stop_talking';
813- newTalkingState = false;
814- }
815- try {
816- // Call the Talkinghead API only if the talking state changed.
817- if (newTalkingState !== lastTalkingState) {
818- console.debug(`updateTalkingState: calling ${url.pathname}`);
819- await doExtrasFetch(url);
820- }
821- }
822- catch (error) {
823- // it's ok if not supported
824- }
825- finally {
826- lastTalkingState = newTalkingState;
827- }
828- }
829- catch (error) {
830- // console.log(error);
831- }
832- finally {
833- lastTalkingStateMessage = currentLastMessage.mes;
834- }
835-}
836-
837-/**
838- * Checks whether the current character has a talkinghead image available.
839- * @returns {Promise<boolean>} True if the character has a talkinghead image available, false otherwise.
840- */
841-async function isTalkingHeadAvailable() {
842- let spriteFolderName = getSpriteFolderName();
843-
844- try {
845- await validateImages(spriteFolderName);
846-
847- let talkingheadObj = spriteCache[spriteFolderName].find(obj => obj.label === 'talkinghead');
848- let talkingheadPath = talkingheadObj ? talkingheadObj.path : null;
849-
850- if (talkingheadPath != null) {
851- return true;
852- } else {
853- await unloadTalkingHead();
854- return false;
855- }
856- } catch (err) {
857- return err;
858- }
859-}
860-
861566function getSpriteFolderName(characterMessage = null, characterName = null) {
862567 const context = getContext();
863568 let spriteFolderName = characterName ?? context.name2;
@@ -872,33 +577,6 @@ function getSpriteFolderName(characterMessage = null, characterName = null) {
872577 return spriteFolderName;
873578}
874579
875-function setTalkingHeadState(newState) {
876- console.debug(`expressions: New talkinghead state: ${newState}`);
877- extension_settings.expressions.talkinghead = newState; // Store setting
878- saveSettingsDebounced();
879-
880- if ([EXPRESSION_API.local, EXPRESSION_API.llm, EXPRESSION_API.webllm].includes(extension_settings.expressions.api)) {
881- return;
882- }
883-
884- isTalkingHeadAvailable().then(result => {
885- if (result) {
886- //console.log("talkinghead exists!");
887-
888- if (extension_settings.expressions.talkinghead) {
889- loadTalkingHead();
890- } else {
891- unloadTalkingHead();
892- }
893- handleImageChange(); // Change image as needed
894-
895-
896- } else {
897- //console.log("talkinghead does not exist.");
898- }
899- });
900-}
901-
902580function getFolderNameByMessage(message) {
903581 const context = getContext();
904582 let avatarPath = '';
@@ -976,19 +654,14 @@ async function classifyCallback(/** @type {{api: string?, prompt: string?}} */ {
976654}
977655
978656async function setSpriteSlashCommand(_, spriteId) {
657+ spriteId = spriteId.trim().toLowerCase();
979658 if (!spriteId) {
980659 console.log('No sprite id provided');
981660 return '';
982661 }
983662
984663 spriteIdconst spriteFolderName = spriteId.trim().toLowerCasegetSpriteFolderName();
985664
986- // In Talkinghead mode, don't check for the existence of the sprite
987- // (emotion names are the same as for sprites, but it only needs "talkinghead.png").
988- const currentLastMessage = getLastCharacterMessage();
989- const spriteFolderName = getSpriteFolderName(currentLastMessage, currentLastMessage.name);
990- let label = spriteId;
991- if (!isTalkingHeadEnabled() || !modules.includes('talkinghead')) {
992665 await validateImages(spriteFolderName);
993666
994667 // Fuzzy search for sprite
@@ -1001,8 +674,7 @@ async function setSpriteSlashCommand(_, spriteId) {
1001674 return '';
1002675 }
1003676
1004677 const label = spriteItem.label;
1005- }
1006678
1007679 const vnMode = isVisualNovelMode();
1008680 await sendExpressionCall(spriteFolderName, label, true, vnMode);
@@ -1190,7 +862,7 @@ function getJsonSchema(emotions) {
1190862function onTextGenSettingsReady(args) {
1191863 // Only call if inside an API call
1192864 if (inApiCall && extension_settings.expressions.api === EXPRESSION_API.llm && isJsonSchemaSupported()) {
1193- const emotions = DEFAULT_EXPRESSIONS.filter((e) => e != 'talkinghead');
865+ const emotions = DEFAULT_EXPRESSIONS;
1194866 Object.assign(args, {
1195867 top_k: 1,
1196868 stop: [],
@@ -1581,7 +1253,7 @@ export async function getExpressionsList() {
15811253 }
15821254
15831255 // If there was no specific list, or an error, just return the default expressions
15841256 expressionsList = DEFAULT_EXPRESSIONS.filter(e => e !== 'talkinghead').slice();
15851257 return expressionsList;
15861258 }
15871259
@@ -1597,7 +1269,6 @@ export async function getExpressionsList() {
15971269 * @returns {Promise<void>} A promise that resolves when the expression has been set.
15981270 */
15991271async function setExpression(character, expression, force = false) {
1600- if (!isTalkingHeadEnabled() || !modules.includes('talkinghead')) {
16011272 console.debug('entered setExpressions');
16021273 await validateImages(character);
16031274 const img = $('img.expression');
@@ -1718,41 +1389,6 @@ async function setExpression(character, expression, force = false) {
17181389 img.addClass('default');
17191390 }
17201391 document.getElementById('expression-holder').style.display = '';
1721-
1722- } else {
1723- // Set the Talkinghead emotion to the specified expression
1724- // TODO: For now, Talkinghead emote only supported when VN mode is off; see also updateVisualNovelMode.
1725- try {
1726- let result = await isTalkingHeadAvailable();
1727- if (result) {
1728- const url = new URL(getApiUrl());
1729- url.pathname = '/api/talkinghead/set_emotion';
1730- await doExtrasFetch(url, {
1731- method: 'POST',
1732- headers: {
1733- 'Content-Type': 'application/json',
1734- },
1735- body: JSON.stringify({ emotion_name: expression }),
1736- });
1737- }
1738- }
1739- catch (error) {
1740- // `set_emotion` is not present in old versions, so let it 404.
1741- }
1742-
1743- try {
1744- // Find the <img> element with id="expression-image" and class="expression"
1745- const imgElement = document.querySelector('img#expression-image.expression');
1746- //console.log("searching");
1747- if (imgElement && imgElement instanceof HTMLImageElement) {
1748- //console.log("setting value");
1749- imgElement.src = getApiUrl() + '/api/talkinghead/result_feed';
1750- }
1751- }
1752- catch (error) {
1753- //console.log("The fetch failed!");
1754- }
1755- }
17561392}
17571393
17581394function onClickExpressionImage() {
@@ -1976,11 +1612,6 @@ async function onClickExpressionUpload(event) {
19761612
19771613 // Reset the input
19781614 e.target.form.reset();
1979-
1980- // In Talkinghead mode, when a new talkinghead image is uploaded, refresh the live char.
1981- if (expression === 'talkinghead' && isTalkingHeadEnabled() && modules.includes('talkinghead')) {
1982- await loadTalkingHead();
1983- }
19841615 };
19851616
19861617 $('#expression_upload')
@@ -2091,11 +1722,6 @@ async function onClickExpressionUploadPackButton() {
20911722
20921723 // Reset the input
20931724 e.target.form.reset();
2094-
2095- // In Talkinghead mode, refresh the live char.
2096- if (isTalkingHeadEnabled() && modules.includes('talkinghead')) {
2097- await loadTalkingHead();
2098- }
20991725 };
21001726
21011727 $('#expression_upload_pack')
@@ -2254,18 +1880,12 @@ function migrateSettings() {
22541880 $(window).on('resize', updateVisualNovelModeDebounced);
22551881 $('#open_chat_expressions').hide();
22561882
2257- $('#image_type_toggle').on('click', function () {
2258- if (this instanceof HTMLInputElement) {
2259- setTalkingHeadState(this.checked);
2260- }
2261- });
2262-
22631883 await renderAdditionalExpressionSettings();
22641884 $('#expression_api').val(extension_settings.expressions.api ?? EXPRESSION_API.extras);
22651885 $('.expression_llm_prompt_block').toggle([EXPRESSION_API.llm, EXPRESSION_API.webllm].includes(extension_settings.expressions.api));
22661886 $('#expression_llm_prompt').val(extension_settings.expressions.llmPrompt ?? '');
22671887 $('#expression_llm_prompt').on('input', function () {
22681888 extension_settings.expressions.llmPrompt = String($(this).val());
22691889 saveSettingsDebounced();
22701890 });
22711891 $('#expression_llm_prompt_restore').on('click', function () {
@@ -2280,34 +1900,6 @@ function migrateSettings() {
22801900 $('#expression_api').on('change', onExpressionApiChanged);
22811901 }
22821902
2283- // Pause Talkinghead to save resources when the ST tab is not visible or the window is minimized.
2284- // We currently do this via loading/unloading. Could be improved by adding new pause/unpause endpoints to Extras.
2285- document.addEventListener('visibilitychange', function (event) {
2286- let pageIsVisible;
2287- if (document.hidden) {
2288- console.debug('expressions: SillyTavern is now hidden');
2289- pageIsVisible = false;
2290- } else {
2291- console.debug('expressions: SillyTavern is now visible');
2292- pageIsVisible = true;
2293- }
2294-
2295- if (isTalkingHeadEnabled() && modules.includes('talkinghead')) {
2296- isTalkingHeadAvailable().then(result => {
2297- if (result) {
2298- if (pageIsVisible) {
2299- loadTalkingHead();
2300- } else {
2301- unloadTalkingHead();
2302- }
2303- handleImageChange(); // Change image as needed
2304- } else {
2305- //console.log("talkinghead does not exist.");
2306- }
2307- });
2308- }
2309- });
2310-
23111903 addExpressionImage();
23121904 addVisualNovelMode();
23131905 migrateSettings();
@@ -2316,11 +1908,6 @@ function migrateSettings() {
23161908 const updateFunction = wrapper.update.bind(wrapper);
23171909 setInterval(updateFunction, UPDATE_INTERVAL);
23181910 moduleWorker();
2319- // For setting the Talkinghead talking animation on/off quickly enough for realtime use, we need another timer on a shorter schedule.
2320- const wrapperTalkingState = new ModuleWorkerWrapper(updateTalkingState);
2321- const updateTalkingStateFunction = wrapperTalkingState.update.bind(wrapperTalkingState);
2322- setInterval(updateTalkingStateFunction, TALKINGCHECK_UPDATE_INTERVAL);
2323- updateTalkingState();
23241911 dragElement($('#expression-holder'));
23251912 eventSource.on(event_types.CHAT_CHANGED, () => {
23261913 // character changed
@@ -2334,12 +1921,6 @@ function migrateSettings() {
23341921 imgElement.src = '';
23351922 }
23361923
2337- //set checkbox to global var
2338- $('#image_type_toggle').prop('checked', extension_settings.expressions.talkinghead);
2339- if (extension_settings.expressions.talkinghead) {
2340- setTalkingHeadState(extension_settings.expressions.talkinghead);
2341- }
2342-
23431924 setExpressionOverrideHtml();
23441925
23451926 if (isVisualNovelMode()) {
@@ -2350,11 +1931,6 @@ function migrateSettings() {
23501931 });
23511932 eventSource.on(event_types.MOVABLE_PANELS_RESET, updateVisualNovelModeDebounced);
23521933 eventSource.on(event_types.GROUP_UPDATED, updateVisualNovelModeDebounced);
2353- eventSource.on(event_types.EXTRAS_CONNECTED, () => {
2354- if (extension_settings.expressions.talkinghead) {
2355- setTalkingHeadState(extension_settings.expressions.talkinghead);
2356- }
2357- });
23581934
23591935 const localEnumProviders = {
23601936 expressions: () => getCachedExpressions().map(expression => {
@@ -2421,13 +1997,6 @@ function migrateSettings() {
24211997 helpString: 'Returns the last set expression for the named character.',
24221998 }));
24231999 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
2424- name: 'expression-talkinghead',
2425- callback: toggleTalkingHeadCommand,
2426- aliases: ['th', 'talkinghead'],
2427- helpString: 'Character Expressions: toggles <i>Image Type - talkinghead (extras)</i> on/off.',
2428- returns: 'the current state of the <i>Image Type - talkinghead (extras)</i> on/off.',
2429- }));
2430- SlashCommandParser.addCommandObject(SlashCommand.fromProps({
24312000 name: 'expression-classify',
24322001 aliases: ['classify-expressions', 'expressions'],
24332002 /** @type {(args: {return: string}) => Promise<string>} */
public/scripts/extensions/expressions/settings.html+0 -4
@@ -22,10 +22,6 @@
2222 <input id="expressions_reroll_if_same" type="checkbox">
2323 <span data-i18n="Re-roll if same expression is used again">Re-roll if same sprite is used again</span>
2424 </label>
25- <label id="image_type_block" class="checkbox_label" for="image_type_toggle">
26- <input id="image_type_toggle" type="checkbox">
27- <span data-i18n="Image Type - talkinghead (extras)">Image Type - talkinghead (extras)</span>
28- </label>
2925 <div class="expression_api_block m-b-1 m-t-1">
3026 <label for="expression_api" data-i18n="Classifier API">Classifier API</label>
3127 <small data-i18n="Select the API for classifying expressions.">Select the API for classifying expressions.</small>
public/scripts/extensions/tts/index.js+0 -26
@@ -27,13 +27,11 @@ import { SlashCommandEnumValue, enumTypes } from '../../slash-commands/SlashComm
2727import { enumIcons } from '../../slash-commands/SlashCommandCommonEnumsProvider.js';
2828import { POPUP_TYPE, callGenericPopup } from '../../popup.js';
2929import { GoogleTranslateTtsProvider } from './google-translate.js';
30-export { talkingAnimation };
3130
3231const UPDATE_INTERVAL = 1000;
3332
3433let voiceMapEntries = [];
3534let voiceMap = {}; // {charName:voiceid, charName2:voiceid2}
36-let talkingHeadState = false;
3735let lastChatId = null;
3836let lastMessage = null;
3937let lastMessageHash = null;
@@ -165,27 +163,6 @@ async function moduleWorker() {
165163 updateUiAudioPlayState();
166164}
167165
168-function talkingAnimation(switchValue) {
169- if (!modules.includes('talkinghead')) {
170- console.debug('Talking Animation module not loaded');
171- return;
172- }
173-
174- const apiUrl = getApiUrl();
175- const animationType = switchValue ? 'start' : 'stop';
176-
177- if (switchValue !== talkingHeadState) {
178- try {
179- console.log(animationType + ' Talking Animation');
180- doExtrasFetch(`${apiUrl}/api/talkinghead/${animationType}_talking`);
181- talkingHeadState = switchValue;
182- } catch (error) {
183- // Handle the error here or simply ignore it to prevent logging
184- }
185- }
186- updateUiAudioPlayState();
187-}
188-
189166function resetTtsPlayback() {
190167 // Stop system TTS utterance
191168 cancelTtsPlay();
@@ -347,7 +324,6 @@ function onAudioControlClicked() {
347324 // Not pausing, doing a full stop to anything TTS is doing. Better UX as pause is not as useful
348325 if (!audioElement.paused || isTtsProcessing()) {
349326 resetTtsPlayback();
350- talkingAnimation(false);
351327 } else {
352328 // Default play behavior if not processing or playing is to play the last message.
353329 ttsJobQueue.push(context.chat[context.chat.length - 1]);
@@ -374,7 +350,6 @@ function addAudioControl() {
374350function completeCurrentAudioJob() {
375351 audioQueueProcessorReady = true;
376352 currentAudioJob = null;
377- talkingAnimation(false); //stop lip animation
378353 // updateUiPlayState();
379354}
380355
@@ -404,7 +379,6 @@ async function processAudioJobQueue() {
404379 audioQueueProcessorReady = false;
405380 currentAudioJob = audioJobQueue.shift();
406381 playAudioData(currentAudioJob);
407- talkingAnimation(true);
408382 } catch (error) {
409383 toastr.error(error.toString());
410384 console.error(error);
public/scripts/extensions/tts/system.js+0 -3
@@ -1,6 +1,5 @@
11import { isMobile } from '../../RossAscends-mods.js';
22import { getPreviewString } from './index.js';
3-import { talkingAnimation } from './index.js';
43import { saveTtsProviderSettings } from './index.js';
54export { SystemTtsProvider };
65
@@ -70,7 +69,6 @@ var speechUtteranceChunker = function (utt, settings, callback) {
7069 //placing the speak invocation inside a callback fixes ordering and onend issues.
7170 setTimeout(function () {
7271 speechSynthesis.speak(newUtt);
73- talkingAnimation(true);
7472 }, 0);
7573};
7674
@@ -240,7 +238,6 @@ class SystemTtsProvider {
240238 //some code to execute when done
241239 resolve(silence);
242240 console.log('System TTS done');
243- talkingAnimation(false);
244241 });
245242 });
246243 }