maltose1 commited on
Commit
f1a9862
·
verified ·
1 Parent(s): 9431a6d

Upload 5 files

Browse files
Files changed (2) hide show
  1. cookie/19245776972.txt +1 -0
  2. tts-server.py +63 -12
cookie/19245776972.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ _ga=GA1.1.437012191.1745432605; d_ticket=7ef9a4bada1454fd46e0dc3fb5d3bbf7af1c9; flow_user_country=CN; _gcl_aw=GCL.1758716523.Cj0KCQjwrc7GBhCfARIsAHGcW5X3vlZenZizh4uxDAOeWTzQDdrT7JOqk-0_Toh0-nyTI98ilypZKRgaAjAREALw_wcB; _gcl_gs=2.1.k1$i1758716513$u91292815; gd_random=eyJtYXRjaCI6dHJ1ZSwicGVyY2VudCI6MC4yNTI2NzkwMDcwMDM2NzQyfQ==.RGhswbjyDU6o266pLNQGIlPw2Y8mDLztp4TGXAWMlcQ=; flow_ssr_sidebar_expand=1; s_v_web_id=verify_mind1jr9_VbD7t7qj_uikr_43bO_8n0b_zX2SCB6AdbOM; passport_csrf_token=32bfed5a75c5103dc3733669211dde21; passport_csrf_token_default=32bfed5a75c5103dc3733669211dde21; odin_tt=7f7bdbabac871052b33748b916fe0d9eeaee8805d718e032dc76eb3331e69248d07b38c4a514cd3ec554bfd56a88948f8d4623af808c742c18c899d4244d4d78; n_mh=8tLRwoSVKOVBgyNwo1ffSlHLPFA-RVJfi6Mvd4NuGM0; sid_guard=3a913278b150423698eda5269004a756%7C1764606680%7C2592000%7CWed%2C+31-Dec-2025+16%3A31%3A20+GMT; uid_tt=d6dad1722ee632bb79eabc2375f0ca93; uid_tt_ss=d6dad1722ee632bb79eabc2375f0ca93; sid_tt=3a913278b150423698eda5269004a756; sessionid=3a913278b150423698eda5269004a756; sessionid_ss=3a913278b150423698eda5269004a756; session_tlb_tag=sttt%7C16%7COpEyeLFQQjaY7aUmkASnVv________-1-1P7zRCjTPIPFv_pUUIHMZVYQsjlpXZD0w_8D__sD7Y%3D; session_tlb_tag_bk=sttt%7C16%7COpEyeLFQQjaY7aUmkASnVv________-1-1P7zRCjTPIPFv_pUUIHMZVYQsjlpXZD0w_8D__sD7Y%3D; is_staff_user=false; sid_ucp_v1=1.0.0-KGMzY2FkMTZkNzkyYjAxZDY3OTFlM2Q0MzYzY2UzNzE0OWRkMDhiZDUKIAiLzpCpts3aAxDYhbfJBhjCsR4gDDCqpdmtBjgHQPQHGgJobCIgM2E5MTMyNzhiMTUwNDIzNjk4ZWRhNTI2OTAwNGE3NTY; ssid_ucp_v1=1.0.0-KGMzY2FkMTZkNzkyYjAxZDY3OTFlM2Q0MzYzY2UzNzE0OWRkMDhiZDUKIAiLzpCpts3aAxDYhbfJBhjCsR4gDDCqpdmtBjgHQPQHGgJobCIgM2E5MTMyNzhiMTUwNDIzNjk4ZWRhNTI2OTAwNGE3NTY; user_language_code=zh; i18next=zh; ttwid=1%7CUWvZl8BhJG24HsPYG-AMSH425URWFERYswXeSG5o7JA%7C1764606681%7Cc06758733f55438250cd0bcd4655efd74e6c85bbaec7b29a4e0993cd7dac2c8f; passport_fe_beating_status=true; msToken=uMPTw6l1KqvcVnqkxi14Lxk9_77FWhrXrKQZEraq2s9cX4hnMNVW-JkviHvPlEgeh2qEDosTsKmlapgsIKH6SEC6PVrpWl1uzRR1hBBGvcSTL_bnIyoAE2fiD0SmV_QBIrHtxQhAeE8-; _ga_G8EP5CG8VZ=GS2.1.s1764606227$o73$g1$t1764606691$j51$l0$h0; tt_scid=t6ZrzYZXXDmFGF8bsZTLqjggfDFJ0m4ulDTSSZExH52G.8iucd7OjH.2oqhCdHPx0472
tts-server.py CHANGED
@@ -8,6 +8,7 @@ import time
8
  import glob
9
  from typing import Optional, List, Dict, Any, AsyncGenerator
10
 
 
11
  import aiohttp
12
  import websockets
13
  from fastapi import FastAPI, HTTPException, Header, Request, BackgroundTasks
@@ -32,6 +33,7 @@ PORT = 1547
32
  HOST = "0.0.0.0"
33
  MODELS_FILE = "models.json"
34
  COOKIE_DIR = "cookie" # Directory to store cookie txt files
 
35
 
36
  # Initialize FastAPI
37
  @asynccontextmanager
@@ -110,6 +112,11 @@ class CookieManager:
110
  if self.failure_count >= 2:
111
  self._rotate()
112
 
 
 
 
 
 
113
  def report_success(self):
114
  """Reset failure count on success."""
115
  if self.failure_count > 0:
@@ -239,6 +246,13 @@ class DoubaoTTS:
239
  async def stream_audio(self, text: str, voice: str, speed: float = 1.0, pitch: float = 1.0) -> AsyncGenerator[bytes, None]:
240
  """Connect to WebSocket and yield audio chunks with retry logic."""
241
 
 
 
 
 
 
 
 
242
  # Map OpenAI speed (0.25 - 4.0) to Doubao rate (-100 to 100)
243
  doubao_rate = int((speed - 1) * 100)
244
  doubao_rate = max(-100, min(100, doubao_rate))
@@ -275,19 +289,56 @@ class DoubaoTTS:
275
 
276
  first_chunk_received = False
277
 
278
- async for message in ws:
279
- if isinstance(message, bytes):
280
- if not first_chunk_received:
281
- first_chunk_received = True
282
- self.cookie_manager.report_success() # Mark cookie as good
283
- yield message
284
- elif isinstance(message, str):
285
- # Check for error messages in text frames
286
- # Sometimes errors come as text JSON
287
- pass
288
 
289
- # If we finished the loop naturally, break retry loop
290
- return
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
291
 
292
  except websockets.exceptions.InvalidStatusCode as e:
293
  logger.warning(f"WebSocket Handshake failed (Attempt {attempt+1}): {e.status_code}")
 
8
  import glob
9
  from typing import Optional, List, Dict, Any, AsyncGenerator
10
 
11
+ import aiofiles
12
  import aiohttp
13
  import websockets
14
  from fastapi import FastAPI, HTTPException, Header, Request, BackgroundTasks
 
33
  HOST = "0.0.0.0"
34
  MODELS_FILE = "models.json"
35
  COOKIE_DIR = "cookie" # Directory to store cookie txt files
36
+ AUDIO_DIR = "saved_audio"
37
 
38
  # Initialize FastAPI
39
  @asynccontextmanager
 
112
  if self.failure_count >= 2:
113
  self._rotate()
114
 
115
+ def force_rotate(self):
116
+ """Force switch to the next cookie (e.g. when blocked)."""
117
+ logger.warning(f"Cookie index {self.current_index} blocked/rate-limited. Forcing rotation.")
118
+ self._rotate()
119
+
120
  def report_success(self):
121
  """Reset failure count on success."""
122
  if self.failure_count > 0:
 
246
  async def stream_audio(self, text: str, voice: str, speed: float = 1.0, pitch: float = 1.0) -> AsyncGenerator[bytes, None]:
247
  """Connect to WebSocket and yield audio chunks with retry logic."""
248
 
249
+ # Ensure audio directory exists
250
+ if not os.path.exists(AUDIO_DIR):
251
+ try:
252
+ os.makedirs(AUDIO_DIR, exist_ok=True)
253
+ except Exception as e:
254
+ logger.error(f"Failed to create audio directory: {e}")
255
+
256
  # Map OpenAI speed (0.25 - 4.0) to Doubao rate (-100 to 100)
257
  doubao_rate = int((speed - 1) * 100)
258
  doubao_rate = max(-100, min(100, doubao_rate))
 
289
 
290
  first_chunk_received = False
291
 
292
+ # Prepare file for saving this attempt
293
+ timestamp = int(time.time() * 1000)
294
+ filename = f"{timestamp}_{voice}.aac"
295
+ filepath = os.path.join(AUDIO_DIR, filename)
 
 
 
 
 
 
296
 
297
+ file_written = False
298
+ try:
299
+ async with aiofiles.open(filepath, "wb") as f_out:
300
+ async for message in ws:
301
+ if isinstance(message, bytes):
302
+ if not first_chunk_received:
303
+ first_chunk_received = True
304
+ self.cookie_manager.report_success() # Mark cookie as good
305
+ logger.info(f"Streaming and saving audio to: {filepath}")
306
+
307
+ yield message
308
+ await f_out.write(message)
309
+ file_written = True
310
+
311
+ elif isinstance(message, str):
312
+ # Check for error messages in text frames
313
+ try:
314
+ msg_json = json.loads(message)
315
+ code = msg_json.get("code")
316
+ if code and code != 0:
317
+ error_msg = msg_json.get("message", "Unknown Error")
318
+ logger.error(f"Doubao API Error (Code {code}): {error_msg}")
319
+
320
+ # If blocked or auth failed, report failure to rotate cookie
321
+ if "block" in str(error_msg).lower() or code == 710022002:
322
+ self.cookie_manager.force_rotate() # Force rotate immediately on block
323
+ raise Exception(f"Blocked by server (Risk Control): {error_msg}")
324
+ except json.JSONDecodeError:
325
+ pass
326
+
327
+ # If we finished the loop naturally, break retry loop
328
+ if file_written:
329
+ return
330
+ else:
331
+ # If no bytes were written, something went wrong but no exception was raised
332
+ logger.warning("Connection closed without receiving audio data.")
333
+
334
+ finally:
335
+ # Cleanup empty files if failed
336
+ if not file_written and os.path.exists(filepath):
337
+ try:
338
+ os.remove(filepath)
339
+ logger.info(f"Removed empty audio file: {filepath}")
340
+ except Exception as e:
341
+ logger.error(f"Failed to remove empty file: {e}")
342
 
343
  except websockets.exceptions.InvalidStatusCode as e:
344
  logger.warning(f"WebSocket Handshake failed (Attempt {attempt+1}): {e.status_code}")