audiopod 1.0.0__py3-none-any.whl → 1.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 CHANGED
@@ -47,7 +47,7 @@ from .models import (
47
47
  TranslationResult
48
48
  )
49
49
 
50
- __version__ = "1.0.0"
50
+ __version__ = "1.1.0"
51
51
  __author__ = "AudioPod AI"
52
52
  __email__ = "support@audiopod.ai"
53
53
  __license__ = "MIT"
audiopod/client.py CHANGED
@@ -23,7 +23,8 @@ from .services import (
23
23
  SpeakerService,
24
24
  DenoiserService,
25
25
  KaraokeService,
26
- CreditService
26
+ CreditService,
27
+ StemExtractionService
27
28
  )
28
29
 
29
30
  logger = logging.getLogger(__name__)
@@ -139,6 +140,7 @@ class Client(BaseClient):
139
140
  self.denoiser = DenoiserService(self)
140
141
  self.karaoke = KaraokeService(self)
141
142
  self.credits = CreditService(self)
143
+ self.stem_extraction = StemExtractionService(self)
142
144
 
143
145
  def request(
144
146
  self,
@@ -227,6 +229,7 @@ class AsyncClient(BaseClient):
227
229
  self.denoiser = DenoiserService(self, async_mode=True)
228
230
  self.karaoke = KaraokeService(self, async_mode=True)
229
231
  self.credits = CreditService(self, async_mode=True)
232
+ self.stem_extraction = StemExtractionService(self, async_mode=True)
230
233
 
231
234
  @property
232
235
  def session(self) -> aiohttp.ClientSession:
audiopod/py.typed ADDED
@@ -0,0 +1,2 @@
1
+ # Marker file for PEP 561
2
+ # This package supports type checking
@@ -11,6 +11,7 @@ from .speaker import SpeakerService
11
11
  from .denoiser import DenoiserService
12
12
  from .karaoke import KaraokeService
13
13
  from .credits import CreditService
14
+ from .stem_extraction import StemExtractionService
14
15
 
15
16
  __all__ = [
16
17
  "VoiceService",
@@ -20,5 +21,6 @@ __all__ = [
20
21
  "SpeakerService",
21
22
  "DenoiserService",
22
23
  "KaraokeService",
23
- "CreditService"
24
+ "CreditService",
25
+ "StemExtractionService"
24
26
  ]
@@ -51,15 +51,15 @@ class MusicService(BaseService):
51
51
  if seed is not None and (seed < 0 or seed > 2**32 - 1):
52
52
  raise ValidationError("Seed must be between 0 and 2^32 - 1")
53
53
 
54
- # Prepare request data
54
+ # Prepare request data - FIXED: Use correct parameter names matching API schema
55
55
  data = {
56
56
  "prompt": prompt,
57
- "duration": duration,
57
+ "audio_duration": duration, # FIXED: API expects "audio_duration" not "duration"
58
58
  "guidance_scale": guidance_scale,
59
- "num_inference_steps": num_inference_steps
59
+ "infer_step": num_inference_steps # FIXED: API expects "infer_step" not "num_inference_steps"
60
60
  }
61
61
  if seed is not None:
62
- data["seed"] = seed
62
+ data["manual_seeds"] = [seed] # FIXED: API expects "manual_seeds" list not "seed"
63
63
  if display_name:
64
64
  data["display_name"] = display_name.strip()
65
65
 
@@ -68,7 +68,9 @@ class MusicService(BaseService):
68
68
  return self._async_generate_music(data, wait_for_completion, timeout)
69
69
  else:
70
70
  response = self.client.request("POST", "/api/v1/music/text2music", data=data)
71
- job = Job.from_dict(response)
71
+ # FIXED: Handle response format correctly - API returns {"job": {...}, "message": "..."}
72
+ job_data = response.get("job", response)
73
+ job = Job.from_dict(job_data)
72
74
 
73
75
  if wait_for_completion:
74
76
  completed_job = self._wait_for_completion(job.id, timeout)
@@ -84,7 +86,9 @@ class MusicService(BaseService):
84
86
  ) -> Union[Job, MusicGenerationResult]:
85
87
  """Async version of generate_music"""
86
88
  response = await self.client.request("POST", "/api/v1/music/text2music", data=data)
87
- job = Job.from_dict(response)
89
+ # FIXED: Handle response format correctly
90
+ job_data = response.get("job", response)
91
+ job = Job.from_dict(job_data)
88
92
 
89
93
  if wait_for_completion:
90
94
  completed_job = await self._async_wait_for_completion(job.id, timeout)
@@ -122,11 +126,14 @@ class MusicService(BaseService):
122
126
  if style not in ["modern", "classic", "trap"]:
123
127
  raise ValidationError("Style must be 'modern', 'classic', or 'trap'")
124
128
 
125
- # Prepare request data
129
+ # Prepare request data - FIXED: Match API schema for text2rap
126
130
  data = {
131
+ "prompt": f"rap music, {style} style", # FIXED: API expects "prompt" field
127
132
  "lyrics": lyrics,
128
- "style": style,
129
- "tempo": tempo
133
+ "audio_duration": 120.0, # Default duration
134
+ "guidance_scale": 7.5,
135
+ "infer_step": 50,
136
+ "lora_name_or_path": "ACE-Step/ACE-Step-v1-chinese-rap-LoRA" # Rap-specific LoRA
130
137
  }
131
138
  if display_name:
132
139
  data["display_name"] = display_name.strip()
@@ -136,7 +143,9 @@ class MusicService(BaseService):
136
143
  return self._async_generate_rap(data, wait_for_completion, timeout)
137
144
  else:
138
145
  response = self.client.request("POST", "/api/v1/music/text2rap", data=data)
139
- job = Job.from_dict(response)
146
+ # FIXED: Handle response format correctly
147
+ job_data = response.get("job", response)
148
+ job = Job.from_dict(job_data)
140
149
 
141
150
  if wait_for_completion:
142
151
  completed_job = self._wait_for_completion(job.id, timeout)
@@ -152,7 +161,9 @@ class MusicService(BaseService):
152
161
  ) -> Union[Job, MusicGenerationResult]:
153
162
  """Async version of generate_rap"""
154
163
  response = await self.client.request("POST", "/api/v1/music/text2rap", data=data)
155
- job = Job.from_dict(response)
164
+ # FIXED: Handle response format correctly
165
+ job_data = response.get("job", response)
166
+ job = Job.from_dict(job_data)
156
167
 
157
168
  if wait_for_completion:
158
169
  completed_job = await self._async_wait_for_completion(job.id, timeout)
@@ -194,10 +205,12 @@ class MusicService(BaseService):
194
205
  if tempo is not None and not 60 <= tempo <= 200:
195
206
  raise ValidationError("Tempo must be between 60 and 200 BPM")
196
207
 
197
- # Prepare request data
208
+ # Prepare request data - FIXED: Match API schema for prompt2instrumental
198
209
  data = {
199
210
  "prompt": prompt,
200
- "duration": duration
211
+ "audio_duration": duration, # FIXED: API expects "audio_duration"
212
+ "guidance_scale": 7.5,
213
+ "infer_step": 50
201
214
  }
202
215
  if instruments:
203
216
  data["instruments"] = instruments
@@ -213,7 +226,9 @@ class MusicService(BaseService):
213
226
  return self._async_generate_instrumental(data, wait_for_completion, timeout)
214
227
  else:
215
228
  response = self.client.request("POST", "/api/v1/music/prompt2instrumental", data=data)
216
- job = Job.from_dict(response)
229
+ # FIXED: Handle response format correctly
230
+ job_data = response.get("job", response)
231
+ job = Job.from_dict(job_data)
217
232
 
218
233
  if wait_for_completion:
219
234
  completed_job = self._wait_for_completion(job.id, timeout)
@@ -229,7 +244,80 @@ class MusicService(BaseService):
229
244
  ) -> Union[Job, MusicGenerationResult]:
230
245
  """Async version of generate_instrumental"""
231
246
  response = await self.client.request("POST", "/api/v1/music/prompt2instrumental", data=data)
232
- job = Job.from_dict(response)
247
+ # FIXED: Handle response format correctly
248
+ job_data = response.get("job", response)
249
+ job = Job.from_dict(job_data)
250
+
251
+ if wait_for_completion:
252
+ completed_job = await self._async_wait_for_completion(job.id, timeout)
253
+ return MusicGenerationResult.from_dict(completed_job.result or completed_job.__dict__)
254
+
255
+ return job
256
+
257
+ def generate_vocals(
258
+ self,
259
+ lyrics: str,
260
+ prompt: str = "vocals",
261
+ duration: float = 120.0,
262
+ display_name: Optional[str] = None,
263
+ wait_for_completion: bool = False,
264
+ timeout: int = 600
265
+ ) -> Union[Job, MusicGenerationResult]:
266
+ """
267
+ Generate vocals from lyrics - NEW METHOD matching API lyric2vocals endpoint
268
+
269
+ Args:
270
+ lyrics: Song lyrics
271
+ prompt: Vocal style description
272
+ duration: Duration in seconds
273
+ display_name: Custom name for the track
274
+ wait_for_completion: Whether to wait for completion
275
+ timeout: Maximum time to wait
276
+
277
+ Returns:
278
+ Job object or generation result
279
+ """
280
+ # Validate inputs
281
+ lyrics = self._validate_text_input(lyrics, max_length=10000)
282
+ prompt = self._validate_text_input(prompt, max_length=2000)
283
+ if not 10.0 <= duration <= 600.0:
284
+ raise ValidationError("Duration must be between 10 and 600 seconds")
285
+
286
+ # Prepare request data - Match API schema for lyric2vocals
287
+ data = {
288
+ "prompt": prompt,
289
+ "lyrics": lyrics,
290
+ "audio_duration": duration,
291
+ "guidance_scale": 7.5,
292
+ "infer_step": 50
293
+ }
294
+ if display_name:
295
+ data["display_name"] = display_name.strip()
296
+
297
+ # Make request
298
+ if self.async_mode:
299
+ return self._async_generate_vocals(data, wait_for_completion, timeout)
300
+ else:
301
+ response = self.client.request("POST", "/api/v1/music/lyric2vocals", data=data)
302
+ job_data = response.get("job", response)
303
+ job = Job.from_dict(job_data)
304
+
305
+ if wait_for_completion:
306
+ completed_job = self._wait_for_completion(job.id, timeout)
307
+ return MusicGenerationResult.from_dict(completed_job.result or completed_job.__dict__)
308
+
309
+ return job
310
+
311
+ async def _async_generate_vocals(
312
+ self,
313
+ data: Dict[str, Any],
314
+ wait_for_completion: bool,
315
+ timeout: int
316
+ ) -> Union[Job, MusicGenerationResult]:
317
+ """Async version of generate_vocals"""
318
+ response = await self.client.request("POST", "/api/v1/music/lyric2vocals", data=data)
319
+ job_data = response.get("job", response)
320
+ job = Job.from_dict(job_data)
233
321
 
234
322
  if wait_for_completion:
235
323
  completed_job = await self._async_wait_for_completion(job.id, timeout)
@@ -322,7 +410,7 @@ class MusicService(BaseService):
322
410
  """
323
411
  params = {
324
412
  "limit": limit,
325
- "skip": offset
413
+ "skip": offset # FIXED: API uses "skip" parameter for offset
326
414
  }
327
415
  if status:
328
416
  params["status"] = status
@@ -0,0 +1,180 @@
1
+ """
2
+ Stem Extraction Service - Audio stem separation operations
3
+ """
4
+
5
+ from typing import List, Optional, Dict, Any, Union
6
+ from .base import BaseService
7
+ from ..models import Job
8
+ from ..exceptions import ValidationError
9
+
10
+
11
+ class StemExtractionService(BaseService):
12
+ """Service for audio stem extraction operations"""
13
+
14
+ def extract_stems(
15
+ self,
16
+ audio_file: Optional[str] = None,
17
+ url: Optional[str] = None,
18
+ stem_types: List[str] = None,
19
+ model_name: str = "htdemucs",
20
+ two_stems_mode: Optional[str] = None,
21
+ wait_for_completion: bool = False,
22
+ timeout: int = 900
23
+ ) -> Job:
24
+ """
25
+ Extract stems from audio file
26
+
27
+ Args:
28
+ audio_file: Path to audio file to process
29
+ url: URL of audio file to process (alternative to audio_file)
30
+ stem_types: List of stems to extract (e.g., ['vocals', 'drums', 'bass', 'other'])
31
+ model_name: Model to use for separation ('htdemucs' or 'htdemucs_6s')
32
+ two_stems_mode: Two-stem mode for vocals/instrumental separation
33
+ wait_for_completion: Whether to wait for completion
34
+ timeout: Maximum time to wait
35
+
36
+ Returns:
37
+ Job object with stem extraction details
38
+ """
39
+ if not audio_file and not url:
40
+ raise ValidationError("Either audio_file or url must be provided")
41
+
42
+ if audio_file and url:
43
+ raise ValidationError("Provide either audio_file or url, not both")
44
+
45
+ # Set default stem types based on model
46
+ if stem_types is None:
47
+ if model_name == "htdemucs_6s":
48
+ stem_types = ["vocals", "drums", "bass", "other", "piano", "guitar"]
49
+ else:
50
+ stem_types = ["vocals", "drums", "bass", "other"]
51
+
52
+ # Validate model name
53
+ if model_name not in ["htdemucs", "htdemucs_6s"]:
54
+ raise ValidationError("Model name must be 'htdemucs' or 'htdemucs_6s'")
55
+
56
+ # Prepare request
57
+ files = {}
58
+ data = {
59
+ "stem_types": str(stem_types), # API expects string representation
60
+ "model_name": model_name
61
+ }
62
+
63
+ if audio_file:
64
+ files = self._prepare_file_upload(audio_file, "file")
65
+
66
+ if url:
67
+ data["url"] = url
68
+
69
+ if two_stems_mode:
70
+ data["two_stems_mode"] = two_stems_mode
71
+
72
+ if self.async_mode:
73
+ return self._async_extract_stems(files, data, wait_for_completion, timeout)
74
+ else:
75
+ response = self.client.request(
76
+ "POST",
77
+ "/api/v1/stem-extraction/extract",
78
+ data=data,
79
+ files=files if files else None
80
+ )
81
+
82
+ job = Job.from_dict(response)
83
+
84
+ if wait_for_completion:
85
+ return self._wait_for_completion(job.id, timeout)
86
+
87
+ return job
88
+
89
+ async def _async_extract_stems(
90
+ self,
91
+ files: Dict[str, Any],
92
+ data: Dict[str, Any],
93
+ wait_for_completion: bool,
94
+ timeout: int
95
+ ) -> Job:
96
+ """Async version of extract_stems"""
97
+ response = await self.client.request(
98
+ "POST",
99
+ "/api/v1/stem-extraction/extract",
100
+ data=data,
101
+ files=files if files else None
102
+ )
103
+
104
+ job = Job.from_dict(response)
105
+
106
+ if wait_for_completion:
107
+ return await self._async_wait_for_completion(job.id, timeout)
108
+
109
+ return job
110
+
111
+ def get_stem_job(self, job_id: int) -> Job:
112
+ """
113
+ Get stem extraction job status
114
+
115
+ Args:
116
+ job_id: ID of the stem extraction job
117
+
118
+ Returns:
119
+ Job object with current status
120
+ """
121
+ if self.async_mode:
122
+ return self._async_get_stem_job(job_id)
123
+ else:
124
+ response = self.client.request("GET", f"/api/v1/stem-extraction/status/{job_id}")
125
+ return Job.from_dict(response)
126
+
127
+ async def _async_get_stem_job(self, job_id: int) -> Job:
128
+ """Async version of get_stem_job"""
129
+ response = await self.client.request("GET", f"/api/v1/stem-extraction/status/{job_id}")
130
+ return Job.from_dict(response)
131
+
132
+ def list_stem_jobs(
133
+ self,
134
+ skip: int = 0,
135
+ limit: int = 50
136
+ ) -> List[Job]:
137
+ """
138
+ List stem extraction jobs
139
+
140
+ Args:
141
+ skip: Number of jobs to skip
142
+ limit: Maximum number of jobs to return
143
+
144
+ Returns:
145
+ List of stem extraction jobs
146
+ """
147
+ params = {
148
+ "skip": skip,
149
+ "limit": limit
150
+ }
151
+
152
+ if self.async_mode:
153
+ return self._async_list_stem_jobs(params)
154
+ else:
155
+ response = self.client.request("GET", "/api/v1/stem-extraction/jobs", params=params)
156
+ return [Job.from_dict(job_data) for job_data in response]
157
+
158
+ async def _async_list_stem_jobs(self, params: Dict[str, Any]) -> List[Job]:
159
+ """Async version of list_stem_jobs"""
160
+ response = await self.client.request("GET", "/api/v1/stem-extraction/jobs", params=params)
161
+ return [Job.from_dict(job_data) for job_data in response]
162
+
163
+ def delete_stem_job(self, job_id: int) -> Dict[str, str]:
164
+ """
165
+ Delete a stem extraction job
166
+
167
+ Args:
168
+ job_id: ID of the job to delete
169
+
170
+ Returns:
171
+ Deletion confirmation
172
+ """
173
+ if self.async_mode:
174
+ return self._async_delete_stem_job(job_id)
175
+ else:
176
+ return self.client.request("DELETE", f"/api/v1/stem-extraction/jobs/{job_id}")
177
+
178
+ async def _async_delete_stem_job(self, job_id: int) -> Dict[str, str]:
179
+ """Async version of delete_stem_job"""
180
+ return await self.client.request("DELETE", f"/api/v1/stem-extraction/jobs/{job_id}")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: audiopod
3
- Version: 1.0.0
3
+ Version: 1.1.0
4
4
  Summary: Professional Audio Processing API Client for Python
5
5
  Home-page: https://github.com/audiopod-ai/audiopod-python
6
6
  Author: AudioPod AI
@@ -25,6 +25,7 @@ Classifier: Topic :: Multimedia :: Sound/Audio :: Conversion
25
25
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
26
26
  Requires-Python: >=3.8
27
27
  Description-Content-Type: text/markdown
28
+ License-File: LICENSE
28
29
  Requires-Dist: requests>=2.28.0
29
30
  Requires-Dist: aiohttp>=3.8.0
30
31
  Requires-Dist: pydantic>=1.10.0
@@ -46,6 +47,7 @@ Requires-Dist: sphinx-rtd-theme>=1.2.0; extra == "docs"
46
47
  Requires-Dist: sphinx-autodoc-typehints>=1.19.0; extra == "docs"
47
48
  Dynamic: author
48
49
  Dynamic: home-page
50
+ Dynamic: license-file
49
51
  Dynamic: requires-python
50
52
 
51
53
  # AudioPod Python SDK
@@ -380,11 +382,10 @@ audiopod transcription transcribe audio.mp3 --language en
380
382
 
381
383
  ## Support
382
384
 
383
- - 📖 [Documentation](https://docs.audiopod.ai)
384
- - 🎯 [API Reference](https://api.audiopod.ai/docs)
385
+ - 📖 [API Reference](https://docs.audiopod.ai)
385
386
  - 💬 [Discord Community](https://discord.gg/audiopod)
386
387
  - 📧 [Email Support](mailto:support@audiopod.ai)
387
- - 🐛 [Bug Reports](https://github.com/audiopod-ai/audiopod-python/issues)
388
+ - 🐛 [Bug Reports](https://github.com/AudiopodAI/audiopod)
388
389
 
389
390
  ## License
390
391
 
@@ -1,21 +1,24 @@
1
- audiopod/__init__.py,sha256=GyQ7PUWGF3t4Ljuk_DD6O9MvG_A96T5bedXc3jJQCD4,1790
1
+ audiopod/__init__.py,sha256=U12jbLmXps3-NP3yXcucGMQbr8b6VCoWAXZSitK1pp4,1790
2
2
  audiopod/cli.py,sha256=ZYzAQ3UpoYuOEWivMwMneJUf2z8DGGYTx1Nb6yRfdVY,9339
3
- audiopod/client.py,sha256=AF5goB5CG5lJZ3XyGojRskvUNrNzn9A34BAx9Z1sEOE,11580
3
+ audiopod/client.py,sha256=67oPSInSNssJpTR00ZuYSdk9lbx5KiRnDQw8UYKNVsA,11742
4
4
  audiopod/config.py,sha256=fuGtbuES4tXdHwqQqoZa5izCH6nVfFRP06D8eK1Cg10,1683
5
5
  audiopod/exceptions.py,sha256=c3Ym2tWyRE1kemVkXDaXFcfP3h6AokhKcUcCBImwGes,2386
6
6
  audiopod/models.py,sha256=gAfQkufA_hZdYBkniFJ_EXETy9ts-wr-soUEbT7ZKFM,7827
7
- audiopod/services/__init__.py,sha256=rt_9CHqmEJgHyVFdF2WwTE403CXpDGBCYzfCxJZZjgE,573
7
+ audiopod/py.typed,sha256=ixa8YukDZ3kLo0WsFJRGohLMyHzbMur1ALmmASML2cs,64
8
+ audiopod/services/__init__.py,sha256=9Ycl9VVscwwY42joBCSL67v8DrITW2T2QyuaokbpehM,653
8
9
  audiopod/services/base.py,sha256=mNbziYy2KsWWZrdHFlTl9pKLvoQUW-ZkkJuVWfCVP74,6731
9
10
  audiopod/services/credits.py,sha256=CjE2W8kmYjM4B02BwfPvvB3kl0YdVimM2kR7lhg43h8,1796
10
11
  audiopod/services/denoiser.py,sha256=Oi_1rCKs_L5lCG_2QuKwHzLe8_mbEIzlzM2X8TJ7Rnc,1750
11
12
  audiopod/services/karaoke.py,sha256=M0Befh4Y2DNM_uWFTmCIrzKfjqkvygrzCt0f_Dubj2o,2012
12
- audiopod/services/music.py,sha256=m6cF_2cbB4X-BHvJH36oqD2NBF_dIWCAUB2HIpZgoxo,15948
13
+ audiopod/services/music.py,sha256=hDPjTSj-gAeEWBVB7PRPQrptMHJyQTz8e9p-p7yaRPI,20032
13
14
  audiopod/services/speaker.py,sha256=OPSOwArfrGXVzRgciS13n1QsCJSK1PB-Mz6VgwxuHAA,1866
15
+ audiopod/services/stem_extraction.py,sha256=3ibMFKFR25xKHpVs3WGMNriZ88sB5PriFNa_s2Bvon4,6026
14
16
  audiopod/services/transcription.py,sha256=HyH6WpGWZsggYxIvt2dhB6_5UHaigk3XwXsVgarWzcE,7565
15
17
  audiopod/services/translation.py,sha256=Gpxmom-ZSLMXBwSQlOL5PyqxQCwRYNT2IUp2ZD5QhYc,3177
16
18
  audiopod/services/voice.py,sha256=_IHv3zU3k184kfijxr1QRBenrIpmhhPOBS96DddZ8yw,13456
17
- audiopod-1.0.0.dist-info/METADATA,sha256=S7iD1nLEk-xXknM1K8sze0ucL2oVGtxcXpvtIFRGtTY,10956
18
- audiopod-1.0.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
19
- audiopod-1.0.0.dist-info/entry_points.txt,sha256=uLcNDzXuOXnJAz9j91TDGayVjjZ7-ZiHBGDydqNUErU,47
20
- audiopod-1.0.0.dist-info/top_level.txt,sha256=M6yyOFFNpLdH4i1AMRqJZLRIgfpg1NvrQVmnPd8A6N8,9
21
- audiopod-1.0.0.dist-info/RECORD,,
19
+ audiopod-1.1.0.dist-info/licenses/LICENSE,sha256=hqEjnOaGNbnLSBxbtbC7WQVREU2vQI8FmwecCiZlMfA,1068
20
+ audiopod-1.1.0.dist-info/METADATA,sha256=ee2Zg4iwkglAgnk3xwNuYg11LFZbKJQeUcxgG8Z_Dw0,10931
21
+ audiopod-1.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
22
+ audiopod-1.1.0.dist-info/entry_points.txt,sha256=uLcNDzXuOXnJAz9j91TDGayVjjZ7-ZiHBGDydqNUErU,47
23
+ audiopod-1.1.0.dist-info/top_level.txt,sha256=M6yyOFFNpLdH4i1AMRqJZLRIgfpg1NvrQVmnPd8A6N8,9
24
+ audiopod-1.1.0.dist-info/RECORD,,
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 AudioPod AI
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.