Skip to content

Commit

Permalink
add signaling and moderation endpoints, start live captions
Browse files Browse the repository at this point in the history
  • Loading branch information
maxkahan committed Sep 25, 2024
1 parent 8aef118 commit 29a1e49
Show file tree
Hide file tree
Showing 7 changed files with 279 additions and 9 deletions.
31 changes: 31 additions & 0 deletions video/src/vonage_video/models/captions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
from typing import Optional

from pydantic import BaseModel, Field

from .enums import LanguageCode


class CaptionsOptions(BaseModel):
"""The Options to send captions.
Args:
session_id (str): The session ID.
token (str): The token.
language_code (LanguageCode, Optional): The language code.
max_duration (int, Optional): The maximum duration.
partial_captions (bool, Optional): The partial captions.
status_callback_url (str, Optional): The status callback URL.
"""

session_id: str = Field(..., serialization_alias='sessionId')
token: str
language_code: Optional[LanguageCode] = Field(
None, serialization_alias='languageCode'
)
max_duration: Optional[int] = Field(
None, le=300, ge=14400, serialization_alias='maxDuration'
)
partial_captions: Optional[bool] = Field(None, serialization_alias='partialCaptions')
status_callback_url: Optional[str] = Field(
None, min_length=15, max_length=2048, serialization_alias='statusCallbackUrl'
)
16 changes: 16 additions & 0 deletions video/src/vonage_video/models/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,19 @@ class MediaMode(str, Enum):
class P2pPreference(str, Enum):
DISABLED = 'disabled'
ALWAYS = 'always'


class LanguageCode(str, Enum):
EN_US = 'en-US'
EN_AU = 'en-AU'
EN_GB = 'en-GB'
ZH_CN = 'zh-CN'
FR_FR = 'fr-FR'
FR_CA = 'fr-CA'
DE_DE = 'de-DE'
HI_IN = 'hi-IN'
IT_IT = 'it-IT'
JA_JP = 'ja-JP'
KO_KR = 'ko-KR'
PT_BR = 'pt-BR'
TH_TH = 'th-TH'
4 changes: 2 additions & 2 deletions video/src/vonage_video/models/signal.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@
class SignalData(BaseModel):
"""The data to send in a signal."""

type: str
data: str = Field(None, max_length=8192)
type: str = Field(..., max_length=128)
data: str = Field(..., max_length=8192)
94 changes: 87 additions & 7 deletions video/src/vonage_video/video.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from pydantic import validate_call
from vonage_http_client.http_client import HttpClient
from vonage_video.models.captions import CaptionsOptions
from vonage_video.models.session import SessionOptions, VideoSession
from vonage_video.models.signal import SignalData
from vonage_video.models.stream import StreamInfo, StreamLayoutOptions
Expand Down Expand Up @@ -131,15 +132,94 @@ def send_signal(
Args:
session_id (str): The session ID.
data (SignalData): The data to send in the signal.
connection_id (str, Optional): The connection ID to send the signal to.
connection_id (str, Optional): The connection ID to send the signal to. If not provided,
the signal will be sent to all connections in the session.
"""
if connection_id is not None:
url = f'/v2/project/{self._http_client.auth.application_id}/session/{session_id}/connection/{connection_id}/signal'
else:
url = (
f'/v2/project/{self._http_client.auth.application_id}/session/{session_id}/signal',
)
url = f'/v2/project/{self._http_client.auth.application_id}/session/{session_id}/signal'

self._http_client.post(
self._http_client.video_host, url, data.model_dump(exclude_none=True)
)
self._http_client.post(
self._http_client.video_host, url, data.model_dump(exclude_none=True)
)

@validate_call
def disconnect_client(self, session_id: str, connection_id: str) -> None:
"""Disconnects a client from a session in the Vonage Video API.
Args:
session_id (str): The session ID.
connection_id (str): The connection ID of the client to disconnect.
"""
self._http_client.delete(
self._http_client.video_host,
f'/v2/project/{self._http_client.auth.application_id}/session/{session_id}/connection/{connection_id}',
)

@validate_call
def mute_stream(self, session_id: str, stream_id: str) -> None:
"""Mutes a stream in a session using the Vonage Video API.
Args:
session_id (str): The session ID.
stream_id (str): The stream ID.
"""
self._http_client.post(
self._http_client.video_host,
f'/v2/project/{self._http_client.auth.application_id}/session/{session_id}/stream/{stream_id}/mute',
)

@validate_call
def mute_all_streams(
self, session_id: str, excluded_stream_ids: List[str] = None
) -> None:
"""Mutes all streams in a session using the Vonage Video API.
Args:
session_id (str): The session ID.
excluded_stream_ids (List[str], Optional): The stream IDs to exclude from muting.
"""
params = {'active': True, 'excludedStreamIds': excluded_stream_ids}
self._toggle_mute_all_streams(session_id, params)

@validate_call
def disable_mute_all_streams(self, session_id: str) -> None:
"""Disables muting all streams in a session using the Vonage Video API.
Args:
session_id (str): The session ID.
"""
self._toggle_mute_all_streams(session_id, {'active': False})

@validate_call
def _toggle_mute_all_streams(self, session_id: str, params: dict) -> None:
"""Mutes all streams in a session using the Vonage Video API.
Args:
session_id (str): The session ID.
params (dict): The parameters to send in the request.
"""
self._http_client.post(
self._http_client.video_host,
f'/v2/project/{self._http_client.auth.application_id}/session/{session_id}/mute',
params,
)

@validate_call
def enable_captions(self, options: CaptionsOptions) -> str:
"""Enables captions in a session using the Vonage Video API.
Args:
options (CaptionsOptions): Options for the captions.
Returns:
str: The captions stream ID.
"""
response = self._http_client.post(
self._http_client.video_host,
f'/v2/project/{self._http_client.auth.application_id}/captions',
options.model_dump(exclude_none=True),
)

return response['captionsId']
26 changes: 26 additions & 0 deletions video/tests/test_captions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from os.path import abspath

import responses
from vonage_http_client import HttpClient
from vonage_video.models.captions import CaptionsOptions
from vonage_video.video import Video

from testutils import build_response, get_mock_jwt_auth

path = abspath(__file__)


video = Video(HttpClient(get_mock_jwt_auth()))


@responses.activate
def test_start_captions():
build_response(
path,
'POST',
'https://video.api.vonage.com/v2/project/test_application_id/captions',
'start_captions.json',
202,
)

options = CaptionsOptions()
70 changes: 70 additions & 0 deletions video/tests/test_moderation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
from os.path import abspath

import responses
from vonage_http_client import HttpClient
from vonage_video.video import Video

from testutils import build_response, get_mock_jwt_auth

path = abspath(__file__)


video = Video(HttpClient(get_mock_jwt_auth()))


@responses.activate
def test_disconnect_client():
build_response(
path,
'DELETE',
'https://video.api.vonage.com/v2/project/test_application_id/session/test_session_id/connection/test_connection_id',
status_code=204,
)

video.disconnect_client(
session_id='test_session_id', connection_id='test_connection_id'
)

assert responses.calls[0].response.status_code == 204


@responses.activate
def test_mute_stream():
build_response(
path,
'POST',
'https://video.api.vonage.com/v2/project/test_application_id/session/test_session_id/stream/test_stream_id/mute',
)

video.mute_stream(session_id='test_session_id', stream_id='test_stream_id')

assert responses.calls[0].response.status_code == 200


@responses.activate
def test_mute_all_streams():
build_response(
path,
'POST',
'https://video.api.vonage.com/v2/project/test_application_id/session/test_session_id/mute',
)

video.mute_all_streams(session_id='test_session_id')
assert responses.calls[0].response.status_code == 200

video.disable_mute_all_streams(session_id='test_session_id')
assert responses.calls[1].response.status_code == 200


@responses.activate
def test_mute_all_streams_excluded_stream_ids():
build_response(
path,
'POST',
'https://video.api.vonage.com/v2/project/test_application_id/session/test_session_id/mute',
)

video.mute_all_streams(
session_id='test_session_id', excluded_stream_ids=['test_stream_id']
)
assert responses.calls[0].response.status_code == 200
47 changes: 47 additions & 0 deletions video/tests/test_signal.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
from os.path import abspath

import responses
from vonage_http_client import HttpClient
from vonage_video.models.signal import SignalData
from vonage_video.video import Video

from testutils import build_response, get_mock_jwt_auth

path = abspath(__file__)


video = Video(HttpClient(get_mock_jwt_auth()))


@responses.activate
def test_send_signal_all():
build_response(
path,
'POST',
'https://video.api.vonage.com/v2/project/test_application_id/session/test_session_id/signal',
status_code=204,
)

video.send_signal(
session_id='test_session_id', data=SignalData(type='msg', data='Hello, World!')
)

assert responses.calls[0].response.status_code == 204


@responses.activate
def test_send_signal_to_connection_id():
build_response(
path,
'POST',
'https://video.api.vonage.com/v2/project/test_application_id/session/test_session_id/connection/test_connection_id/signal',
status_code=204,
)

video.send_signal(
session_id='test_session_id',
data=SignalData(type='msg', data='Hello, World!'),
connection_id='test_connection_id',
)

assert responses.calls[0].response.status_code == 204

0 comments on commit 29a1e49

Please sign in to comment.