Spaces:
Sleeping
Sleeping
Commit ·
8721de0
1
Parent(s): 0d8df70
Add comprehensive WebSocket testing suite and documentation
Browse files- TEST_WEBSOCKET.md +236 -0
- test_websocket.py +247 -0
TEST_WEBSOCKET.md
ADDED
|
@@ -0,0 +1,236 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# WebSocket Testing Guide
|
| 2 |
+
|
| 3 |
+
## Quick Test Methods
|
| 4 |
+
|
| 5 |
+
### 1. **Python Test Suite** (Recommended)
|
| 6 |
+
```bash
|
| 7 |
+
python3 test_websocket.py
|
| 8 |
+
```
|
| 9 |
+
|
| 10 |
+
Tests:
|
| 11 |
+
- ✅ Connection establishment
|
| 12 |
+
- ✅ Request/response handling
|
| 13 |
+
- ✅ Latency measurement
|
| 14 |
+
- ✅ Audio streaming and saving
|
| 15 |
+
|
| 16 |
+
---
|
| 17 |
+
|
| 18 |
+
### 2. **Command Line with wscat**
|
| 19 |
+
|
| 20 |
+
Install wscat:
|
| 21 |
+
```bash
|
| 22 |
+
npm install -g wscat
|
| 23 |
+
```
|
| 24 |
+
|
| 25 |
+
Test connection:
|
| 26 |
+
```bash
|
| 27 |
+
wscat -c wss://ebitlogix-parler-tts-api.hf.space/ws/tts
|
| 28 |
+
```
|
| 29 |
+
|
| 30 |
+
Then send JSON:
|
| 31 |
+
```json
|
| 32 |
+
{"text":"سلام دنیا","speaker":"Divya"}
|
| 33 |
+
```
|
| 34 |
+
|
| 35 |
+
You should see:
|
| 36 |
+
```
|
| 37 |
+
> {"text":"سلام دنیا","speaker":"Divya"}
|
| 38 |
+
< {"status":"generating","message":"Generating audio for speaker Divya..."}
|
| 39 |
+
< [binary audio chunk 1]
|
| 40 |
+
< [binary audio chunk 2]
|
| 41 |
+
< [binary audio chunk 3]
|
| 42 |
+
< {"status":"complete","chunks_sent":5}
|
| 43 |
+
```
|
| 44 |
+
|
| 45 |
+
---
|
| 46 |
+
|
| 47 |
+
### 3. **JavaScript/Node Test**
|
| 48 |
+
|
| 49 |
+
```javascript
|
| 50 |
+
const WebSocket = require('ws');
|
| 51 |
+
|
| 52 |
+
const ws = new WebSocket('wss://ebitlogix-parler-tts-api.hf.space/ws/tts');
|
| 53 |
+
|
| 54 |
+
ws.on('open', () => {
|
| 55 |
+
console.log('✅ Connected');
|
| 56 |
+
|
| 57 |
+
ws.send(JSON.stringify({
|
| 58 |
+
text: 'سلام دنیا',
|
| 59 |
+
speaker: 'Divya'
|
| 60 |
+
}));
|
| 61 |
+
});
|
| 62 |
+
|
| 63 |
+
ws.on('message', (data) => {
|
| 64 |
+
if (typeof data === 'string') {
|
| 65 |
+
console.log('📨 Status:', JSON.parse(data));
|
| 66 |
+
} else {
|
| 67 |
+
console.log('🔊 Audio chunk:', data.length, 'bytes');
|
| 68 |
+
}
|
| 69 |
+
});
|
| 70 |
+
|
| 71 |
+
ws.on('error', (err) => {
|
| 72 |
+
console.error('❌ Error:', err);
|
| 73 |
+
});
|
| 74 |
+
```
|
| 75 |
+
|
| 76 |
+
---
|
| 77 |
+
|
| 78 |
+
### 4. **Curl Test** (for connection only)
|
| 79 |
+
```bash
|
| 80 |
+
curl -i -N -H "Connection: Upgrade" -H "Upgrade: websocket" \
|
| 81 |
+
-H "Sec-WebSocket-Key: SGVsbG8sIHdvcmxkIQ==" \
|
| 82 |
+
-H "Sec-WebSocket-Version: 13" \
|
| 83 |
+
https://ebitlogix-parler-tts-api.hf.space/ws/tts
|
| 84 |
+
```
|
| 85 |
+
|
| 86 |
+
---
|
| 87 |
+
|
| 88 |
+
## Expected Responses
|
| 89 |
+
|
| 90 |
+
### Success Response Flow:
|
| 91 |
+
```
|
| 92 |
+
1. Connection accepted (WebSocket upgrade)
|
| 93 |
+
↓
|
| 94 |
+
2. Status message: {"status":"generating",...}
|
| 95 |
+
↓
|
| 96 |
+
3. Audio chunks (binary data) arriving in real-time
|
| 97 |
+
↓
|
| 98 |
+
4. Complete message: {"status":"complete","chunks_sent":N}
|
| 99 |
+
↓
|
| 100 |
+
5. Connection closes
|
| 101 |
+
```
|
| 102 |
+
|
| 103 |
+
### Error Response:
|
| 104 |
+
```json
|
| 105 |
+
{"error": "Text cannot be empty"}
|
| 106 |
+
```
|
| 107 |
+
|
| 108 |
+
---
|
| 109 |
+
|
| 110 |
+
## What to Check
|
| 111 |
+
|
| 112 |
+
✅ **Connection**
|
| 113 |
+
- [ ] WebSocket handshake completes (101 Switching Protocols)
|
| 114 |
+
- [ ] No connection refused or timeout errors
|
| 115 |
+
|
| 116 |
+
✅ **Response Time**
|
| 117 |
+
- [ ] Status message within 1-2 seconds
|
| 118 |
+
- [ ] First audio chunk within 3-5 seconds
|
| 119 |
+
- [ ] Audio chunks every 0.5 seconds
|
| 120 |
+
|
| 121 |
+
✅ **Data**
|
| 122 |
+
- [ ] Receives JSON status messages (parseable)
|
| 123 |
+
- [ ] Receives binary audio chunks (non-zero size)
|
| 124 |
+
- [ ] All chunks have data (not empty)
|
| 125 |
+
|
| 126 |
+
✅ **Completion**
|
| 127 |
+
- [ ] Complete message sent
|
| 128 |
+
- [ ] Connection closes gracefully
|
| 129 |
+
- [ ] No hanging connections
|
| 130 |
+
|
| 131 |
+
---
|
| 132 |
+
|
| 133 |
+
## Troubleshooting
|
| 134 |
+
|
| 135 |
+
### Connection Refused
|
| 136 |
+
```
|
| 137 |
+
❌ Error: Connection refused
|
| 138 |
+
```
|
| 139 |
+
→ Space might be rebuilding. Check: https://huggingface.co/spaces/ebitlogix/Parler_TTS_API
|
| 140 |
+
|
| 141 |
+
### Timeout
|
| 142 |
+
```
|
| 143 |
+
❌ timeout waiting for response
|
| 144 |
+
```
|
| 145 |
+
→ Model might be loading. Wait 30 seconds and try again.
|
| 146 |
+
|
| 147 |
+
### No Audio Chunks
|
| 148 |
+
```
|
| 149 |
+
❌ Only status message, no audio chunks
|
| 150 |
+
```
|
| 151 |
+
→ Check text is not empty. Try different text.
|
| 152 |
+
|
| 153 |
+
### Wrong Protocol
|
| 154 |
+
```
|
| 155 |
+
❌ WebSocket is not supported
|
| 156 |
+
```
|
| 157 |
+
→ Make sure you're using `wss://` (secure) or `ws://` (local), not `https://` or `http://`
|
| 158 |
+
|
| 159 |
+
---
|
| 160 |
+
|
| 161 |
+
## Test Matrix
|
| 162 |
+
|
| 163 |
+
| Test | URL | Expected | Status |
|
| 164 |
+
|------|-----|----------|--------|
|
| 165 |
+
| Connection | `wss://...` | 101 Upgrade | ✅ |
|
| 166 |
+
| Send JSON | `{"text":"..."}` | Status message | ✅ |
|
| 167 |
+
| Audio Stream | Binary chunks | Every 0.5s | ✅ |
|
| 168 |
+
| Latency | Complete flow | <10 seconds | ✅ |
|
| 169 |
+
|
| 170 |
+
---
|
| 171 |
+
|
| 172 |
+
## Live Testing
|
| 173 |
+
|
| 174 |
+
### Test 1: Simple Message
|
| 175 |
+
```json
|
| 176 |
+
{"text":"سلام دنیا","speaker":"Divya"}
|
| 177 |
+
```
|
| 178 |
+
Expected: Audio generated in ~4-5 seconds
|
| 179 |
+
|
| 180 |
+
### Test 2: Longer Text
|
| 181 |
+
```json
|
| 182 |
+
{"text":"مرحبا بك في تطبيق تحويل النص إلى كلام","speaker":"Rani"}
|
| 183 |
+
```
|
| 184 |
+
Expected: More chunks, ~8-10 seconds total
|
| 185 |
+
|
| 186 |
+
### Test 3: Different Speaker
|
| 187 |
+
```json
|
| 188 |
+
{"text":"یہ ایک اردو متن ہے","speaker":"Generic Female"}
|
| 189 |
+
```
|
| 190 |
+
Expected: Same timing, different voice
|
| 191 |
+
|
| 192 |
+
---
|
| 193 |
+
|
| 194 |
+
## Pipecat Integration Testing
|
| 195 |
+
|
| 196 |
+
```python
|
| 197 |
+
# test_pipecat_integration.py
|
| 198 |
+
import asyncio
|
| 199 |
+
import websockets
|
| 200 |
+
import json
|
| 201 |
+
|
| 202 |
+
async def test_pipecat():
|
| 203 |
+
async with websockets.connect('wss://ebitlogix-parler-tts-api.hf.space/ws/tts') as ws:
|
| 204 |
+
# Simulate Pipecat sending TTS request
|
| 205 |
+
await ws.send(json.dumps({
|
| 206 |
+
"text": "سلام دنیا",
|
| 207 |
+
"speaker": "Divya"
|
| 208 |
+
}))
|
| 209 |
+
|
| 210 |
+
# Simulate receiving audio for playback
|
| 211 |
+
while True:
|
| 212 |
+
msg = await ws.recv()
|
| 213 |
+
if isinstance(msg, bytes):
|
| 214 |
+
print(f"Got audio: {len(msg)} bytes - READY FOR PIPECAT")
|
| 215 |
+
elif isinstance(msg, str):
|
| 216 |
+
data = json.loads(msg)
|
| 217 |
+
if data.get("status") == "complete":
|
| 218 |
+
print("Audio stream complete")
|
| 219 |
+
break
|
| 220 |
+
|
| 221 |
+
asyncio.run(test_pipecat())
|
| 222 |
+
```
|
| 223 |
+
|
| 224 |
+
---
|
| 225 |
+
|
| 226 |
+
## Success Indicators
|
| 227 |
+
|
| 228 |
+
✅ All these should be true:
|
| 229 |
+
- Connection establishes without error
|
| 230 |
+
- Receives status message within 2 seconds
|
| 231 |
+
- First audio chunk arrives within 5 seconds
|
| 232 |
+
- Subsequent chunks arrive continuously every 0.5-1 second
|
| 233 |
+
- Complete message received
|
| 234 |
+
- Connection closes normally
|
| 235 |
+
|
| 236 |
+
If any fail, check Space status and rebuild if needed.
|
test_websocket.py
ADDED
|
@@ -0,0 +1,247 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""
|
| 3 |
+
WebSocket TTS API Test Script
|
| 4 |
+
Tests connection, latency, and audio streaming
|
| 5 |
+
"""
|
| 6 |
+
import asyncio
|
| 7 |
+
import websockets
|
| 8 |
+
import json
|
| 9 |
+
import time
|
| 10 |
+
from pathlib import Path
|
| 11 |
+
|
| 12 |
+
# Configuration
|
| 13 |
+
WS_URL = "wss://ebitlogix-parler-tts-api.hf.space/ws/tts"
|
| 14 |
+
# For local testing: WS_URL = "ws://localhost:7860/ws/tts"
|
| 15 |
+
|
| 16 |
+
OUTPUT_DIR = Path("tts_output")
|
| 17 |
+
OUTPUT_DIR.mkdir(exist_ok=True)
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
async def test_websocket_connection():
|
| 21 |
+
"""Test basic WebSocket connection"""
|
| 22 |
+
print("\n" + "="*60)
|
| 23 |
+
print("Testing WebSocket Connection")
|
| 24 |
+
print("="*60)
|
| 25 |
+
|
| 26 |
+
try:
|
| 27 |
+
async with websockets.connect(WS_URL) as websocket:
|
| 28 |
+
print("✅ Connected to WebSocket server")
|
| 29 |
+
print(f" URL: {WS_URL}")
|
| 30 |
+
print(f" Connection state: {websocket.state}")
|
| 31 |
+
return True
|
| 32 |
+
except Exception as e:
|
| 33 |
+
print(f"❌ Failed to connect: {e}")
|
| 34 |
+
return False
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
async def test_websocket_response():
|
| 38 |
+
"""Test WebSocket request/response"""
|
| 39 |
+
print("\n" + "="*60)
|
| 40 |
+
print("Testing WebSocket Request/Response")
|
| 41 |
+
print("="*60)
|
| 42 |
+
|
| 43 |
+
try:
|
| 44 |
+
async with websockets.connect(WS_URL) as websocket:
|
| 45 |
+
# Send request
|
| 46 |
+
request = {
|
| 47 |
+
"text": "سلام دنیا",
|
| 48 |
+
"speaker": "Divya",
|
| 49 |
+
"pitch": "Moderate",
|
| 50 |
+
"rate": "Moderate"
|
| 51 |
+
}
|
| 52 |
+
|
| 53 |
+
print(f"\nSending request:")
|
| 54 |
+
print(f" Text: {request['text']}")
|
| 55 |
+
print(f" Speaker: {request['speaker']}")
|
| 56 |
+
|
| 57 |
+
await websocket.send(json.dumps(request))
|
| 58 |
+
print("✅ Request sent")
|
| 59 |
+
|
| 60 |
+
# Receive responses
|
| 61 |
+
responses = []
|
| 62 |
+
while True:
|
| 63 |
+
try:
|
| 64 |
+
message = await asyncio.wait_for(websocket.recv(), timeout=60)
|
| 65 |
+
|
| 66 |
+
if isinstance(message, str):
|
| 67 |
+
data = json.loads(message)
|
| 68 |
+
print(f"\n📨 Status Message: {data}")
|
| 69 |
+
responses.append(data)
|
| 70 |
+
|
| 71 |
+
if data.get("status") == "complete":
|
| 72 |
+
print(f"✅ Generation complete! Received {data['chunks_sent']} chunks")
|
| 73 |
+
break
|
| 74 |
+
else:
|
| 75 |
+
# Binary audio data
|
| 76 |
+
print(f"🔊 Audio chunk received: {len(message)} bytes")
|
| 77 |
+
responses.append({"type": "audio", "size": len(message)})
|
| 78 |
+
|
| 79 |
+
except asyncio.TimeoutError:
|
| 80 |
+
print("❌ Timeout waiting for response")
|
| 81 |
+
break
|
| 82 |
+
|
| 83 |
+
return len(responses) > 0
|
| 84 |
+
|
| 85 |
+
except Exception as e:
|
| 86 |
+
print(f"❌ Error: {e}")
|
| 87 |
+
import traceback
|
| 88 |
+
traceback.print_exc()
|
| 89 |
+
return False
|
| 90 |
+
|
| 91 |
+
|
| 92 |
+
async def test_websocket_latency():
|
| 93 |
+
"""Test WebSocket latency and streaming speed"""
|
| 94 |
+
print("\n" + "="*60)
|
| 95 |
+
print("Testing WebSocket Latency & Streaming Speed")
|
| 96 |
+
print("="*60)
|
| 97 |
+
|
| 98 |
+
try:
|
| 99 |
+
start_time = time.time()
|
| 100 |
+
|
| 101 |
+
async with websockets.connect(WS_URL) as websocket:
|
| 102 |
+
connection_time = time.time() - start_time
|
| 103 |
+
print(f"\n✅ Connection established in {connection_time*1000:.1f}ms")
|
| 104 |
+
|
| 105 |
+
# Send request
|
| 106 |
+
request = {
|
| 107 |
+
"text": "یہ ایک ٹیسٹ ہے",
|
| 108 |
+
"speaker": "Rani"
|
| 109 |
+
}
|
| 110 |
+
|
| 111 |
+
send_time = time.time()
|
| 112 |
+
await websocket.send(json.dumps(request))
|
| 113 |
+
print(f"✅ Request sent in {(time.time()-send_time)*1000:.1f}ms")
|
| 114 |
+
|
| 115 |
+
# Track first chunk time
|
| 116 |
+
first_chunk_time = None
|
| 117 |
+
total_audio_size = 0
|
| 118 |
+
chunk_count = 0
|
| 119 |
+
|
| 120 |
+
while True:
|
| 121 |
+
message = await asyncio.wait_for(websocket.recv(), timeout=60)
|
| 122 |
+
|
| 123 |
+
if isinstance(message, str):
|
| 124 |
+
data = json.loads(message)
|
| 125 |
+
if data.get("status") == "generating":
|
| 126 |
+
print(f"📊 Status: {data.get('message')}")
|
| 127 |
+
elif data.get("status") == "complete":
|
| 128 |
+
total_time = time.time() - start_time
|
| 129 |
+
print(f"\n✅ Complete!")
|
| 130 |
+
print(f" Total time: {total_time:.2f}s")
|
| 131 |
+
print(f" First chunk: {first_chunk_time*1000:.1f}ms")
|
| 132 |
+
print(f" Total chunks: {chunk_count}")
|
| 133 |
+
print(f" Total audio size: {total_audio_size/1024:.1f} KB")
|
| 134 |
+
if chunk_count > 0:
|
| 135 |
+
print(f" Avg chunk size: {total_audio_size/chunk_count:.0f} bytes")
|
| 136 |
+
break
|
| 137 |
+
else:
|
| 138 |
+
# Audio chunk
|
| 139 |
+
if first_chunk_time is None:
|
| 140 |
+
first_chunk_time = time.time() - start_time
|
| 141 |
+
print(f"\n🔊 First audio chunk received in {first_chunk_time*1000:.1f}ms")
|
| 142 |
+
|
| 143 |
+
total_audio_size += len(message)
|
| 144 |
+
chunk_count += 1
|
| 145 |
+
print(f" Chunk {chunk_count}: {len(message)} bytes")
|
| 146 |
+
|
| 147 |
+
return True
|
| 148 |
+
|
| 149 |
+
except Exception as e:
|
| 150 |
+
print(f"❌ Error: {e}")
|
| 151 |
+
import traceback
|
| 152 |
+
traceback.print_exc()
|
| 153 |
+
return False
|
| 154 |
+
|
| 155 |
+
|
| 156 |
+
async def test_websocket_streaming_save():
|
| 157 |
+
"""Test WebSocket streaming and save audio"""
|
| 158 |
+
print("\n" + "="*60)
|
| 159 |
+
print("Testing WebSocket Streaming & Audio Save")
|
| 160 |
+
print("="*60)
|
| 161 |
+
|
| 162 |
+
try:
|
| 163 |
+
async with websockets.connect(WS_URL) as websocket:
|
| 164 |
+
request = {
|
| 165 |
+
"text": "مرحبا، یہ ایک WebSocket ٹیسٹ ہے",
|
| 166 |
+
"speaker": "Generic Female"
|
| 167 |
+
}
|
| 168 |
+
|
| 169 |
+
print(f"\nSending: {request['text']}")
|
| 170 |
+
await websocket.send(json.dumps(request))
|
| 171 |
+
|
| 172 |
+
# Collect all audio chunks
|
| 173 |
+
audio_chunks = []
|
| 174 |
+
chunk_count = 0
|
| 175 |
+
|
| 176 |
+
while True:
|
| 177 |
+
message = await asyncio.wait_for(websocket.recv(), timeout=60)
|
| 178 |
+
|
| 179 |
+
if isinstance(message, str):
|
| 180 |
+
data = json.loads(message)
|
| 181 |
+
print(f"Status: {data}")
|
| 182 |
+
|
| 183 |
+
if data.get("status") == "complete":
|
| 184 |
+
break
|
| 185 |
+
else:
|
| 186 |
+
# Audio chunk
|
| 187 |
+
audio_chunks.append(message)
|
| 188 |
+
chunk_count += 1
|
| 189 |
+
print(f"Received chunk {chunk_count}: {len(message)} bytes")
|
| 190 |
+
|
| 191 |
+
# Save combined audio
|
| 192 |
+
if audio_chunks:
|
| 193 |
+
combined_audio = b"".join(audio_chunks)
|
| 194 |
+
timestamp = time.strftime("%Y%m%d_%H%M%S")
|
| 195 |
+
filename = OUTPUT_DIR / f"websocket_test_{timestamp}.wav"
|
| 196 |
+
|
| 197 |
+
with open(filename, "wb") as f:
|
| 198 |
+
f.write(combined_audio)
|
| 199 |
+
|
| 200 |
+
print(f"\n✅ Audio saved: {filename}")
|
| 201 |
+
print(f" Total size: {len(combined_audio)/1024:.1f} KB")
|
| 202 |
+
return True
|
| 203 |
+
|
| 204 |
+
except Exception as e:
|
| 205 |
+
print(f"❌ Error: {e}")
|
| 206 |
+
import traceback
|
| 207 |
+
traceback.print_exc()
|
| 208 |
+
return False
|
| 209 |
+
|
| 210 |
+
|
| 211 |
+
async def main():
|
| 212 |
+
"""Run all tests"""
|
| 213 |
+
print("\n" + "█"*60)
|
| 214 |
+
print("█ WebSocket TTS API Test Suite")
|
| 215 |
+
print("█"*60)
|
| 216 |
+
|
| 217 |
+
# Run tests
|
| 218 |
+
tests = [
|
| 219 |
+
("Connection Test", test_websocket_connection()),
|
| 220 |
+
("Request/Response Test", test_websocket_response()),
|
| 221 |
+
("Latency Test", test_websocket_latency()),
|
| 222 |
+
("Streaming & Save Test", test_websocket_streaming_save()),
|
| 223 |
+
]
|
| 224 |
+
|
| 225 |
+
results = {}
|
| 226 |
+
for test_name, test_coro in tests:
|
| 227 |
+
try:
|
| 228 |
+
results[test_name] = await test_coro
|
| 229 |
+
except Exception as e:
|
| 230 |
+
print(f"\n❌ {test_name} failed: {e}")
|
| 231 |
+
results[test_name] = False
|
| 232 |
+
|
| 233 |
+
# Summary
|
| 234 |
+
print("\n" + "="*60)
|
| 235 |
+
print("Test Summary")
|
| 236 |
+
print("="*60)
|
| 237 |
+
for test_name, result in results.items():
|
| 238 |
+
status = "✅ PASSED" if result else "❌ FAILED"
|
| 239 |
+
print(f"{status} - {test_name}")
|
| 240 |
+
|
| 241 |
+
passed = sum(1 for r in results.values() if r)
|
| 242 |
+
print(f"\nTotal: {passed}/{len(results)} tests passed")
|
| 243 |
+
print("="*60 + "\n")
|
| 244 |
+
|
| 245 |
+
|
| 246 |
+
if __name__ == "__main__":
|
| 247 |
+
asyncio.run(main())
|