audiopod 1.1.0__py3-none-any.whl → 1.1.1__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.1.0"
50
+ __version__ = "1.1.1"
51
51
  __author__ = "AudioPod AI"
52
52
  __email__ = "support@audiopod.ai"
53
53
  __license__ = "MIT"
audiopod/models.py CHANGED
@@ -151,13 +151,18 @@ class MusicGenerationResult:
151
151
 
152
152
  @dataclass
153
153
  class TranslationResult:
154
- """Translation job result"""
154
+ """Speech translation job result"""
155
155
  job: Job
156
156
  source_language: Optional[str] = None
157
157
  target_language: Optional[str] = None
158
- audio_output_url: Optional[str] = None
159
- video_output_url: Optional[str] = None
158
+ display_name: Optional[str] = None
159
+ audio_output_path: Optional[str] = None
160
+ video_output_path: Optional[str] = None
160
161
  transcript_path: Optional[str] = None
162
+ translated_audio_url: Optional[str] = None
163
+ video_output_url: Optional[str] = None
164
+ transcript_urls: Optional[Dict[str, str]] = None
165
+ is_video: bool = False
161
166
 
162
167
  @classmethod
163
168
  def from_dict(cls, data: Dict[str, Any]) -> 'TranslationResult':
@@ -166,10 +171,20 @@ class TranslationResult:
166
171
  job=Job.from_dict(data),
167
172
  source_language=data.get('source_language'),
168
173
  target_language=data.get('target_language'),
169
- audio_output_url=data.get('audio_output_path'),
170
- video_output_url=data.get('video_output_path'),
171
- transcript_path=data.get('transcript_path')
174
+ display_name=data.get('display_name'),
175
+ audio_output_path=data.get('audio_output_path'),
176
+ video_output_path=data.get('video_output_path'),
177
+ transcript_path=data.get('transcript_path'),
178
+ translated_audio_url=data.get('translated_audio_url'),
179
+ video_output_url=data.get('video_output_url'),
180
+ transcript_urls=data.get('transcript_urls'),
181
+ is_video=data.get('is_video', False)
172
182
  )
183
+
184
+ @property
185
+ def audio_output_url(self) -> Optional[str]:
186
+ """Backward compatibility property - returns translated_audio_url"""
187
+ return self.translated_audio_url
173
188
 
174
189
 
175
190
  @dataclass
@@ -1,42 +1,59 @@
1
1
  """
2
- Translation Service - Audio/video translation operations
2
+ Translation Service - Speech-to-speech translation operations
3
3
  """
4
4
 
5
5
  from typing import Optional, Union
6
6
  from .base import BaseService
7
7
  from ..models import Job, TranslationResult
8
+ from ..exceptions import ValidationError
8
9
 
9
10
 
10
11
  class TranslationService(BaseService):
11
- """Service for audio and video translation operations"""
12
+ """Service for speech-to-speech translation operations"""
12
13
 
13
14
  def translate_audio(
14
15
  self,
15
- audio_file: str,
16
- target_language: str,
16
+ audio_file: Optional[str] = None,
17
+ url: Optional[str] = None,
18
+ target_language: str = "en",
17
19
  source_language: Optional[str] = None,
18
20
  wait_for_completion: bool = False,
19
21
  timeout: int = 900
20
22
  ) -> Union[Job, TranslationResult]:
21
23
  """
22
- Translate audio to another language
24
+ Translate speech from audio/video file to another language while preserving voice characteristics
23
25
 
24
26
  Args:
25
- audio_file: Path to audio file
26
- target_language: Target language code
27
- source_language: Source language (auto-detect if None)
27
+ audio_file: Path to audio/video file (required if no URL)
28
+ url: Direct media URL (required if no file)
29
+ target_language: Target language code (ISO 639-1, e.g., 'es' for Spanish)
30
+ source_language: Source language code (auto-detect if None)
28
31
  wait_for_completion: Whether to wait for completion
29
32
  timeout: Maximum time to wait
30
33
 
31
34
  Returns:
32
35
  Job object or translation result
33
36
  """
37
+ if not audio_file and not url:
38
+ raise ValidationError("Either audio_file or url must be provided")
39
+
40
+ if audio_file and url:
41
+ raise ValidationError("Provide either audio_file or url, not both")
42
+
34
43
  target_language = self._validate_language_code(target_language)
35
44
  if source_language:
36
45
  source_language = self._validate_language_code(source_language)
37
46
 
38
- files = self._prepare_file_upload(audio_file, "file")
47
+ # Prepare request data
48
+ files = {}
39
49
  data = {"target_language": target_language}
50
+
51
+ if audio_file:
52
+ files = self._prepare_file_upload(audio_file, "file")
53
+
54
+ if url:
55
+ data["url"] = url
56
+
40
57
  if source_language:
41
58
  data["source_language"] = source_language
42
59
 
@@ -44,7 +61,10 @@ class TranslationService(BaseService):
44
61
  return self._async_translate_audio(files, data, wait_for_completion, timeout)
45
62
  else:
46
63
  response = self.client.request(
47
- "POST", "/api/v1/translation/translate", data=data, files=files
64
+ "POST",
65
+ "/api/v1/translation/translate/speech", # FIXED: Use correct speech-to-speech endpoint
66
+ data=data,
67
+ files=files if files else None
48
68
  )
49
69
  job = Job.from_dict(response)
50
70
 
@@ -53,11 +73,35 @@ class TranslationService(BaseService):
53
73
  return TranslationResult.from_dict(completed_job.result or completed_job.__dict__)
54
74
 
55
75
  return job
76
+
77
+ def translate_speech(
78
+ self,
79
+ audio_file: Optional[str] = None,
80
+ url: Optional[str] = None,
81
+ target_language: str = "en",
82
+ source_language: Optional[str] = None,
83
+ wait_for_completion: bool = False,
84
+ timeout: int = 900
85
+ ) -> Union[Job, TranslationResult]:
86
+ """
87
+ Alias for translate_audio - more descriptive method name for speech translation
88
+ """
89
+ return self.translate_audio(
90
+ audio_file=audio_file,
91
+ url=url,
92
+ target_language=target_language,
93
+ source_language=source_language,
94
+ wait_for_completion=wait_for_completion,
95
+ timeout=timeout
96
+ )
56
97
 
57
98
  async def _async_translate_audio(self, files, data, wait_for_completion, timeout):
58
99
  """Async version of translate_audio"""
59
100
  response = await self.client.request(
60
- "POST", "/api/v1/translation/translate", data=data, files=files
101
+ "POST",
102
+ "/api/v1/translation/translate/speech", # FIXED: Use correct speech-to-speech endpoint
103
+ data=data,
104
+ files=files if files else None
61
105
  )
62
106
  job = Job.from_dict(response)
63
107
 
@@ -79,3 +123,74 @@ class TranslationService(BaseService):
79
123
  """Async version of get_translation_job"""
80
124
  response = await self.client.request("GET", f"/api/v1/translation/translations/{job_id}")
81
125
  return TranslationResult.from_dict(response)
126
+
127
+ def list_translation_jobs(
128
+ self,
129
+ skip: int = 0,
130
+ limit: int = 50
131
+ ) -> list:
132
+ """
133
+ List translation jobs
134
+
135
+ Args:
136
+ skip: Number of jobs to skip (pagination offset)
137
+ limit: Maximum number of jobs to return (max 100)
138
+
139
+ Returns:
140
+ List of translation jobs
141
+ """
142
+ params = {
143
+ "skip": skip,
144
+ "limit": min(limit, 100) # API max is 100
145
+ }
146
+
147
+ if self.async_mode:
148
+ return self._async_list_translation_jobs(params)
149
+ else:
150
+ response = self.client.request("GET", "/api/v1/translation/translations", params=params)
151
+ return [TranslationResult.from_dict(job_data) for job_data in response]
152
+
153
+ async def _async_list_translation_jobs(self, params: dict) -> list:
154
+ """Async version of list_translation_jobs"""
155
+ response = await self.client.request("GET", "/api/v1/translation/translations", params=params)
156
+ return [TranslationResult.from_dict(job_data) for job_data in response]
157
+
158
+ def retry_translation(self, job_id: int) -> Job:
159
+ """
160
+ Retry a failed translation job
161
+
162
+ Args:
163
+ job_id: ID of the failed translation job to retry
164
+
165
+ Returns:
166
+ New job object for the retry attempt
167
+ """
168
+ if self.async_mode:
169
+ return self._async_retry_translation(job_id)
170
+ else:
171
+ response = self.client.request("POST", f"/api/v1/translation/translations/{job_id}/retry")
172
+ return Job.from_dict(response)
173
+
174
+ async def _async_retry_translation(self, job_id: int) -> Job:
175
+ """Async version of retry_translation"""
176
+ response = await self.client.request("POST", f"/api/v1/translation/translations/{job_id}/retry")
177
+ return Job.from_dict(response)
178
+
179
+ def delete_translation_job(self, job_id: int) -> dict:
180
+ """
181
+ Delete a translation job
182
+
183
+ Args:
184
+ job_id: ID of the translation job to delete
185
+
186
+ Returns:
187
+ Deletion confirmation
188
+ """
189
+ if self.async_mode:
190
+ return self._async_delete_translation_job(job_id)
191
+ else:
192
+ return self.client.request("DELETE", f"/api/v1/translation/translations/{job_id}")
193
+
194
+ async def _async_delete_translation_job(self, job_id: int) -> dict:
195
+ """Async version of delete_translation_job"""
196
+ return await self.client.request("DELETE", f"/api/v1/translation/translations/{job_id}")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: audiopod
3
- Version: 1.1.0
3
+ Version: 1.1.1
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
@@ -142,17 +142,25 @@ print(f"Transcript: {transcript.transcript}")
142
142
  print(f"Detected {len(transcript.segments)} speakers")
143
143
  ```
144
144
 
145
- #### Audio Translation
145
+ #### Speech-to-Speech Translation
146
146
 
147
147
  ```python
148
- # Translate audio to another language
149
- translation = client.translation.translate_audio(
148
+ # Translate speech while preserving voice characteristics
149
+ translation = client.translation.translate_speech(
150
150
  audio_file="path/to/english_audio.wav",
151
151
  target_language="es", # Spanish
152
+ source_language="en", # English (optional - auto-detect)
152
153
  wait_for_completion=True
153
154
  )
154
155
 
155
- print(f"Translated audio URL: {translation.audio_output_url}")
156
+ print(f"Translated audio URL: {translation.translated_audio_url}")
157
+
158
+ # Or translate from URL
159
+ url_translation = client.translation.translate_speech(
160
+ url="https://example.com/audio.mp3",
161
+ target_language="fr", # French
162
+ wait_for_completion=True
163
+ )
156
164
  ```
157
165
 
158
166
  ### Async Support
@@ -1,9 +1,9 @@
1
- audiopod/__init__.py,sha256=U12jbLmXps3-NP3yXcucGMQbr8b6VCoWAXZSitK1pp4,1790
1
+ audiopod/__init__.py,sha256=UsB5ET6nUy1Upx8wCiB17mMErdF3RvEHfAr51-pPPGQ,1790
2
2
  audiopod/cli.py,sha256=ZYzAQ3UpoYuOEWivMwMneJUf2z8DGGYTx1Nb6yRfdVY,9339
3
3
  audiopod/client.py,sha256=67oPSInSNssJpTR00ZuYSdk9lbx5KiRnDQw8UYKNVsA,11742
4
4
  audiopod/config.py,sha256=fuGtbuES4tXdHwqQqoZa5izCH6nVfFRP06D8eK1Cg10,1683
5
5
  audiopod/exceptions.py,sha256=c3Ym2tWyRE1kemVkXDaXFcfP3h6AokhKcUcCBImwGes,2386
6
- audiopod/models.py,sha256=gAfQkufA_hZdYBkniFJ_EXETy9ts-wr-soUEbT7ZKFM,7827
6
+ audiopod/models.py,sha256=R70iMqKDZfLtTB9FQ7KrFLBi-bFA5-FrS-5eMOtfK1o,8517
7
7
  audiopod/py.typed,sha256=ixa8YukDZ3kLo0WsFJRGohLMyHzbMur1ALmmASML2cs,64
8
8
  audiopod/services/__init__.py,sha256=9Ycl9VVscwwY42joBCSL67v8DrITW2T2QyuaokbpehM,653
9
9
  audiopod/services/base.py,sha256=mNbziYy2KsWWZrdHFlTl9pKLvoQUW-ZkkJuVWfCVP74,6731
@@ -14,11 +14,11 @@ audiopod/services/music.py,sha256=hDPjTSj-gAeEWBVB7PRPQrptMHJyQTz8e9p-p7yaRPI,20
14
14
  audiopod/services/speaker.py,sha256=OPSOwArfrGXVzRgciS13n1QsCJSK1PB-Mz6VgwxuHAA,1866
15
15
  audiopod/services/stem_extraction.py,sha256=3ibMFKFR25xKHpVs3WGMNriZ88sB5PriFNa_s2Bvon4,6026
16
16
  audiopod/services/transcription.py,sha256=HyH6WpGWZsggYxIvt2dhB6_5UHaigk3XwXsVgarWzcE,7565
17
- audiopod/services/translation.py,sha256=Gpxmom-ZSLMXBwSQlOL5PyqxQCwRYNT2IUp2ZD5QhYc,3177
17
+ audiopod/services/translation.py,sha256=oUU82c61CeAt13lzlWx8S-9xEgYlskwX8bLMbQw2Ni8,7396
18
18
  audiopod/services/voice.py,sha256=_IHv3zU3k184kfijxr1QRBenrIpmhhPOBS96DddZ8yw,13456
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,,
19
+ audiopod-1.1.1.dist-info/licenses/LICENSE,sha256=hqEjnOaGNbnLSBxbtbC7WQVREU2vQI8FmwecCiZlMfA,1068
20
+ audiopod-1.1.1.dist-info/METADATA,sha256=UG_csntfzscrZTjy-2v-lCTlS7-VSEM6WEwUw26hSYc,11217
21
+ audiopod-1.1.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
22
+ audiopod-1.1.1.dist-info/entry_points.txt,sha256=uLcNDzXuOXnJAz9j91TDGayVjjZ7-ZiHBGDydqNUErU,47
23
+ audiopod-1.1.1.dist-info/top_level.txt,sha256=M6yyOFFNpLdH4i1AMRqJZLRIgfpg1NvrQVmnPd8A6N8,9
24
+ audiopod-1.1.1.dist-info/RECORD,,