sixtydb-python 1.0.5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
sixtydb/__init__.py
ADDED
sixtydb/client.py
ADDED
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
"""
|
|
2
|
+
60db API Client
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import requests
|
|
6
|
+
from typing import Optional, Dict, Any, List, Callable
|
|
7
|
+
import json
|
|
8
|
+
import base64
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class SixtyDBClient:
|
|
12
|
+
"""Official 60db API client"""
|
|
13
|
+
|
|
14
|
+
def __init__(self, api_key: str, base_url: str = "https://api-dev.qcall.ai/tts"):
|
|
15
|
+
"""
|
|
16
|
+
Initialize the 60db client
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
api_key: Your 60db API key
|
|
20
|
+
base_url: API base URL (default: https://api-dev.qcall.ai/tts)
|
|
21
|
+
"""
|
|
22
|
+
self.api_key = api_key
|
|
23
|
+
self.base_url = base_url.rstrip('/')
|
|
24
|
+
self.session = requests.Session()
|
|
25
|
+
self.session.headers.update({
|
|
26
|
+
'Authorization': f'Bearer {api_key}',
|
|
27
|
+
'Content-Type': 'application/json'
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
def _request(self, method: str, endpoint: str, **kwargs) -> Any:
|
|
31
|
+
"""Make HTTP request"""
|
|
32
|
+
url = f"{self.base_url}{endpoint}"
|
|
33
|
+
response = self.session.request(method, url, **kwargs)
|
|
34
|
+
response.raise_for_status()
|
|
35
|
+
|
|
36
|
+
if kwargs.get('stream'):
|
|
37
|
+
return response
|
|
38
|
+
|
|
39
|
+
content_type = response.headers.get('content-type', '')
|
|
40
|
+
if 'application/json' in content_type:
|
|
41
|
+
return response.json()
|
|
42
|
+
return response.content
|
|
43
|
+
|
|
44
|
+
# TTS Methods
|
|
45
|
+
def text_to_speech(self, text: str, voice_id: Optional[str] = None, enhance: Optional[bool] = True, speed: Optional[float] = 1.0, **params) -> bytes:
|
|
46
|
+
"""
|
|
47
|
+
Convert text to speech
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
text: Text to convert
|
|
51
|
+
voice_id: Voice ID to use
|
|
52
|
+
enhance: Enable audio enhancement
|
|
53
|
+
speed: Speech speed (0.5 to 2.0)
|
|
54
|
+
**params: Additional parameters
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
Audio data as bytes
|
|
58
|
+
"""
|
|
59
|
+
data = {'text': text, 'enhance': enhance, 'speed': speed, **params}
|
|
60
|
+
if voice_id:
|
|
61
|
+
data['voice_id'] = voice_id
|
|
62
|
+
|
|
63
|
+
response = self._request('POST', '/tts-stream', json=data)
|
|
64
|
+
|
|
65
|
+
# Handle JSON response with base64 audio
|
|
66
|
+
if isinstance(response, dict) and response.get('success') and response.get('audio_base64'):
|
|
67
|
+
return base64.b64decode(response['audio_base64'])
|
|
68
|
+
|
|
69
|
+
# Fallback for binary response
|
|
70
|
+
if isinstance(response, bytes):
|
|
71
|
+
return response
|
|
72
|
+
|
|
73
|
+
raise Exception(response.get('message', 'Failed to generate audio'))
|
|
74
|
+
|
|
75
|
+
def text_to_speech_stream(
|
|
76
|
+
self,
|
|
77
|
+
text: str,
|
|
78
|
+
on_chunk: Callable[[bytes], None],
|
|
79
|
+
voice_id: Optional[str] = None,
|
|
80
|
+
on_complete: Optional[Callable[[], None]] = None,
|
|
81
|
+
on_error: Optional[Callable[[str], None]] = None,
|
|
82
|
+
**params
|
|
83
|
+
) -> None:
|
|
84
|
+
"""
|
|
85
|
+
Stream text to speech with callbacks
|
|
86
|
+
|
|
87
|
+
Args:
|
|
88
|
+
text: Text to convert
|
|
89
|
+
on_chunk: Callback for audio chunks
|
|
90
|
+
voice_id: Voice ID to use
|
|
91
|
+
on_complete: Callback when complete
|
|
92
|
+
on_error: Callback for errors
|
|
93
|
+
**params: Additional parameters
|
|
94
|
+
"""
|
|
95
|
+
data = {'text': text, **params}
|
|
96
|
+
if voice_id:
|
|
97
|
+
data['voice_id'] = voice_id
|
|
98
|
+
|
|
99
|
+
try:
|
|
100
|
+
response = requests.post(
|
|
101
|
+
f"{self.base_url}/tts-stream",
|
|
102
|
+
headers={'Authorization': f'Bearer {self.api_key}', 'Content-Type': 'application/json'},
|
|
103
|
+
json=data,
|
|
104
|
+
stream=True
|
|
105
|
+
)
|
|
106
|
+
response.raise_for_status()
|
|
107
|
+
|
|
108
|
+
buffer = ""
|
|
109
|
+
for chunk in response.iter_content(chunk_size=None, decode_unicode=True):
|
|
110
|
+
if chunk:
|
|
111
|
+
buffer += chunk
|
|
112
|
+
lines = buffer.split('\n')
|
|
113
|
+
buffer = lines.pop()
|
|
114
|
+
|
|
115
|
+
for line in lines:
|
|
116
|
+
if not line.strip():
|
|
117
|
+
continue
|
|
118
|
+
try:
|
|
119
|
+
parsed = json.loads(line)
|
|
120
|
+
if parsed.get('type') == 'error':
|
|
121
|
+
if on_error:
|
|
122
|
+
on_error(parsed.get('message', 'Unknown error'))
|
|
123
|
+
elif 'result' in parsed and 'audioContent' in parsed['result']:
|
|
124
|
+
audio_bytes = base64.b64decode(parsed['result']['audioContent'])
|
|
125
|
+
on_chunk(audio_bytes)
|
|
126
|
+
except json.JSONDecodeError:
|
|
127
|
+
pass
|
|
128
|
+
|
|
129
|
+
if on_complete:
|
|
130
|
+
on_complete()
|
|
131
|
+
except Exception as e:
|
|
132
|
+
if on_error:
|
|
133
|
+
on_error(str(e))
|
|
134
|
+
raise
|
|
135
|
+
|
|
136
|
+
# STT Methods
|
|
137
|
+
def speech_to_text(self, audio_file, language: Optional[str] = None) -> Dict[str, Any]:
|
|
138
|
+
"""
|
|
139
|
+
Transcribe audio to text
|
|
140
|
+
|
|
141
|
+
Args:
|
|
142
|
+
audio_file: Audio file path or file object
|
|
143
|
+
language: Language code
|
|
144
|
+
|
|
145
|
+
Returns:
|
|
146
|
+
Transcription result
|
|
147
|
+
"""
|
|
148
|
+
files = {'audio': audio_file}
|
|
149
|
+
data = {}
|
|
150
|
+
if language:
|
|
151
|
+
data['language'] = language
|
|
152
|
+
|
|
153
|
+
headers = {'Authorization': f'Bearer {self.api_key}'}
|
|
154
|
+
response = requests.post(
|
|
155
|
+
f"{self.base_url}/stt",
|
|
156
|
+
headers=headers,
|
|
157
|
+
files=files,
|
|
158
|
+
data=data
|
|
159
|
+
)
|
|
160
|
+
response.raise_for_status()
|
|
161
|
+
return response.json()
|
|
162
|
+
|
|
163
|
+
def get_languages(self) -> List[Dict[str, Any]]:
|
|
164
|
+
"""Get supported languages"""
|
|
165
|
+
return self._request('GET', '/stt/languages')
|
|
166
|
+
|
|
167
|
+
# Voice Methods
|
|
168
|
+
def get_voices(self) -> List[Dict[str, Any]]:
|
|
169
|
+
"""Get all voices"""
|
|
170
|
+
return self._request('GET', '/voices')
|
|
171
|
+
|
|
172
|
+
def get_voice(self, voice_id: str) -> Dict[str, Any]:
|
|
173
|
+
"""Get specific voice"""
|
|
174
|
+
return self._request('GET', f'/voices/{voice_id}')
|
|
175
|
+
|
|
176
|
+
def create_voice(self, name: str, files: List, **params) -> Dict[str, Any]:
|
|
177
|
+
"""Create custom voice"""
|
|
178
|
+
file_data = [('files', f) for f in files]
|
|
179
|
+
data = {'name': name, **params}
|
|
180
|
+
|
|
181
|
+
headers = {'Authorization': f'Bearer {self.api_key}'}
|
|
182
|
+
response = requests.post(
|
|
183
|
+
f"{self.base_url}/voices",
|
|
184
|
+
headers=headers,
|
|
185
|
+
files=file_data,
|
|
186
|
+
data=data
|
|
187
|
+
)
|
|
188
|
+
response.raise_for_status()
|
|
189
|
+
return response.json()
|
|
190
|
+
|
|
191
|
+
def update_voice(self, voice_id: str, **params) -> Dict[str, Any]:
|
|
192
|
+
"""Update voice"""
|
|
193
|
+
return self._request('PUT', f'/voices/{voice_id}', json=params)
|
|
194
|
+
|
|
195
|
+
def delete_voice(self, voice_id: str) -> None:
|
|
196
|
+
"""Delete voice"""
|
|
197
|
+
self._request('DELETE', f'/voices/{voice_id}')
|
|
198
|
+
|
|
199
|
+
# Auth Methods
|
|
200
|
+
def sign_up(self, email: str, password: str, **params) -> Dict[str, Any]:
|
|
201
|
+
"""Register new user"""
|
|
202
|
+
data = {'email': email, 'password': password, **params}
|
|
203
|
+
return self._request('POST', '/auth/signup', json=data)
|
|
204
|
+
|
|
205
|
+
def sign_in(self, email: str, password: str) -> Dict[str, Any]:
|
|
206
|
+
"""Login user"""
|
|
207
|
+
data = {'email': email, 'password': password}
|
|
208
|
+
return self._request('POST', '/auth/login', json=data)
|
|
209
|
+
|
|
210
|
+
def get_profile(self) -> Dict[str, Any]:
|
|
211
|
+
"""Get user profile"""
|
|
212
|
+
return self._request('GET', '/auth/me')
|
|
213
|
+
|
|
214
|
+
def update_profile(self, **params) -> Dict[str, Any]:
|
|
215
|
+
"""Update user profile"""
|
|
216
|
+
return self._request('PUT', '/auth/profile', json=params)
|
|
217
|
+
|
|
218
|
+
# Workspace Methods
|
|
219
|
+
def get_workspaces(self) -> List[Dict[str, Any]]:
|
|
220
|
+
"""Get all workspaces"""
|
|
221
|
+
return self._request('GET', '/workspaces')
|
|
222
|
+
|
|
223
|
+
def create_workspace(self, name: str, **params) -> Dict[str, Any]:
|
|
224
|
+
"""Create workspace"""
|
|
225
|
+
data = {'name': name, **params}
|
|
226
|
+
return self._request('POST', '/workspaces', json=data)
|
|
227
|
+
|
|
228
|
+
# Billing Methods
|
|
229
|
+
def get_plans(self) -> List[Dict[str, Any]]:
|
|
230
|
+
"""Get available plans"""
|
|
231
|
+
return self._request('GET', '/plans')
|
|
232
|
+
|
|
233
|
+
def get_current_plan(self) -> Dict[str, Any]:
|
|
234
|
+
"""Get current subscription"""
|
|
235
|
+
return self._request('GET', '/billing/current-plan')
|
|
236
|
+
|
|
237
|
+
def subscribe(self, plan_id: str) -> Dict[str, Any]:
|
|
238
|
+
"""Subscribe to plan"""
|
|
239
|
+
return self._request('POST', '/billing/subscribe', json={'plan_id': plan_id})
|
|
240
|
+
|
|
241
|
+
# Analytics
|
|
242
|
+
def get_usage(self) -> Dict[str, Any]:
|
|
243
|
+
"""Get usage statistics"""
|
|
244
|
+
return self._request('GET', '/analytics/usage')
|
|
245
|
+
|
|
246
|
+
# API Keys
|
|
247
|
+
def get_api_keys(self) -> List[Dict[str, Any]]:
|
|
248
|
+
"""Get all API keys"""
|
|
249
|
+
return self._request('GET', '/developer/api')
|
|
250
|
+
|
|
251
|
+
def create_api_key(self, name: str) -> Dict[str, Any]:
|
|
252
|
+
"""Create API key"""
|
|
253
|
+
return self._request('POST', '/developer/api', json={'name': name})
|
|
254
|
+
|
|
255
|
+
def delete_api_key(self, key_id: str) -> None:
|
|
256
|
+
"""Delete API key"""
|
|
257
|
+
self._request('DELETE', f'/developer/api/{key_id}')
|
|
258
|
+
|
|
259
|
+
# Webhooks
|
|
260
|
+
def get_webhooks(self) -> List[Dict[str, Any]]:
|
|
261
|
+
"""Get all webhooks"""
|
|
262
|
+
return self._request('GET', '/webhooks')
|
|
263
|
+
|
|
264
|
+
def create_webhook(self, url: str, events: List[str], **params) -> Dict[str, Any]:
|
|
265
|
+
"""Create webhook"""
|
|
266
|
+
data = {'url': url, 'events': events, **params}
|
|
267
|
+
return self._request('POST', '/webhooks', json=data)
|
|
268
|
+
|
|
269
|
+
def delete_webhook(self, webhook_id: str) -> None:
|
|
270
|
+
"""Delete webhook"""
|
|
271
|
+
self._request('DELETE', f'/webhooks/{webhook_id}')
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: sixtydb-python
|
|
3
|
+
Version: 1.0.5
|
|
4
|
+
Summary: Official 60db SDK for Python
|
|
5
|
+
Author: 60db
|
|
6
|
+
License: MIT
|
|
7
|
+
Keywords: 60db,tts,stt,voice,ai
|
|
8
|
+
Classifier: Development Status :: 4 - Beta
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Requires-Python: >=3.8
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
Requires-Dist: requests>=2.28.0
|
|
19
|
+
Requires-Dist: typing-extensions>=4.0.0
|
|
20
|
+
|
|
21
|
+
# 60db
|
|
22
|
+
|
|
23
|
+
Official 60db SDK for Python
|
|
24
|
+
|
|
25
|
+
## Installation
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
pip install 60db
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Quick Start
|
|
32
|
+
|
|
33
|
+
```python
|
|
34
|
+
from sixtydb import SixtyDBClient
|
|
35
|
+
|
|
36
|
+
client = SixtyDBClient('your-api-key')
|
|
37
|
+
|
|
38
|
+
# Text to Speech
|
|
39
|
+
audio = client.text_to_speech(
|
|
40
|
+
'Hello, world!',
|
|
41
|
+
voice_id='default-voice',
|
|
42
|
+
speed=1.0 # 0.5 to 2.0 (default 1.0)
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
# Save audio
|
|
46
|
+
with open('output.mp3', 'wb') as f:
|
|
47
|
+
f.write(audio)
|
|
48
|
+
|
|
49
|
+
# Get all voices
|
|
50
|
+
voices = client.get_voices()
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## API Methods
|
|
54
|
+
|
|
55
|
+
### Text-to-Speech
|
|
56
|
+
- `text_to_speech(text, voice_id=None, **params)` - Convert text to speech
|
|
57
|
+
|
|
58
|
+
### Speech-to-Text
|
|
59
|
+
- `speech_to_text(audio_file, language=None)` - Transcribe audio
|
|
60
|
+
- `get_languages()` - Get supported languages
|
|
61
|
+
|
|
62
|
+
### Voices
|
|
63
|
+
- `get_voices()` - List all voices
|
|
64
|
+
- `get_voice(voice_id)` - Get specific voice
|
|
65
|
+
|
|
66
|
+
## Examples
|
|
67
|
+
|
|
68
|
+
### Streaming TTS
|
|
69
|
+
|
|
70
|
+
```python
|
|
71
|
+
def handle_chunk(chunk):
|
|
72
|
+
print(f"Received {len(chunk)} bytes")
|
|
73
|
+
|
|
74
|
+
client.text_to_speech_stream(
|
|
75
|
+
"This is streaming audio",
|
|
76
|
+
on_chunk=handle_chunk,
|
|
77
|
+
on_complete=lambda: print("Done!"),
|
|
78
|
+
voice_id='default-voice'
|
|
79
|
+
)
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Speech to Text
|
|
83
|
+
|
|
84
|
+
```python
|
|
85
|
+
with open('audio.mp3', 'rb') as f:
|
|
86
|
+
result = client.speech_to_text(f, language='en')
|
|
87
|
+
print(result['text'])
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## License
|
|
91
|
+
|
|
92
|
+
MIT
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
sixtydb/__init__.py,sha256=IuOBXXadTRHlM6vQxsIpF5SAxQhZ63W4Mu-5qOR3jP0,150
|
|
2
|
+
sixtydb/client.py,sha256=eCVhjiEVwiXI5xdovRzg3MP0NOMN02gCQpRKGD7Jz9s,9504
|
|
3
|
+
sixtydb_python-1.0.5.dist-info/METADATA,sha256=n7EgH8fa_2pzG13vfuc7tdXC9M6qWMJlgXTDSZR6so0,1993
|
|
4
|
+
sixtydb_python-1.0.5.dist-info/WHEEL,sha256=YCfwYGOYMi5Jhw2fU4yNgwErybb2IX5PEwBKV4ZbdBo,91
|
|
5
|
+
sixtydb_python-1.0.5.dist-info/top_level.txt,sha256=oTqInjPZSaLsdxC7ZTMhDgJNYQmLpO8nyMbLglr7z4I,8
|
|
6
|
+
sixtydb_python-1.0.5.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
sixtydb
|