audiopod 1.4.0__tar.gz → 2.1.0__tar.gz

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.
Files changed (40) hide show
  1. audiopod-2.1.0/PKG-INFO +205 -0
  2. audiopod-2.1.0/README.md +165 -0
  3. {audiopod-1.4.0 → audiopod-2.1.0}/audiopod/__init__.py +13 -8
  4. audiopod-2.1.0/audiopod/client.py +198 -0
  5. audiopod-2.1.0/audiopod/exceptions.py +49 -0
  6. audiopod-2.1.0/audiopod/resources/__init__.py +23 -0
  7. audiopod-2.1.0/audiopod/resources/denoiser.py +116 -0
  8. audiopod-2.1.0/audiopod/resources/music.py +166 -0
  9. audiopod-2.1.0/audiopod/resources/speaker.py +132 -0
  10. audiopod-2.1.0/audiopod/resources/stems.py +267 -0
  11. audiopod-2.1.0/audiopod/resources/transcription.py +205 -0
  12. audiopod-2.1.0/audiopod/resources/voice.py +139 -0
  13. audiopod-2.1.0/audiopod/resources/wallet.py +110 -0
  14. audiopod-2.1.0/audiopod.egg-info/PKG-INFO +205 -0
  15. audiopod-2.1.0/audiopod.egg-info/SOURCES.txt +19 -0
  16. audiopod-2.1.0/audiopod.egg-info/requires.txt +10 -0
  17. {audiopod-1.4.0 → audiopod-2.1.0}/pyproject.toml +37 -14
  18. audiopod-1.4.0/PKG-INFO +0 -206
  19. audiopod-1.4.0/README.md +0 -173
  20. audiopod-1.4.0/audiopod/client.py +0 -306
  21. audiopod-1.4.0/audiopod/config.py +0 -17
  22. audiopod-1.4.0/audiopod/exceptions.py +0 -41
  23. audiopod-1.4.0/audiopod/services/__init__.py +0 -28
  24. audiopod-1.4.0/audiopod/services/base.py +0 -69
  25. audiopod-1.4.0/audiopod/services/credits.py +0 -42
  26. audiopod-1.4.0/audiopod/services/denoiser.py +0 -131
  27. audiopod-1.4.0/audiopod/services/music.py +0 -217
  28. audiopod-1.4.0/audiopod/services/speaker.py +0 -134
  29. audiopod-1.4.0/audiopod/services/stem_extraction.py +0 -168
  30. audiopod-1.4.0/audiopod/services/transcription.py +0 -187
  31. audiopod-1.4.0/audiopod/services/translation.py +0 -135
  32. audiopod-1.4.0/audiopod/services/voice.py +0 -187
  33. audiopod-1.4.0/audiopod/services/wallet.py +0 -235
  34. audiopod-1.4.0/audiopod.egg-info/PKG-INFO +0 -206
  35. audiopod-1.4.0/audiopod.egg-info/SOURCES.txt +0 -23
  36. audiopod-1.4.0/audiopod.egg-info/requires.txt +0 -8
  37. {audiopod-1.4.0 → audiopod-2.1.0}/LICENSE +0 -0
  38. {audiopod-1.4.0 → audiopod-2.1.0}/audiopod.egg-info/dependency_links.txt +0 -0
  39. {audiopod-1.4.0 → audiopod-2.1.0}/audiopod.egg-info/top_level.txt +0 -0
  40. {audiopod-1.4.0 → audiopod-2.1.0}/setup.cfg +0 -0
@@ -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
+ [![PyPI version](https://badge.fury.io/py/audiopod.svg)](https://pypi.org/project/audiopod/)
46
+ [![Python 3.8+](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/)
47
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](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,165 @@
1
+ # AudioPod Python SDK
2
+
3
+ Official Python SDK for [AudioPod AI](https://audiopod.ai) - Professional Audio Processing powered by AI.
4
+
5
+ [![PyPI version](https://badge.fury.io/py/audiopod.svg)](https://pypi.org/project/audiopod/)
6
+ [![Python 3.8+](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/)
7
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
8
+
9
+ ## Installation
10
+
11
+ ```bash
12
+ pip install audiopod
13
+ ```
14
+
15
+ ## Quick Start
16
+
17
+ ```python
18
+ from audiopod import AudioPod
19
+
20
+ # Initialize client
21
+ client = AudioPod(api_key="ap_your_api_key")
22
+
23
+ # Separate audio into 6 stems
24
+ result = client.stems.separate(
25
+ url="https://youtube.com/watch?v=VIDEO_ID",
26
+ mode="six"
27
+ )
28
+
29
+ # Download stems
30
+ for stem, url in result["download_urls"].items():
31
+ print(f"{stem}: {url}")
32
+ ```
33
+
34
+ ## Stem Separation
35
+
36
+ Extract individual audio components from mixed recordings.
37
+
38
+ ### Available Modes
39
+
40
+ | Mode | Stems | Output |
41
+ |------|-------|--------|
42
+ | `single` | 1 | Specified stem only (vocals, drums, bass, guitar, piano, other) |
43
+ | `two` | 2 | Vocals + Instrumental |
44
+ | `four` | 4 | Vocals, Drums, Bass, Other |
45
+ | `six` | 6 | Vocals, Drums, Bass, Guitar, Piano, Other |
46
+ | `producer` | 8 | + Kick, Snare, Hihat |
47
+ | `studio` | 12 | Full production toolkit |
48
+ | `mastering` | 16 | Maximum detail |
49
+
50
+ ### Examples
51
+
52
+ ```python
53
+ from audiopod import AudioPod
54
+
55
+ client = AudioPod(api_key="ap_your_api_key")
56
+
57
+ # Six-stem separation from YouTube
58
+ job = client.stems.extract(
59
+ url="https://youtube.com/watch?v=VIDEO_ID",
60
+ mode="six"
61
+ )
62
+ print(f"Job ID: {job['id']}")
63
+
64
+ # Wait for completion
65
+ result = client.stems.wait_for_completion(job["id"])
66
+ print(result["download_urls"])
67
+
68
+ # Or use the convenience method (extract + wait)
69
+ result = client.stems.separate(
70
+ url="https://youtube.com/watch?v=VIDEO_ID",
71
+ mode="six"
72
+ )
73
+
74
+ # From local file
75
+ result = client.stems.separate(
76
+ file="./song.mp3",
77
+ mode="four"
78
+ )
79
+
80
+ # Extract only vocals
81
+ result = client.stems.separate(
82
+ url="https://youtube.com/watch?v=VIDEO_ID",
83
+ mode="single",
84
+ stem="vocals"
85
+ )
86
+
87
+ # Get available modes
88
+ modes = client.stems.modes()
89
+ for m in modes["modes"]:
90
+ print(f"{m['mode']}: {m['description']}")
91
+ ```
92
+
93
+ ## API Wallet
94
+
95
+ Check balance and manage your wallet.
96
+
97
+ ```python
98
+ # Check balance
99
+ balance = client.wallet.balance()
100
+ print(f"Balance: {balance['balance_usd']}")
101
+
102
+ # Estimate cost
103
+ estimate = client.wallet.estimate("stem_extraction", duration_seconds=180)
104
+ print(f"Estimated cost: {estimate['cost_usd']}")
105
+
106
+ # Get usage history
107
+ usage = client.wallet.usage()
108
+ for log in usage["logs"]:
109
+ print(f"{log['service_type']}: {log['amount_usd']}")
110
+ ```
111
+
112
+ ## Other Services
113
+
114
+ ```python
115
+ # Transcription
116
+ job = client.transcription.create(url="https://...")
117
+ result = client.transcription.wait_for_completion(job["id"])
118
+
119
+ # Voice cloning
120
+ voice = client.voice.clone(file="./sample.wav", name="My Voice")
121
+
122
+ # Music generation
123
+ song = client.music.generate(prompt="upbeat electronic dance music")
124
+
125
+ # Noise reduction
126
+ clean = client.denoiser.denoise(file="./noisy.wav")
127
+
128
+ # Speaker diarization
129
+ speakers = client.speaker.diarize(url="https://...")
130
+ ```
131
+
132
+ ## Error Handling
133
+
134
+ ```python
135
+ from audiopod import AudioPod, InsufficientBalanceError, AuthenticationError
136
+
137
+ try:
138
+ client = AudioPod(api_key="ap_...")
139
+ result = client.stems.separate(url="...", mode="six")
140
+ except AuthenticationError:
141
+ print("Invalid API key")
142
+ except InsufficientBalanceError as e:
143
+ print(f"Need more credits. Required: {e.required_cents} cents")
144
+ ```
145
+
146
+ ## Environment Variables
147
+
148
+ ```bash
149
+ export AUDIOPOD_API_KEY="ap_your_api_key"
150
+ ```
151
+
152
+ ```python
153
+ # Client reads from env automatically
154
+ client = AudioPod()
155
+ ```
156
+
157
+ ## Documentation
158
+
159
+ - [API Documentation](https://docs.audiopod.ai)
160
+ - [API Reference](https://docs.audiopod.ai/api-reference/stem-splitter)
161
+ - [Get API Key](https://www.audiopod.ai/dashboard/account/api-keys)
162
+
163
+ ## License
164
+
165
+ MIT License - see [LICENSE](LICENSE) for details.
@@ -1,29 +1,34 @@
1
1
  """
2
2
  AudioPod SDK for Python
3
3
  Professional Audio Processing powered by AI
4
+
5
+ Example:
6
+ from audiopod import AudioPod
7
+
8
+ client = AudioPod(api_key="ap_...")
9
+
10
+ # Transcribe audio
11
+ job = client.transcription.create(url="https://...")
12
+ result = client.transcription.wait_for_completion(job.id)
4
13
  """
5
14
 
6
- __version__ = "1.3.0"
15
+ __version__ = "2.1.0"
7
16
 
8
- from .client import Client, AsyncClient
17
+ from .client import AudioPod
9
18
  from .exceptions import (
10
19
  AudioPodError,
11
20
  AuthenticationError,
12
21
  APIError,
13
22
  RateLimitError,
14
- ValidationError,
15
23
  InsufficientBalanceError,
16
24
  )
17
25
 
18
26
  __all__ = [
19
- "Client",
20
- "AsyncClient",
27
+ "AudioPod",
21
28
  "AudioPodError",
22
- "AuthenticationError",
29
+ "AuthenticationError",
23
30
  "APIError",
24
31
  "RateLimitError",
25
- "ValidationError",
26
32
  "InsufficientBalanceError",
27
33
  "__version__",
28
34
  ]
29
-
@@ -0,0 +1,198 @@
1
+ """
2
+ AudioPod API Client
3
+
4
+ Clean, minimal API inspired by OpenAI's SDK design.
5
+ """
6
+
7
+ import os
8
+ from typing import Optional, Dict, Any, BinaryIO
9
+ from urllib.parse import urljoin
10
+
11
+ import requests
12
+ from requests.adapters import HTTPAdapter
13
+ from urllib3.util.retry import Retry
14
+
15
+ from .exceptions import (
16
+ AuthenticationError,
17
+ APIError,
18
+ RateLimitError,
19
+ InsufficientBalanceError,
20
+ )
21
+ from .resources.transcription import Transcription
22
+ from .resources.voice import Voice
23
+ from .resources.music import Music
24
+ from .resources.stems import StemExtraction
25
+ from .resources.denoiser import Denoiser
26
+ from .resources.speaker import Speaker
27
+ from .resources.wallet import Wallet
28
+
29
+ VERSION = "2.1.0"
30
+ DEFAULT_BASE_URL = "https://api.audiopod.ai"
31
+ DEFAULT_TIMEOUT = 60
32
+
33
+
34
+ class AudioPod:
35
+ """
36
+ AudioPod API Client.
37
+
38
+ Args:
39
+ api_key: Your AudioPod API key (starts with 'ap_').
40
+ If not provided, reads from AUDIOPOD_API_KEY env var.
41
+ base_url: Base URL for the API (default: https://api.audiopod.ai)
42
+ timeout: Request timeout in seconds (default: 60)
43
+ max_retries: Maximum retries for failed requests (default: 3)
44
+
45
+ Example:
46
+ >>> from audiopod import AudioPod
47
+ >>> client = AudioPod(api_key="ap_...")
48
+ >>> result = client.transcription.transcribe(url="https://...")
49
+ """
50
+
51
+ def __init__(
52
+ self,
53
+ api_key: Optional[str] = None,
54
+ base_url: Optional[str] = None,
55
+ timeout: int = DEFAULT_TIMEOUT,
56
+ max_retries: int = 3,
57
+ ):
58
+ self.api_key = api_key or os.getenv("AUDIOPOD_API_KEY")
59
+
60
+ if not self.api_key:
61
+ raise AuthenticationError(
62
+ "API key is required. Pass api_key or set AUDIOPOD_API_KEY environment variable."
63
+ )
64
+
65
+ if not self.api_key.startswith("ap_"):
66
+ raise AuthenticationError(
67
+ "Invalid API key format. AudioPod API keys start with 'ap_'"
68
+ )
69
+
70
+ self.base_url = base_url or DEFAULT_BASE_URL
71
+ self.timeout = timeout
72
+
73
+ # Configure session with retries
74
+ self._session = requests.Session()
75
+ retry_strategy = Retry(
76
+ total=max_retries,
77
+ status_forcelist=[429, 500, 502, 503, 504],
78
+ allowed_methods=["HEAD", "GET", "OPTIONS", "POST", "DELETE"],
79
+ backoff_factor=1,
80
+ )
81
+ adapter = HTTPAdapter(max_retries=retry_strategy)
82
+ self._session.mount("http://", adapter)
83
+ self._session.mount("https://", adapter)
84
+
85
+ # Initialize services
86
+ self.transcription = Transcription(self)
87
+ self.voice = Voice(self)
88
+ self.music = Music(self)
89
+ self.stems = StemExtraction(self)
90
+ self.denoiser = Denoiser(self)
91
+ self.speaker = Speaker(self)
92
+ self.wallet = Wallet(self)
93
+
94
+ def _get_headers(self) -> Dict[str, str]:
95
+ return {
96
+ "Authorization": f"Bearer {self.api_key}",
97
+ "X-API-Key": self.api_key,
98
+ "Content-Type": "application/json",
99
+ "User-Agent": f"audiopod-python/{VERSION}",
100
+ "Accept": "application/json",
101
+ }
102
+
103
+ def _handle_response(self, response: requests.Response) -> Dict[str, Any]:
104
+ try:
105
+ response.raise_for_status()
106
+ if response.status_code == 204:
107
+ return {}
108
+ return response.json()
109
+ except requests.exceptions.HTTPError:
110
+ status = response.status_code
111
+ try:
112
+ data = response.json()
113
+ message = data.get("detail") or data.get("message") or str(data)
114
+ except Exception:
115
+ message = response.text or f"HTTP {status}"
116
+
117
+ if status == 401:
118
+ raise AuthenticationError(message)
119
+ elif status == 402:
120
+ try:
121
+ data = response.json()
122
+ raise InsufficientBalanceError(
123
+ message,
124
+ required_cents=data.get("required_cents"),
125
+ available_cents=data.get("available_cents"),
126
+ )
127
+ except (ValueError, KeyError):
128
+ raise InsufficientBalanceError(message)
129
+ elif status == 429:
130
+ raise RateLimitError(message)
131
+ else:
132
+ raise APIError(message, status_code=status)
133
+
134
+ def get(self, endpoint: str, params: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
135
+ """Make a GET request."""
136
+ url = urljoin(self.base_url, endpoint)
137
+ headers = self._get_headers()
138
+ response = self._session.get(
139
+ url, headers=headers, params=params, timeout=self.timeout
140
+ )
141
+ return self._handle_response(response)
142
+
143
+ def post(
144
+ self,
145
+ endpoint: str,
146
+ data: Optional[Dict[str, Any]] = None,
147
+ json_data: Optional[Dict[str, Any]] = None,
148
+ ) -> Dict[str, Any]:
149
+ """Make a POST request."""
150
+ url = urljoin(self.base_url, endpoint)
151
+ headers = self._get_headers()
152
+ response = self._session.post(
153
+ url, headers=headers, data=data, json=json_data, timeout=self.timeout
154
+ )
155
+ return self._handle_response(response)
156
+
157
+ def delete(self, endpoint: str) -> Dict[str, Any]:
158
+ """Make a DELETE request."""
159
+ url = urljoin(self.base_url, endpoint)
160
+ headers = self._get_headers()
161
+ response = self._session.delete(url, headers=headers, timeout=self.timeout)
162
+ return self._handle_response(response)
163
+
164
+ def upload(
165
+ self,
166
+ endpoint: str,
167
+ file_path: str,
168
+ field_name: str = "file",
169
+ additional_fields: Optional[Dict[str, Any]] = None,
170
+ ) -> Dict[str, Any]:
171
+ """Upload a file."""
172
+ url = urljoin(self.base_url, endpoint)
173
+ headers = self._get_headers()
174
+ headers.pop("Content-Type", None) # Let requests set multipart boundary
175
+
176
+ with open(file_path, "rb") as f:
177
+ files = {field_name: f}
178
+ data = {}
179
+ if additional_fields:
180
+ for key, value in additional_fields.items():
181
+ if value is not None:
182
+ data[key] = str(value) if not isinstance(value, str) else value
183
+
184
+ response = self._session.post(
185
+ url, headers=headers, files=files, data=data, timeout=self.timeout
186
+ )
187
+
188
+ return self._handle_response(response)
189
+
190
+ def close(self):
191
+ """Close the client session."""
192
+ self._session.close()
193
+
194
+ def __enter__(self):
195
+ return self
196
+
197
+ def __exit__(self, exc_type, exc_val, exc_tb):
198
+ self.close()
@@ -0,0 +1,49 @@
1
+ """
2
+ AudioPod SDK Exceptions
3
+ """
4
+
5
+ from typing import Optional
6
+
7
+
8
+ class AudioPodError(Exception):
9
+ """Base exception for AudioPod SDK."""
10
+
11
+ def __init__(self, message: str = "An error occurred"):
12
+ self.message = message
13
+ super().__init__(self.message)
14
+
15
+
16
+ class AuthenticationError(AudioPodError):
17
+ """Raised when authentication fails."""
18
+
19
+ def __init__(self, message: str = "Authentication failed"):
20
+ super().__init__(message)
21
+
22
+
23
+ class APIError(AudioPodError):
24
+ """Raised when an API request fails."""
25
+
26
+ def __init__(self, message: str = "API request failed", status_code: Optional[int] = None):
27
+ self.status_code = status_code
28
+ super().__init__(message)
29
+
30
+
31
+ class RateLimitError(AudioPodError):
32
+ """Raised when rate limit is exceeded."""
33
+
34
+ def __init__(self, message: str = "Rate limit exceeded"):
35
+ super().__init__(message)
36
+
37
+
38
+ class InsufficientBalanceError(AudioPodError):
39
+ """Raised when wallet balance is insufficient."""
40
+
41
+ def __init__(
42
+ self,
43
+ message: str = "Insufficient wallet balance",
44
+ required_cents: Optional[int] = None,
45
+ available_cents: Optional[int] = None,
46
+ ):
47
+ self.required_cents = required_cents
48
+ self.available_cents = available_cents
49
+ super().__init__(message)
@@ -0,0 +1,23 @@
1
+ """AudioPod SDK Resources"""
2
+
3
+ from .transcription import Transcription
4
+ from .voice import Voice
5
+ from .music import Music
6
+ from .stems import StemExtraction
7
+ from .denoiser import Denoiser
8
+ from .speaker import Speaker
9
+ from .wallet import Wallet
10
+
11
+ __all__ = [
12
+ "Transcription",
13
+ "Voice",
14
+ "Music",
15
+ "StemExtraction",
16
+ "Denoiser",
17
+ "Speaker",
18
+ "Wallet",
19
+ ]
20
+
21
+
22
+
23
+