Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion custom_components/universal_notifier/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,10 +94,23 @@ def get_current_slot_info(slots_conf: dict, now_time,
current_vol = vol_val
return current_slot, current_vol

# Tag SSML supportati dai motori TTS (Alexa, Google): vanno preservati.
SSML_TAGS = {
"speak", "voice", "prosody", "break", "say-as", "emphasis",
"lang", "phoneme",
}
_TAG_PATTERN = re.compile(r'<\s*/?\s*([a-zA-Z][\w:-]*)[^>]*>')

def _strip_non_ssml_tags(text: str) -> str:
"""Rimuove i tag HTML mantenendo i tag SSML noti."""
return _TAG_PATTERN.sub(
lambda m: m.group(0) if m.group(1).lower() in SSML_TAGS else '', text
)

def clean_text_for_tts(text: str) -> str:
"""Rimuove caratteri speciali per la sintesi vocale."""
if not text: return ""
text = re.sub(r'<[^>]+>', '', text) # Via HTML tags
text = _strip_non_ssml_tags(text) # Via HTML tags, tiene SSML
text = re.sub(r'[*_`\[\]]', '', text) # Via markdown
text = re.sub(r'http\S+', '', text) # Via URL
# Via emoji/icon (preserva lettere accentate latin-1)
Expand Down
15 changes: 15 additions & 0 deletions tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,21 @@ def test_none_text(self):
def test_collapses_whitespace(self):
assert clean_text_for_tts("hello world") == "hello world"

def test_preserves_ssml_voice_tag(self):
assert clean_text_for_tts(
"<voice name='Giorgio'>Il sistema è operativo!</voice>"
) == "<voice name='Giorgio'>Il sistema è operativo!</voice>"

@pytest.mark.parametrize("tag", ["prosody rate='slow'", "break time='500ms'",
"say-as interpret-as='date'", "emphasis"])
def test_preserves_other_ssml_tags(self, tag):
assert f"<{tag}>" in clean_text_for_tts(f"<{tag}>ciao")

def test_strips_html_around_ssml(self):
assert clean_text_for_tts(
"<b><voice name='Giorgio'>ciao</voice></b>"
) == "<voice name='Giorgio'>ciao</voice>"


# ============================================================================
# sanitize_text_visual
Expand Down