Skip to content

Commit

Permalink
add captioning, start audio connector
Browse files Browse the repository at this point in the history
  • Loading branch information
maxkahan committed Sep 26, 2024
1 parent 29a1e49 commit e5c31e1
Show file tree
Hide file tree
Showing 9 changed files with 197 additions and 9 deletions.
41 changes: 41 additions & 0 deletions video/src/vonage_video/models/audio_connector.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
from typing import Optional

from pydantic import BaseModel, Field
from vonage_video.models.enums import AudioSampleRate


class AudioConnectorWebsocket(BaseModel):
"""The audio connector websocket options.
Args:
uri (str): The URI.
streams (list): The streams.
headers (dict): The headers.
audio_rate (AudioSampleRate): The audio sample rate.
"""

uri: str
streams: Optional[list] = None
headers: Optional[dict] = None
audio_rate: Optional[AudioSampleRate] = Field(None, serialization_alias='audioRate')


class AudioConnectorOptions(BaseModel):
"""Options for the audio connector.
Args:
session_id (str): The session ID.
token (str): The token.
websocket (AudioConnectorWebsocket): The audio connector websocket.
"""

session_id: str = Field(..., serialization_alias='sessionId')
token: str
websocket: AudioConnectorWebsocket


class AudioConnectorData(BaseModel):
"""Class containing audio connector ID and audio captioning session ID."""

id: Optional[str] = None
captions_id: Optional[str] = Field(None, serialization_alias='captionsId')
14 changes: 12 additions & 2 deletions video/src/vonage_video/models/captions.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class CaptionsOptions(BaseModel):
Args:
session_id (str): The session ID.
token (str): The token.
token (str): A valid token with moderation privileges.
language_code (LanguageCode, Optional): The language code.
max_duration (int, Optional): The maximum duration.
partial_captions (bool, Optional): The partial captions.
Expand All @@ -23,9 +23,19 @@ class CaptionsOptions(BaseModel):
None, serialization_alias='languageCode'
)
max_duration: Optional[int] = Field(
None, le=300, ge=14400, serialization_alias='maxDuration'
None, ge=300, le=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'
)


class CaptionsData(BaseModel):
"""Class containing captions ID.
Args:
captions_id (str): The captions ID.
"""

captions_id: str = Field(..., serialization_alias='captionsId')
5 changes: 5 additions & 0 deletions video/src/vonage_video/models/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,8 @@ class LanguageCode(str, Enum):
KO_KR = 'ko-KR'
PT_BR = 'pt-BR'
TH_TH = 'th-TH'


class AudioSampleRate(str, Enum):
KHZ_8 = 8000
KHZ_16 = 16000
2 changes: 2 additions & 0 deletions video/src/vonage_video/models/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ def set_p2p_preference_if_archive_mode_set(self):


class VideoSession(BaseModel):
"""The new session ID and options specified in the request."""

session_id: str
archive_mode: Optional[ArchiveMode] = None
media_mode: Optional[MediaMode] = None
Expand Down
18 changes: 18 additions & 0 deletions video/src/vonage_video/models/stream.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,15 @@


class StreamInfo(BaseModel):
"""The stream information.
Args:
id (str): The stream ID.
video_type (str): The video type.
name (str): The name.
layout_class_list (list(str)): The layout class list.
"""

id: Optional[str] = Field(None, validation_alias='id')
video_type: Optional[str] = Field(None, validation_alias='videoType')
name: Optional[str] = Field(None, validation_alias='name')
Expand All @@ -13,9 +22,18 @@ class StreamInfo(BaseModel):


class StreamLayout(BaseModel):
"""The stream layout.
Args:
id (str): The stream ID.
layout_class_list (list): The layout class list.
"""

id: str
layout_class_list: List[str] = Field(..., serialization_alias='layoutClassList')


class StreamLayoutOptions(BaseModel):
"""The options for the stream layout."""

items: List[StreamLayout]
40 changes: 35 additions & 5 deletions video/src/vonage_video/video.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +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.captions import CaptionsData, 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 @@ -207,19 +207,49 @@ def _toggle_mute_all_streams(self, session_id: str, params: dict) -> None:
)

@validate_call
def enable_captions(self, options: CaptionsOptions) -> str:
def start_captions(self, options: CaptionsOptions) -> CaptionsData:
"""Enables captions in a session using the Vonage Video API.
Args:
options (CaptionsOptions): Options for the captions.
Returns:
str: The captions stream ID.
CaptionsData: Class containing captions 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),
options.model_dump(exclude_none=True, by_alias=True),
)

return response['captionsId']
return CaptionsData(captions_id=response['captionsId'])

@validate_call
def stop_captions(self, captions: CaptionsData) -> None:
"""Disables captions in a session using the Vonage Video API.
Args:
captions (CaptionsData): The captions data.
"""
self._http_client.post(
self._http_client.video_host,
f'/v2/project/{self._http_client.auth.application_id}/captions/{captions.captions_id}/stop',
)

@validate_call
def start_audio_connector(self, options: AudioConnectorOptions) -> AudioConnectorData:
"""Starts an audio connector in a session using the Vonage Video API.
Args:
options (AudioConnectorOptions): Options for the audio connector.
Returns:
AudioConnectorData: Class containing audio connector ID.
"""
response = self._http_client.post(
self._http_client.video_host,
f'/v2/project/{self._http_client.auth.application_id}/connect',
options.model_dump(exclude_none=True, by_alias=True),
)

return AudioConnectorData(audio_connector_id=response['audioConnectorId'])
5 changes: 5 additions & 0 deletions video/tests/data/captions_error_already_enabled.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"code": 60003,
"message": "Audio captioning is already enabled",
"description": "Audio captioning is already enabled"
}
3 changes: 3 additions & 0 deletions video/tests/data/start_captions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"captionsId": "bc01a6b7-0e8e-4aa0-bb4e-2390f7cb18a1"
}
78 changes: 76 additions & 2 deletions video/tests/test_captions.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
from os.path import abspath

import responses
from pytest import raises
from vonage_http_client import HttpClient
from vonage_video.models.captions import CaptionsOptions
from vonage_http_client.errors import HttpRequestError
from vonage_video.models.captions import CaptionsData, CaptionsOptions
from vonage_video.models.enums import LanguageCode, TokenRole
from vonage_video.models.token import TokenOptions
from vonage_video.video import Video

from testutils import build_response, get_mock_jwt_auth
Expand All @@ -13,6 +17,26 @@
video = Video(HttpClient(get_mock_jwt_auth()))


def test_captions_options_model():
options = CaptionsOptions(
session_id='test_session_id',
token='test_token',
language_code=LanguageCode.EN_GB,
max_duration=300,
partial_captions=True,
status_callback_url='example.com/status',
)

assert options.model_dump(by_alias=True) == {
'sessionId': 'test_session_id',
'token': 'test_token',
'languageCode': 'en-GB',
'maxDuration': 300,
'partialCaptions': True,
'statusCallbackUrl': 'example.com/status',
}


@responses.activate
def test_start_captions():
build_response(
Expand All @@ -23,4 +47,54 @@ def test_start_captions():
202,
)

options = CaptionsOptions()
session_id = 'test_session_id'
options = CaptionsOptions(
session_id=session_id,
token=video.generate_client_token(
TokenOptions(session_id=session_id, role=TokenRole.MODERATOR)
),
language_code=LanguageCode.EN_GB,
max_duration=300,
partial_captions=True,
status_callback_url='https://example.com/status',
)
captions = video.start_captions(options)

assert captions.captions_id == 'bc01a6b7-0e8e-4aa0-bb4e-2390f7cb18a1'


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

session_id = 'test_session_id'
options = CaptionsOptions(
session_id=session_id,
token=video.generate_client_token(
TokenOptions(session_id=session_id, role=TokenRole.MODERATOR)
),
)

with raises(HttpRequestError) as e:
video.start_captions(options)
assert 'Audio captioning is already enabled' in e.value.message


@responses.activate
def test_stop_captions():
build_response(
path,
'POST',
'https://video.api.vonage.com/v2/project/test_application_id/captions/test_captions_id/stop',
status_code=202,
)

video.stop_captions(CaptionsData(captions_id='test_captions_id'))

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

0 comments on commit e5c31e1

Please sign in to comment.