mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-06-24 21:38:09 +02:00
55 lines
2.2 KiB
Python
55 lines
2.2 KiB
Python
"""The voice catalog: look up and filter selectable voices.
|
|
|
|
A :class:`VoiceCatalog` is the single source of truth for which voices exist,
|
|
replacing the hardcoded speaker-id maps. Resolution uses it to pick defaults
|
|
for a brief, the API exposes it as picker options, and the renderer uses it to
|
|
turn a stored ``voice_id`` back into the provider-native reference.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from collections.abc import Iterable
|
|
from functools import lru_cache
|
|
|
|
from .data import AZURE_VOICES, KOKORO_VOICES, OPENAI_VOICES, VERTEX_VOICES
|
|
from .provider import TtsProvider
|
|
from .voice import CatalogVoice
|
|
|
|
|
|
class VoiceCatalog:
|
|
"""An indexed, read-only collection of :class:`CatalogVoice`."""
|
|
|
|
def __init__(self, voices: Iterable[CatalogVoice]) -> None:
|
|
self._by_id: dict[str, CatalogVoice] = {}
|
|
self._by_provider: dict[TtsProvider, list[CatalogVoice]] = {}
|
|
for voice in voices:
|
|
if voice.voice_id in self._by_id:
|
|
raise ValueError(f"duplicate voice_id: {voice.voice_id}")
|
|
self._by_id[voice.voice_id] = voice
|
|
self._by_provider.setdefault(voice.provider, []).append(voice)
|
|
|
|
def get(self, voice_id: str) -> CatalogVoice:
|
|
"""Return the voice with ``voice_id`` or raise ``KeyError``."""
|
|
return self._by_id[voice_id]
|
|
|
|
def for_provider(self, provider: TtsProvider) -> list[CatalogVoice]:
|
|
"""All voices offered by ``provider``, in catalog order."""
|
|
return list(self._by_provider.get(provider, ()))
|
|
|
|
def for_language(
|
|
self, provider: TtsProvider, language: str
|
|
) -> list[CatalogVoice]:
|
|
"""``provider`` voices that can render ``language``, in catalog order."""
|
|
return [v for v in self.for_provider(provider) if v.speaks(language)]
|
|
|
|
def supports_language(self, provider: TtsProvider, language: str) -> bool:
|
|
"""Whether ``provider`` has at least one voice for ``language``."""
|
|
return any(v.speaks(language) for v in self.for_provider(provider))
|
|
|
|
|
|
@lru_cache(maxsize=1)
|
|
def get_voice_catalog() -> VoiceCatalog:
|
|
"""The process-wide catalog assembled from every provider's roster."""
|
|
return VoiceCatalog(
|
|
(*KOKORO_VOICES, *OPENAI_VOICES, *AZURE_VOICES, *VERTEX_VOICES)
|
|
)
|