Added regex filter option to TTS extension (#4924) * Add regex filter option to TTS extension - Added checkbox to enable/disable regex filtering of TTS text - Added text input for custom regex patterns - Default pattern removes emojis and non-English text while preserving letters, numbers, spaces, and punctuation - Regex is applied after asterisk removal and other text processing - Includes error handling for invalid regex patterns - Automatically cleans up extra spaces after regex removal * reverted the accidental change in tts_skip_tags description clarified the regex description * Add regex validation warning to TTS settings * fixed regex field in UI * reworked the regex warning * changed the regex event to change (input might not have worked correctly) * Apply review comments * Spacing fix --------- Co-authored-by: Cohee <18619528+Cohee1207@users.noreply.github.com>

ef89d0f291a788ba1529cb009bdc988c452ec5d8

ZhenyaPav <61346893+ZhenyaPav@users.noreply.github.com>

Signed
2 files changed, +60 -1Ignore whitespace
public/scripts/extensions/tts/index.js+49 -1
@@ -1,6 +1,6 @@
11import { cancelTtsPlay, eventSource, event_types, getCurrentChatId, isStreamingEnabled, name2, saveSettingsDebounced, substituteParams } from '../../../script.js';
22import { ModuleWorkerWrapper, extension_settings, getContext, renderExtensionTemplateAsync } from '../../extensions.js';
33import { delay, escapeRegex, getBase64Async, getStringHash, onlyUnique, regexFromString } from '../../utils.js';
44import { EdgeTtsProvider } from './edge.js';
55import { ElevenLabsTtsProvider } from './elevenlabs.js';
66import { SileroTtsProvider } from './silerotts.js';
@@ -640,6 +640,16 @@ async function processTtsQueue() {
640640 : text.replaceAll('*', '').trim(); // remove just the asterisks
641641 }
642642
643+ if (extension_settings.tts.apply_regex && extension_settings.tts.regex_pattern) {
644+ const regex = regexFromString(extension_settings.tts.regex_pattern);
645+ if (regex) {
646+ // Clean up extra spaces that might be left after removal
647+ text = text.replace(regex, '').replace(/\s+/g, ' ').trim();
648+ } else {
649+ console.warn('Invalid regex pattern:', extension_settings.tts.regex_pattern);
650+ }
651+ }
652+
643653 if (extension_settings.tts.narrate_quoted_only) {
644654 const partJoiner = (ttsProvider?.separator || ' ... ');
645655 text = joinQuotedBlocks(text, { separator: partJoiner, includeQuotes: true });
@@ -827,6 +837,10 @@ function loadSettings() {
827837 $('#tts_skip_codeblocks').prop('checked', extension_settings.tts.skip_codeblocks);
828838 $('#tts_skip_tags').prop('checked', extension_settings.tts.skip_tags);
829839 $('#tts_multi_voice_enabled').prop('checked', extension_settings.tts.multi_voice_enabled);
840+ $('#tts_apply_regex').prop('checked', extension_settings.tts.apply_regex);
841+ $('#tts_regex_pattern').val(extension_settings.tts.regex_pattern);
842+ $('#tts_regex_block').toggle(extension_settings.tts.apply_regex);
843+ updateRegexPatternWarning();
830844 $('#playback_rate').val(extension_settings.tts.playback_rate);
831845 $('#playback_rate_counter').val(Number(extension_settings.tts.playback_rate).toFixed(2));
832846 $('#playback_rate_block').toggle(extension_settings.tts.currentProvider !== 'System');
@@ -842,6 +856,8 @@ const defaultSettings = {
842856 narrate_user: false,
843857 playback_rate: 1,
844858 multi_voice_enabled: false,
859+ apply_regex: false,
860+ regex_pattern: '',
845861};
846862
847863function setTtsStatus(status, success) {
@@ -942,6 +958,36 @@ function onMultiVoiceClick() {
942958 initVoiceMap();
943959}
944960
961+function onApplyRegexChange() {
962+ extension_settings.tts.apply_regex = !!$('#tts_apply_regex').prop('checked');
963+ saveSettingsDebounced();
964+ $('#tts_regex_block').toggle(extension_settings.tts.apply_regex);
965+ updateRegexPatternWarning();
966+}
967+
968+function onRegexPatternChange() {
969+ extension_settings.tts.regex_pattern = $('#tts_regex_pattern').val().toString();
970+ saveSettingsDebounced();
971+ updateRegexPatternWarning();
972+}
973+
974+function updateRegexPatternWarning() {
975+ const warning = $('#tts_regex_warning');
976+ if (!extension_settings.tts.apply_regex) {
977+ warning.hide();
978+ return;
979+ }
980+
981+ const pattern = extension_settings.tts.regex_pattern;
982+ if (!pattern) {
983+ warning.hide();
984+ return;
985+ }
986+
987+ const regex = regexFromString(pattern);
988+ warning.toggle(!regex);
989+}
990+
945991//##############//
946992// TTS Provider //
947993//##############//
@@ -1451,6 +1497,8 @@ jQuery(async function () {
14511497 $('#tts_narrate_by_paragraphs').on('click', onNarrateByParagraphsClick);
14521498 $('#tts_narrate_user').on('click', onNarrateUserClick);
14531499 $('#tts_multi_voice_enabled').on('click', onMultiVoiceClick);
1500+ $('#tts_apply_regex').on('change', onApplyRegexChange);
1501+ $('#tts_regex_pattern').on('input', onRegexPatternChange);
14541502
14551503 $('#playback_rate').on('input', function () {
14561504 const value = $(this).val();
public/scripts/extensions/tts/settings.html+11 -0
@@ -67,6 +67,17 @@
6767 Different voices for "quotes", *text inside asterisks* and other text
6868 </small>
6969 </label>
70+ <label class="checkbox_label" for="tts_apply_regex" title="Applies the regex pattern to filter text before TTS.">
71+ <input type="checkbox" id="tts_apply_regex">
72+ <small data-i18n="Apply regex filter">Apply regex filter to text</small>
73+ </label>
74+ <div id="tts_regex_block" class="marginTop5">
75+ <div class="range-block-title justifyLeft">
76+ <small data-i18n="Regex Pattern">Regex Pattern (removes matching text)</small>
77+ </div>
78+ <input type="text" id="tts_regex_pattern" class="text_pole width100p" placeholder="Example: /[^a-zA-Z0-9\s.,!?;:'&quot;()]+/g" />
79+ <small id="tts_regex_warning" class="warning" data-i18n="Invalid regex pattern.">Invalid regex pattern.</small>
80+ </div>
7081 </div>
7182 <div id="playback_rate_block" class="range-block">
7283 <hr>