Фикс произношения дат и температуры + wheather -лишние файлы

This commit is contained in:
2026-01-10 23:50:02 +03:00
parent 3818f0ad22
commit 8f44fd2460
10 changed files with 295 additions and 82 deletions

View File

@@ -20,6 +20,8 @@ from ..core.config import TTS_SPEAKER, TTS_EN_SPEAKER, TTS_SAMPLE_RATE
# Подавляем предупреждения Silero о длинном тексте (мы сами его режем)
warnings.filterwarnings("ignore", message="Text string is longer than 1000 symbols")
_EN_WORD_RE = re.compile(r"[A-Za-z][A-Za-z0-9'-]*")
class TextToSpeech:
"""Класс синтеза речи с поддержкой прерывания."""
@@ -109,19 +111,52 @@ class TextToSpeech:
return [c for c in chunks if c]
def speak(self, text: str, check_interrupt=None, language: str = "ru") -> bool:
def _split_mixed_language(self, text: str) -> list[tuple[str, str]]:
"""
Основная функция: генерирует аудио и воспроизводит его.
Args:
text: Текст для озвучки.
check_interrupt: Функция, возвращающая True, если надо прерваться (например, check_wakeword_once).
language: "ru" или "en".
Returns:
True, если договорил до конца.
False, если был прерван.
Разбивает текст на сегменты русского и английского текста.
Английские слова (латиница) будут озвучены английской моделью.
"""
matches = list(_EN_WORD_RE.finditer(text))
if not matches:
return [(text, "ru")]
segments = []
idx = 0
for match in matches:
if match.start() > idx:
segments.append((text[idx : match.start()], "ru"))
segments.append((match.group(0), "en"))
idx = match.end()
if idx < len(text):
segments.append((text[idx:], "ru"))
# Склеиваем соседние сегменты и прикрепляем чистую пунктуацию к предыдущему.
merged = []
for segment, lang in segments:
if not segment:
continue
if not any(ch.isalnum() for ch in segment):
if merged:
merged[-1] = (merged[-1][0] + segment, merged[-1][1])
else:
merged.append((segment, lang))
continue
if merged and merged[-1][1] == lang:
merged[-1] = (merged[-1][0] + segment, lang)
else:
merged.append((segment, lang))
if merged and not any(ch.isalnum() for ch in merged[0][0]) and len(merged) > 1:
merged[1] = (merged[0][0] + merged[1][0], merged[1][1])
merged = merged[1:]
return merged
def _speak_single_language(
self, text: str, check_interrupt=None, language: str = "ru"
) -> bool:
"""Озвучивание текста одной моделью языка."""
if not text.strip():
return True
@@ -197,6 +232,45 @@ class TextToSpeech:
else:
return False
def _speak_mixed(
self, segments: list[tuple[str, str]], check_interrupt=None
) -> bool:
"""Озвучивание текста с переключением RU/EN по сегментам."""
for segment, lang in segments:
if not segment.strip():
continue
completed = self._speak_single_language(
segment, check_interrupt=check_interrupt, language=lang
)
if not completed:
return False
return True
def speak(self, text: str, check_interrupt=None, language: str = "ru") -> bool:
"""
Основная функция: генерирует аудио и воспроизводит его.
Args:
text: Текст для озвучки.
check_interrupt: Функция, возвращающая True, если надо прерваться (например, check_wakeword_once).
language: "ru" или "en".
Returns:
True, если договорил до конца.
False, если был прерван.
"""
if not text.strip():
return True
if language == "ru":
segments = self._split_mixed_language(text)
if any(lang == "en" for _, lang in segments):
return self._speak_mixed(segments, check_interrupt=check_interrupt)
return self._speak_single_language(
text, check_interrupt=check_interrupt, language=language
)
def _check_interrupt_worker(self, check_interrupt):
"""
Фоновая функция для потока: постоянно опрашивает check_interrupt.