videosdk-plugins-resemble 0.0.17__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.

Potentially problematic release.


This version of videosdk-plugins-resemble might be problematic. Click here for more details.

@@ -0,0 +1,18 @@
1
+ myenv/
2
+ venv/
3
+ env/
4
+ __pycache__/
5
+
6
+ .env
7
+ .env.local
8
+ test_env/
9
+ dist/
10
+ .DS_Store
11
+
12
+ node_modules/
13
+ credentials.json
14
+ .Python
15
+ build/
16
+ eggs/
17
+ sdist/
18
+ wheels/
@@ -0,0 +1,26 @@
1
+ Metadata-Version: 2.4
2
+ Name: videosdk-plugins-resemble
3
+ Version: 0.0.17
4
+ Summary: VideoSDK Agent Framework plugin for Resemble
5
+ Author: videosdk
6
+ License-Expression: Apache-2.0
7
+ Keywords: ai,audio,resemble,video,videosdk
8
+ Classifier: Development Status :: 4 - Beta
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: Topic :: Communications :: Conferencing
11
+ Classifier: Topic :: Multimedia :: Sound/Audio
12
+ Classifier: Topic :: Multimedia :: Video
13
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
14
+ Requires-Python: >=3.11
15
+ Requires-Dist: videosdk-agents>=0.0.17
16
+ Description-Content-Type: text/markdown
17
+
18
+ VideoSDK Resemble Plugin
19
+
20
+ Agent Framework plugin for tts services from Resemble.
21
+
22
+ ## Installation
23
+
24
+ ```bash
25
+ pip install videosdk-plugins-resemble
26
+ ```
@@ -0,0 +1,9 @@
1
+ VideoSDK Resemble Plugin
2
+
3
+ Agent Framework plugin for tts services from Resemble.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pip install videosdk-plugins-resemble
9
+ ```
@@ -0,0 +1,34 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "videosdk-plugins-resemble"
7
+ dynamic = ["version"]
8
+ description = "VideoSDK Agent Framework plugin for Resemble"
9
+ readme = "README.md"
10
+ license = "Apache-2.0"
11
+ requires-python = ">=3.11"
12
+ authors = [{ name = "videosdk"}]
13
+ keywords = ["video", "audio", "ai", "videosdk", "resemble"]
14
+ classifiers = [
15
+ "Intended Audience :: Developers",
16
+ "Development Status :: 4 - Beta",
17
+ "Intended Audience :: Developers",
18
+ "Topic :: Communications :: Conferencing",
19
+ "Topic :: Multimedia :: Sound/Audio",
20
+ "Topic :: Multimedia :: Video",
21
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
22
+ ]
23
+ dependencies = [
24
+ "videosdk-agents>=0.0.17"
25
+ ]
26
+
27
+ [tool.hatch.version]
28
+ path = "videosdk/plugins/resemble/version.py"
29
+
30
+ [tool.hatch.build.targets.wheel]
31
+ packages = ["videosdk"]
32
+
33
+ [tool.hatch.build.targets.sdist]
34
+ include = ["/videosdk"]
@@ -0,0 +1,3 @@
1
+ from .tts import ResembleTTS
2
+
3
+ __all__ = ["ResembleTTS"]
@@ -0,0 +1,114 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any, AsyncIterator, Optional
4
+ import os
5
+ import asyncio
6
+ import httpx
7
+ from dataclasses import dataclass
8
+
9
+ from videosdk.agents import TTS
10
+
11
+ RESEMBLE_HTTP_STREAMING_URL = "https://f.cluster.resemble.ai/stream"
12
+ DEFAULT_VOICE_UUID = "55592656"
13
+ DEFAULT_SAMPLE_RATE = 22050
14
+ DEFAULT_PRECISION = "PCM_16"
15
+
16
+ class ResembleTTS(TTS):
17
+ def __init__(
18
+ self,
19
+ *,
20
+ api_key: str | None = None,
21
+ voice_uuid: str = DEFAULT_VOICE_UUID,
22
+ sample_rate: int = DEFAULT_SAMPLE_RATE,
23
+ precision: str = DEFAULT_PRECISION,
24
+ ) -> None:
25
+ super().__init__(sample_rate=sample_rate, num_channels=1)
26
+
27
+ self.api_key = api_key or os.getenv("RESEMBLE_API_KEY")
28
+ if not self.api_key:
29
+ raise ValueError("Resemble API key is required. Provide either `api_key` or set `RESEMBLE_API_KEY` environment variable.")
30
+
31
+ self.voice_uuid = voice_uuid
32
+ self.precision = precision
33
+
34
+ self.audio_track = None
35
+ self.loop = None
36
+ self._http_client = httpx.AsyncClient(
37
+ timeout=httpx.Timeout(connect=15.0, read=30.0, write=5.0, pool=5.0),
38
+ follow_redirects=True,
39
+ )
40
+
41
+ async def synthesize(
42
+ self,
43
+ text: AsyncIterator[str] | str,
44
+ **kwargs: Any,
45
+ ) -> None:
46
+ try:
47
+ if isinstance(text, AsyncIterator):
48
+ full_text = ""
49
+ async for chunk in text:
50
+ full_text += chunk
51
+ else:
52
+ full_text = text
53
+
54
+ if not self.audio_track or not self.loop:
55
+ self.emit("error", "Audio track or event loop not set")
56
+ return
57
+
58
+ await self._http_stream_synthesis(full_text)
59
+
60
+ except Exception as e:
61
+ self.emit("error", f"Resemble TTS synthesis failed: {str(e)}")
62
+
63
+ async def _http_stream_synthesis(self, text: str) -> None:
64
+ headers = {
65
+ "Authorization": f"Token {self.api_key}",
66
+ "Content-Type": "application/json",
67
+ }
68
+
69
+ payload = {
70
+ "voice_uuid": self.voice_uuid,
71
+ "data": text,
72
+ "precision": self.precision,
73
+ "sample_rate": self.sample_rate,
74
+ }
75
+
76
+ try:
77
+ async with self._http_client.stream(
78
+ "POST",
79
+ RESEMBLE_HTTP_STREAMING_URL,
80
+ headers=headers,
81
+ json=payload
82
+ ) as response:
83
+ response.raise_for_status()
84
+
85
+ header_processed = False
86
+ buffer = b''
87
+
88
+ async for chunk in response.aiter_bytes():
89
+ if not header_processed:
90
+ buffer += chunk
91
+ data_pos = buffer.find(b'data')
92
+ if data_pos != -1:
93
+ header_size = data_pos + 8
94
+ audio_data = buffer[header_size:]
95
+ if audio_data:
96
+ self.loop.create_task(self.audio_track.add_new_bytes(audio_data))
97
+ header_processed = True
98
+ else:
99
+ if chunk:
100
+ self.loop.create_task(self.audio_track.add_new_bytes(chunk))
101
+
102
+ except httpx.HTTPStatusError as e:
103
+ self.emit("error", f"HTTP error {e.response.status_code}: {e.response.text}")
104
+ except Exception as e:
105
+ self.emit("error", f"HTTP streaming synthesis failed: {str(e)}")
106
+
107
+ async def aclose(self) -> None:
108
+ if self._http_client:
109
+ await self._http_client.aclose()
110
+ await super().aclose()
111
+
112
+ async def interrupt(self) -> None:
113
+ if self.audio_track:
114
+ self.audio_track.interrupt()
@@ -0,0 +1 @@
1
+ __version__ = "0.0.17"