audiopod 1.0.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 +83 -0
- audiopod/cli.py +285 -0
- audiopod/client.py +332 -0
- audiopod/config.py +63 -0
- audiopod/exceptions.py +96 -0
- audiopod/models.py +235 -0
- audiopod/services/__init__.py +24 -0
- audiopod/services/base.py +213 -0
- audiopod/services/credits.py +46 -0
- audiopod/services/denoiser.py +51 -0
- audiopod/services/karaoke.py +61 -0
- audiopod/services/music.py +434 -0
- audiopod/services/speaker.py +53 -0
- audiopod/services/transcription.py +212 -0
- audiopod/services/translation.py +81 -0
- audiopod/services/voice.py +376 -0
- audiopod-1.0.0.dist-info/METADATA +395 -0
- audiopod-1.0.0.dist-info/RECORD +21 -0
- audiopod-1.0.0.dist-info/WHEEL +5 -0
- audiopod-1.0.0.dist-info/entry_points.txt +2 -0
- audiopod-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Denoiser Service - Audio denoising operations
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Optional, Union
|
|
6
|
+
from .base import BaseService
|
|
7
|
+
from ..models import Job, DenoiseResult
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class DenoiserService(BaseService):
|
|
11
|
+
"""Service for audio denoising operations"""
|
|
12
|
+
|
|
13
|
+
def denoise_audio(
|
|
14
|
+
self,
|
|
15
|
+
audio_file: str,
|
|
16
|
+
quality_mode: str = "balanced",
|
|
17
|
+
wait_for_completion: bool = False,
|
|
18
|
+
timeout: int = 600
|
|
19
|
+
) -> Union[Job, DenoiseResult]:
|
|
20
|
+
"""Remove noise from audio"""
|
|
21
|
+
files = self._prepare_file_upload(audio_file, "file")
|
|
22
|
+
data = {"quality_mode": quality_mode}
|
|
23
|
+
|
|
24
|
+
if self.async_mode:
|
|
25
|
+
return self._async_denoise(files, data, wait_for_completion, timeout)
|
|
26
|
+
else:
|
|
27
|
+
response = self.client.request(
|
|
28
|
+
"POST", "/api/v1/denoiser/denoise",
|
|
29
|
+
data=data, files=files
|
|
30
|
+
)
|
|
31
|
+
job = Job.from_dict(response)
|
|
32
|
+
|
|
33
|
+
if wait_for_completion:
|
|
34
|
+
completed_job = self._wait_for_completion(job.id, timeout)
|
|
35
|
+
return DenoiseResult.from_dict(completed_job.result or completed_job.__dict__)
|
|
36
|
+
|
|
37
|
+
return job
|
|
38
|
+
|
|
39
|
+
async def _async_denoise(self, files, data, wait_for_completion, timeout):
|
|
40
|
+
"""Async version of denoise_audio"""
|
|
41
|
+
response = await self.client.request(
|
|
42
|
+
"POST", "/api/v1/denoiser/denoise",
|
|
43
|
+
data=data, files=files
|
|
44
|
+
)
|
|
45
|
+
job = Job.from_dict(response)
|
|
46
|
+
|
|
47
|
+
if wait_for_completion:
|
|
48
|
+
completed_job = await self._async_wait_for_completion(job.id, timeout)
|
|
49
|
+
return DenoiseResult.from_dict(completed_job.result or completed_job.__dict__)
|
|
50
|
+
|
|
51
|
+
return job
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Karaoke Service - Karaoke video generation
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Optional, Union
|
|
6
|
+
from .base import BaseService
|
|
7
|
+
from ..models import Job
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class KaraokeService(BaseService):
|
|
11
|
+
"""Service for karaoke video generation"""
|
|
12
|
+
|
|
13
|
+
def generate_karaoke(
|
|
14
|
+
self,
|
|
15
|
+
audio_file: Optional[str] = None,
|
|
16
|
+
youtube_url: Optional[str] = None,
|
|
17
|
+
custom_lyrics: Optional[str] = None,
|
|
18
|
+
video_style: str = "modern",
|
|
19
|
+
wait_for_completion: bool = False,
|
|
20
|
+
timeout: int = 1200
|
|
21
|
+
) -> Job:
|
|
22
|
+
"""Generate karaoke video"""
|
|
23
|
+
if not audio_file and not youtube_url:
|
|
24
|
+
raise ValueError("Either audio_file or youtube_url must be provided")
|
|
25
|
+
|
|
26
|
+
data = {"video_style": video_style}
|
|
27
|
+
files = {}
|
|
28
|
+
|
|
29
|
+
if audio_file:
|
|
30
|
+
files = self._prepare_file_upload(audio_file, "file")
|
|
31
|
+
if youtube_url:
|
|
32
|
+
data["youtube_url"] = youtube_url
|
|
33
|
+
if custom_lyrics:
|
|
34
|
+
data["custom_lyrics"] = custom_lyrics
|
|
35
|
+
|
|
36
|
+
if self.async_mode:
|
|
37
|
+
return self._async_generate_karaoke(files, data, wait_for_completion, timeout)
|
|
38
|
+
else:
|
|
39
|
+
response = self.client.request(
|
|
40
|
+
"POST", "/api/v1/karaoke/generate",
|
|
41
|
+
data=data, files=files if files else None
|
|
42
|
+
)
|
|
43
|
+
job = Job.from_dict(response)
|
|
44
|
+
|
|
45
|
+
if wait_for_completion:
|
|
46
|
+
return self._wait_for_completion(job.id, timeout)
|
|
47
|
+
|
|
48
|
+
return job
|
|
49
|
+
|
|
50
|
+
async def _async_generate_karaoke(self, files, data, wait_for_completion, timeout):
|
|
51
|
+
"""Async version of generate_karaoke"""
|
|
52
|
+
response = await self.client.request(
|
|
53
|
+
"POST", "/api/v1/karaoke/generate",
|
|
54
|
+
data=data, files=files if files else None
|
|
55
|
+
)
|
|
56
|
+
job = Job.from_dict(response)
|
|
57
|
+
|
|
58
|
+
if wait_for_completion:
|
|
59
|
+
return await self._async_wait_for_completion(job.id, timeout)
|
|
60
|
+
|
|
61
|
+
return job
|
|
@@ -0,0 +1,434 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Music Service - Music generation operations
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import List, Optional, Dict, Any, Union
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
from .base import BaseService
|
|
9
|
+
from ..models import Job, MusicGenerationResult
|
|
10
|
+
from ..exceptions import ValidationError
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class MusicService(BaseService):
|
|
14
|
+
"""Service for music generation operations"""
|
|
15
|
+
|
|
16
|
+
def generate_music(
|
|
17
|
+
self,
|
|
18
|
+
prompt: str,
|
|
19
|
+
duration: float = 120.0,
|
|
20
|
+
guidance_scale: float = 7.5,
|
|
21
|
+
num_inference_steps: int = 50,
|
|
22
|
+
seed: Optional[int] = None,
|
|
23
|
+
display_name: Optional[str] = None,
|
|
24
|
+
wait_for_completion: bool = False,
|
|
25
|
+
timeout: int = 600
|
|
26
|
+
) -> Union[Job, MusicGenerationResult]:
|
|
27
|
+
"""
|
|
28
|
+
Generate music from text prompt
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
prompt: Text description of the music to generate
|
|
32
|
+
duration: Duration in seconds (10-600)
|
|
33
|
+
guidance_scale: How closely to follow the prompt (1.0-20.0)
|
|
34
|
+
num_inference_steps: Number of denoising steps (20-100)
|
|
35
|
+
seed: Random seed for reproducible results
|
|
36
|
+
display_name: Custom name for the generated track
|
|
37
|
+
wait_for_completion: Whether to wait for generation completion
|
|
38
|
+
timeout: Maximum time to wait if wait_for_completion=True
|
|
39
|
+
|
|
40
|
+
Returns:
|
|
41
|
+
Job object if wait_for_completion=False, otherwise MusicGenerationResult
|
|
42
|
+
"""
|
|
43
|
+
# Validate inputs
|
|
44
|
+
prompt = self._validate_text_input(prompt, max_length=1000)
|
|
45
|
+
if not 10.0 <= duration <= 600.0:
|
|
46
|
+
raise ValidationError("Duration must be between 10 and 600 seconds")
|
|
47
|
+
if not 1.0 <= guidance_scale <= 20.0:
|
|
48
|
+
raise ValidationError("Guidance scale must be between 1.0 and 20.0")
|
|
49
|
+
if not 20 <= num_inference_steps <= 100:
|
|
50
|
+
raise ValidationError("Inference steps must be between 20 and 100")
|
|
51
|
+
if seed is not None and (seed < 0 or seed > 2**32 - 1):
|
|
52
|
+
raise ValidationError("Seed must be between 0 and 2^32 - 1")
|
|
53
|
+
|
|
54
|
+
# Prepare request data
|
|
55
|
+
data = {
|
|
56
|
+
"prompt": prompt,
|
|
57
|
+
"duration": duration,
|
|
58
|
+
"guidance_scale": guidance_scale,
|
|
59
|
+
"num_inference_steps": num_inference_steps
|
|
60
|
+
}
|
|
61
|
+
if seed is not None:
|
|
62
|
+
data["seed"] = seed
|
|
63
|
+
if display_name:
|
|
64
|
+
data["display_name"] = display_name.strip()
|
|
65
|
+
|
|
66
|
+
# Make request
|
|
67
|
+
if self.async_mode:
|
|
68
|
+
return self._async_generate_music(data, wait_for_completion, timeout)
|
|
69
|
+
else:
|
|
70
|
+
response = self.client.request("POST", "/api/v1/music/text2music", data=data)
|
|
71
|
+
job = Job.from_dict(response)
|
|
72
|
+
|
|
73
|
+
if wait_for_completion:
|
|
74
|
+
completed_job = self._wait_for_completion(job.id, timeout)
|
|
75
|
+
return MusicGenerationResult.from_dict(completed_job.result or completed_job.__dict__)
|
|
76
|
+
|
|
77
|
+
return job
|
|
78
|
+
|
|
79
|
+
async def _async_generate_music(
|
|
80
|
+
self,
|
|
81
|
+
data: Dict[str, Any],
|
|
82
|
+
wait_for_completion: bool,
|
|
83
|
+
timeout: int
|
|
84
|
+
) -> Union[Job, MusicGenerationResult]:
|
|
85
|
+
"""Async version of generate_music"""
|
|
86
|
+
response = await self.client.request("POST", "/api/v1/music/text2music", data=data)
|
|
87
|
+
job = Job.from_dict(response)
|
|
88
|
+
|
|
89
|
+
if wait_for_completion:
|
|
90
|
+
completed_job = await self._async_wait_for_completion(job.id, timeout)
|
|
91
|
+
return MusicGenerationResult.from_dict(completed_job.result or completed_job.__dict__)
|
|
92
|
+
|
|
93
|
+
return job
|
|
94
|
+
|
|
95
|
+
def generate_rap(
|
|
96
|
+
self,
|
|
97
|
+
lyrics: str,
|
|
98
|
+
style: str = "modern",
|
|
99
|
+
tempo: int = 120,
|
|
100
|
+
display_name: Optional[str] = None,
|
|
101
|
+
wait_for_completion: bool = False,
|
|
102
|
+
timeout: int = 600
|
|
103
|
+
) -> Union[Job, MusicGenerationResult]:
|
|
104
|
+
"""
|
|
105
|
+
Generate rap music with lyrics
|
|
106
|
+
|
|
107
|
+
Args:
|
|
108
|
+
lyrics: Rap lyrics
|
|
109
|
+
style: Style of rap ('modern', 'classic', 'trap')
|
|
110
|
+
tempo: Beats per minute (80-200)
|
|
111
|
+
display_name: Custom name for the track
|
|
112
|
+
wait_for_completion: Whether to wait for completion
|
|
113
|
+
timeout: Maximum time to wait
|
|
114
|
+
|
|
115
|
+
Returns:
|
|
116
|
+
Job object or generation result
|
|
117
|
+
"""
|
|
118
|
+
# Validate inputs
|
|
119
|
+
lyrics = self._validate_text_input(lyrics, max_length=2000)
|
|
120
|
+
if not 80 <= tempo <= 200:
|
|
121
|
+
raise ValidationError("Tempo must be between 80 and 200 BPM")
|
|
122
|
+
if style not in ["modern", "classic", "trap"]:
|
|
123
|
+
raise ValidationError("Style must be 'modern', 'classic', or 'trap'")
|
|
124
|
+
|
|
125
|
+
# Prepare request data
|
|
126
|
+
data = {
|
|
127
|
+
"lyrics": lyrics,
|
|
128
|
+
"style": style,
|
|
129
|
+
"tempo": tempo
|
|
130
|
+
}
|
|
131
|
+
if display_name:
|
|
132
|
+
data["display_name"] = display_name.strip()
|
|
133
|
+
|
|
134
|
+
# Make request
|
|
135
|
+
if self.async_mode:
|
|
136
|
+
return self._async_generate_rap(data, wait_for_completion, timeout)
|
|
137
|
+
else:
|
|
138
|
+
response = self.client.request("POST", "/api/v1/music/text2rap", data=data)
|
|
139
|
+
job = Job.from_dict(response)
|
|
140
|
+
|
|
141
|
+
if wait_for_completion:
|
|
142
|
+
completed_job = self._wait_for_completion(job.id, timeout)
|
|
143
|
+
return MusicGenerationResult.from_dict(completed_job.result or completed_job.__dict__)
|
|
144
|
+
|
|
145
|
+
return job
|
|
146
|
+
|
|
147
|
+
async def _async_generate_rap(
|
|
148
|
+
self,
|
|
149
|
+
data: Dict[str, Any],
|
|
150
|
+
wait_for_completion: bool,
|
|
151
|
+
timeout: int
|
|
152
|
+
) -> Union[Job, MusicGenerationResult]:
|
|
153
|
+
"""Async version of generate_rap"""
|
|
154
|
+
response = await self.client.request("POST", "/api/v1/music/text2rap", data=data)
|
|
155
|
+
job = Job.from_dict(response)
|
|
156
|
+
|
|
157
|
+
if wait_for_completion:
|
|
158
|
+
completed_job = await self._async_wait_for_completion(job.id, timeout)
|
|
159
|
+
return MusicGenerationResult.from_dict(completed_job.result or completed_job.__dict__)
|
|
160
|
+
|
|
161
|
+
return job
|
|
162
|
+
|
|
163
|
+
def generate_instrumental(
|
|
164
|
+
self,
|
|
165
|
+
prompt: str,
|
|
166
|
+
duration: float = 120.0,
|
|
167
|
+
instruments: Optional[List[str]] = None,
|
|
168
|
+
key: Optional[str] = None,
|
|
169
|
+
tempo: Optional[int] = None,
|
|
170
|
+
display_name: Optional[str] = None,
|
|
171
|
+
wait_for_completion: bool = False,
|
|
172
|
+
timeout: int = 600
|
|
173
|
+
) -> Union[Job, MusicGenerationResult]:
|
|
174
|
+
"""
|
|
175
|
+
Generate instrumental music
|
|
176
|
+
|
|
177
|
+
Args:
|
|
178
|
+
prompt: Description of the instrumental
|
|
179
|
+
duration: Duration in seconds
|
|
180
|
+
instruments: List of instruments to include
|
|
181
|
+
key: Musical key (e.g., 'C', 'Am', 'F#')
|
|
182
|
+
tempo: Beats per minute
|
|
183
|
+
display_name: Custom name for the track
|
|
184
|
+
wait_for_completion: Whether to wait for completion
|
|
185
|
+
timeout: Maximum time to wait
|
|
186
|
+
|
|
187
|
+
Returns:
|
|
188
|
+
Job object or generation result
|
|
189
|
+
"""
|
|
190
|
+
# Validate inputs
|
|
191
|
+
prompt = self._validate_text_input(prompt, max_length=1000)
|
|
192
|
+
if not 10.0 <= duration <= 600.0:
|
|
193
|
+
raise ValidationError("Duration must be between 10 and 600 seconds")
|
|
194
|
+
if tempo is not None and not 60 <= tempo <= 200:
|
|
195
|
+
raise ValidationError("Tempo must be between 60 and 200 BPM")
|
|
196
|
+
|
|
197
|
+
# Prepare request data
|
|
198
|
+
data = {
|
|
199
|
+
"prompt": prompt,
|
|
200
|
+
"duration": duration
|
|
201
|
+
}
|
|
202
|
+
if instruments:
|
|
203
|
+
data["instruments"] = instruments
|
|
204
|
+
if key:
|
|
205
|
+
data["key"] = key
|
|
206
|
+
if tempo:
|
|
207
|
+
data["tempo"] = tempo
|
|
208
|
+
if display_name:
|
|
209
|
+
data["display_name"] = display_name.strip()
|
|
210
|
+
|
|
211
|
+
# Make request
|
|
212
|
+
if self.async_mode:
|
|
213
|
+
return self._async_generate_instrumental(data, wait_for_completion, timeout)
|
|
214
|
+
else:
|
|
215
|
+
response = self.client.request("POST", "/api/v1/music/prompt2instrumental", data=data)
|
|
216
|
+
job = Job.from_dict(response)
|
|
217
|
+
|
|
218
|
+
if wait_for_completion:
|
|
219
|
+
completed_job = self._wait_for_completion(job.id, timeout)
|
|
220
|
+
return MusicGenerationResult.from_dict(completed_job.result or completed_job.__dict__)
|
|
221
|
+
|
|
222
|
+
return job
|
|
223
|
+
|
|
224
|
+
async def _async_generate_instrumental(
|
|
225
|
+
self,
|
|
226
|
+
data: Dict[str, Any],
|
|
227
|
+
wait_for_completion: bool,
|
|
228
|
+
timeout: int
|
|
229
|
+
) -> Union[Job, MusicGenerationResult]:
|
|
230
|
+
"""Async version of generate_instrumental"""
|
|
231
|
+
response = await self.client.request("POST", "/api/v1/music/prompt2instrumental", data=data)
|
|
232
|
+
job = Job.from_dict(response)
|
|
233
|
+
|
|
234
|
+
if wait_for_completion:
|
|
235
|
+
completed_job = await self._async_wait_for_completion(job.id, timeout)
|
|
236
|
+
return MusicGenerationResult.from_dict(completed_job.result or completed_job.__dict__)
|
|
237
|
+
|
|
238
|
+
return job
|
|
239
|
+
|
|
240
|
+
def extend_music(
|
|
241
|
+
self,
|
|
242
|
+
source_job_id: int,
|
|
243
|
+
extend_duration: float = 30.0,
|
|
244
|
+
display_name: Optional[str] = None,
|
|
245
|
+
wait_for_completion: bool = False,
|
|
246
|
+
timeout: int = 600
|
|
247
|
+
) -> Union[Job, MusicGenerationResult]:
|
|
248
|
+
"""
|
|
249
|
+
Extend an existing music track
|
|
250
|
+
|
|
251
|
+
Args:
|
|
252
|
+
source_job_id: ID of the original music generation job
|
|
253
|
+
extend_duration: How many seconds to extend
|
|
254
|
+
display_name: Custom name for the extended track
|
|
255
|
+
wait_for_completion: Whether to wait for completion
|
|
256
|
+
timeout: Maximum time to wait
|
|
257
|
+
|
|
258
|
+
Returns:
|
|
259
|
+
Job object or generation result
|
|
260
|
+
"""
|
|
261
|
+
# Validate inputs
|
|
262
|
+
if not 5.0 <= extend_duration <= 120.0:
|
|
263
|
+
raise ValidationError("Extend duration must be between 5 and 120 seconds")
|
|
264
|
+
|
|
265
|
+
# Prepare request data
|
|
266
|
+
data = {
|
|
267
|
+
"source_job_id": source_job_id,
|
|
268
|
+
"extend_duration": extend_duration
|
|
269
|
+
}
|
|
270
|
+
if display_name:
|
|
271
|
+
data["display_name"] = display_name.strip()
|
|
272
|
+
|
|
273
|
+
# Make request
|
|
274
|
+
if self.async_mode:
|
|
275
|
+
return self._async_extend_music(data, wait_for_completion, timeout)
|
|
276
|
+
else:
|
|
277
|
+
response = self.client.request("POST", "/api/v1/music/extend", data=data)
|
|
278
|
+
job = Job.from_dict(response)
|
|
279
|
+
|
|
280
|
+
if wait_for_completion:
|
|
281
|
+
completed_job = self._wait_for_completion(job.id, timeout)
|
|
282
|
+
return MusicGenerationResult.from_dict(completed_job.result or completed_job.__dict__)
|
|
283
|
+
|
|
284
|
+
return job
|
|
285
|
+
|
|
286
|
+
async def _async_extend_music(
|
|
287
|
+
self,
|
|
288
|
+
data: Dict[str, Any],
|
|
289
|
+
wait_for_completion: bool,
|
|
290
|
+
timeout: int
|
|
291
|
+
) -> Union[Job, MusicGenerationResult]:
|
|
292
|
+
"""Async version of extend_music"""
|
|
293
|
+
response = await self.client.request("POST", "/api/v1/music/extend", data=data)
|
|
294
|
+
job = Job.from_dict(response)
|
|
295
|
+
|
|
296
|
+
if wait_for_completion:
|
|
297
|
+
completed_job = await self._async_wait_for_completion(job.id, timeout)
|
|
298
|
+
return MusicGenerationResult.from_dict(completed_job.result or completed_job.__dict__)
|
|
299
|
+
|
|
300
|
+
return job
|
|
301
|
+
|
|
302
|
+
def list_music_jobs(
|
|
303
|
+
self,
|
|
304
|
+
limit: int = 50,
|
|
305
|
+
offset: int = 0,
|
|
306
|
+
status: Optional[str] = None,
|
|
307
|
+
task: Optional[str] = None,
|
|
308
|
+
liked: Optional[bool] = None
|
|
309
|
+
) -> List[MusicGenerationResult]:
|
|
310
|
+
"""
|
|
311
|
+
List music generation jobs
|
|
312
|
+
|
|
313
|
+
Args:
|
|
314
|
+
limit: Maximum number of results
|
|
315
|
+
offset: Number of results to skip
|
|
316
|
+
status: Filter by job status
|
|
317
|
+
task: Filter by task type
|
|
318
|
+
liked: Filter by liked status
|
|
319
|
+
|
|
320
|
+
Returns:
|
|
321
|
+
List of music generation results
|
|
322
|
+
"""
|
|
323
|
+
params = {
|
|
324
|
+
"limit": limit,
|
|
325
|
+
"skip": offset
|
|
326
|
+
}
|
|
327
|
+
if status:
|
|
328
|
+
params["status"] = status
|
|
329
|
+
if task:
|
|
330
|
+
params["task"] = task
|
|
331
|
+
if liked is not None:
|
|
332
|
+
params["liked"] = liked
|
|
333
|
+
|
|
334
|
+
if self.async_mode:
|
|
335
|
+
return self._async_list_music_jobs(params)
|
|
336
|
+
else:
|
|
337
|
+
response = self.client.request("GET", "/api/v1/music/jobs", params=params)
|
|
338
|
+
return [MusicGenerationResult.from_dict(job_data) for job_data in response]
|
|
339
|
+
|
|
340
|
+
async def _async_list_music_jobs(self, params: Dict[str, Any]) -> List[MusicGenerationResult]:
|
|
341
|
+
"""Async version of list_music_jobs"""
|
|
342
|
+
response = await self.client.request("GET", "/api/v1/music/jobs", params=params)
|
|
343
|
+
return [MusicGenerationResult.from_dict(job_data) for job_data in response]
|
|
344
|
+
|
|
345
|
+
def get_music_job(self, job_id: int) -> MusicGenerationResult:
|
|
346
|
+
"""
|
|
347
|
+
Get details of a specific music generation job
|
|
348
|
+
|
|
349
|
+
Args:
|
|
350
|
+
job_id: ID of the music job
|
|
351
|
+
|
|
352
|
+
Returns:
|
|
353
|
+
Music generation result
|
|
354
|
+
"""
|
|
355
|
+
if self.async_mode:
|
|
356
|
+
return self._async_get_music_job(job_id)
|
|
357
|
+
else:
|
|
358
|
+
response = self.client.request("GET", f"/api/v1/music/jobs/{job_id}/status")
|
|
359
|
+
return MusicGenerationResult.from_dict(response)
|
|
360
|
+
|
|
361
|
+
async def _async_get_music_job(self, job_id: int) -> MusicGenerationResult:
|
|
362
|
+
"""Async version of get_music_job"""
|
|
363
|
+
response = await self.client.request("GET", f"/api/v1/music/jobs/{job_id}/status")
|
|
364
|
+
return MusicGenerationResult.from_dict(response)
|
|
365
|
+
|
|
366
|
+
def like_music_track(self, job_id: int) -> Dict[str, Any]:
|
|
367
|
+
"""
|
|
368
|
+
Like a music track
|
|
369
|
+
|
|
370
|
+
Args:
|
|
371
|
+
job_id: ID of the music job
|
|
372
|
+
|
|
373
|
+
Returns:
|
|
374
|
+
Like response
|
|
375
|
+
"""
|
|
376
|
+
if self.async_mode:
|
|
377
|
+
return self._async_like_music_track(job_id)
|
|
378
|
+
else:
|
|
379
|
+
return self.client.request("POST", f"/api/v1/music/jobs/{job_id}/like")
|
|
380
|
+
|
|
381
|
+
async def _async_like_music_track(self, job_id: int) -> Dict[str, Any]:
|
|
382
|
+
"""Async version of like_music_track"""
|
|
383
|
+
return await self.client.request("POST", f"/api/v1/music/jobs/{job_id}/like")
|
|
384
|
+
|
|
385
|
+
def share_music_track(
|
|
386
|
+
self,
|
|
387
|
+
job_id: int,
|
|
388
|
+
platform: Optional[str] = None,
|
|
389
|
+
message: Optional[str] = None
|
|
390
|
+
) -> Dict[str, Any]:
|
|
391
|
+
"""
|
|
392
|
+
Share a music track
|
|
393
|
+
|
|
394
|
+
Args:
|
|
395
|
+
job_id: ID of the music job
|
|
396
|
+
platform: Platform to share on
|
|
397
|
+
message: Optional message to include
|
|
398
|
+
|
|
399
|
+
Returns:
|
|
400
|
+
Share response with shareable URL
|
|
401
|
+
"""
|
|
402
|
+
data = {}
|
|
403
|
+
if platform:
|
|
404
|
+
data["platform"] = platform
|
|
405
|
+
if message:
|
|
406
|
+
data["message"] = message
|
|
407
|
+
|
|
408
|
+
if self.async_mode:
|
|
409
|
+
return self._async_share_music_track(job_id, data)
|
|
410
|
+
else:
|
|
411
|
+
return self.client.request("POST", f"/api/v1/music/jobs/{job_id}/share", data=data)
|
|
412
|
+
|
|
413
|
+
async def _async_share_music_track(self, job_id: int, data: Dict[str, Any]) -> Dict[str, Any]:
|
|
414
|
+
"""Async version of share_music_track"""
|
|
415
|
+
return await self.client.request("POST", f"/api/v1/music/jobs/{job_id}/share", data=data)
|
|
416
|
+
|
|
417
|
+
def delete_music_job(self, job_id: int) -> Dict[str, str]:
|
|
418
|
+
"""
|
|
419
|
+
Delete a music generation job
|
|
420
|
+
|
|
421
|
+
Args:
|
|
422
|
+
job_id: ID of the music job
|
|
423
|
+
|
|
424
|
+
Returns:
|
|
425
|
+
Deletion confirmation
|
|
426
|
+
"""
|
|
427
|
+
if self.async_mode:
|
|
428
|
+
return self._async_delete_music_job(job_id)
|
|
429
|
+
else:
|
|
430
|
+
return self.client.request("DELETE", f"/api/v1/music/jobs/{job_id}")
|
|
431
|
+
|
|
432
|
+
async def _async_delete_music_job(self, job_id: int) -> Dict[str, str]:
|
|
433
|
+
"""Async version of delete_music_job"""
|
|
434
|
+
return await self.client.request("DELETE", f"/api/v1/music/jobs/{job_id}")
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Speaker Service - Speaker analysis and diarization
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Optional, Union
|
|
6
|
+
from .base import BaseService
|
|
7
|
+
from ..models import Job, SpeakerAnalysisResult
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class SpeakerService(BaseService):
|
|
11
|
+
"""Service for speaker diarization and analysis"""
|
|
12
|
+
|
|
13
|
+
def diarize_speakers(
|
|
14
|
+
self,
|
|
15
|
+
audio_file: str,
|
|
16
|
+
num_speakers: Optional[int] = None,
|
|
17
|
+
wait_for_completion: bool = False,
|
|
18
|
+
timeout: int = 600
|
|
19
|
+
) -> Union[Job, SpeakerAnalysisResult]:
|
|
20
|
+
"""Identify and separate speakers in audio"""
|
|
21
|
+
files = self._prepare_file_upload(audio_file, "file")
|
|
22
|
+
data = {}
|
|
23
|
+
if num_speakers:
|
|
24
|
+
data["num_speakers"] = num_speakers
|
|
25
|
+
|
|
26
|
+
if self.async_mode:
|
|
27
|
+
return self._async_diarize(files, data, wait_for_completion, timeout)
|
|
28
|
+
else:
|
|
29
|
+
response = self.client.request(
|
|
30
|
+
"POST", "/api/v1/speaker/diarize",
|
|
31
|
+
data=data, files=files
|
|
32
|
+
)
|
|
33
|
+
job = Job.from_dict(response)
|
|
34
|
+
|
|
35
|
+
if wait_for_completion:
|
|
36
|
+
completed_job = self._wait_for_completion(job.id, timeout)
|
|
37
|
+
return SpeakerAnalysisResult.from_dict(completed_job.result or completed_job.__dict__)
|
|
38
|
+
|
|
39
|
+
return job
|
|
40
|
+
|
|
41
|
+
async def _async_diarize(self, files, data, wait_for_completion, timeout):
|
|
42
|
+
"""Async version of diarize_speakers"""
|
|
43
|
+
response = await self.client.request(
|
|
44
|
+
"POST", "/api/v1/speaker/diarize",
|
|
45
|
+
data=data, files=files
|
|
46
|
+
)
|
|
47
|
+
job = Job.from_dict(response)
|
|
48
|
+
|
|
49
|
+
if wait_for_completion:
|
|
50
|
+
completed_job = await self._async_wait_for_completion(job.id, timeout)
|
|
51
|
+
return SpeakerAnalysisResult.from_dict(completed_job.result or completed_job.__dict__)
|
|
52
|
+
|
|
53
|
+
return job
|