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>
Signed| @@ -1,6 +1,6 @@ | ||
| 1 | 1 | import { cancelTtsPlay, eventSource, event_types, getCurrentChatId, isStreamingEnabled, name2, saveSettingsDebounced, substituteParams } from '../../../script.js'; |
| 2 | 2 | import { ModuleWorkerWrapper, extension_settings, getContext, renderExtensionTemplateAsync } from '../../extensions.js'; |
| 3 | 3 | import { delay, escapeRegex, getBase64Async, getStringHash, onlyUnique, regexFromString } from '../../utils.js'; |
| 4 | 4 | import { EdgeTtsProvider } from './edge.js'; |
| 5 | 5 | import { ElevenLabsTtsProvider } from './elevenlabs.js'; |
| 6 | 6 | import { SileroTtsProvider } from './silerotts.js'; |
| @@ -640,6 +640,16 @@ async function processTtsQueue() { | ||
| 640 | 640 | : text.replaceAll('*', '').trim(); // remove just the asterisks |
| 641 | 641 | } |
| 642 | 642 | |
| 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 | + | |
| 643 | 653 | if (extension_settings.tts.narrate_quoted_only) { |
| 644 | 654 | const partJoiner = (ttsProvider?.separator || ' ... '); |
| 645 | 655 | text = joinQuotedBlocks(text, { separator: partJoiner, includeQuotes: true }); |
| @@ -827,6 +837,10 @@ function loadSettings() { | ||
| 827 | 837 | $('#tts_skip_codeblocks').prop('checked', extension_settings.tts.skip_codeblocks); |
| 828 | 838 | $('#tts_skip_tags').prop('checked', extension_settings.tts.skip_tags); |
| 829 | 839 | $('#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(); | |
| 830 | 844 | $('#playback_rate').val(extension_settings.tts.playback_rate); |
| 831 | 845 | $('#playback_rate_counter').val(Number(extension_settings.tts.playback_rate).toFixed(2)); |
| 832 | 846 | $('#playback_rate_block').toggle(extension_settings.tts.currentProvider !== 'System'); |
| @@ -842,6 +856,8 @@ const defaultSettings = { | ||
| 842 | 856 | narrate_user: false, |
| 843 | 857 | playback_rate: 1, |
| 844 | 858 | multi_voice_enabled: false, |
| 859 | + apply_regex: false, | |
| 860 | + regex_pattern: '', | |
| 845 | 861 | }; |
| 846 | 862 | |
| 847 | 863 | function setTtsStatus(status, success) { |
| @@ -942,6 +958,36 @@ function onMultiVoiceClick() { | ||
| 942 | 958 | initVoiceMap(); |
| 943 | 959 | } |
| 944 | 960 | |
| 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 | + | |
| 945 | 991 | //##############// |
| 946 | 992 | // TTS Provider // |
| 947 | 993 | //##############// |
| @@ -1451,6 +1497,8 @@ jQuery(async function () { | ||
| 1451 | 1497 | $('#tts_narrate_by_paragraphs').on('click', onNarrateByParagraphsClick); |
| 1452 | 1498 | $('#tts_narrate_user').on('click', onNarrateUserClick); |
| 1453 | 1499 | $('#tts_multi_voice_enabled').on('click', onMultiVoiceClick); |
| 1500 | + $('#tts_apply_regex').on('change', onApplyRegexChange); | |
| 1501 | + $('#tts_regex_pattern').on('input', onRegexPatternChange); | |
| 1454 | 1502 | |
| 1455 | 1503 | $('#playback_rate').on('input', function () { |
| 1456 | 1504 | const value = $(this).val(); |
| @@ -67,6 +67,17 @@ | ||
| 67 | 67 | Different voices for "quotes", *text inside asterisks* and other text |
| 68 | 68 | </small> |
| 69 | 69 | </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.,!?;:'"()]+/g" /> | |
| 79 | + <small id="tts_regex_warning" class="warning" data-i18n="Invalid regex pattern.">Invalid regex pattern.</small> | |
| 80 | + </div> | |
| 70 | 81 | </div> |
| 71 | 82 | <div id="playback_rate_block" class="range-block"> |
| 72 | 83 | <hr> |