typecast-python 0.3.6__py3-none-any.whl → 0.3.8__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.
typecast/__init__.py CHANGED
@@ -19,6 +19,7 @@ from .models import (
19
19
  OutputStream,
20
20
  PlanTier,
21
21
  Prompt,
22
+ RecommendedVoice,
22
23
  SubscriptionResponse,
23
24
  TTSRequest,
24
25
  TTSRequestStream,
@@ -48,6 +49,7 @@ __all__ = [
48
49
  "OutputStream",
49
50
  "PlanTier",
50
51
  "Prompt",
52
+ "RecommendedVoice",
51
53
  "SubscriptionResponse",
52
54
  "TTSRequest",
53
55
  "TTSRequestStream",
@@ -0,0 +1,70 @@
1
+ import platform
2
+ import sys
3
+ from importlib import metadata
4
+
5
+ import aiohttp
6
+ import requests
7
+
8
+ from . import conf
9
+
10
+
11
+ def _package_version(package_name: str) -> str:
12
+ try:
13
+ return metadata.version(package_name)
14
+ except metadata.PackageNotFoundError:
15
+ return "dev"
16
+
17
+
18
+ def _os_name() -> str:
19
+ system = platform.system().lower()
20
+ if system == "darwin":
21
+ return "macos"
22
+ if system.startswith("windows"):
23
+ return "windows"
24
+ return system or "unknown"
25
+
26
+
27
+ def _arch_name() -> str:
28
+ machine = platform.machine().lower()
29
+ if machine in {"x86_64", "amd64"}:
30
+ return "x64"
31
+ if machine in {"aarch64", "arm64"}:
32
+ return "arm64"
33
+ return machine or "unknown"
34
+
35
+
36
+ def build_user_agent(
37
+ *,
38
+ mode: str,
39
+ http_library: str,
40
+ host: str,
41
+ transport: str = "rest",
42
+ ) -> str:
43
+ sdk_version = _package_version("typecast-python")
44
+ base = "default" if conf.is_default_host(host) else "custom"
45
+ return (
46
+ f"typecast-python/{sdk_version} "
47
+ f"Python/{sys.version_info.major}.{sys.version_info.minor} "
48
+ f"{platform.python_implementation()}/{platform.python_version()} "
49
+ f"{http_library} "
50
+ f"(mode={mode}; base={base}; transport={transport}; "
51
+ f"os={_os_name()}; arch={_arch_name()}; sdk_env=python; platform=server)"
52
+ )
53
+
54
+
55
+ def requests_user_agent(host: str, transport: str = "rest") -> str:
56
+ return build_user_agent(
57
+ mode="sync",
58
+ http_library=f"requests/{requests.__version__}",
59
+ host=host,
60
+ transport=transport,
61
+ )
62
+
63
+
64
+ def aiohttp_user_agent(host: str, transport: str = "rest") -> str:
65
+ return build_user_agent(
66
+ mode="async",
67
+ http_library=f"aiohttp/{aiohttp.__version__}",
68
+ host=host,
69
+ transport=transport,
70
+ )
typecast/async_client.py CHANGED
@@ -11,6 +11,7 @@ from ._voice_clone import (
11
11
  validate_clone_inputs,
12
12
  validate_custom_voice_id,
13
13
  )
14
+ from ._user_agent import aiohttp_user_agent
14
15
  from .client import _guess_audio_mime
15
16
  from .client import _output_with_inferred_format
16
17
  from .client import _validate_output_path
@@ -28,6 +29,7 @@ from .models import (
28
29
  CustomVoice,
29
30
  LanguageCode,
30
31
  Output,
32
+ RecommendedVoice,
31
33
  SubscriptionResponse,
32
34
  TTSModel,
33
35
  TTSPrompt,
@@ -61,34 +63,48 @@ class AsyncTypecast:
61
63
  ... f.write(response.audio_data)
62
64
  """
63
65
 
64
- def __init__(self, host: Optional[str] = None, api_key: Optional[str] = None):
66
+ def __init__(
67
+ self,
68
+ host: Optional[str] = None,
69
+ api_key: Optional[str] = None,
70
+ session: Optional[aiohttp.ClientSession] = None,
71
+ ):
65
72
  """Initialize the async Typecast client.
66
73
 
67
74
  Args:
68
75
  host: API host URL. Defaults to TYPECAST_API_HOST env var
69
76
  or 'https://api.typecast.ai'.
70
77
  api_key: API key for authentication. Defaults to TYPECAST_API_KEY env var.
78
+ session: Optional externally-managed aiohttp.ClientSession. When provided,
79
+ __aenter__ will not create a new session and __aexit__ will not close it
80
+ (the caller owns its lifecycle). Auth headers (`X-API-KEY`, `User-Agent`)
81
+ are attached per-request via `_request_headers()`.
71
82
 
72
83
  Raises:
73
- ValueError: If no API key is provided and TYPECAST_API_KEY is not set.
84
+ ValueError: If no API key is provided and TYPECAST_API_KEY is not set
85
+ for the default host.
74
86
  """
75
87
  self.host = conf.get_host(host)
76
88
  self.api_key = conf.get_api_key(api_key)
77
89
  if not self.api_key and conf.is_default_host(self.host):
78
90
  raise ValueError("API key is required for the default Typecast API host")
79
- self.session: Optional[aiohttp.ClientSession] = None
91
+ self._owns_session = session is None
92
+ self.session: Optional[aiohttp.ClientSession] = session
80
93
 
81
94
  async def __aenter__(self):
82
- # Auth header at session scope; per-request Content-Type is set by aiohttp
83
- # (json= auto-sets application/json, data=FormData() auto-sets multipart).
84
- headers = {}
85
- if self.api_key:
86
- headers["X-API-KEY"] = self.api_key
87
- self.session = aiohttp.ClientSession(headers=headers)
95
+ # When an external session is injected, do not create a new one.
96
+ # Per-request auth headers are attached via _request_headers() on each call.
97
+ if self.session is None:
98
+ headers = {"User-Agent": aiohttp_user_agent(self.host)}
99
+ if self.api_key:
100
+ headers["X-API-KEY"] = self.api_key
101
+ self.session = aiohttp.ClientSession(headers=headers)
88
102
  return self
89
103
 
90
104
  async def __aexit__(self, exc_type, exc_val, exc_tb):
91
- if self.session:
105
+ # Only close sessions we created. External sessions are caller-owned;
106
+ # leave the reference intact so the client can be re-entered.
107
+ if self.session and self._owns_session:
92
108
  await self.session.close()
93
109
 
94
110
  def _handle_error(self, status_code: int, response_text: str):
@@ -113,6 +129,20 @@ class AsyncTypecast:
113
129
  status_code=status_code,
114
130
  )
115
131
 
132
+ def _request_headers(self) -> Optional[dict]:
133
+ """Headers to attach to each individual request.
134
+
135
+ For owned sessions, auth is set at session scope, so return None and let
136
+ aiohttp use the session headers. For external sessions, the session has
137
+ no auth headers, so we attach X-API-KEY and User-Agent per-request.
138
+ """
139
+ if self._owns_session:
140
+ return None
141
+ headers = {"User-Agent": aiohttp_user_agent(self.host)}
142
+ if self.api_key:
143
+ headers["X-API-KEY"] = self.api_key
144
+ return headers
145
+
116
146
  async def text_to_speech(self, request: TTSRequest) -> TTSResponse:
117
147
  """Convert text to speech asynchronously.
118
148
 
@@ -134,7 +164,9 @@ class AsyncTypecast:
134
164
  raise TypecastError("Client session not initialized. Use async with.")
135
165
  endpoint = "/v1/text-to-speech"
136
166
  async with self.session.post(
137
- f"{self.host}{endpoint}", json=request.model_dump(exclude_none=True)
167
+ f"{self.host}{endpoint}",
168
+ json=request.model_dump(exclude_none=True),
169
+ headers=self._request_headers(),
138
170
  ) as response:
139
171
  if response.status != 200:
140
172
  error_text = await response.text()
@@ -203,7 +235,11 @@ class AsyncTypecast:
203
235
  NotFoundError, UnprocessableEntityError, RateLimitError,
204
236
  InternalServerError, TypecastError: depending on response status.
205
237
  """
206
- if not isinstance(chunk_size, int) or isinstance(chunk_size, bool) or chunk_size < 1:
238
+ if (
239
+ not isinstance(chunk_size, int)
240
+ or isinstance(chunk_size, bool)
241
+ or chunk_size < 1
242
+ ):
207
243
  raise ValueError("chunk_size must be a positive integer")
208
244
  if not self.session:
209
245
  raise TypecastError("Client session not initialized. Use async with.")
@@ -213,6 +249,7 @@ class AsyncTypecast:
213
249
  f"{self.host}{endpoint}",
214
250
  json=request.model_dump(exclude_none=True),
215
251
  timeout=stream_timeout,
252
+ headers=self._request_headers(),
216
253
  ) as response:
217
254
  if response.status != 200:
218
255
  error_text = await response.text()
@@ -256,6 +293,7 @@ class AsyncTypecast:
256
293
  f"{self.host}{endpoint}",
257
294
  json=request.model_dump(exclude_none=True),
258
295
  params=params,
296
+ headers=self._request_headers(),
259
297
  ) as response:
260
298
  if response.status != 200:
261
299
  text = await response.text()
@@ -305,6 +343,7 @@ class AsyncTypecast:
305
343
  f"{self.host}/v1/voices/clone",
306
344
  data=form,
307
345
  timeout=timeout,
346
+ headers=self._request_headers(),
308
347
  ) as response:
309
348
  if response.status != 200:
310
349
  text = await response.text()
@@ -329,6 +368,7 @@ class AsyncTypecast:
329
368
  async with self.session.delete(
330
369
  f"{self.host}/v1/voices/{quote(voice_id, safe='')}",
331
370
  timeout=timeout,
371
+ headers=self._request_headers(),
332
372
  ) as response:
333
373
  if response.status not in (200, 204):
334
374
  text = await response.text()
@@ -355,7 +395,9 @@ class AsyncTypecast:
355
395
  params["model"] = model
356
396
 
357
397
  async with self.session.get(
358
- f"{self.host}{endpoint}", params=params
398
+ f"{self.host}{endpoint}",
399
+ params=params,
400
+ headers=self._request_headers(),
359
401
  ) as response:
360
402
  if response.status != 200:
361
403
  error_text = await response.text()
@@ -383,7 +425,9 @@ class AsyncTypecast:
383
425
  raise TypecastError("Client session not initialized. Use async with.")
384
426
  endpoint = f"/v1/voices/{voice_id}"
385
427
 
386
- async with self.session.get(f"{self.host}{endpoint}") as response:
428
+ async with self.session.get(
429
+ f"{self.host}{endpoint}", headers=self._request_headers()
430
+ ) as response:
387
431
  if response.status != 200:
388
432
  error_text = await response.text()
389
433
  self._handle_error(response.status, error_text)
@@ -420,7 +464,9 @@ class AsyncTypecast:
420
464
  params[key] = getattr(value, "value", value)
421
465
 
422
466
  async with self.session.get(
423
- f"{self.host}{endpoint}", params=params
467
+ f"{self.host}{endpoint}",
468
+ params=params,
469
+ headers=self._request_headers(),
424
470
  ) as response:
425
471
  if response.status != 200:
426
472
  error_text = await response.text()
@@ -447,7 +493,9 @@ class AsyncTypecast:
447
493
  if not self.session:
448
494
  raise TypecastError("Client session not initialized. Use async with.")
449
495
  endpoint = "/v1/users/me/subscription"
450
- async with self.session.get(f"{self.host}{endpoint}") as response:
496
+ async with self.session.get(
497
+ f"{self.host}{endpoint}", headers=self._request_headers()
498
+ ) as response:
451
499
  if response.status != 200:
452
500
  error_text = await response.text()
453
501
  self._handle_error(response.status, error_text)
@@ -470,10 +518,37 @@ class AsyncTypecast:
470
518
  raise TypecastError("Client session not initialized. Use async with.")
471
519
  endpoint = f"/v2/voices/{voice_id}"
472
520
 
473
- async with self.session.get(f"{self.host}{endpoint}") as response:
521
+ async with self.session.get(
522
+ f"{self.host}{endpoint}", headers=self._request_headers()
523
+ ) as response:
474
524
  if response.status != 200:
475
525
  error_text = await response.text()
476
526
  self._handle_error(response.status, error_text)
477
527
 
478
528
  data = await response.json()
479
529
  return VoiceV2Response.model_validate(data)
530
+
531
+ async def recommend_voices(
532
+ self, query: str, count: int = 5
533
+ ) -> list[RecommendedVoice]:
534
+ """Recommend voices from a text description.
535
+
536
+ Recommendation results only include ``voice_id``, ``voice_name``, and
537
+ ``score``. Use ``voice_v2`` or ``voices_v2`` to fetch detailed metadata
538
+ for returned voice IDs.
539
+ """
540
+ if count < 1 or count > 10:
541
+ raise ValueError("count must be between 1 and 10")
542
+ if not self.session:
543
+ raise TypecastError("Client session not initialized. Use async with.")
544
+
545
+ async with self.session.get(
546
+ f"{self.host}/v1/voices/recommendations",
547
+ params={"query": query, "count": count},
548
+ ) as response:
549
+ if response.status != 200:
550
+ error_text = await response.text()
551
+ self._handle_error(response.status, error_text)
552
+
553
+ data = await response.json()
554
+ return [RecommendedVoice.model_validate(item) for item in data]
typecast/client.py CHANGED
@@ -10,6 +10,7 @@ from ._voice_clone import (
10
10
  validate_clone_inputs,
11
11
  validate_custom_voice_id,
12
12
  )
13
+ from ._user_agent import requests_user_agent
13
14
  from .composer import SpeechComposer
14
15
  from .exceptions import (
15
16
  BadRequestError,
@@ -25,6 +26,7 @@ from .models import (
25
26
  CustomVoice,
26
27
  LanguageCode,
27
28
  Output,
29
+ RecommendedVoice,
28
30
  SubscriptionResponse,
29
31
  TTSModel,
30
32
  TTSPrompt,
@@ -95,13 +97,22 @@ class Typecast:
95
97
  ... f.write(response.audio_data)
96
98
  """
97
99
 
98
- def __init__(self, host: Optional[str] = None, api_key: Optional[str] = None):
100
+ def __init__(
101
+ self,
102
+ host: Optional[str] = None,
103
+ api_key: Optional[str] = None,
104
+ session: Optional[requests.Session] = None,
105
+ ):
99
106
  """Initialize the Typecast client.
100
107
 
101
108
  Args:
102
109
  host: API host URL. Defaults to TYPECAST_API_HOST env var
103
110
  or 'https://api.typecast.ai'.
104
111
  api_key: API key for authentication. Defaults to TYPECAST_API_KEY env var.
112
+ session: Optional externally-managed requests.Session. When provided,
113
+ the client will not create a new session nor close it; auth headers
114
+ (`X-API-KEY`, `User-Agent`) are attached per-request via
115
+ `_request_headers()`.
105
116
 
106
117
  Raises:
107
118
  ValueError: If no API key is provided and TYPECAST_API_KEY is not set.
@@ -110,11 +121,18 @@ class Typecast:
110
121
  self.api_key = conf.get_api_key(api_key)
111
122
  if not self.api_key and conf.is_default_host(self.host):
112
123
  raise ValueError("API key is required for the default Typecast API host")
113
- self.session = requests.Session()
114
- headers = {"Content-Type": "application/json"}
115
- if self.api_key:
116
- headers["X-API-KEY"] = self.api_key
117
- self.session.headers.update(headers)
124
+ self._owns_session = session is None
125
+ if session is not None:
126
+ self.session = session
127
+ else:
128
+ self.session = requests.Session()
129
+ headers = {
130
+ "Content-Type": "application/json",
131
+ "User-Agent": requests_user_agent(self.host),
132
+ }
133
+ if self.api_key:
134
+ headers["X-API-KEY"] = self.api_key
135
+ self.session.headers.update(headers)
118
136
 
119
137
  def _handle_error(self, status_code: int, response_text: str):
120
138
  """Handle HTTP error responses with specific exception types."""
@@ -138,6 +156,20 @@ class Typecast:
138
156
  status_code=status_code,
139
157
  )
140
158
 
159
+ def _request_headers(self) -> Optional[dict]:
160
+ """Headers to attach to each individual request.
161
+
162
+ For owned sessions, auth is set at session scope, so return None and let
163
+ requests use the session headers. For external sessions, the session has
164
+ no auth headers, so we attach X-API-KEY and User-Agent per-request.
165
+ """
166
+ if self._owns_session:
167
+ return None
168
+ headers = {"User-Agent": requests_user_agent(self.host)}
169
+ if self.api_key:
170
+ headers["X-API-KEY"] = self.api_key
171
+ return headers
172
+
141
173
  def text_to_speech(self, request: TTSRequest) -> TTSResponse:
142
174
  """Convert text to speech.
143
175
 
@@ -156,7 +188,9 @@ class Typecast:
156
188
  """
157
189
  endpoint = "/v1/text-to-speech"
158
190
  response = self.session.post(
159
- f"{self.host}{endpoint}", json=request.model_dump(exclude_none=True)
191
+ f"{self.host}{endpoint}",
192
+ json=request.model_dump(exclude_none=True),
193
+ headers=self._request_headers(),
160
194
  )
161
195
  if response.status_code != 200:
162
196
  self._handle_error(response.status_code, response.text)
@@ -236,7 +270,11 @@ class Typecast:
236
270
  NotFoundError, UnprocessableEntityError, RateLimitError,
237
271
  InternalServerError, TypecastError: depending on response status.
238
272
  """
239
- if not isinstance(chunk_size, int) or isinstance(chunk_size, bool) or chunk_size < 1:
273
+ if (
274
+ not isinstance(chunk_size, int)
275
+ or isinstance(chunk_size, bool)
276
+ or chunk_size < 1
277
+ ):
240
278
  raise ValueError("chunk_size must be a positive integer")
241
279
  endpoint = "/v1/text-to-speech/stream"
242
280
  response = self.session.post(
@@ -244,6 +282,7 @@ class Typecast:
244
282
  json=request.model_dump(exclude_none=True),
245
283
  stream=True,
246
284
  timeout=(10, 300),
285
+ headers=self._request_headers(),
247
286
  )
248
287
  if response.status_code != 200:
249
288
  error_text = response.text
@@ -291,6 +330,7 @@ class Typecast:
291
330
  json=request.model_dump(exclude_none=True),
292
331
  params=params,
293
332
  timeout=(10, 300),
333
+ headers=self._request_headers(),
294
334
  )
295
335
  if response.status_code != 200:
296
336
  self._handle_error(response.status_code, response.text)
@@ -327,12 +367,19 @@ class Typecast:
327
367
  }
328
368
  data = {"name": name, "model": model_str}
329
369
  # Remove the session-level Content-Type so requests can set the
330
- # correct multipart/form-data boundary for this request.
370
+ # correct multipart/form-data boundary for this request. For external
371
+ # sessions, also attach the per-request auth headers (X-API-KEY,
372
+ # User-Agent); _request_headers() returns None for owned sessions, so
373
+ # owned-session behavior is unchanged.
374
+ headers = {"Content-Type": None}
375
+ per_request = self._request_headers()
376
+ if per_request:
377
+ headers.update(per_request)
331
378
  response = self.session.post(
332
379
  f"{self.host}/v1/voices/clone",
333
380
  files=files,
334
381
  data=data,
335
- headers={"Content-Type": None},
382
+ headers=headers,
336
383
  timeout=(10, 300),
337
384
  )
338
385
  if response.status_code != 200:
@@ -353,6 +400,7 @@ class Typecast:
353
400
  response = self.session.delete(
354
401
  f"{self.host}/v1/voices/{quote(voice_id, safe='')}",
355
402
  timeout=(10, 60),
403
+ headers=self._request_headers(),
356
404
  )
357
405
  if response.status_code not in (200, 204):
358
406
  self._handle_error(response.status_code, response.text)
@@ -375,7 +423,9 @@ class Typecast:
375
423
  if model:
376
424
  params["model"] = model
377
425
 
378
- response = self.session.get(f"{self.host}{endpoint}", params=params)
426
+ response = self.session.get(
427
+ f"{self.host}{endpoint}", params=params, headers=self._request_headers()
428
+ )
379
429
 
380
430
  if response.status_code != 200:
381
431
  self._handle_error(response.status_code, response.text)
@@ -398,7 +448,9 @@ class Typecast:
398
448
  This method is deprecated. Use voices_v2() for enhanced metadata.
399
449
  """
400
450
  endpoint = f"/v1/voices/{voice_id}"
401
- response = self.session.get(f"{self.host}{endpoint}")
451
+ response = self.session.get(
452
+ f"{self.host}{endpoint}", headers=self._request_headers()
453
+ )
402
454
 
403
455
  if response.status_code != 200:
404
456
  self._handle_error(response.status_code, response.text)
@@ -432,7 +484,9 @@ class Typecast:
432
484
  for key, value in filter_dict.items():
433
485
  params[key] = getattr(value, "value", value)
434
486
 
435
- response = self.session.get(f"{self.host}{endpoint}", params=params)
487
+ response = self.session.get(
488
+ f"{self.host}{endpoint}", params=params, headers=self._request_headers()
489
+ )
436
490
 
437
491
  if response.status_code != 200:
438
492
  self._handle_error(response.status_code, response.text)
@@ -454,7 +508,9 @@ class Typecast:
454
508
  InternalServerError: On server-side failures.
455
509
  """
456
510
  endpoint = "/v1/users/me/subscription"
457
- response = self.session.get(f"{self.host}{endpoint}")
511
+ response = self.session.get(
512
+ f"{self.host}{endpoint}", headers=self._request_headers()
513
+ )
458
514
  if response.status_code != 200:
459
515
  self._handle_error(response.status_code, response.text)
460
516
  return SubscriptionResponse.model_validate(response.json())
@@ -472,9 +528,33 @@ class Typecast:
472
528
  NotFoundError: If the voice ID does not exist.
473
529
  """
474
530
  endpoint = f"/v2/voices/{voice_id}"
475
- response = self.session.get(f"{self.host}{endpoint}")
531
+ response = self.session.get(
532
+ f"{self.host}{endpoint}", headers=self._request_headers()
533
+ )
476
534
 
477
535
  if response.status_code != 200:
478
536
  self._handle_error(response.status_code, response.text)
479
537
 
480
538
  return VoiceV2Response.model_validate(response.json())
539
+
540
+ def recommend_voices(self, query: str, count: int = 5) -> list[RecommendedVoice]:
541
+ """Recommend voices from a text description.
542
+
543
+ Recommendation results only include ``voice_id``, ``voice_name``, and
544
+ ``score``. Use ``voice_v2`` or ``voices_v2`` to fetch detailed metadata
545
+ for returned voice IDs.
546
+
547
+ Args:
548
+ query: Text description of the desired voice.
549
+ count: Number of recommendations to return, from 1 to 10. Defaults to 5.
550
+ """
551
+ if count < 1 or count > 10:
552
+ raise ValueError("count must be between 1 and 10")
553
+
554
+ response = self.session.get(
555
+ f"{self.host}/v1/voices/recommendations",
556
+ params={"query": query, "count": count},
557
+ )
558
+ if response.status_code != 200:
559
+ self._handle_error(response.status_code, response.text)
560
+ return [RecommendedVoice.model_validate(item) for item in response.json()]
@@ -23,6 +23,7 @@ from .voices import (
23
23
  CustomVoice,
24
24
  GenderEnum,
25
25
  ModelInfo,
26
+ RecommendedVoice,
26
27
  UseCaseEnum,
27
28
  VoicesResponse,
28
29
  VoicesV2Filter,
@@ -46,6 +47,7 @@ __all__ = [
46
47
  "PlanTier",
47
48
  "Prompt",
48
49
  "PresetPrompt",
50
+ "RecommendedVoice",
49
51
  "SmartPrompt",
50
52
  "SubscriptionResponse",
51
53
  "TTSModel",
typecast/models/voices.py CHANGED
@@ -68,6 +68,19 @@ class VoiceV2Response(BaseModel):
68
68
  use_cases: Optional[list[str]] = None
69
69
 
70
70
 
71
+ class RecommendedVoice(BaseModel):
72
+ """Recommended voice result.
73
+
74
+ Recommendation results only include the matched voice ID, voice name, and
75
+ similarity score. Use ``voice_v2`` or ``voices_v2`` to fetch detailed voice
76
+ metadata for a returned ``voice_id``.
77
+ """
78
+
79
+ voice_id: str
80
+ voice_name: str
81
+ score: float
82
+
83
+
71
84
  class VoicesV2Filter(BaseModel):
72
85
  """Filter options for V2 voices endpoint"""
73
86
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: typecast-python
3
- Version: 0.3.6
3
+ Version: 0.3.8
4
4
  Summary: Official Typecast Python SDK - Convert text to lifelike speech using AI-powered voices
5
5
  Project-URL: Homepage, https://typecast.ai
6
6
  Project-URL: Documentation, https://typecast.ai/docs/overview
@@ -0,0 +1,18 @@
1
+ typecast/__init__.py,sha256=L6m0Z1jn1UgGjPp9AFiTwhHk9DVV_B4WrLfQDEfbcZI,1093
2
+ typecast/_user_agent.py,sha256=R_0eSKpuP9jLp_wDJcJGFvFtUh9BZwEgsT1buVeHRAA,1786
3
+ typecast/_voice_clone.py,sha256=TN2tbB3b5lC5uFCBnbERhz37bNJlbv6VZ-vj70EfTs4,3464
4
+ typecast/async_client.py,sha256=H3hCoGb6h0UyrnYUlQ0lhnpaCV-hxnZtsQKYlqaGPSk,21592
5
+ typecast/client.py,sha256=VPZVRnB9_T4VkSlrHpKLRahhEaxIF05XUAk-skqYKz8,20850
6
+ typecast/composer.py,sha256=r1kpRGlSJ1s4C1PkE7yIpGwnKtxYfIdFSLWaf8v1RAQ,9422
7
+ typecast/conf.py,sha256=yfOHYZDvIshWWMriN5wE0GFPR8AX3zv4Rg2655OvA9Q,724
8
+ typecast/exceptions.py,sha256=Y0ZzYebe8zOSOSAHbXfKR0G_RJgdmZXxi15Z7ZxPLIk,1568
9
+ typecast/utils.py,sha256=XuNuX7gW8_CGKqZ-cv_tKlPVMPBluAYJBw2clwmjIMI,708
10
+ typecast/models/__init__.py,sha256=OmaFdGpH6gK7EJyNgvbwqZHXVxjnWKJzynn7D3pfULo,1249
11
+ typecast/models/error.py,sha256=XomIjx7jvlCjItqzJuCAT4mXC9jwTjxR8lLDUk6P8KA,152
12
+ typecast/models/subscription.py,sha256=EIaAAo3cCRw8LYT_O6D9AVwxqIHrWCijzl4UTx7FZB8,894
13
+ typecast/models/tts.py,sha256=W2Bp9M-xPJPODlk89P9CnLA2fwb__4SR4yoMkG_O8kA,16453
14
+ typecast/models/voices.py,sha256=3yJD78QbZe3pTZv61GnB8up8qlIHKd1iSe5Bow-4ocU,2645
15
+ typecast_python-0.3.8.dist-info/METADATA,sha256=5Gbl-QxbRCGnqUwn_8Yr2xssRMZD_3GvZghgWsUxByg,25530
16
+ typecast_python-0.3.8.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
17
+ typecast_python-0.3.8.dist-info/licenses/LICENSE,sha256=HvtJ-S89uUkuYmt-OvVk4MRxmzwtbn84__qJtSrGU2Q,11348
18
+ typecast_python-0.3.8.dist-info/RECORD,,
@@ -1,17 +0,0 @@
1
- typecast/__init__.py,sha256=3pdJqNkXCZ7svzqab4sBR_qwyoM5E2sPjfuci1g1Ub8,1047
2
- typecast/_voice_clone.py,sha256=TN2tbB3b5lC5uFCBnbERhz37bNJlbv6VZ-vj70EfTs4,3464
3
- typecast/async_client.py,sha256=OwCqOUa82xvcSTbMYkRHsw_QK828gshrOV7IZotV8Ig,18611
4
- typecast/client.py,sha256=befvVQUzZXJ0E_almTW9aKyi2HvcjrdDirucbdd2E6U,17800
5
- typecast/composer.py,sha256=r1kpRGlSJ1s4C1PkE7yIpGwnKtxYfIdFSLWaf8v1RAQ,9422
6
- typecast/conf.py,sha256=yfOHYZDvIshWWMriN5wE0GFPR8AX3zv4Rg2655OvA9Q,724
7
- typecast/exceptions.py,sha256=Y0ZzYebe8zOSOSAHbXfKR0G_RJgdmZXxi15Z7ZxPLIk,1568
8
- typecast/utils.py,sha256=XuNuX7gW8_CGKqZ-cv_tKlPVMPBluAYJBw2clwmjIMI,708
9
- typecast/models/__init__.py,sha256=UEPUjg86fpCMXUvAzcNgxSPPhuPwCY9aQbzK3w90Fj0,1203
10
- typecast/models/error.py,sha256=XomIjx7jvlCjItqzJuCAT4mXC9jwTjxR8lLDUk6P8KA,152
11
- typecast/models/subscription.py,sha256=EIaAAo3cCRw8LYT_O6D9AVwxqIHrWCijzl4UTx7FZB8,894
12
- typecast/models/tts.py,sha256=W2Bp9M-xPJPODlk89P9CnLA2fwb__4SR4yoMkG_O8kA,16453
13
- typecast/models/voices.py,sha256=-EXP35jDy7_G30k5bDnVrFJHp6svEDTA5jJ8oHAgXNQ,2310
14
- typecast_python-0.3.6.dist-info/METADATA,sha256=lK8mTT7BBgfvCThmHehW0tCutsi8_yIPF6U6QoHmUX4,25530
15
- typecast_python-0.3.6.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
16
- typecast_python-0.3.6.dist-info/licenses/LICENSE,sha256=HvtJ-S89uUkuYmt-OvVk4MRxmzwtbn84__qJtSrGU2Q,11348
17
- typecast_python-0.3.6.dist-info/RECORD,,