From 6f8370fb961d0b1f04d17a06b71187e55db77b0e Mon Sep 17 00:00:00 2001 From: Asger Geel Weirsoee Date: Thu, 1 Oct 2026 21:58:30 +0200 Subject: [PATCH] =?UTF-8?q?Speaker:=20h=C3=B8jst=20tre=20samtidige=20kald?= =?UTF-8?q?=20til=20ElevenLabs=20og=20nyt=20fors=C3=B8g=20ved=20429?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Afsløringen bestilte alle trin på én gang og ramte abonnementets loft for samtidige kald. Forklaringen genereres nu allerede, mens der løjes. Claude-Session: https://claude.ai/code/session_01JmhJG6JnhXfzNiKv8f2vHu --- party/rooms.py | 3 +++ party/tts.py | 20 +++++++++++++++++++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/party/rooms.py b/party/rooms.py index 57eb7f9..d27bfa4 100644 --- a/party/rooms.py +++ b/party/rooms.py @@ -124,6 +124,9 @@ class Room: self.say(text) if ev["type"] == "lie_start": self._at_deadline_minus(10, "lie", "lie_hurry") + note = (self.game.question or {}).get("note") + if note: # forklaringen kendes allerede nu; lav den, mens der løjes + asyncio.get_running_loop().create_task(tts.synth(note)) elif ev["type"] == "guess_start": self._at_deadline_minus(5, "guess", "guess_hurry") diff --git a/party/tts.py b/party/tts.py index 1389ae8..9800693 100644 --- a/party/tts.py +++ b/party/tts.py @@ -19,7 +19,10 @@ log = logging.getLogger(__name__) API = "https://api.elevenlabs.io/v1/text-to-speech/{voice}?output_format=mp3_44100_128" BITRATE = 128_000 +CONCURRENCY = 3 # ElevenLabs begrænser samtidige kald pr. abonnement (Creator: få) +RETRIES_ON_429 = 3 _inflight: dict[str, asyncio.Future] = {} +_sem: asyncio.Semaphore | None = None def enabled() -> bool: @@ -74,7 +77,7 @@ async def synth(text: str) -> dict | None: return _result(digest) fut = _inflight.get(digest) if fut is None: - fut = asyncio.get_running_loop().create_task(asyncio.to_thread(_fetch, text, cache_path(digest))) + fut = asyncio.get_running_loop().create_task(_fetch_limited(text, cache_path(digest))) _inflight[digest] = fut fut.add_done_callback(lambda _f: _inflight.pop(digest, None)) try: @@ -85,6 +88,21 @@ async def synth(text: str) -> dict | None: return _result(digest) +async def _fetch_limited(text: str, path: Path) -> None: + global _sem + if _sem is None: + _sem = asyncio.Semaphore(CONCURRENCY) + for attempt in range(RETRIES_ON_429 + 1): + async with _sem: + try: + await asyncio.to_thread(_fetch, text, path) + return + except urllib.error.HTTPError as e: + if e.code != 429 or attempt == RETRIES_ON_429: + raise + await asyncio.sleep(0.7 * (attempt + 1)) + + def _describe(e: Exception) -> str: if isinstance(e, urllib.error.HTTPError): try: