audiopod 1.1.1__py3-none-any.whl → 1.4.0__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.
- audiopod/__init__.py +10 -64
- audiopod/client.py +143 -172
- audiopod/config.py +4 -50
- audiopod/exceptions.py +16 -71
- audiopod/services/__init__.py +8 -6
- audiopod/services/base.py +51 -195
- audiopod/services/credits.py +26 -30
- audiopod/services/denoiser.py +120 -40
- audiopod/services/music.py +180 -485
- audiopod/services/speaker.py +117 -36
- audiopod/services/stem_extraction.py +130 -142
- audiopod/services/transcription.py +159 -184
- audiopod/services/translation.py +109 -170
- audiopod/services/voice.py +138 -327
- audiopod/services/wallet.py +235 -0
- audiopod-1.4.0.dist-info/METADATA +206 -0
- audiopod-1.4.0.dist-info/RECORD +20 -0
- {audiopod-1.1.1.dist-info → audiopod-1.4.0.dist-info}/WHEEL +1 -1
- audiopod/cli.py +0 -285
- audiopod/models.py +0 -250
- audiopod/py.typed +0 -2
- audiopod/services/karaoke.py +0 -61
- audiopod-1.1.1.dist-info/METADATA +0 -404
- audiopod-1.1.1.dist-info/RECORD +0 -24
- audiopod-1.1.1.dist-info/entry_points.txt +0 -2
- {audiopod-1.1.1.dist-info → audiopod-1.4.0.dist-info}/licenses/LICENSE +0 -0
- {audiopod-1.1.1.dist-info → audiopod-1.4.0.dist-info}/top_level.txt +0 -0
audiopod/services/voice.py
CHANGED
|
@@ -1,376 +1,187 @@
|
|
|
1
1
|
"""
|
|
2
|
-
Voice Service - Voice cloning and
|
|
3
|
-
"""
|
|
2
|
+
Voice Service - Voice cloning and text-to-speech
|
|
4
3
|
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
API Routes:
|
|
5
|
+
- GET /api/v1/voice/voice-profiles - List all voices
|
|
6
|
+
- GET /api/v1/voice/voices/{id}/status - Get voice details
|
|
7
|
+
- POST /api/v1/voice/voice-profiles - Create voice clone
|
|
8
|
+
- DELETE /api/v1/voice/voices/{id} - Delete voice
|
|
9
|
+
- POST /api/v1/voice/voices/{id}/generate - Generate TTS
|
|
10
|
+
- GET /api/v1/voice/tts-jobs/{id}/status - Get TTS job status
|
|
11
|
+
"""
|
|
7
12
|
|
|
13
|
+
from typing import Optional, Dict, Any, List, Union
|
|
8
14
|
from .base import BaseService
|
|
9
|
-
from ..models import Job, VoiceProfile, JobStatus
|
|
10
|
-
from ..exceptions import ValidationError
|
|
11
15
|
|
|
12
16
|
|
|
13
17
|
class VoiceService(BaseService):
|
|
14
|
-
"""Service for voice cloning and text-to-speech
|
|
15
|
-
|
|
16
|
-
def
|
|
18
|
+
"""Service for voice cloning and text-to-speech."""
|
|
19
|
+
|
|
20
|
+
def list_voices(
|
|
17
21
|
self,
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
Args:
|
|
29
|
-
voice_file: Path to audio file containing voice to clone
|
|
30
|
-
text: Text to generate with the cloned voice
|
|
31
|
-
language: Target language code (e.g., 'en', 'es')
|
|
32
|
-
speed: Speech speed (0.5 to 2.0)
|
|
33
|
-
wait_for_completion: Whether to wait for job completion
|
|
34
|
-
timeout: Maximum time to wait if wait_for_completion=True
|
|
35
|
-
|
|
36
|
-
Returns:
|
|
37
|
-
Job object if wait_for_completion=False, otherwise job result
|
|
38
|
-
"""
|
|
39
|
-
# Validate inputs
|
|
40
|
-
text = self._validate_text_input(text)
|
|
41
|
-
if language:
|
|
42
|
-
language = self._validate_language_code(language)
|
|
43
|
-
if not 0.5 <= speed <= 2.0:
|
|
44
|
-
raise ValidationError("Speed must be between 0.5 and 2.0")
|
|
45
|
-
|
|
46
|
-
# Prepare file upload
|
|
47
|
-
files = self._prepare_file_upload(voice_file, "file")
|
|
48
|
-
|
|
49
|
-
# Prepare form data
|
|
50
|
-
data = {
|
|
51
|
-
"input_text": text,
|
|
52
|
-
"speed": speed
|
|
22
|
+
skip: int = 0,
|
|
23
|
+
limit: int = 100,
|
|
24
|
+
include_public: bool = True,
|
|
25
|
+
) -> List[Dict[str, Any]]:
|
|
26
|
+
"""List available voices (both custom and public)."""
|
|
27
|
+
params = {
|
|
28
|
+
"skip": skip,
|
|
29
|
+
"limit": limit,
|
|
30
|
+
"include_public": str(include_public).lower(),
|
|
53
31
|
}
|
|
54
|
-
if language:
|
|
55
|
-
data["target_language"] = language
|
|
56
|
-
|
|
57
|
-
# Make request
|
|
58
32
|
if self.async_mode:
|
|
59
|
-
return self.
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
async def _async_clone_voice(
|
|
77
|
-
self,
|
|
78
|
-
files: Dict[str, Any],
|
|
79
|
-
data: Dict[str, Any],
|
|
80
|
-
wait_for_completion: bool,
|
|
81
|
-
timeout: int
|
|
82
|
-
) -> Union[Job, Dict[str, Any]]:
|
|
83
|
-
"""Async version of clone_voice"""
|
|
84
|
-
response = await self.client.request(
|
|
85
|
-
"POST",
|
|
86
|
-
"/api/v1/voice/voice-clone",
|
|
87
|
-
data=data,
|
|
88
|
-
files=files
|
|
89
|
-
)
|
|
90
|
-
|
|
91
|
-
job = Job.from_dict(response)
|
|
92
|
-
|
|
93
|
-
if wait_for_completion:
|
|
94
|
-
job = await self._async_wait_for_completion(job.id, timeout)
|
|
95
|
-
return job.result if job.result else job
|
|
96
|
-
|
|
97
|
-
return job
|
|
98
|
-
|
|
99
|
-
def create_voice_profile(
|
|
33
|
+
return self._async_list_voices(params)
|
|
34
|
+
return self.client.request("GET", "/api/v1/voice/voice-profiles", params=params)
|
|
35
|
+
|
|
36
|
+
async def _async_list_voices(self, params: Dict) -> List[Dict[str, Any]]:
|
|
37
|
+
return await self.client.request("GET", "/api/v1/voice/voice-profiles", params=params)
|
|
38
|
+
|
|
39
|
+
def get_voice(self, voice_id: Union[int, str]) -> Dict[str, Any]:
|
|
40
|
+
"""Get voice details by ID or UUID."""
|
|
41
|
+
if self.async_mode:
|
|
42
|
+
return self._async_get_voice(voice_id)
|
|
43
|
+
return self.client.request("GET", f"/api/v1/voice/voices/{voice_id}/status")
|
|
44
|
+
|
|
45
|
+
async def _async_get_voice(self, voice_id: Union[int, str]) -> Dict[str, Any]:
|
|
46
|
+
return await self.client.request("GET", f"/api/v1/voice/voices/{voice_id}/status")
|
|
47
|
+
|
|
48
|
+
def create_voice(
|
|
100
49
|
self,
|
|
101
50
|
name: str,
|
|
102
|
-
|
|
51
|
+
audio_file: str,
|
|
103
52
|
description: Optional[str] = None,
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
"""
|
|
109
|
-
Create a reusable voice profile
|
|
110
|
-
|
|
111
|
-
Args:
|
|
112
|
-
name: Name for the voice profile
|
|
113
|
-
voice_file: Path to audio file containing voice sample
|
|
114
|
-
description: Optional description
|
|
115
|
-
is_public: Whether to make the voice profile public
|
|
116
|
-
wait_for_completion: Whether to wait for processing completion
|
|
117
|
-
timeout: Maximum time to wait if wait_for_completion=True
|
|
118
|
-
|
|
119
|
-
Returns:
|
|
120
|
-
Job object if wait_for_completion=False, otherwise VoiceProfile
|
|
121
|
-
"""
|
|
122
|
-
# Validate inputs
|
|
123
|
-
if not name or len(name.strip()) < 1:
|
|
124
|
-
raise ValidationError("Voice profile name cannot be empty")
|
|
125
|
-
if len(name) > 100:
|
|
126
|
-
raise ValidationError("Voice profile name too long (max 100 characters)")
|
|
127
|
-
|
|
128
|
-
# Prepare file upload
|
|
129
|
-
files = self._prepare_file_upload(voice_file, "file")
|
|
130
|
-
|
|
131
|
-
# Prepare form data
|
|
132
|
-
data = {
|
|
133
|
-
"name": name.strip(),
|
|
134
|
-
"is_public": is_public
|
|
135
|
-
}
|
|
53
|
+
) -> Dict[str, Any]:
|
|
54
|
+
"""Create a new voice clone from an audio file."""
|
|
55
|
+
files = self._prepare_file_upload(audio_file, "file")
|
|
56
|
+
data = {"name": name}
|
|
136
57
|
if description:
|
|
137
|
-
data["description"] = description
|
|
138
|
-
|
|
139
|
-
# Make request
|
|
58
|
+
data["description"] = description
|
|
59
|
+
|
|
140
60
|
if self.async_mode:
|
|
141
|
-
return self.
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
voice_data = self.client.request("GET", f"/api/v1/voice/voice-profiles/{voice_id}")
|
|
157
|
-
if voice_data["status"] == "completed":
|
|
158
|
-
return VoiceProfile.from_dict(voice_data)
|
|
159
|
-
elif voice_data["status"] == "failed":
|
|
160
|
-
raise ValidationError(f"Voice profile creation failed: {voice_data.get('error_message')}")
|
|
161
|
-
time.sleep(5)
|
|
162
|
-
raise ValidationError("Voice profile creation timed out")
|
|
163
|
-
else:
|
|
164
|
-
return VoiceProfile.from_dict(response)
|
|
165
|
-
|
|
166
|
-
async def _async_create_voice_profile(
|
|
167
|
-
self,
|
|
168
|
-
files: Dict[str, Any],
|
|
169
|
-
data: Dict[str, Any],
|
|
170
|
-
wait_for_completion: bool,
|
|
171
|
-
timeout: int
|
|
172
|
-
) -> Union[Job, VoiceProfile]:
|
|
173
|
-
"""Async version of create_voice_profile"""
|
|
174
|
-
import asyncio
|
|
175
|
-
|
|
176
|
-
response = await self.client.request(
|
|
177
|
-
"POST",
|
|
178
|
-
"/api/v1/voice/voice-profiles",
|
|
179
|
-
data=data,
|
|
180
|
-
files=files
|
|
181
|
-
)
|
|
182
|
-
|
|
183
|
-
if wait_for_completion:
|
|
184
|
-
voice_id = response["id"]
|
|
185
|
-
# Poll for completion
|
|
186
|
-
start_time = time.time()
|
|
187
|
-
while time.time() - start_time < timeout:
|
|
188
|
-
voice_data = await self.client.request("GET", f"/api/v1/voice/voice-profiles/{voice_id}")
|
|
189
|
-
if voice_data["status"] == "completed":
|
|
190
|
-
return VoiceProfile.from_dict(voice_data)
|
|
191
|
-
elif voice_data["status"] == "failed":
|
|
192
|
-
raise ValidationError(f"Voice profile creation failed: {voice_data.get('error_message')}")
|
|
193
|
-
await asyncio.sleep(5)
|
|
194
|
-
raise ValidationError("Voice profile creation timed out")
|
|
195
|
-
else:
|
|
196
|
-
return VoiceProfile.from_dict(response)
|
|
197
|
-
|
|
61
|
+
return self._async_create_voice(data, files)
|
|
62
|
+
return self.client.request("POST", "/api/v1/voice/voice-profiles", data=data, files=files)
|
|
63
|
+
|
|
64
|
+
async def _async_create_voice(self, data: Dict, files: Dict) -> Dict[str, Any]:
|
|
65
|
+
return await self.client.request("POST", "/api/v1/voice/voice-profiles", data=data, files=files)
|
|
66
|
+
|
|
67
|
+
def delete_voice(self, voice_id: Union[int, str]) -> Dict[str, str]:
|
|
68
|
+
"""Delete a voice by ID or UUID."""
|
|
69
|
+
if self.async_mode:
|
|
70
|
+
return self._async_delete_voice(voice_id)
|
|
71
|
+
return self.client.request("DELETE", f"/api/v1/voice/voices/{voice_id}")
|
|
72
|
+
|
|
73
|
+
async def _async_delete_voice(self, voice_id: Union[int, str]) -> Dict[str, str]:
|
|
74
|
+
return await self.client.request("DELETE", f"/api/v1/voice/voices/{voice_id}")
|
|
75
|
+
|
|
198
76
|
def generate_speech(
|
|
199
77
|
self,
|
|
200
78
|
voice_id: Union[int, str],
|
|
201
79
|
text: str,
|
|
202
|
-
language: Optional[str] = None,
|
|
203
80
|
speed: float = 1.0,
|
|
81
|
+
language: str = "en",
|
|
204
82
|
audio_format: str = "mp3",
|
|
205
83
|
wait_for_completion: bool = False,
|
|
206
|
-
timeout: int = 300
|
|
207
|
-
) ->
|
|
84
|
+
timeout: int = 300,
|
|
85
|
+
) -> Dict[str, Any]:
|
|
208
86
|
"""
|
|
209
|
-
Generate speech using
|
|
87
|
+
Generate speech from text using a voice.
|
|
210
88
|
|
|
211
89
|
Args:
|
|
212
|
-
voice_id: ID or UUID
|
|
213
|
-
text: Text to
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
audio_format: Output
|
|
217
|
-
wait_for_completion:
|
|
218
|
-
timeout:
|
|
90
|
+
voice_id: Voice ID (int) or UUID (str) to use for generation
|
|
91
|
+
text: Text to convert to speech
|
|
92
|
+
speed: Speech speed (0.25 to 4.0, default 1.0)
|
|
93
|
+
language: Language code (default "en")
|
|
94
|
+
audio_format: Output format - mp3, wav, ogg (default "mp3")
|
|
95
|
+
wait_for_completion: If True, poll until job completes
|
|
96
|
+
timeout: Max seconds to wait for completion
|
|
219
97
|
|
|
220
98
|
Returns:
|
|
221
|
-
Job
|
|
99
|
+
Job info dict with job_id, status, etc.
|
|
100
|
+
If wait_for_completion=True, includes output_url when done.
|
|
222
101
|
"""
|
|
223
|
-
# Validate inputs
|
|
224
|
-
text = self._validate_text_input(text)
|
|
225
|
-
if language:
|
|
226
|
-
language = self._validate_language_code(language)
|
|
227
|
-
if not 0.5 <= speed <= 2.0:
|
|
228
|
-
raise ValidationError("Speed must be between 0.5 and 2.0")
|
|
229
|
-
if audio_format not in ["mp3", "wav"]:
|
|
230
|
-
raise ValidationError("Audio format must be 'mp3' or 'wav'")
|
|
231
|
-
|
|
232
|
-
# Prepare form data
|
|
233
102
|
data = {
|
|
234
103
|
"input_text": text,
|
|
235
104
|
"speed": speed,
|
|
236
|
-
"
|
|
105
|
+
"language": language,
|
|
106
|
+
"audio_format": audio_format,
|
|
237
107
|
}
|
|
238
|
-
|
|
239
|
-
data["language"] = language
|
|
240
|
-
|
|
241
|
-
# Make request
|
|
242
|
-
endpoint = f"/api/v1/voice/voices/{voice_id}/generate"
|
|
243
|
-
|
|
108
|
+
|
|
244
109
|
if self.async_mode:
|
|
245
|
-
return self._async_generate_speech(
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
110
|
+
return self._async_generate_speech(voice_id, data, wait_for_completion, timeout)
|
|
111
|
+
|
|
112
|
+
response = self.client.request(
|
|
113
|
+
"POST",
|
|
114
|
+
f"/api/v1/voice/voices/{voice_id}/generate",
|
|
115
|
+
data=data,
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
if wait_for_completion:
|
|
119
|
+
job_id = response.get("job_id") or response.get("id")
|
|
120
|
+
return self._wait_for_job_completion(job_id, timeout)
|
|
121
|
+
return response
|
|
122
|
+
|
|
259
123
|
async def _async_generate_speech(
|
|
260
|
-
self,
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
return job.result if job.result else job
|
|
274
|
-
return job
|
|
275
|
-
else:
|
|
276
|
-
return response
|
|
277
|
-
|
|
278
|
-
def list_voice_profiles(
|
|
279
|
-
self,
|
|
280
|
-
voice_type: Optional[str] = None,
|
|
281
|
-
is_public: Optional[bool] = None,
|
|
282
|
-
include_public: bool = True,
|
|
283
|
-
limit: int = 50
|
|
284
|
-
) -> List[VoiceProfile]:
|
|
124
|
+
self, voice_id: Union[int, str], data: Dict, wait_for_completion: bool, timeout: int
|
|
125
|
+
) -> Dict[str, Any]:
|
|
126
|
+
response = await self.client.request(
|
|
127
|
+
"POST",
|
|
128
|
+
f"/api/v1/voice/voices/{voice_id}/generate",
|
|
129
|
+
data=data,
|
|
130
|
+
)
|
|
131
|
+
if wait_for_completion:
|
|
132
|
+
job_id = response.get("job_id") or response.get("id")
|
|
133
|
+
return await self._async_wait_for_job_completion(job_id, timeout)
|
|
134
|
+
return response
|
|
135
|
+
|
|
136
|
+
def get_job_status(self, job_id: int) -> Dict[str, Any]:
|
|
285
137
|
"""
|
|
286
|
-
|
|
138
|
+
Get TTS job status.
|
|
287
139
|
|
|
288
140
|
Args:
|
|
289
|
-
|
|
290
|
-
is_public: Filter by public status
|
|
291
|
-
include_public: Include public voices
|
|
292
|
-
limit: Maximum number of results
|
|
141
|
+
job_id: The job ID returned from generate_speech
|
|
293
142
|
|
|
294
143
|
Returns:
|
|
295
|
-
|
|
144
|
+
Job status dict with status, progress, output_url (when completed), etc.
|
|
296
145
|
"""
|
|
297
|
-
params = {
|
|
298
|
-
"limit": limit,
|
|
299
|
-
"include_public": include_public
|
|
300
|
-
}
|
|
301
|
-
if voice_type:
|
|
302
|
-
params["voice_type"] = voice_type
|
|
303
|
-
if is_public is not None:
|
|
304
|
-
params["is_public"] = is_public
|
|
305
|
-
|
|
306
146
|
if self.async_mode:
|
|
307
|
-
return self.
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
def get_voice_profile(self, voice_id: Union[int, str]) -> VoiceProfile:
|
|
318
|
-
"""
|
|
319
|
-
Get details of a specific voice profile
|
|
147
|
+
return self._async_get_job_status(job_id)
|
|
148
|
+
return self.client.request("GET", f"/api/v1/voice/tts-jobs/{job_id}/status")
|
|
149
|
+
|
|
150
|
+
async def _async_get_job_status(self, job_id: int) -> Dict[str, Any]:
|
|
151
|
+
return await self.client.request("GET", f"/api/v1/voice/tts-jobs/{job_id}/status")
|
|
152
|
+
|
|
153
|
+
def _wait_for_job_completion(self, job_id: int, timeout: int) -> Dict[str, Any]:
|
|
154
|
+
"""Poll job status until completion or timeout."""
|
|
155
|
+
import time
|
|
156
|
+
start_time = time.time()
|
|
320
157
|
|
|
321
|
-
|
|
322
|
-
|
|
158
|
+
while time.time() - start_time < timeout:
|
|
159
|
+
status = self.get_job_status(job_id)
|
|
323
160
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
return self._async_get_voice_profile(voice_id)
|
|
329
|
-
else:
|
|
330
|
-
response = self.client.request("GET", f"/api/v1/voice/voice-profiles/{voice_id}")
|
|
331
|
-
return VoiceProfile.from_dict(response)
|
|
161
|
+
if status.get("status") in ("completed", "COMPLETED"):
|
|
162
|
+
return status
|
|
163
|
+
elif status.get("status") in ("failed", "FAILED", "error", "ERROR"):
|
|
164
|
+
raise Exception(f"Job failed: {status.get('error_message', 'Unknown error')}")
|
|
332
165
|
|
|
333
|
-
|
|
334
|
-
"""Async version of get_voice_profile"""
|
|
335
|
-
response = await self.client.request("GET", f"/api/v1/voice/voice-profiles/{voice_id}")
|
|
336
|
-
return VoiceProfile.from_dict(response)
|
|
166
|
+
time.sleep(2)
|
|
337
167
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
168
|
+
raise TimeoutError(f"Job {job_id} did not complete within {timeout} seconds")
|
|
169
|
+
|
|
170
|
+
async def _async_wait_for_job_completion(self, job_id: int, timeout: int) -> Dict[str, Any]:
|
|
171
|
+
"""Async poll job status until completion or timeout."""
|
|
172
|
+
import asyncio
|
|
173
|
+
import time
|
|
174
|
+
start_time = time.time()
|
|
341
175
|
|
|
342
|
-
|
|
343
|
-
|
|
176
|
+
while time.time() - start_time < timeout:
|
|
177
|
+
status = await self.get_job_status(job_id)
|
|
344
178
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
return self._async_delete_voice_profile(voice_id)
|
|
350
|
-
else:
|
|
351
|
-
return self.client.request("DELETE", f"/api/v1/voice/voices/{voice_id}")
|
|
179
|
+
if status.get("status") in ("completed", "COMPLETED"):
|
|
180
|
+
return status
|
|
181
|
+
elif status.get("status") in ("failed", "FAILED", "error", "ERROR"):
|
|
182
|
+
raise Exception(f"Job failed: {status.get('error_message', 'Unknown error')}")
|
|
352
183
|
|
|
353
|
-
|
|
354
|
-
"""Async version of delete_voice_profile"""
|
|
355
|
-
return await self.client.request("DELETE", f"/api/v1/voice/voices/{voice_id}")
|
|
356
|
-
|
|
357
|
-
def get_job_status(self, job_id: int) -> Job:
|
|
358
|
-
"""
|
|
359
|
-
Get status of a voice processing job
|
|
184
|
+
await asyncio.sleep(2)
|
|
360
185
|
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
Returns:
|
|
365
|
-
Job status and details
|
|
366
|
-
"""
|
|
367
|
-
if self.async_mode:
|
|
368
|
-
return self._async_get_job_status(job_id)
|
|
369
|
-
else:
|
|
370
|
-
response = self.client.request("GET", f"/api/v1/voice/clone/{job_id}/status")
|
|
371
|
-
return Job.from_dict(response)
|
|
372
|
-
|
|
373
|
-
async def _async_get_job_status(self, job_id: int) -> Job:
|
|
374
|
-
"""Async version of get_job_status"""
|
|
375
|
-
response = await self.client.request("GET", f"/api/v1/voice/clone/{job_id}/status")
|
|
376
|
-
return Job.from_dict(response)
|
|
186
|
+
raise TimeoutError(f"Job {job_id} did not complete within {timeout} seconds")
|
|
187
|
+
|