Upload 6 files
Browse files- requirements.txt +2 -1
- tts-server.py +25 -16
requirements.txt
CHANGED
|
@@ -2,4 +2,5 @@ fastapi
|
|
| 2 |
uvicorn
|
| 3 |
aiohttp
|
| 4 |
websockets==10.4
|
| 5 |
-
pydantic
|
|
|
|
|
|
| 2 |
uvicorn
|
| 3 |
aiohttp
|
| 4 |
websockets==10.4
|
| 5 |
+
pydantic
|
| 6 |
+
aiofiles
|
tts-server.py
CHANGED
|
@@ -6,6 +6,7 @@ import os
|
|
| 6 |
import logging
|
| 7 |
import time
|
| 8 |
import glob
|
|
|
|
| 9 |
from typing import Optional, List, Dict, Any, AsyncGenerator
|
| 10 |
|
| 11 |
import aiofiles
|
|
@@ -142,23 +143,22 @@ class DoubaoTTS:
|
|
| 142 |
self.voice_url = "https://www.doubao.com/alice/user_voice/recommend"
|
| 143 |
self.voices = []
|
| 144 |
self.req_count = 0
|
| 145 |
-
self.current_id = self._generate_id()
|
| 146 |
self.cookie_manager = CookieManager(COOKIE_DIR)
|
| 147 |
|
| 148 |
-
def
|
| 149 |
-
"""Generate a
|
| 150 |
-
|
| 151 |
-
|
| 152 |
-
|
| 153 |
-
|
| 154 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 155 |
"""Generate the common query parameters for requests."""
|
| 156 |
-
self.
|
| 157 |
-
if self.req_count > 5:
|
| 158 |
-
self.req_count = 0
|
| 159 |
-
self.current_id = self._generate_id()
|
| 160 |
-
|
| 161 |
-
cid = self.current_id
|
| 162 |
return (
|
| 163 |
f"&mode=0&language=zh&browser_language=zh-CN&device_platform=web"
|
| 164 |
f"&aid=586861&real_aid=586861&pkg_type=release_version"
|
|
@@ -192,7 +192,7 @@ class DoubaoTTS:
|
|
| 192 |
|
| 193 |
async with aiohttp.ClientSession() as session:
|
| 194 |
for rec_type, tab_key in tabs:
|
| 195 |
-
params = self._get_common_params().strip('&')
|
| 196 |
url = f"{self.voice_url}?{params}"
|
| 197 |
|
| 198 |
payload = {
|
|
@@ -267,7 +267,7 @@ class DoubaoTTS:
|
|
| 267 |
yield b"" # End stream
|
| 268 |
return
|
| 269 |
|
| 270 |
-
params = self._get_common_params()
|
| 271 |
ws_url = f"{self.ws_url}?format=aac&speaker={voice}&speech_rate={doubao_rate}&pitch={doubao_pitch}{params}"
|
| 272 |
|
| 273 |
headers = {
|
|
@@ -289,6 +289,15 @@ class DoubaoTTS:
|
|
| 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"
|
|
|
|
| 6 |
import logging
|
| 7 |
import time
|
| 8 |
import glob
|
| 9 |
+
import hashlib
|
| 10 |
from typing import Optional, List, Dict, Any, AsyncGenerator
|
| 11 |
|
| 12 |
import aiofiles
|
|
|
|
| 143 |
self.voice_url = "https://www.doubao.com/alice/user_voice/recommend"
|
| 144 |
self.voices = []
|
| 145 |
self.req_count = 0
|
| 146 |
+
# self.current_id = self._generate_id() # Removed global ID
|
| 147 |
self.cookie_manager = CookieManager(COOKIE_DIR)
|
| 148 |
|
| 149 |
+
def _get_device_id(self, cookie: str) -> str:
|
| 150 |
+
"""Generate a deterministic device ID based on the cookie."""
|
| 151 |
+
# Use MD5 of cookie to get a consistent hash
|
| 152 |
+
hash_object = hashlib.md5(cookie.encode())
|
| 153 |
+
hex_dig = hash_object.hexdigest()
|
| 154 |
+
# Convert first 15 chars of hex to int to get a large number
|
| 155 |
+
num_val = int(hex_dig[:15], 16)
|
| 156 |
+
# Ensure it's 19 digits long (similar to original random ID)
|
| 157 |
+
return str(num_val).zfill(19)[:19]
|
| 158 |
+
|
| 159 |
+
def _get_common_params(self, cookie: str) -> str:
|
| 160 |
"""Generate the common query parameters for requests."""
|
| 161 |
+
cid = self._get_device_id(cookie)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 162 |
return (
|
| 163 |
f"&mode=0&language=zh&browser_language=zh-CN&device_platform=web"
|
| 164 |
f"&aid=586861&real_aid=586861&pkg_type=release_version"
|
|
|
|
| 192 |
|
| 193 |
async with aiohttp.ClientSession() as session:
|
| 194 |
for rec_type, tab_key in tabs:
|
| 195 |
+
params = self._get_common_params(cookie).strip('&')
|
| 196 |
url = f"{self.voice_url}?{params}"
|
| 197 |
|
| 198 |
payload = {
|
|
|
|
| 267 |
yield b"" # End stream
|
| 268 |
return
|
| 269 |
|
| 270 |
+
params = self._get_common_params(cookie)
|
| 271 |
ws_url = f"{self.ws_url}?format=aac&speaker={voice}&speech_rate={doubao_rate}&pitch={doubao_pitch}{params}"
|
| 272 |
|
| 273 |
headers = {
|
|
|
|
| 289 |
|
| 290 |
first_chunk_received = False
|
| 291 |
|
| 292 |
+
# Clean up old files (Keep only the latest)
|
| 293 |
+
try:
|
| 294 |
+
for f in os.listdir(AUDIO_DIR):
|
| 295 |
+
f_path = os.path.join(AUDIO_DIR, f)
|
| 296 |
+
if os.path.isfile(f_path):
|
| 297 |
+
os.remove(f_path)
|
| 298 |
+
except Exception as e:
|
| 299 |
+
logger.warning(f"Failed to clean up old audio files: {e}")
|
| 300 |
+
|
| 301 |
# Prepare file for saving this attempt
|
| 302 |
timestamp = int(time.time() * 1000)
|
| 303 |
filename = f"{timestamp}_{voice}.aac"
|