videohub-python 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.
- videohub/__init__.py +4 -0
- videohub/client.py +75 -0
- videohub/config.py +23 -0
- videohub/constants.py +4 -0
- videohub/exceptions.py +14 -0
- videohub/http.py +135 -0
- videohub/services/__init__.py +0 -0
- videohub/services/admin.py +39 -0
- videohub/services/auth.py +15 -0
- videohub/services/calls.py +95 -0
- videohub/services/rooms.py +35 -0
- videohub/utils/__init__.py +0 -0
- videohub/utils/validators.py +47 -0
- videohub/ws.py +30 -0
- videohub_python-1.1.1.dist-info/METADATA +233 -0
- videohub_python-1.1.1.dist-info/RECORD +19 -0
- videohub_python-1.1.1.dist-info/WHEEL +5 -0
- videohub_python-1.1.1.dist-info/licenses/LICENSE +19 -0
- videohub_python-1.1.1.dist-info/top_level.txt +1 -0
videohub/__init__.py
ADDED
videohub/client.py
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
from .config import VideoHubConfig
|
|
2
|
+
from .http import HTTPClient
|
|
3
|
+
from .constants import WS_URL
|
|
4
|
+
|
|
5
|
+
from .services.auth import AuthService
|
|
6
|
+
from .services.rooms import RoomService
|
|
7
|
+
from .services.calls import CallService
|
|
8
|
+
from .services.admin import AdminService
|
|
9
|
+
|
|
10
|
+
from .ws import WSClient
|
|
11
|
+
|
|
12
|
+
import asyncio
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Client:
|
|
16
|
+
|
|
17
|
+
def __init__(
|
|
18
|
+
self,
|
|
19
|
+
api_key: str,
|
|
20
|
+
app_id: str,
|
|
21
|
+
app_secret: str,
|
|
22
|
+
app_platform: str,
|
|
23
|
+
app_identifier: str,
|
|
24
|
+
timeout: int = 10,
|
|
25
|
+
app_name: str | None = None,
|
|
26
|
+
domain: str | None = None,
|
|
27
|
+
allow_video: bool = True,
|
|
28
|
+
allow_audio: bool = True,
|
|
29
|
+
allow_screen: bool = True,
|
|
30
|
+
debug: bool = False,
|
|
31
|
+
):
|
|
32
|
+
|
|
33
|
+
self.config = VideoHubConfig(
|
|
34
|
+
api_key=api_key,
|
|
35
|
+
app_id=app_id,
|
|
36
|
+
app_secret=app_secret,
|
|
37
|
+
app_platform=app_platform,
|
|
38
|
+
app_identifier=app_identifier,
|
|
39
|
+
timeout=timeout,
|
|
40
|
+
app_name=app_name,
|
|
41
|
+
domain=domain,
|
|
42
|
+
allow_video=allow_video,
|
|
43
|
+
allow_audio=allow_audio,
|
|
44
|
+
allow_screen=allow_screen,
|
|
45
|
+
debug=debug,
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
self.http = HTTPClient(self.config)
|
|
49
|
+
|
|
50
|
+
# services
|
|
51
|
+
self.auth = AuthService(self.http)
|
|
52
|
+
self.rooms = RoomService(self.http)
|
|
53
|
+
self.calls = CallService(self.http, self.config)
|
|
54
|
+
self.admin = AdminService(self.http)
|
|
55
|
+
self.ws_url = WS_URL
|
|
56
|
+
# realtime
|
|
57
|
+
self.ws = WSClient(WS_URL)
|
|
58
|
+
|
|
59
|
+
async def close(self):
|
|
60
|
+
await self.http.close()
|
|
61
|
+
await self.ws.close()
|
|
62
|
+
|
|
63
|
+
async def __aenter__(self):
|
|
64
|
+
return self
|
|
65
|
+
|
|
66
|
+
async def __aexit__(self, exc_type, exc, tb):
|
|
67
|
+
await self.close()
|
|
68
|
+
|
|
69
|
+
def __del__(self):
|
|
70
|
+
try:
|
|
71
|
+
loop = asyncio.get_event_loop()
|
|
72
|
+
if loop.is_running():
|
|
73
|
+
loop.create_task(self.close())
|
|
74
|
+
except:
|
|
75
|
+
pass
|
videohub/config.py
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from typing import Optional
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
@dataclass
|
|
6
|
+
class VideoHubConfig:
|
|
7
|
+
|
|
8
|
+
api_key: str
|
|
9
|
+
app_id: str
|
|
10
|
+
app_secret: str
|
|
11
|
+
app_platform: str
|
|
12
|
+
app_identifier: str
|
|
13
|
+
|
|
14
|
+
timeout: int = 10
|
|
15
|
+
|
|
16
|
+
app_name: Optional[str] = None
|
|
17
|
+
domain: Optional[str] = None
|
|
18
|
+
|
|
19
|
+
allow_video: bool = True
|
|
20
|
+
allow_audio: bool = True
|
|
21
|
+
allow_screen: bool = True
|
|
22
|
+
|
|
23
|
+
debug: bool = False
|
videohub/constants.py
ADDED
videohub/exceptions.py
ADDED
videohub/http.py
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
from typing import Optional
|
|
3
|
+
|
|
4
|
+
import httpx
|
|
5
|
+
|
|
6
|
+
from .constants import API_BASE, SDK_NAME, SDK_VERSION
|
|
7
|
+
from .exceptions import APIRequestError
|
|
8
|
+
from .utils.validators import RequestValidator
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class HTTPClient:
|
|
12
|
+
|
|
13
|
+
def __init__(self, config):
|
|
14
|
+
self.config = config
|
|
15
|
+
self.validator = RequestValidator(config)
|
|
16
|
+
|
|
17
|
+
# lazy client
|
|
18
|
+
self._client: Optional[httpx.AsyncClient] = None
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _get_client(self):
|
|
22
|
+
|
|
23
|
+
if self._client is None:
|
|
24
|
+
|
|
25
|
+
limits = httpx.Limits(
|
|
26
|
+
max_connections=100,
|
|
27
|
+
max_keepalive_connections=20
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
self._client = httpx.AsyncClient(
|
|
31
|
+
base_url=API_BASE,
|
|
32
|
+
timeout=self.config.timeout,
|
|
33
|
+
limits=limits
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
return self._client
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _build_headers(self, auth_required: bool):
|
|
41
|
+
|
|
42
|
+
headers = {
|
|
43
|
+
"Content-Type": "application/json",
|
|
44
|
+
"User-Agent": f"{SDK_NAME}/{SDK_VERSION}",
|
|
45
|
+
"X-SDK-Version": SDK_VERSION,
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if auth_required:
|
|
49
|
+
headers.update(self.validator.build_headers())
|
|
50
|
+
|
|
51
|
+
return headers
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
async def _request(self, method: str, endpoint: str, **kwargs):
|
|
56
|
+
|
|
57
|
+
retries = 3
|
|
58
|
+
response = None
|
|
59
|
+
|
|
60
|
+
client = self._get_client()
|
|
61
|
+
|
|
62
|
+
for attempt in range(retries):
|
|
63
|
+
try:
|
|
64
|
+
|
|
65
|
+
response = await client.request(
|
|
66
|
+
method,
|
|
67
|
+
endpoint,
|
|
68
|
+
**kwargs
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
if response.status_code < 500:
|
|
72
|
+
break
|
|
73
|
+
|
|
74
|
+
except httpx.RequestError as e:
|
|
75
|
+
|
|
76
|
+
if attempt == retries - 1:
|
|
77
|
+
raise APIRequestError(f"Network error: {str(e)}")
|
|
78
|
+
|
|
79
|
+
await asyncio.sleep(2 ** attempt * 0.2)
|
|
80
|
+
|
|
81
|
+
if response is None:
|
|
82
|
+
raise APIRequestError("No response from server")
|
|
83
|
+
|
|
84
|
+
if response.status_code >= 400:
|
|
85
|
+
try:
|
|
86
|
+
error = response.json()
|
|
87
|
+
message = error.get("error") or error.get("message") or str(error)
|
|
88
|
+
raise APIRequestError(message)
|
|
89
|
+
except Exception:
|
|
90
|
+
raise APIRequestError(response.text)
|
|
91
|
+
|
|
92
|
+
try:
|
|
93
|
+
return response.json()
|
|
94
|
+
except Exception:
|
|
95
|
+
return response.text
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
async def post(self, endpoint: str, payload: dict, auth_required: bool = True):
|
|
100
|
+
|
|
101
|
+
headers = self._build_headers(auth_required)
|
|
102
|
+
|
|
103
|
+
if getattr(self.config, "debug", False):
|
|
104
|
+
print(f"[VideoHub SDK] POST {endpoint}")
|
|
105
|
+
print("Payload:", payload)
|
|
106
|
+
|
|
107
|
+
return await self._request(
|
|
108
|
+
"POST",
|
|
109
|
+
endpoint,
|
|
110
|
+
json=payload,
|
|
111
|
+
headers=headers
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
async def get(self, endpoint: str, params: Optional[dict] = None, auth_required: bool = True):
|
|
115
|
+
|
|
116
|
+
headers = self._build_headers(auth_required)
|
|
117
|
+
|
|
118
|
+
if getattr(self.config, "debug", False):
|
|
119
|
+
print(f"[VideoHub SDK] GET {endpoint}")
|
|
120
|
+
print("Params:", params)
|
|
121
|
+
|
|
122
|
+
return await self._request(
|
|
123
|
+
"GET",
|
|
124
|
+
endpoint,
|
|
125
|
+
params=params,
|
|
126
|
+
headers=headers
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
async def close(self):
|
|
132
|
+
|
|
133
|
+
if self._client:
|
|
134
|
+
await self._client.aclose()
|
|
135
|
+
self._client = None
|
|
File without changes
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
class AdminService:
|
|
2
|
+
|
|
3
|
+
def __init__(self, http):
|
|
4
|
+
self.http = http
|
|
5
|
+
|
|
6
|
+
async def kick(self, room: str, identity: str):
|
|
7
|
+
|
|
8
|
+
return await self.http.post(
|
|
9
|
+
"/client/va/kick",
|
|
10
|
+
{
|
|
11
|
+
"room_name": room,
|
|
12
|
+
"identity": identity
|
|
13
|
+
},
|
|
14
|
+
auth_required=True
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
async def mute(self, room: str, identity: str):
|
|
20
|
+
|
|
21
|
+
return await self.http.post(
|
|
22
|
+
"/client/video-hub/mute",
|
|
23
|
+
{
|
|
24
|
+
"room_name": room,
|
|
25
|
+
"identity": identity
|
|
26
|
+
},
|
|
27
|
+
auth_required=True
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
async def room_cleaner(self, room: str):
|
|
32
|
+
|
|
33
|
+
return await self.http.post(
|
|
34
|
+
"/client/video-hub/end",
|
|
35
|
+
{
|
|
36
|
+
"room_name": room,
|
|
37
|
+
},
|
|
38
|
+
auth_required=True
|
|
39
|
+
)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
class AuthService:
|
|
2
|
+
|
|
3
|
+
def __init__(self, http):
|
|
4
|
+
self.http = http
|
|
5
|
+
|
|
6
|
+
async def login(self, login: str, password: str):
|
|
7
|
+
|
|
8
|
+
return await self.http.post(
|
|
9
|
+
"/auth/login",
|
|
10
|
+
{
|
|
11
|
+
"login": login,
|
|
12
|
+
"password": password
|
|
13
|
+
},
|
|
14
|
+
auth_required=False
|
|
15
|
+
)
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
from ..exceptions import ValidationError
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class CallService:
|
|
5
|
+
def __init__(self, http, config):
|
|
6
|
+
self.http = http
|
|
7
|
+
self.config = config
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
async def start(self, **payload):
|
|
11
|
+
|
|
12
|
+
media = payload.pop("media", None)
|
|
13
|
+
|
|
14
|
+
if media:
|
|
15
|
+
audio = media.get("audio", True)
|
|
16
|
+
video = media.get("video", True)
|
|
17
|
+
screen = media.get("screen", False)
|
|
18
|
+
else:
|
|
19
|
+
audio = payload.pop("audio", True)
|
|
20
|
+
video = payload.pop("video", True)
|
|
21
|
+
screen = payload.pop("screen", False)
|
|
22
|
+
|
|
23
|
+
if video and not self.config.allow_video:
|
|
24
|
+
raise ValidationError("Video feature not allowed")
|
|
25
|
+
|
|
26
|
+
if audio and not self.config.allow_audio:
|
|
27
|
+
raise ValidationError("Audio feature not allowed")
|
|
28
|
+
|
|
29
|
+
if screen and not self.config.allow_screen:
|
|
30
|
+
raise ValidationError("Screen sharing not allowed")
|
|
31
|
+
|
|
32
|
+
payload["media"] = {
|
|
33
|
+
"audio": audio,
|
|
34
|
+
"video": video,
|
|
35
|
+
"screen": screen,
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return await self.http.post(
|
|
39
|
+
"/client/calls/start",
|
|
40
|
+
payload,
|
|
41
|
+
auth_required=True
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
async def host_token(self, call_id: str):
|
|
46
|
+
return await self.http.post(
|
|
47
|
+
"/client/calls/token",
|
|
48
|
+
{"call_id": call_id},
|
|
49
|
+
auth_required=True
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
async def guest_token(self, call_id: str):
|
|
54
|
+
return await self.http.post(
|
|
55
|
+
"/call/guest/token",
|
|
56
|
+
{"call_id": call_id},
|
|
57
|
+
auth_required=False
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
async def end(self, call_id: str):
|
|
62
|
+
return await self.http.post(
|
|
63
|
+
"/client/calls/end",
|
|
64
|
+
{"call_id": call_id},
|
|
65
|
+
auth_required=True
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
async def mute_user(self, call_id: str, identity: str, mute: bool = True):
|
|
70
|
+
return await self.http.post(
|
|
71
|
+
f"/client/calls/{call_id}/mute",
|
|
72
|
+
{"identity": identity, "mute": mute},
|
|
73
|
+
auth_required=True
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
async def video_off_user(self, call_id: str, identity: str, disable: bool = True):
|
|
77
|
+
return await self.http.post(
|
|
78
|
+
f"/client/calls/{call_id}/video-off",
|
|
79
|
+
{"identity": identity, "disable": disable},
|
|
80
|
+
auth_required=True
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
async def screen_off_user(self, call_id: str, identity: str):
|
|
84
|
+
return await self.http.post(
|
|
85
|
+
f"/client/calls/{call_id}/screen-off",
|
|
86
|
+
{"identity": identity},
|
|
87
|
+
auth_required=True
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
async def kick_user(self, call_id: str, identity: str):
|
|
91
|
+
return await self.http.post(
|
|
92
|
+
f"/client/calls/{call_id}/kick",
|
|
93
|
+
{"identity": identity},
|
|
94
|
+
auth_required=True
|
|
95
|
+
)
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
class RoomService:
|
|
2
|
+
|
|
3
|
+
def __init__(self, http):
|
|
4
|
+
self.http = http
|
|
5
|
+
|
|
6
|
+
async def create(self, **payload):
|
|
7
|
+
|
|
8
|
+
if "room_name" not in payload:
|
|
9
|
+
raise ValueError("room_name is required")
|
|
10
|
+
|
|
11
|
+
payload.setdefault("mode", "df_video_party")
|
|
12
|
+
payload.setdefault("max_participants", 5)
|
|
13
|
+
|
|
14
|
+
return await self.http.post(
|
|
15
|
+
"/client/create/rooms",
|
|
16
|
+
payload,
|
|
17
|
+
auth_required=True
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
async def host_token(self, room_name: str):
|
|
21
|
+
|
|
22
|
+
return await self.http.post(
|
|
23
|
+
"/client/rooms/token/host",
|
|
24
|
+
{"room_name": room_name},
|
|
25
|
+
auth_required=True
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
async def guest_token(self, room_name: str):
|
|
30
|
+
|
|
31
|
+
return await self.http.post(
|
|
32
|
+
"/rooms/token/guest",
|
|
33
|
+
{"room_name": room_name},
|
|
34
|
+
auth_required=False
|
|
35
|
+
)
|
|
File without changes
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
from ..exceptions import ValidationError
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class RequestValidator:
|
|
5
|
+
|
|
6
|
+
def __init__(self, config):
|
|
7
|
+
self.config = config
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def _validate(self):
|
|
11
|
+
|
|
12
|
+
if not self.config.api_key:
|
|
13
|
+
raise ValidationError("API key is required")
|
|
14
|
+
|
|
15
|
+
if not self.config.app_id:
|
|
16
|
+
raise ValidationError("App ID is required")
|
|
17
|
+
|
|
18
|
+
if not self.config.app_secret:
|
|
19
|
+
raise ValidationError("App secret is required")
|
|
20
|
+
|
|
21
|
+
if not self.config.app_platform:
|
|
22
|
+
raise ValidationError("App platform is required")
|
|
23
|
+
|
|
24
|
+
if not self.config.app_identifier:
|
|
25
|
+
raise ValidationError("App identifier is required")
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def build_headers(self):
|
|
29
|
+
|
|
30
|
+
self._validate()
|
|
31
|
+
|
|
32
|
+
headers = {
|
|
33
|
+
"X-API-Key": self.config.api_key,
|
|
34
|
+
"X-App-ID": self.config.app_id,
|
|
35
|
+
"X-App-Secret": self.config.app_secret,
|
|
36
|
+
"X-App-Platform": self.config.app_platform,
|
|
37
|
+
"X-App-Identifier": self.config.app_identifier,
|
|
38
|
+
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if self.config.app_name:
|
|
42
|
+
headers["X-App-Name"] = self.config.app_name
|
|
43
|
+
|
|
44
|
+
if self.config.domain:
|
|
45
|
+
headers["X-Domain"] = self.config.domain
|
|
46
|
+
|
|
47
|
+
return headers
|
videohub/ws.py
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import websockets
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class WSClient:
|
|
6
|
+
|
|
7
|
+
def __init__(self, url: str):
|
|
8
|
+
self.url = url
|
|
9
|
+
self.ws = None
|
|
10
|
+
|
|
11
|
+
async def connect(self, token: str):
|
|
12
|
+
self.ws = await websockets.connect(f"{self.url}?token={token}")
|
|
13
|
+
|
|
14
|
+
async def send(self, data: dict):
|
|
15
|
+
if not self.ws:
|
|
16
|
+
raise RuntimeError("WebSocket not connected")
|
|
17
|
+
|
|
18
|
+
await self.ws.send(json.dumps(data))
|
|
19
|
+
|
|
20
|
+
async def receive(self):
|
|
21
|
+
if not self.ws:
|
|
22
|
+
raise RuntimeError("WebSocket not connected")
|
|
23
|
+
|
|
24
|
+
msg = await self.ws.recv()
|
|
25
|
+
return json.loads(msg)
|
|
26
|
+
|
|
27
|
+
async def close(self):
|
|
28
|
+
if self.ws:
|
|
29
|
+
await self.ws.close()
|
|
30
|
+
self.ws = None
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: videohub-python
|
|
3
|
+
Version: 1.1.1
|
|
4
|
+
Summary: Official VideoHub Python SDK for building video, audio, and screen sharing applications.
|
|
5
|
+
Author-email: Letscms <videohubsupport@letscms.com>
|
|
6
|
+
License: Copyright (c) 2026 VideoHub (https://github.com/letscms/videohub-python)
|
|
7
|
+
|
|
8
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
9
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
10
|
+
in the Software without restriction, including without limitation the rights
|
|
11
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
12
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
13
|
+
furnished to do so, subject to the following conditions:
|
|
14
|
+
|
|
15
|
+
The above copyright notice and this permission notice shall be included in all
|
|
16
|
+
copies or substantial portions of the Software.
|
|
17
|
+
|
|
18
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
19
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
20
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
21
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
22
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
23
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
24
|
+
SOFTWARE.
|
|
25
|
+
Project-URL: Homepage, https://videohub.askjitendra.com
|
|
26
|
+
Project-URL: Documentation, https://docs.videohub.askjitendra.com
|
|
27
|
+
Project-URL: Repository, https://github.com/letscms/videohub-python
|
|
28
|
+
Project-URL: Issues, https://github.com/letscms/videohub-python/issues
|
|
29
|
+
Keywords: videohub,video sdk,webrtc,video calling,audio rooms,screen sharing,pk battle
|
|
30
|
+
Classifier: Development Status :: 4 - Beta
|
|
31
|
+
Classifier: Intended Audience :: Developers
|
|
32
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
33
|
+
Classifier: Programming Language :: Python
|
|
34
|
+
Classifier: Programming Language :: Python :: 3
|
|
35
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
36
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
37
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
38
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
39
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
40
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
41
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
42
|
+
Classifier: Topic :: Internet
|
|
43
|
+
Requires-Python: >=3.9
|
|
44
|
+
Description-Content-Type: text/markdown
|
|
45
|
+
License-File: LICENSE
|
|
46
|
+
Requires-Dist: httpx>=0.25
|
|
47
|
+
Dynamic: license-file
|
|
48
|
+
|
|
49
|
+
# VideoHub Python SDK
|
|
50
|
+
|
|
51
|
+
Official Python SDK for **VideoHub** – build video calls, audio rooms, and real-time communication applications easily.
|
|
52
|
+
|
|
53
|
+
VideoHub helps developers integrate **video conferencing, audio rooms, screen sharing, and real-time communication** into their applications with simple APIs.
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## Installation
|
|
58
|
+
|
|
59
|
+
Install the SDK from PyPI:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
pip install videohub-python
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## Requirements
|
|
68
|
+
|
|
69
|
+
* Python **3.9+**
|
|
70
|
+
* VideoHub API Key
|
|
71
|
+
* App credentials (App ID & App Secret)
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## Quick Start
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
from videohub import VideoHubClient, VideoHubConfig
|
|
79
|
+
|
|
80
|
+
config = VideoHubConfig(
|
|
81
|
+
api_key="vhub_live_xxxxxxxxx",
|
|
82
|
+
app_id="your_app_id",
|
|
83
|
+
app_secret="your_app_secret",
|
|
84
|
+
app_platform="web",
|
|
85
|
+
app_identifier="example.com"
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
client = VideoHubClient(config)
|
|
89
|
+
|
|
90
|
+
# Example: create a room token
|
|
91
|
+
token = client.rooms.create_token(
|
|
92
|
+
room_name="my-room",
|
|
93
|
+
user_id="user_123"
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
print(token)
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## Authentication
|
|
102
|
+
|
|
103
|
+
All requests require the following credentials:
|
|
104
|
+
|
|
105
|
+
| Field | Description |
|
|
106
|
+
| -------------- | ------------------------------------ |
|
|
107
|
+
| API Key | Account authentication key |
|
|
108
|
+
| App ID | Unique application identifier |
|
|
109
|
+
| App Secret | Secret used for request verification |
|
|
110
|
+
| App Platform | `web`, `android`, or `ios` |
|
|
111
|
+
| App Identifier | Domain or package name |
|
|
112
|
+
|
|
113
|
+
Example configuration:
|
|
114
|
+
|
|
115
|
+
```python
|
|
116
|
+
config = VideoHubConfig(
|
|
117
|
+
api_key="vhub_live_xxxxx",
|
|
118
|
+
app_id="app_uuid",
|
|
119
|
+
app_secret="secret_xxxxx",
|
|
120
|
+
app_platform="web",
|
|
121
|
+
app_identifier="example.com"
|
|
122
|
+
)
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
## Features
|
|
128
|
+
|
|
129
|
+
The VideoHub Python SDK supports:
|
|
130
|
+
|
|
131
|
+
* Video calling
|
|
132
|
+
* Audio rooms
|
|
133
|
+
* Screen sharing
|
|
134
|
+
* Room management
|
|
135
|
+
* Call management
|
|
136
|
+
* Admin APIs
|
|
137
|
+
* Secure authentication
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
## Example: Create Room
|
|
142
|
+
|
|
143
|
+
```python
|
|
144
|
+
room = client.rooms.create(
|
|
145
|
+
room_name="demo-room",
|
|
146
|
+
max_participants=5
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
print(room)
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## Example: Generate User Token
|
|
155
|
+
|
|
156
|
+
```python
|
|
157
|
+
token = client.rooms.create_token(
|
|
158
|
+
room_name="demo-room",
|
|
159
|
+
user_id="user_123"
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
print(token)
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
## Error Handling
|
|
168
|
+
|
|
169
|
+
The SDK raises structured exceptions for API errors.
|
|
170
|
+
|
|
171
|
+
```python
|
|
172
|
+
from videohub.exceptions import VideoHubError
|
|
173
|
+
|
|
174
|
+
try:
|
|
175
|
+
client.rooms.create("room1")
|
|
176
|
+
except VideoHubError as e:
|
|
177
|
+
print(e)
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
## Project Structure
|
|
183
|
+
|
|
184
|
+
```
|
|
185
|
+
videohub/
|
|
186
|
+
│
|
|
187
|
+
├── client.py
|
|
188
|
+
├── config.py
|
|
189
|
+
├── constants.py
|
|
190
|
+
├── exceptions.py
|
|
191
|
+
├── http.py
|
|
192
|
+
│
|
|
193
|
+
├── services/
|
|
194
|
+
│ ├── auth.py
|
|
195
|
+
│ ├── rooms.py
|
|
196
|
+
│ ├── calls.py
|
|
197
|
+
│ └── admin.py
|
|
198
|
+
│
|
|
199
|
+
└── utils/
|
|
200
|
+
└── validators.py
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
## Documentation
|
|
206
|
+
|
|
207
|
+
Full API documentation is available at:
|
|
208
|
+
|
|
209
|
+
https://docs.videohub.askjitendra.com
|
|
210
|
+
|
|
211
|
+
---
|
|
212
|
+
|
|
213
|
+
## Contributing
|
|
214
|
+
|
|
215
|
+
Contributions are welcome!
|
|
216
|
+
|
|
217
|
+
1. Fork the repository
|
|
218
|
+
2. Create a new branch
|
|
219
|
+
3. Submit a pull request
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
## Reporting Issues
|
|
224
|
+
|
|
225
|
+
If you encounter a bug or want to request a feature, please open an issue:
|
|
226
|
+
|
|
227
|
+
https://github.com/letscms/videohub-python/issues
|
|
228
|
+
|
|
229
|
+
---
|
|
230
|
+
|
|
231
|
+
## License
|
|
232
|
+
|
|
233
|
+
This project is licensed under the **MIT License**.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
videohub/__init__.py,sha256=M6dI0Q21vy8dDWbEeZp32VgakwweRd3YVrDkHTGB6ko,98
|
|
2
|
+
videohub/client.py,sha256=d81MQ8ca02iGnfON2YQ88OZP_Wg4naMS02svd1Ao7FY,1907
|
|
3
|
+
videohub/config.py,sha256=D3ZdQSQ1RxPlUN2zzpZBJ6aEoRqvrmlemTelvsYTGRI,402
|
|
4
|
+
videohub/constants.py,sha256=pCsSvm0-PZu32DHMt0BLV7MZqCk_xDemXEHp3cSnUeI,144
|
|
5
|
+
videohub/exceptions.py,sha256=eKpCSGCakUv1BEeRWplRNHKY9-KRtFV2JNVU5WhoavA,191
|
|
6
|
+
videohub/http.py,sha256=UgOherO59GB47kNTpSVeuI1Apw5yT9kuR37M5Ke8UOs,3317
|
|
7
|
+
videohub/ws.py,sha256=oKFu3UnpH-gaAo2ip_PW2gP9hI944Z-ZYzwr3l9Y3I0,697
|
|
8
|
+
videohub/services/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
|
+
videohub/services/admin.py,sha256=5ww5G8XAOZf9zvSar_FDycGIaEDEAZZRWFz_Mu1Kv64,879
|
|
10
|
+
videohub/services/auth.py,sha256=IIzsYYtVYTnU4i8aNS9KkXUl0ZaM_LAKrf8OatBK9R0,334
|
|
11
|
+
videohub/services/calls.py,sha256=4jSvnINrRaqGqQ0x04FBUgUfqPTICh3rIO6w-TY43jQ,2727
|
|
12
|
+
videohub/services/rooms.py,sha256=g53uBdBnP18c47dlhVfVDV5-UdoA157HNXiGWYKGMYk,861
|
|
13
|
+
videohub/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
14
|
+
videohub/utils/validators.py,sha256=TdzEOitAiwAQa5c8qwFglrKGiNJWv6kAowEdmZUHypU,1252
|
|
15
|
+
videohub_python-1.1.1.dist-info/licenses/LICENSE,sha256=pOYQq7qGHhJitw-aDfiflDm7fbiknnItC8HWtgWzPhk,1096
|
|
16
|
+
videohub_python-1.1.1.dist-info/METADATA,sha256=JhwgS_ohYLh-ldjEUhB6-eD48mXnclBEm300-p-WUP8,5663
|
|
17
|
+
videohub_python-1.1.1.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
18
|
+
videohub_python-1.1.1.dist-info/top_level.txt,sha256=Ggz9Kw-6DU7kCS7_C4n4bAx13cFeSfiGmolZ86T9dyc,9
|
|
19
|
+
videohub_python-1.1.1.dist-info/RECORD,,
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
Copyright (c) 2026 VideoHub (https://github.com/letscms/videohub-python)
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
5
|
+
in the Software without restriction, including without limitation the rights
|
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
8
|
+
furnished to do so, subject to the following conditions:
|
|
9
|
+
|
|
10
|
+
The above copyright notice and this permission notice shall be included in all
|
|
11
|
+
copies or substantial portions of the Software.
|
|
12
|
+
|
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
19
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
videohub
|