audiopod 1.5.0__py3-none-any.whl → 2.1.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.
@@ -1,329 +0,0 @@
1
- """
2
- Video Service - Video generation
3
-
4
- API Routes:
5
- - POST /api/v2/video/karaoke - Create karaoke video
6
- - POST /api/v2/video/music-video - Create AI music video
7
- - POST /api/v2/video/lyric-video - Create lyric video
8
- - GET /api/v2/video/{id} - Get job status
9
- - GET /api/v2/video/{id}/download - Get download URLs
10
- - DELETE /api/v2/video/{id} - Delete job
11
- """
12
-
13
- from typing import Optional, Dict, Any, List, Literal
14
- from .base import BaseService
15
-
16
-
17
- VideoType = Literal["karaoke", "music_video", "lyric_video"]
18
- AspectRatio = Literal["16:9", "9:16", "1:1"]
19
- SceneGeneration = Literal["flux", "veo3", "static"]
20
- AnimationStyle = Literal["typewriter", "fade-word", "slide-up", "bounce", "wave", "zoom"]
21
-
22
-
23
- class VideoService(BaseService):
24
- """Service for video generation."""
25
-
26
- def create_karaoke(
27
- self,
28
- audio_file: Optional[str] = None,
29
- url: Optional[str] = None,
30
- aspect_ratio: AspectRatio = "16:9",
31
- scene_generation: SceneGeneration = "flux",
32
- key_shift: int = 0,
33
- style: Optional[Dict[str, Any]] = None,
34
- webhook_url: Optional[str] = None,
35
- wait_for_completion: bool = False,
36
- timeout: int = 600,
37
- ) -> Dict[str, Any]:
38
- """
39
- Create a karaoke video from audio.
40
-
41
- Args:
42
- audio_file: Path to local audio file
43
- url: URL of audio/video (YouTube, etc.)
44
- aspect_ratio: Video aspect ratio
45
- scene_generation: Background generation method
46
- key_shift: Pitch shift in semitones (-12 to 12)
47
- style: Style configuration (colors, font, etc.)
48
- webhook_url: URL for completion notification
49
- wait_for_completion: Wait for video to finish
50
- timeout: Max wait time in seconds
51
-
52
- Returns:
53
- Job dict with id, status, video_url (when completed)
54
- """
55
- if audio_file:
56
- return self._create_from_upload(
57
- "karaoke", audio_file, aspect_ratio, scene_generation,
58
- key_shift, webhook_url, wait_for_completion, timeout
59
- )
60
-
61
- data = {
62
- "audio_url": url,
63
- "aspect_ratio": aspect_ratio,
64
- "scene_generation": scene_generation,
65
- "key_shift": key_shift,
66
- }
67
- if style:
68
- data["style"] = style
69
- if webhook_url:
70
- data["webhook_url"] = webhook_url
71
-
72
- if self.async_mode:
73
- return self._async_create("karaoke", data, wait_for_completion, timeout)
74
-
75
- response = self.client.request("POST", "/api/v2/video/karaoke", json_data=data)
76
-
77
- if wait_for_completion:
78
- return self._wait_for_video(response["id"], timeout)
79
- return response
80
-
81
- def create_music_video(
82
- self,
83
- audio_file: Optional[str] = None,
84
- url: Optional[str] = None,
85
- music_job_id: Optional[str] = None,
86
- aspect_ratio: AspectRatio = "16:9",
87
- scene_generation: SceneGeneration = "flux",
88
- scene_count: int = 10,
89
- include_lyrics: bool = True,
90
- style: Optional[Dict[str, Any]] = None,
91
- webhook_url: Optional[str] = None,
92
- wait_for_completion: bool = False,
93
- timeout: int = 900,
94
- ) -> Dict[str, Any]:
95
- """
96
- Create an AI music video.
97
-
98
- Args:
99
- audio_file: Path to local audio file
100
- url: URL of audio file
101
- music_job_id: AudioPod music generation job ID
102
- aspect_ratio: Video aspect ratio
103
- scene_generation: Scene generation method (flux or veo3)
104
- scene_count: Number of scenes to generate (5-20)
105
- include_lyrics: Overlay lyrics on video
106
- style: Style configuration
107
- webhook_url: URL for completion notification
108
- wait_for_completion: Wait for video to finish
109
- timeout: Max wait time in seconds
110
-
111
- Returns:
112
- Job dict with video_url when completed
113
- """
114
- data = {
115
- "aspect_ratio": aspect_ratio,
116
- "scene_generation": scene_generation,
117
- "scene_count": scene_count,
118
- "include_lyrics": include_lyrics,
119
- }
120
- if url:
121
- data["audio_url"] = url
122
- if music_job_id:
123
- data["music_job_id"] = music_job_id
124
- if style:
125
- data["style"] = style
126
- if webhook_url:
127
- data["webhook_url"] = webhook_url
128
-
129
- if self.async_mode:
130
- return self._async_create("music-video", data, wait_for_completion, timeout)
131
-
132
- response = self.client.request("POST", "/api/v2/video/music-video", json_data=data)
133
-
134
- if wait_for_completion:
135
- return self._wait_for_video(response["id"], timeout)
136
- return response
137
-
138
- def create_lyric_video(
139
- self,
140
- audio_file: Optional[str] = None,
141
- url: Optional[str] = None,
142
- lyrics: Optional[str] = None,
143
- aspect_ratio: AspectRatio = "16:9",
144
- animation_style: AnimationStyle = "fade-word",
145
- background_color: str = "#1a1a2e",
146
- accent_color: str = "#e94560",
147
- particles_enabled: bool = True,
148
- background_image_url: Optional[str] = None,
149
- webhook_url: Optional[str] = None,
150
- wait_for_completion: bool = False,
151
- timeout: int = 600,
152
- ) -> Dict[str, Any]:
153
- """
154
- Create a kinetic typography lyric video.
155
-
156
- Args:
157
- audio_file: Path to local audio file
158
- url: URL of audio file
159
- lyrics: Custom lyrics (auto-transcribed if not provided)
160
- aspect_ratio: Video aspect ratio
161
- animation_style: Text animation style
162
- background_color: Background color
163
- accent_color: Accent/highlight color
164
- particles_enabled: Enable particle effects
165
- background_image_url: Optional background image
166
- webhook_url: URL for completion notification
167
- wait_for_completion: Wait for video to finish
168
- timeout: Max wait time in seconds
169
-
170
- Returns:
171
- Job dict with video_url when completed
172
- """
173
- data = {
174
- "aspect_ratio": aspect_ratio,
175
- "style": {
176
- "animation_style": animation_style,
177
- "background_color": background_color,
178
- "accent_color": accent_color,
179
- "particles_enabled": particles_enabled,
180
- },
181
- }
182
- if url:
183
- data["audio_url"] = url
184
- if lyrics:
185
- data["lyrics"] = lyrics
186
- if background_image_url:
187
- data["background_image_url"] = background_image_url
188
- if webhook_url:
189
- data["webhook_url"] = webhook_url
190
-
191
- if self.async_mode:
192
- return self._async_create("lyric-video", data, wait_for_completion, timeout)
193
-
194
- response = self.client.request("POST", "/api/v2/video/lyric-video", json_data=data)
195
-
196
- if wait_for_completion:
197
- return self._wait_for_video(response["id"], timeout)
198
- return response
199
-
200
- def get_job(self, job_id: str) -> Dict[str, Any]:
201
- """Get video job status."""
202
- if self.async_mode:
203
- return self._async_get_job(job_id)
204
- return self.client.request("GET", f"/api/v2/video/{job_id}")
205
-
206
- async def _async_get_job(self, job_id: str) -> Dict[str, Any]:
207
- return await self.client.request("GET", f"/api/v2/video/{job_id}")
208
-
209
- def get_download_urls(self, job_id: str) -> Dict[str, Any]:
210
- """Get download URLs for completed video."""
211
- if self.async_mode:
212
- return self._async_get_download(job_id)
213
- return self.client.request("GET", f"/api/v2/video/{job_id}/download")
214
-
215
- async def _async_get_download(self, job_id: str) -> Dict[str, Any]:
216
- return await self.client.request("GET", f"/api/v2/video/{job_id}/download")
217
-
218
- def delete_job(self, job_id: str) -> Dict[str, str]:
219
- """Delete a video job."""
220
- if self.async_mode:
221
- return self._async_delete(job_id)
222
- return self.client.request("DELETE", f"/api/v2/video/{job_id}")
223
-
224
- async def _async_delete(self, job_id: str) -> Dict[str, str]:
225
- return await self.client.request("DELETE", f"/api/v2/video/{job_id}")
226
-
227
- def list_jobs(
228
- self,
229
- video_type: Optional[VideoType] = None,
230
- limit: int = 20,
231
- offset: int = 0,
232
- ) -> Dict[str, Any]:
233
- """List video jobs."""
234
- params = {"limit": limit, "offset": offset}
235
- if video_type:
236
- params["type"] = video_type
237
-
238
- if self.async_mode:
239
- return self._async_list(params)
240
- return self.client.request("GET", "/api/v2/video/", params=params)
241
-
242
- async def _async_list(self, params: Dict) -> Dict[str, Any]:
243
- return await self.client.request("GET", "/api/v2/video/", params=params)
244
-
245
- def _create_from_upload(
246
- self,
247
- video_type: str,
248
- audio_file: str,
249
- aspect_ratio: str,
250
- scene_generation: str,
251
- key_shift: int,
252
- webhook_url: Optional[str],
253
- wait_for_completion: bool,
254
- timeout: int,
255
- ) -> Dict[str, Any]:
256
- """Create video from file upload."""
257
- data = {
258
- "aspect_ratio": aspect_ratio,
259
- "scene_generation": scene_generation,
260
- "key_shift": str(key_shift),
261
- }
262
- if webhook_url:
263
- data["webhook_url"] = webhook_url
264
-
265
- files = self._prepare_file_upload(audio_file, "file")
266
-
267
- response = self.client.request(
268
- "POST", f"/api/v2/video/{video_type}/upload", data=data, files=files
269
- )
270
-
271
- if wait_for_completion:
272
- return self._wait_for_video(response["id"], timeout)
273
- return response
274
-
275
- async def _async_create(
276
- self,
277
- endpoint: str,
278
- data: Dict,
279
- wait_for_completion: bool,
280
- timeout: int,
281
- ) -> Dict[str, Any]:
282
- response = await self.client.request(
283
- "POST", f"/api/v2/video/{endpoint}", json_data=data
284
- )
285
- if wait_for_completion:
286
- return await self._async_wait_for_video(response["id"], timeout)
287
- return response
288
-
289
- def _wait_for_video(self, job_id: str, timeout: int) -> Dict[str, Any]:
290
- """Wait for video job completion."""
291
- import time
292
- start = time.time()
293
-
294
- while time.time() - start < timeout:
295
- job = self.get_job(job_id)
296
- status = job.get("status", "")
297
-
298
- if status == "completed":
299
- # Get download URLs
300
- download = self.get_download_urls(job_id)
301
- job.update(download)
302
- return job
303
- elif status == "failed":
304
- raise Exception(f"Video generation failed: {job.get('error', 'Unknown')}")
305
-
306
- time.sleep(5)
307
-
308
- raise TimeoutError(f"Video {job_id} timed out after {timeout}s")
309
-
310
- async def _async_wait_for_video(self, job_id: str, timeout: int) -> Dict[str, Any]:
311
- """Async wait for video job completion."""
312
- import asyncio
313
- import time
314
- start = time.time()
315
-
316
- while time.time() - start < timeout:
317
- job = await self.get_job(job_id)
318
- status = job.get("status", "")
319
-
320
- if status == "completed":
321
- download = await self.get_download_urls(job_id)
322
- job.update(download)
323
- return job
324
- elif status == "failed":
325
- raise Exception(f"Video generation failed: {job.get('error', 'Unknown')}")
326
-
327
- await asyncio.sleep(5)
328
-
329
- raise TimeoutError(f"Video {job_id} timed out after {timeout}s")
@@ -1,187 +0,0 @@
1
- """
2
- Voice Service - Voice cloning and text-to-speech
3
-
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
- """
12
-
13
- from typing import Optional, Dict, Any, List, Union
14
- from .base import BaseService
15
-
16
-
17
- class VoiceService(BaseService):
18
- """Service for voice cloning and text-to-speech."""
19
-
20
- def list_voices(
21
- self,
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(),
31
- }
32
- if self.async_mode:
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(
49
- self,
50
- name: str,
51
- audio_file: str,
52
- description: Optional[str] = None,
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}
57
- if description:
58
- data["description"] = description
59
-
60
- if self.async_mode:
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
-
76
- def generate_speech(
77
- self,
78
- voice_id: Union[int, str],
79
- text: str,
80
- speed: float = 1.0,
81
- language: str = "en",
82
- audio_format: str = "mp3",
83
- wait_for_completion: bool = False,
84
- timeout: int = 300,
85
- ) -> Dict[str, Any]:
86
- """
87
- Generate speech from text using a voice.
88
-
89
- Args:
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
97
-
98
- Returns:
99
- Job info dict with job_id, status, etc.
100
- If wait_for_completion=True, includes output_url when done.
101
- """
102
- data = {
103
- "input_text": text,
104
- "speed": speed,
105
- "language": language,
106
- "audio_format": audio_format,
107
- }
108
-
109
- if self.async_mode:
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
-
123
- async def _async_generate_speech(
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]:
137
- """
138
- Get TTS job status.
139
-
140
- Args:
141
- job_id: The job ID returned from generate_speech
142
-
143
- Returns:
144
- Job status dict with status, progress, output_url (when completed), etc.
145
- """
146
- if self.async_mode:
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()
157
-
158
- while time.time() - start_time < timeout:
159
- status = self.get_job_status(job_id)
160
-
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')}")
165
-
166
- time.sleep(2)
167
-
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()
175
-
176
- while time.time() - start_time < timeout:
177
- status = await self.get_job_status(job_id)
178
-
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')}")
183
-
184
- await asyncio.sleep(2)
185
-
186
- raise TimeoutError(f"Job {job_id} did not complete within {timeout} seconds")
187
-