hlassiege/darylpublic Fork
⑂ 0a62d55
Commits
⬇ Clone ▾
git clone https://git.rickub.com/hlassiege/daryl.git
git clone ssh://git@rickub.com/hlassiege/daryl.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

Download ZIP

fix(transcript): retry lost connections to the cloud transcription APIs

A flaky network dropped every SYN to api.elevenlabs.io and the job died on WinError 10060. Connection failures are now retried (nothing was sent yet), and a remaining one is reported as an unreachable API instead of the raw socket error.
hlassiege committed 2026-10-20T19:49:40+02:00 Browse files
0a62d55 parent: d8cb823
modified backend/studio/modules/transcript/cloud.py +10 -4
@@ -24,6 +24,8 @@ LABELS = {"elevenlabs": "ElevenLabs Scribe v2", "gladia": "Gladia Solaria"}
2424 TIMEOUT = 30 * 60
2525 POLL_EVERY = 2.0
2626 # ElevenLabs renvoie des codes ISO 639-3
27+# connexion perdue (paquets perdus, WinError 10060) : reessayee, rien n'a encore ete envoye
28+_http = httpx.Client(transport=httpx.HTTPTransport(retries=3))
2729 ISO3 = {"fra": "fr", "eng": "en", "spa": "es", "deu": "de", "ita": "it", "por": "pt"}
2830
2931 Words = list[tuple[float, float, str]]
@@ -48,6 +50,10 @@ class CloudEngine:
4850 try:
4951 run = self._elevenlabs if self.provider == "elevenlabs" else self._gladia
5052 words, detected = run(mp3, language, check_cancel)
53+ except httpx.TransportError as e:
54+ # coupure reseau, pare-feu, proxy : le message brut (WinError 10060...) ne dit pas qui
55+ raise RuntimeError(f"{LABELS[self.provider]} injoignable ({type(e).__name__}) : "
56+ "vérifie la connexion, ou repasse sur Whisper dans les réglages") from e
5157 finally:
5258 mp3.unlink(missing_ok=True)
5359 segments = group(words)
@@ -62,7 +68,7 @@ class CloudEngine:
6268 if language:
6369 data["language_code"] = language
6470 with audio.open("rb") as f:
65- r = httpx.post(ELEVENLABS_URL, headers={"xi-api-key": self.key}, data=data,
71+ r = _http.post(ELEVENLABS_URL, headers={"xi-api-key": self.key}, data=data,
6672 files={"file": (audio.name, f, "audio/mpeg")}, timeout=TIMEOUT)
6773 _raise(r, "ElevenLabs")
6874 raw = r.json()
@@ -73,18 +79,18 @@ class CloudEngine:
7379 def _gladia(self, audio: Path, language: str | None, check_cancel) -> tuple[Words, str | None]:
7480 h = {"x-gladia-key": self.key}
7581 with audio.open("rb") as f:
76- up = httpx.post(f"{GLADIA_URL}/upload", headers=h, files={"audio": (audio.name, f, "audio/mpeg")},
82+ up = _http.post(f"{GLADIA_URL}/upload", headers=h, files={"audio": (audio.name, f, "audio/mpeg")},
7783 timeout=TIMEOUT)
7884 _raise(up, "Gladia")
7985 body: dict = {"audio_url": up.json()["audio_url"]}
8086 if language:
8187 body["language_config"] = {"languages": [language]}
82- job = httpx.post(f"{GLADIA_URL}/pre-recorded", headers=h, json=body, timeout=60)
88+ job = _http.post(f"{GLADIA_URL}/pre-recorded", headers=h, json=body, timeout=60)
8389 _raise(job, "Gladia")
8490 url = f"{GLADIA_URL}/pre-recorded/{job.json()['id']}"
8591 while True:
8692 check_cancel()
87- r = httpx.get(url, headers=h, timeout=60)
93+ r = _http.get(url, headers=h, timeout=60)
8894 _raise(r, "Gladia")
8995 raw = r.json()
9096 if raw.get("status") == "done":
@@ -24,6 +24,8 @@ LABELS = {"elevenlabs": "ElevenLabs Scribe v2", "gladia": "Gladia Solaria"}
24 TIMEOUT = 30 * 6024 TIMEOUT = 30 * 60
25 POLL_EVERY = 2.025 POLL_EVERY = 2.0
26 # ElevenLabs renvoie des codes ISO 639-326 # ElevenLabs renvoie des codes ISO 639-3
27+# connexion perdue (paquets perdus, WinError 10060) : reessayee, rien n'a encore ete envoye
28+_http = httpx.Client(transport=httpx.HTTPTransport(retries=3))
27 ISO3 = {"fra": "fr", "eng": "en", "spa": "es", "deu": "de", "ita": "it", "por": "pt"}29 ISO3 = {"fra": "fr", "eng": "en", "spa": "es", "deu": "de", "ita": "it", "por": "pt"}
28 30
29 Words = list[tuple[float, float, str]]31 Words = list[tuple[float, float, str]]
@@ -48,6 +50,10 @@ class CloudEngine:
48 try:50 try:
49 run = self._elevenlabs if self.provider == "elevenlabs" else self._gladia51 run = self._elevenlabs if self.provider == "elevenlabs" else self._gladia
50 words, detected = run(mp3, language, check_cancel)52 words, detected = run(mp3, language, check_cancel)
53+ except httpx.TransportError as e:
54+ # coupure reseau, pare-feu, proxy : le message brut (WinError 10060...) ne dit pas qui
55+ raise RuntimeError(f"{LABELS[self.provider]} injoignable ({type(e).__name__}) : "
56+ "vérifie la connexion, ou repasse sur Whisper dans les réglages") from e
51 finally:57 finally:
52 mp3.unlink(missing_ok=True)58 mp3.unlink(missing_ok=True)
53 segments = group(words)59 segments = group(words)
@@ -62,7 +68,7 @@ class CloudEngine:
62 if language:68 if language:
63 data["language_code"] = language69 data["language_code"] = language
64 with audio.open("rb") as f:70 with audio.open("rb") as f:
65- r = httpx.post(ELEVENLABS_URL, headers={"xi-api-key": self.key}, data=data,71+ r = _http.post(ELEVENLABS_URL, headers={"xi-api-key": self.key}, data=data,
66 files={"file": (audio.name, f, "audio/mpeg")}, timeout=TIMEOUT)72 files={"file": (audio.name, f, "audio/mpeg")}, timeout=TIMEOUT)
67 _raise(r, "ElevenLabs")73 _raise(r, "ElevenLabs")
68 raw = r.json()74 raw = r.json()
@@ -73,18 +79,18 @@ class CloudEngine:
73 def _gladia(self, audio: Path, language: str | None, check_cancel) -> tuple[Words, str | None]:79 def _gladia(self, audio: Path, language: str | None, check_cancel) -> tuple[Words, str | None]:
74 h = {"x-gladia-key": self.key}80 h = {"x-gladia-key": self.key}
75 with audio.open("rb") as f:81 with audio.open("rb") as f:
76- up = httpx.post(f"{GLADIA_URL}/upload", headers=h, files={"audio": (audio.name, f, "audio/mpeg")},82+ up = _http.post(f"{GLADIA_URL}/upload", headers=h, files={"audio": (audio.name, f, "audio/mpeg")},
77 timeout=TIMEOUT)83 timeout=TIMEOUT)
78 _raise(up, "Gladia")84 _raise(up, "Gladia")
79 body: dict = {"audio_url": up.json()["audio_url"]}85 body: dict = {"audio_url": up.json()["audio_url"]}
80 if language:86 if language:
81 body["language_config"] = {"languages": [language]}87 body["language_config"] = {"languages": [language]}
82- job = httpx.post(f"{GLADIA_URL}/pre-recorded", headers=h, json=body, timeout=60)88+ job = _http.post(f"{GLADIA_URL}/pre-recorded", headers=h, json=body, timeout=60)
83 _raise(job, "Gladia")89 _raise(job, "Gladia")
84 url = f"{GLADIA_URL}/pre-recorded/{job.json()['id']}"90 url = f"{GLADIA_URL}/pre-recorded/{job.json()['id']}"
85 while True:91 while True:
86 check_cancel()92 check_cancel()
87- r = httpx.get(url, headers=h, timeout=60)93+ r = _http.get(url, headers=h, timeout=60)
88 _raise(r, "Gladia")94 _raise(r, "Gladia")
89 raw = r.json()95 raw = r.json()
90 if raw.get("status") == "done":96 if raw.get("status") == "done":