audiopod 1.4.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.
- audiopod/__init__.py +13 -8
- audiopod/client.py +125 -233
- audiopod/exceptions.py +27 -19
- audiopod/resources/__init__.py +23 -0
- audiopod/resources/denoiser.py +116 -0
- audiopod/resources/music.py +166 -0
- audiopod/resources/speaker.py +132 -0
- audiopod/resources/stems.py +267 -0
- audiopod/resources/transcription.py +205 -0
- audiopod/resources/voice.py +139 -0
- audiopod/resources/wallet.py +110 -0
- audiopod-2.1.0.dist-info/METADATA +205 -0
- audiopod-2.1.0.dist-info/RECORD +16 -0
- {audiopod-1.4.0.dist-info → audiopod-2.1.0.dist-info}/WHEEL +1 -1
- audiopod/config.py +0 -17
- audiopod/services/__init__.py +0 -28
- audiopod/services/base.py +0 -69
- audiopod/services/credits.py +0 -42
- audiopod/services/denoiser.py +0 -131
- audiopod/services/music.py +0 -217
- audiopod/services/speaker.py +0 -134
- audiopod/services/stem_extraction.py +0 -168
- audiopod/services/transcription.py +0 -187
- audiopod/services/translation.py +0 -135
- audiopod/services/voice.py +0 -187
- audiopod/services/wallet.py +0 -235
- audiopod-1.4.0.dist-info/METADATA +0 -206
- audiopod-1.4.0.dist-info/RECORD +0 -20
- {audiopod-1.4.0.dist-info → audiopod-2.1.0.dist-info}/licenses/LICENSE +0 -0
- {audiopod-1.4.0.dist-info → audiopod-2.1.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Transcription Service - Speech-to-Text
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import time
|
|
6
|
+
from typing import Optional, List, Dict, Any, TYPE_CHECKING
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
from ..client import AudioPod
|
|
10
|
+
|
|
11
|
+
POLL_INTERVAL = 3 # seconds
|
|
12
|
+
DEFAULT_TIMEOUT = 600 # 10 minutes
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Transcription:
|
|
16
|
+
"""Speech-to-text transcription service."""
|
|
17
|
+
|
|
18
|
+
def __init__(self, client: "AudioPod"):
|
|
19
|
+
self._client = client
|
|
20
|
+
|
|
21
|
+
def create(
|
|
22
|
+
self,
|
|
23
|
+
*,
|
|
24
|
+
url: Optional[str] = None,
|
|
25
|
+
urls: Optional[List[str]] = None,
|
|
26
|
+
language: Optional[str] = None,
|
|
27
|
+
speaker_diarization: bool = False,
|
|
28
|
+
min_speakers: Optional[int] = None,
|
|
29
|
+
max_speakers: Optional[int] = None,
|
|
30
|
+
word_timestamps: bool = True,
|
|
31
|
+
) -> Dict[str, Any]:
|
|
32
|
+
"""
|
|
33
|
+
Create a transcription job from URL(s).
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
url: Single URL to transcribe
|
|
37
|
+
urls: List of URLs to transcribe
|
|
38
|
+
language: Language code (ISO 639-1). Auto-detected if not specified
|
|
39
|
+
speaker_diarization: Enable speaker diarization
|
|
40
|
+
min_speakers: Minimum number of speakers (hint for diarization)
|
|
41
|
+
max_speakers: Maximum number of speakers (hint for diarization)
|
|
42
|
+
word_timestamps: Enable word-level timestamps
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
Job object with id and status
|
|
46
|
+
|
|
47
|
+
Example:
|
|
48
|
+
>>> job = client.transcription.create(
|
|
49
|
+
... url="https://youtube.com/watch?v=...",
|
|
50
|
+
... speaker_diarization=True,
|
|
51
|
+
... )
|
|
52
|
+
"""
|
|
53
|
+
source_urls = urls or ([url] if url else [])
|
|
54
|
+
if not source_urls:
|
|
55
|
+
raise ValueError("At least one URL is required")
|
|
56
|
+
|
|
57
|
+
return self._client.post(
|
|
58
|
+
"/api/v1/transcribe/transcribe",
|
|
59
|
+
json_data={
|
|
60
|
+
"source_urls": source_urls,
|
|
61
|
+
"language": language,
|
|
62
|
+
"enable_speaker_diarization": speaker_diarization,
|
|
63
|
+
"min_speakers": min_speakers,
|
|
64
|
+
"max_speakers": max_speakers,
|
|
65
|
+
"enable_word_timestamps": word_timestamps,
|
|
66
|
+
},
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
def upload(
|
|
70
|
+
self,
|
|
71
|
+
file_path: str,
|
|
72
|
+
*,
|
|
73
|
+
language: Optional[str] = None,
|
|
74
|
+
speaker_diarization: bool = False,
|
|
75
|
+
) -> Dict[str, Any]:
|
|
76
|
+
"""
|
|
77
|
+
Upload a file for transcription.
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
file_path: Path to the audio file
|
|
81
|
+
language: Language code (ISO 639-1)
|
|
82
|
+
speaker_diarization: Enable speaker diarization
|
|
83
|
+
|
|
84
|
+
Returns:
|
|
85
|
+
Job object with id and status
|
|
86
|
+
"""
|
|
87
|
+
return self._client.upload(
|
|
88
|
+
"/api/v1/transcribe/transcribe-upload",
|
|
89
|
+
file_path,
|
|
90
|
+
field_name="files",
|
|
91
|
+
additional_fields={
|
|
92
|
+
"language": language,
|
|
93
|
+
"enable_speaker_diarization": speaker_diarization,
|
|
94
|
+
},
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
def get(self, job_id: int) -> Dict[str, Any]:
|
|
98
|
+
"""Get a transcription job by ID."""
|
|
99
|
+
return self._client.get(f"/api/v1/transcribe/jobs/{job_id}")
|
|
100
|
+
|
|
101
|
+
def list(
|
|
102
|
+
self,
|
|
103
|
+
*,
|
|
104
|
+
skip: int = 0,
|
|
105
|
+
limit: int = 50,
|
|
106
|
+
status: Optional[str] = None,
|
|
107
|
+
) -> List[Dict[str, Any]]:
|
|
108
|
+
"""List transcription jobs."""
|
|
109
|
+
return self._client.get(
|
|
110
|
+
"/api/v1/transcribe/jobs",
|
|
111
|
+
params={"offset": skip, "limit": limit, "status": status},
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
def delete(self, job_id: int) -> None:
|
|
115
|
+
"""Delete a transcription job."""
|
|
116
|
+
self._client.delete(f"/api/v1/transcribe/jobs/{job_id}")
|
|
117
|
+
|
|
118
|
+
def get_transcript(
|
|
119
|
+
self, job_id: int, format: str = "json"
|
|
120
|
+
) -> Any:
|
|
121
|
+
"""
|
|
122
|
+
Get transcript content.
|
|
123
|
+
|
|
124
|
+
Args:
|
|
125
|
+
job_id: Job ID
|
|
126
|
+
format: Output format ('json', 'txt', 'srt', 'vtt')
|
|
127
|
+
"""
|
|
128
|
+
return self._client.get(
|
|
129
|
+
f"/api/v1/transcribe/jobs/{job_id}/transcript",
|
|
130
|
+
params={"format": format},
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
def wait_for_completion(
|
|
134
|
+
self, job_id: int, timeout: int = DEFAULT_TIMEOUT
|
|
135
|
+
) -> Dict[str, Any]:
|
|
136
|
+
"""
|
|
137
|
+
Wait for a transcription job to complete.
|
|
138
|
+
|
|
139
|
+
Args:
|
|
140
|
+
job_id: Job ID to wait for
|
|
141
|
+
timeout: Maximum time to wait in seconds
|
|
142
|
+
|
|
143
|
+
Returns:
|
|
144
|
+
Completed job object
|
|
145
|
+
|
|
146
|
+
Raises:
|
|
147
|
+
TimeoutError: If job doesn't complete within timeout
|
|
148
|
+
RuntimeError: If job fails
|
|
149
|
+
"""
|
|
150
|
+
start_time = time.time()
|
|
151
|
+
|
|
152
|
+
while time.time() - start_time < timeout:
|
|
153
|
+
job = self.get(job_id)
|
|
154
|
+
status = job.get("status", "")
|
|
155
|
+
|
|
156
|
+
if status == "COMPLETED":
|
|
157
|
+
return job
|
|
158
|
+
if status == "FAILED":
|
|
159
|
+
raise RuntimeError(
|
|
160
|
+
f"Transcription failed: {job.get('error_message', 'Unknown error')}"
|
|
161
|
+
)
|
|
162
|
+
if status == "CANCELLED":
|
|
163
|
+
raise RuntimeError("Transcription was cancelled")
|
|
164
|
+
|
|
165
|
+
time.sleep(POLL_INTERVAL)
|
|
166
|
+
|
|
167
|
+
raise TimeoutError(f"Transcription timed out after {timeout}s")
|
|
168
|
+
|
|
169
|
+
def transcribe(
|
|
170
|
+
self,
|
|
171
|
+
*,
|
|
172
|
+
url: Optional[str] = None,
|
|
173
|
+
urls: Optional[List[str]] = None,
|
|
174
|
+
language: Optional[str] = None,
|
|
175
|
+
speaker_diarization: bool = False,
|
|
176
|
+
min_speakers: Optional[int] = None,
|
|
177
|
+
max_speakers: Optional[int] = None,
|
|
178
|
+
timeout: int = DEFAULT_TIMEOUT,
|
|
179
|
+
) -> Dict[str, Any]:
|
|
180
|
+
"""
|
|
181
|
+
Create and wait for transcription to complete.
|
|
182
|
+
|
|
183
|
+
This is a convenience method that combines create() and wait_for_completion().
|
|
184
|
+
|
|
185
|
+
Example:
|
|
186
|
+
>>> result = client.transcription.transcribe(
|
|
187
|
+
... url="https://youtube.com/watch?v=...",
|
|
188
|
+
... speaker_diarization=True,
|
|
189
|
+
... )
|
|
190
|
+
>>> print(result["detected_language"])
|
|
191
|
+
"""
|
|
192
|
+
job = self.create(
|
|
193
|
+
url=url,
|
|
194
|
+
urls=urls,
|
|
195
|
+
language=language,
|
|
196
|
+
speaker_diarization=speaker_diarization,
|
|
197
|
+
min_speakers=min_speakers,
|
|
198
|
+
max_speakers=max_speakers,
|
|
199
|
+
)
|
|
200
|
+
job_id = job.get("id") or job.get("job_id")
|
|
201
|
+
return self.wait_for_completion(job_id, timeout=timeout)
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Voice Service - Voice Cloning & Text-to-Speech
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import time
|
|
6
|
+
from typing import Optional, List, Dict, Any, TYPE_CHECKING
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
from ..client import AudioPod
|
|
10
|
+
|
|
11
|
+
POLL_INTERVAL = 2 # seconds
|
|
12
|
+
DEFAULT_TIMEOUT = 300 # 5 minutes
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Voice:
|
|
16
|
+
"""Voice cloning and text-to-speech service."""
|
|
17
|
+
|
|
18
|
+
def __init__(self, client: "AudioPod"):
|
|
19
|
+
self._client = client
|
|
20
|
+
|
|
21
|
+
def list(self) -> List[Dict[str, Any]]:
|
|
22
|
+
"""List all available voices."""
|
|
23
|
+
return self._client.get("/api/v1/voice/voices")
|
|
24
|
+
|
|
25
|
+
def get(self, voice_id: int) -> Dict[str, Any]:
|
|
26
|
+
"""Get a voice by ID."""
|
|
27
|
+
return self._client.get(f"/api/v1/voice/voices/{voice_id}")
|
|
28
|
+
|
|
29
|
+
def create(
|
|
30
|
+
self,
|
|
31
|
+
name: str,
|
|
32
|
+
audio_file: str,
|
|
33
|
+
*,
|
|
34
|
+
description: Optional[str] = None,
|
|
35
|
+
) -> Dict[str, Any]:
|
|
36
|
+
"""
|
|
37
|
+
Create a new voice clone.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
name: Name for the voice
|
|
41
|
+
audio_file: Path to audio sample file
|
|
42
|
+
description: Optional description
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
Created voice object
|
|
46
|
+
|
|
47
|
+
Example:
|
|
48
|
+
>>> voice = client.voice.create(
|
|
49
|
+
... name="My Voice",
|
|
50
|
+
... audio_file="./sample.mp3",
|
|
51
|
+
... )
|
|
52
|
+
"""
|
|
53
|
+
return self._client.upload(
|
|
54
|
+
"/api/v1/voice/voices",
|
|
55
|
+
audio_file,
|
|
56
|
+
field_name="audio_file",
|
|
57
|
+
additional_fields={"name": name, "description": description},
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
def delete(self, voice_id: int) -> None:
|
|
61
|
+
"""Delete a voice."""
|
|
62
|
+
self._client.delete(f"/api/v1/voice/voices/{voice_id}")
|
|
63
|
+
|
|
64
|
+
def speak(
|
|
65
|
+
self,
|
|
66
|
+
text: str,
|
|
67
|
+
voice_id: int,
|
|
68
|
+
*,
|
|
69
|
+
speed: float = 1.0,
|
|
70
|
+
) -> Dict[str, Any]:
|
|
71
|
+
"""
|
|
72
|
+
Generate speech from text.
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
text: Text to convert to speech
|
|
76
|
+
voice_id: Voice ID to use
|
|
77
|
+
speed: Speech speed (0.5 to 2.0)
|
|
78
|
+
|
|
79
|
+
Returns:
|
|
80
|
+
TTS job object
|
|
81
|
+
"""
|
|
82
|
+
return self._client.post(
|
|
83
|
+
"/api/v1/voice/tts/generate",
|
|
84
|
+
json_data={
|
|
85
|
+
"text": text,
|
|
86
|
+
"voice_id": voice_id,
|
|
87
|
+
"speed": speed,
|
|
88
|
+
},
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
def get_job(self, job_id: int) -> Dict[str, Any]:
|
|
92
|
+
"""Get TTS job status."""
|
|
93
|
+
return self._client.get(f"/api/v1/voice/tts/status/{job_id}")
|
|
94
|
+
|
|
95
|
+
def wait_for_completion(
|
|
96
|
+
self, job_id: int, timeout: int = DEFAULT_TIMEOUT
|
|
97
|
+
) -> Dict[str, Any]:
|
|
98
|
+
"""Wait for TTS job to complete."""
|
|
99
|
+
start_time = time.time()
|
|
100
|
+
|
|
101
|
+
while time.time() - start_time < timeout:
|
|
102
|
+
job = self.get_job(job_id)
|
|
103
|
+
status = job.get("status", "")
|
|
104
|
+
|
|
105
|
+
if status == "COMPLETED":
|
|
106
|
+
return job
|
|
107
|
+
if status == "FAILED":
|
|
108
|
+
raise RuntimeError(
|
|
109
|
+
f"TTS generation failed: {job.get('error_message', 'Unknown error')}"
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
time.sleep(POLL_INTERVAL)
|
|
113
|
+
|
|
114
|
+
raise TimeoutError(f"TTS generation timed out after {timeout}s")
|
|
115
|
+
|
|
116
|
+
def generate(
|
|
117
|
+
self,
|
|
118
|
+
text: str,
|
|
119
|
+
voice_id: int,
|
|
120
|
+
*,
|
|
121
|
+
speed: float = 1.0,
|
|
122
|
+
timeout: int = DEFAULT_TIMEOUT,
|
|
123
|
+
) -> Dict[str, Any]:
|
|
124
|
+
"""
|
|
125
|
+
Generate speech and wait for completion.
|
|
126
|
+
|
|
127
|
+
Example:
|
|
128
|
+
>>> result = client.voice.generate(
|
|
129
|
+
... text="Hello, world!",
|
|
130
|
+
... voice_id=123,
|
|
131
|
+
... )
|
|
132
|
+
>>> print(result["output_url"])
|
|
133
|
+
"""
|
|
134
|
+
job = self.speak(text, voice_id, speed=speed)
|
|
135
|
+
return self.wait_for_completion(job["id"], timeout=timeout)
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Wallet Service - API Wallet & Billing
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Optional, Dict, Any, TYPE_CHECKING
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from ..client import AudioPod
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Wallet:
|
|
12
|
+
"""API wallet and billing service."""
|
|
13
|
+
|
|
14
|
+
def __init__(self, client: "AudioPod"):
|
|
15
|
+
self._client = client
|
|
16
|
+
|
|
17
|
+
def get_balance(self) -> Dict[str, Any]:
|
|
18
|
+
"""
|
|
19
|
+
Get current wallet balance.
|
|
20
|
+
|
|
21
|
+
Returns:
|
|
22
|
+
Balance information including balance_cents, balance_usd, etc.
|
|
23
|
+
|
|
24
|
+
Example:
|
|
25
|
+
>>> balance = client.wallet.get_balance()
|
|
26
|
+
>>> print(f"Balance: {balance['balance_usd']}")
|
|
27
|
+
"""
|
|
28
|
+
return self._client.get("/api/v1/api-wallet/balance")
|
|
29
|
+
|
|
30
|
+
def get_usage(
|
|
31
|
+
self,
|
|
32
|
+
*,
|
|
33
|
+
page: int = 1,
|
|
34
|
+
limit: int = 50,
|
|
35
|
+
api_key_id: Optional[str] = None,
|
|
36
|
+
) -> Dict[str, Any]:
|
|
37
|
+
"""
|
|
38
|
+
Get usage history.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
page: Page number
|
|
42
|
+
limit: Items per page
|
|
43
|
+
api_key_id: Filter by specific API key
|
|
44
|
+
"""
|
|
45
|
+
return self._client.get(
|
|
46
|
+
"/api/v1/api-wallet/usage",
|
|
47
|
+
params={"page": page, "limit": limit, "api_key_id": api_key_id},
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
def get_pricing(self) -> Dict[str, Any]:
|
|
51
|
+
"""
|
|
52
|
+
Get pricing information for all services.
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
Pricing table with rates per service
|
|
56
|
+
"""
|
|
57
|
+
return self._client.get("/api/v1/api-wallet/pricing")
|
|
58
|
+
|
|
59
|
+
def estimate_cost(
|
|
60
|
+
self, service_type: str, duration_seconds: int
|
|
61
|
+
) -> Dict[str, Any]:
|
|
62
|
+
"""
|
|
63
|
+
Estimate cost for an operation.
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
service_type: Service type (e.g., 'transcription', 'stem_extraction')
|
|
67
|
+
duration_seconds: Duration in seconds
|
|
68
|
+
|
|
69
|
+
Returns:
|
|
70
|
+
Cost estimate with cost_cents and cost_usd
|
|
71
|
+
|
|
72
|
+
Example:
|
|
73
|
+
>>> estimate = client.wallet.estimate_cost(
|
|
74
|
+
... service_type="transcription",
|
|
75
|
+
... duration_seconds=3600,
|
|
76
|
+
... )
|
|
77
|
+
>>> print(f"Estimated cost: {estimate['cost_usd']}")
|
|
78
|
+
"""
|
|
79
|
+
return self._client.post(
|
|
80
|
+
"/api/v1/api-wallet/estimate",
|
|
81
|
+
json_data={
|
|
82
|
+
"service_type": service_type,
|
|
83
|
+
"duration_seconds": duration_seconds,
|
|
84
|
+
},
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
def check_balance(
|
|
88
|
+
self, service_type: str, duration_seconds: int
|
|
89
|
+
) -> Dict[str, Any]:
|
|
90
|
+
"""
|
|
91
|
+
Check if balance is sufficient for an operation.
|
|
92
|
+
|
|
93
|
+
Args:
|
|
94
|
+
service_type: Service type
|
|
95
|
+
duration_seconds: Duration in seconds
|
|
96
|
+
|
|
97
|
+
Returns:
|
|
98
|
+
Object with 'sufficient' boolean and cost details
|
|
99
|
+
"""
|
|
100
|
+
return self._client.post(
|
|
101
|
+
"/api/v1/api-wallet/check-balance",
|
|
102
|
+
json_data={
|
|
103
|
+
"service_type": service_type,
|
|
104
|
+
"duration_seconds": duration_seconds,
|
|
105
|
+
},
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: audiopod
|
|
3
|
+
Version: 2.1.0
|
|
4
|
+
Summary: Official AudioPod SDK for Python - Professional Audio Processing powered by AI
|
|
5
|
+
Author-email: AudioPod AI <support@audiopod.ai>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://audiopod.ai
|
|
8
|
+
Project-URL: Documentation, https://docs.audiopod.ai
|
|
9
|
+
Project-URL: Repository, https://github.com/AudiopodAI/audiopod-sdk-python
|
|
10
|
+
Project-URL: Issues, https://github.com/AudiopodAI/audiopod-sdk-python/issues
|
|
11
|
+
Keywords: audiopod,audio,ai,speech-to-text,transcription,text-to-speech,tts,voice-cloning,music-generation,stem-separation,denoiser,speaker-diarization
|
|
12
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Topic :: Multimedia :: Sound/Audio
|
|
23
|
+
Classifier: Topic :: Multimedia :: Sound/Audio :: Analysis
|
|
24
|
+
Classifier: Topic :: Multimedia :: Sound/Audio :: Speech
|
|
25
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
26
|
+
Classifier: Typing :: Typed
|
|
27
|
+
Requires-Python: >=3.8
|
|
28
|
+
Description-Content-Type: text/markdown
|
|
29
|
+
License-File: LICENSE
|
|
30
|
+
Requires-Dist: requests>=2.28.0
|
|
31
|
+
Requires-Dist: urllib3>=1.26.0
|
|
32
|
+
Provides-Extra: dev
|
|
33
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
34
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
|
|
35
|
+
Requires-Dist: black>=23.0.0; extra == "dev"
|
|
36
|
+
Requires-Dist: isort>=5.12.0; extra == "dev"
|
|
37
|
+
Requires-Dist: mypy>=1.0.0; extra == "dev"
|
|
38
|
+
Requires-Dist: types-requests>=2.28.0; extra == "dev"
|
|
39
|
+
Dynamic: license-file
|
|
40
|
+
|
|
41
|
+
# AudioPod Python SDK
|
|
42
|
+
|
|
43
|
+
Official Python SDK for [AudioPod AI](https://audiopod.ai) - Professional Audio Processing powered by AI.
|
|
44
|
+
|
|
45
|
+
[](https://pypi.org/project/audiopod/)
|
|
46
|
+
[](https://www.python.org/downloads/)
|
|
47
|
+
[](https://opensource.org/licenses/MIT)
|
|
48
|
+
|
|
49
|
+
## Installation
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
pip install audiopod
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Quick Start
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
from audiopod import AudioPod
|
|
59
|
+
|
|
60
|
+
# Initialize client
|
|
61
|
+
client = AudioPod(api_key="ap_your_api_key")
|
|
62
|
+
|
|
63
|
+
# Separate audio into 6 stems
|
|
64
|
+
result = client.stems.separate(
|
|
65
|
+
url="https://youtube.com/watch?v=VIDEO_ID",
|
|
66
|
+
mode="six"
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
# Download stems
|
|
70
|
+
for stem, url in result["download_urls"].items():
|
|
71
|
+
print(f"{stem}: {url}")
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Stem Separation
|
|
75
|
+
|
|
76
|
+
Extract individual audio components from mixed recordings.
|
|
77
|
+
|
|
78
|
+
### Available Modes
|
|
79
|
+
|
|
80
|
+
| Mode | Stems | Output |
|
|
81
|
+
|------|-------|--------|
|
|
82
|
+
| `single` | 1 | Specified stem only (vocals, drums, bass, guitar, piano, other) |
|
|
83
|
+
| `two` | 2 | Vocals + Instrumental |
|
|
84
|
+
| `four` | 4 | Vocals, Drums, Bass, Other |
|
|
85
|
+
| `six` | 6 | Vocals, Drums, Bass, Guitar, Piano, Other |
|
|
86
|
+
| `producer` | 8 | + Kick, Snare, Hihat |
|
|
87
|
+
| `studio` | 12 | Full production toolkit |
|
|
88
|
+
| `mastering` | 16 | Maximum detail |
|
|
89
|
+
|
|
90
|
+
### Examples
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
from audiopod import AudioPod
|
|
94
|
+
|
|
95
|
+
client = AudioPod(api_key="ap_your_api_key")
|
|
96
|
+
|
|
97
|
+
# Six-stem separation from YouTube
|
|
98
|
+
job = client.stems.extract(
|
|
99
|
+
url="https://youtube.com/watch?v=VIDEO_ID",
|
|
100
|
+
mode="six"
|
|
101
|
+
)
|
|
102
|
+
print(f"Job ID: {job['id']}")
|
|
103
|
+
|
|
104
|
+
# Wait for completion
|
|
105
|
+
result = client.stems.wait_for_completion(job["id"])
|
|
106
|
+
print(result["download_urls"])
|
|
107
|
+
|
|
108
|
+
# Or use the convenience method (extract + wait)
|
|
109
|
+
result = client.stems.separate(
|
|
110
|
+
url="https://youtube.com/watch?v=VIDEO_ID",
|
|
111
|
+
mode="six"
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
# From local file
|
|
115
|
+
result = client.stems.separate(
|
|
116
|
+
file="./song.mp3",
|
|
117
|
+
mode="four"
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
# Extract only vocals
|
|
121
|
+
result = client.stems.separate(
|
|
122
|
+
url="https://youtube.com/watch?v=VIDEO_ID",
|
|
123
|
+
mode="single",
|
|
124
|
+
stem="vocals"
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
# Get available modes
|
|
128
|
+
modes = client.stems.modes()
|
|
129
|
+
for m in modes["modes"]:
|
|
130
|
+
print(f"{m['mode']}: {m['description']}")
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## API Wallet
|
|
134
|
+
|
|
135
|
+
Check balance and manage your wallet.
|
|
136
|
+
|
|
137
|
+
```python
|
|
138
|
+
# Check balance
|
|
139
|
+
balance = client.wallet.balance()
|
|
140
|
+
print(f"Balance: {balance['balance_usd']}")
|
|
141
|
+
|
|
142
|
+
# Estimate cost
|
|
143
|
+
estimate = client.wallet.estimate("stem_extraction", duration_seconds=180)
|
|
144
|
+
print(f"Estimated cost: {estimate['cost_usd']}")
|
|
145
|
+
|
|
146
|
+
# Get usage history
|
|
147
|
+
usage = client.wallet.usage()
|
|
148
|
+
for log in usage["logs"]:
|
|
149
|
+
print(f"{log['service_type']}: {log['amount_usd']}")
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## Other Services
|
|
153
|
+
|
|
154
|
+
```python
|
|
155
|
+
# Transcription
|
|
156
|
+
job = client.transcription.create(url="https://...")
|
|
157
|
+
result = client.transcription.wait_for_completion(job["id"])
|
|
158
|
+
|
|
159
|
+
# Voice cloning
|
|
160
|
+
voice = client.voice.clone(file="./sample.wav", name="My Voice")
|
|
161
|
+
|
|
162
|
+
# Music generation
|
|
163
|
+
song = client.music.generate(prompt="upbeat electronic dance music")
|
|
164
|
+
|
|
165
|
+
# Noise reduction
|
|
166
|
+
clean = client.denoiser.denoise(file="./noisy.wav")
|
|
167
|
+
|
|
168
|
+
# Speaker diarization
|
|
169
|
+
speakers = client.speaker.diarize(url="https://...")
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
## Error Handling
|
|
173
|
+
|
|
174
|
+
```python
|
|
175
|
+
from audiopod import AudioPod, InsufficientBalanceError, AuthenticationError
|
|
176
|
+
|
|
177
|
+
try:
|
|
178
|
+
client = AudioPod(api_key="ap_...")
|
|
179
|
+
result = client.stems.separate(url="...", mode="six")
|
|
180
|
+
except AuthenticationError:
|
|
181
|
+
print("Invalid API key")
|
|
182
|
+
except InsufficientBalanceError as e:
|
|
183
|
+
print(f"Need more credits. Required: {e.required_cents} cents")
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
## Environment Variables
|
|
187
|
+
|
|
188
|
+
```bash
|
|
189
|
+
export AUDIOPOD_API_KEY="ap_your_api_key"
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
```python
|
|
193
|
+
# Client reads from env automatically
|
|
194
|
+
client = AudioPod()
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## Documentation
|
|
198
|
+
|
|
199
|
+
- [API Documentation](https://docs.audiopod.ai)
|
|
200
|
+
- [API Reference](https://docs.audiopod.ai/api-reference/stem-splitter)
|
|
201
|
+
- [Get API Key](https://www.audiopod.ai/dashboard/account/api-keys)
|
|
202
|
+
|
|
203
|
+
## License
|
|
204
|
+
|
|
205
|
+
MIT License - see [LICENSE](LICENSE) for details.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
audiopod/__init__.py,sha256=UacaYDOzZMyFlXUtfNBnIZZPC5wjePjhYgxClJ31wio,661
|
|
2
|
+
audiopod/client.py,sha256=mt9rYyxecSnODywkW7xuPQ9SZqhPO560VFLY7c-4f84,6632
|
|
3
|
+
audiopod/exceptions.py,sha256=Xg3tNIwb0MoVyLZFqg5ifLIhh3mHgt-poneUB_a--Ag,1301
|
|
4
|
+
audiopod/resources/__init__.py,sha256=I4R-sJmLwLCDbliAmhHwfjqVILCo2RnxjxYaI6PYDTs,375
|
|
5
|
+
audiopod/resources/denoiser.py,sha256=n6-vyAor7FZnbEDZxgAtpnWIlMmHAIyoZsV5hY9xryQ,2996
|
|
6
|
+
audiopod/resources/music.py,sha256=pXScN9DXNXYgFtvgjhiDShJpGNx5A0r_HsqTFiweBZ8,4742
|
|
7
|
+
audiopod/resources/speaker.py,sha256=uEGbfuIUNGof6FJe9vODKwQ8NG-3Tz293ZUYEfd96NA,3612
|
|
8
|
+
audiopod/resources/stems.py,sha256=kaYvJq84rJOH8wFDzVhN9kECzr2LeCseoa7_dXV4dsc,8524
|
|
9
|
+
audiopod/resources/transcription.py,sha256=p-X5l8mbXrcUnsjqcdvN4SiZtM4EOitAg9Ipf_ubjkQ,6142
|
|
10
|
+
audiopod/resources/voice.py,sha256=CCsRA23r3IMjVrU6sM2uiSMDs-D4Vi2pcu_nUGp9iAk,3646
|
|
11
|
+
audiopod/resources/wallet.py,sha256=PksHlUv8jPcjwIpH0s2uYvr2y_g8BQWBhwyPwFr6Qco,2898
|
|
12
|
+
audiopod-2.1.0.dist-info/licenses/LICENSE,sha256=hqEjnOaGNbnLSBxbtbC7WQVREU2vQI8FmwecCiZlMfA,1068
|
|
13
|
+
audiopod-2.1.0.dist-info/METADATA,sha256=MIJ9npAwwfraOMxfrrN1LZtOC7y1VQw363t-5EbFoTk,5769
|
|
14
|
+
audiopod-2.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
15
|
+
audiopod-2.1.0.dist-info/top_level.txt,sha256=M6yyOFFNpLdH4i1AMRqJZLRIgfpg1NvrQVmnPd8A6N8,9
|
|
16
|
+
audiopod-2.1.0.dist-info/RECORD,,
|
audiopod/config.py
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
AudioPod SDK Configuration
|
|
3
|
-
"""
|
|
4
|
-
|
|
5
|
-
from dataclasses import dataclass
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
@dataclass
|
|
9
|
-
class ClientConfig:
|
|
10
|
-
"""Client configuration settings"""
|
|
11
|
-
base_url: str = "https://api.audiopod.ai"
|
|
12
|
-
timeout: int = 30
|
|
13
|
-
max_retries: int = 3
|
|
14
|
-
verify_ssl: bool = True
|
|
15
|
-
debug: bool = False
|
|
16
|
-
version: str = "1.3.0"
|
|
17
|
-
|