diff --git a/.github/workflows/build-package.yml b/.github/workflows/build-package.yml index e8bb278..52896ec 100644 --- a/.github/workflows/build-package.yml +++ b/.github/workflows/build-package.yml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout 🛎️ - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: persist-credentials: false diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 40aca48..af72a85 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -38,7 +38,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL diff --git a/.github/workflows/publish-package-to-pypi.yml b/.github/workflows/publish-package-to-pypi.yml index c871c32..42d5473 100644 --- a/.github/workflows/publish-package-to-pypi.yml +++ b/.github/workflows/publish-package-to-pypi.yml @@ -9,7 +9,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout 🛎️ - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: persist-credentials: false diff --git a/LICENSE b/LICENSE index 456b15d..f0c86ca 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2023 Dolby Laboratories +Copyright (c) 2024 Dolby Laboratories Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 532c150..1303c77 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ # Dolby.io REST APIs Client for Python -Python wrapper for the dolby.io REST [Communications](https://docs.dolby.io/communications-apis/reference/authentication-api), [Streaming](https://docs.dolby.io/streaming-apis/reference) and [Media](https://docs.dolby.io/media-processing/reference/media-enhance-overview) APIs. +Python wrapper for the [Dolby Millicast](https://docs.dolby.io/streaming-apis/reference) and [Media](https://docs.dolby.io/media-processing/reference/media-enhance-overview) APIs. ## Build the builder diff --git a/client/README.md b/client/README.md index 731b412..960359e 100644 --- a/client/README.md +++ b/client/README.md @@ -1,6 +1,6 @@ # Dolby.io REST APIs Client for Python -Python wrapper for the dolby.io REST [Communications](https://docs.dolby.io/communications-apis/reference/authentication-api), [Streaming](https://docs.dolby.io/streaming-apis/reference) and [Media](https://docs.dolby.io/media-processing/reference/media-enhance-overview) APIs. +Python wrapper for the [Dolby Millicast](https://docs.dolby.io/streaming-apis/reference) and [Media](https://docs.dolby.io/media-processing/reference/media-enhance-overview) APIs. ## Install this project @@ -16,135 +16,6 @@ Upgrade your package to the latest version: python3 -m pip install --upgrade dolbyio-rest-apis ``` -## Logging - -You can change the log level by using the Python [logging](https://docs.python.org/3/library/logging.html) library. - -```python -import logging - -logging.basicConfig(level='DEBUG') -``` - -## Authentication - -In order to make API calls for most operations of the **Communications APIs** and **Media APIs**, you must get an access token using this API: - -```python -import asyncio -from dolbyio_rest_apis import authentication - -APP_KEY = 'YOUR_APP_KEY' -APP_SECRET = 'YOUR_APP_SECRET' - -loop = asyncio.get_event_loop() - -task = authentication.get_api_token(APP_KEY, APP_SECRET) -at = loop.run_until_complete(task) - -print(f'API Token: {at.access_token}') -``` - -To request a particular scope for this access token: - -```python -task = authentication.get_api_token(APP_KEY, APP_SECRET, scope=['comms:*']) -at = loop.run_until_complete(task) - -print(f'API Token: {at.access_token}') -print(f'Scope: {at.scope}') -``` - -## Communications Examples - -### Get a client access token - -To get an access token that will be used by the client SDK for an end user to open a session against dolby.io, use the following code: - -```python -import asyncio -from dolbyio_rest_apis import authentication as auth -from dolbyio_rest_apis.communications import authentication - -APP_KEY = 'YOUR_APP_KEY' -APP_SECRET = 'YOUR_APP_SECRET' - -loop = asyncio.get_event_loop() - -# Request an API Token -task = auth.get_api_token(APP_KEY, APP_SECRET, scope=['comms:client_access_token:create']) -api_token = loop.run_until_complete(task) - -print(f'API Token: {api_token.access_token}') - -# Request the Client Access Token -task = authentication.get_client_access_token_v2(api_token.access_token, ['*']) -cat = loop.run_until_complete(task) - -print(f'Client Access Token: {cat.access_token}') -``` - -Because most of the APIs are asynchronous, you can write an async function like that: - -```python -from dolbyio_rest_apis import authentication as auth -from dolbyio_rest_apis.communications import authentication - -APP_KEY = 'YOUR_APP_KEY' -APP_SECRET = 'YOUR_APP_SECRET' - -async def get_client_access_token(): - # Request an API Token - api_token = await auth.get_api_token(APP_KEY, APP_SECRET, scope=['comms:client_access_token:create']) - - # Request the Client Access Token - cat = await authentication.get_client_access_token_v2(api_token.access_token, ['*']) - print(f'Client Access Token: {cat.access_token}') - - return cat.access_token - -``` - -### Create a conference - -To create a Dolby Voice conference, you first must retrieve an API Access Token, then use the following code to create the conference. - -```python -import asyncio -from dolbyio_rest_apis import authentication -from dolbyio_rest_apis.communications import conference -from dolbyio_rest_apis.communications.models import Participant, Permission, VideoCodec - -APP_KEY = 'YOUR_APP_KEY' -APP_SECRET = 'YOUR_APP_SECRET' - -owner_id = '' # Identifier of the owner of the conference -alias = '' # Conference alias - -participants = [ - Participant('hostA', [Permission.JOIN, Permission.SEND_AUDIO, Permission.SEND_VIDEO], notify=True), - Participant('listener1', [Permission.JOIN], notify=False), -] - -loop = asyncio.get_event_loop() - -# Request an API token -task = authentication.get_api_token(APP_KEY, APP_SECRET, scope=['comms:conf:create']) -at = loop.run_until_complete(task) - -# Create the conference -task = conference.create_conference( - at.access_token, - owner_id, - alias, - video_codec=VideoCodec.VP8, - participants=participants -) -conf = loop.run_until_complete(task) - -print(f'Conference created: {conf.id}') -``` - ## Real-time Streaming Examples ### Create a publish token @@ -152,12 +23,12 @@ print(f'Conference created: {conf.id}') ```python import asyncio from dolbyio_rest_apis.streaming import publish_token -from dolbyio_rest_apis.streaming.models.publish_token import CreatePublishToken, CreateUpdatePublishTokenStream +from dolbyio_rest_apis.streaming.models.publish_token import CreatePublishToken, TokenStreamName API_SECRET = '' # Retrieve your API Secret from the dashboard create_token = CreatePublishToken('my_token') -create_token.streams.append(CreateUpdatePublishTokenStream('feed1', False)) +create_token.streams.append(TokenStreamName('feed1', False)) loop = asyncio.get_event_loop() @@ -172,12 +43,13 @@ print(token) ```python import asyncio from dolbyio_rest_apis.streaming import subscribe_token -from dolbyio_rest_apis.streaming.models.subscribe_token import CreateSubscribeToken, CreateUpdateSubscribeTokenStream +from dolbyio_rest_apis.streaming.models.publish_token import TokenStreamName +from dolbyio_rest_apis.streaming.models.subscribe_token import CreateSubscribeToken API_SECRET = '' # Retrieve your API Secret from the dashboard create_token = CreateSubscribeToken('my_token') -create_token.streams.append(CreateUpdateSubscribeTokenStream('feed1', False)) +create_token.streams.append(TokenStreamName('feed1', False)) loop = asyncio.get_event_loop() @@ -197,7 +69,7 @@ Get the App Key and Secret from the Dolby.io dashboard and use the following cod ```python import asyncio -from dolbyio_rest_apis import authentication +from dolbyio_rest_apis.media import authentication APP_KEY = 'YOUR_APP_KEY' APP_SECRET = 'YOUR_APP_SECRET' @@ -320,3 +192,13 @@ task = io.download_file( ) loop.run_until_complete(task) ``` + +## Logging + +You can change the log level by using the Python [logging](https://docs.python.org/3/library/logging.html) library. + +```python +import logging + +logging.basicConfig(level='DEBUG') +``` diff --git a/client/requirements.txt b/client/requirements.txt index 157acd2..ddeaea1 100644 --- a/client/requirements.txt +++ b/client/requirements.txt @@ -1,4 +1,5 @@ aiohttp>=3.7.4 aiofiles>=0.7.0 aiohttp-retry>=2.4.6 -certifi>=2022.12.7 +certifi>=2024.7.4 +dataclasses-json>=0.6.7 diff --git a/client/setup.py b/client/setup.py index 8e3e5c6..649c33b 100644 --- a/client/setup.py +++ b/client/setup.py @@ -24,13 +24,13 @@ license='MIT', url='https://github.com/dolbyio/dolbyio-rest-apis-client-python', project_urls={ - 'Documentation': 'https://docs.dolby.io/communications-apis/reference', + 'Documentation': 'https://docs.dolby.io/streaming-apis/reference', 'Source': 'https://github.com/dolbyio/dolbyio-rest-apis-client-python', 'Bug Tracker': 'https://github.com/dolbyio/dolbyio-rest-apis-client-python/issues', }, package_dir={'': os.path.join(current_path, 'src')}, packages=setuptools.find_packages(where=os.path.join(current_path, 'src')), - python_requires='>=3.7', + python_requires='>=3.10', use_scm_version= { 'local_scheme': 'no-local-version', 'version_scheme': 'release-branch-semver', @@ -43,10 +43,8 @@ 'License :: OSI Approved :: MIT License', 'Programming Language :: Python', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.7', - 'Programming Language :: Python :: 3.8', - 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.10', + 'Programming Language :: Python :: 3.11', 'Programming Language :: Python :: 3 :: Only', 'Operating System :: OS Independent', 'Intended Audience :: Developers', diff --git a/client/src/dolbyio_rest_apis/authentication.py b/client/src/dolbyio_rest_apis/authentication.py deleted file mode 100644 index 861d7c0..0000000 --- a/client/src/dolbyio_rest_apis/authentication.py +++ /dev/null @@ -1,74 +0,0 @@ -""" -dolbyio_rest_apis.authentication -~~~~~~~~~~~~~~~ - -This module contains the functions to work with the authentication API. -""" - -from dolbyio_rest_apis.core.helpers import add_if_not_none -from dolbyio_rest_apis.core.urls import get_api_url -from dolbyio_rest_apis.communications.internal.http_context import CommunicationsHttpContext -from .models import AccessToken -from typing import List - -async def get_api_token( - app_key: str, - app_secret: str, - expires_in: int=None, - scope: List[str]=None, - ) -> AccessToken: - r""" - To make any API call, you must acquire a JWT (JSON Web Token) format API token. - - See: https://docs.dolby.io/communications-apis/reference/get-api-token - - See: https://docs.dolby.io/media-apis/reference/get-api-token - - Args: - app_key: Your Dolby.io App Key. - app_secret: Your Dolby.io App Secret. - expires_in: (Optional) API token expiration time in seconds. - If no value is specified, the default is 1800, indicating 30 minutes. - The maximum value is 86,400, indicating 24 hours. - scope: (Optional) A list of case-sensitive strings allowing you to control what scope of access the API token should have. - If not specified, the API token will possess unrestricted access to all resources and actions. - The API supports the following scopes: - - comms:client_access_token:create: Allows requesting a client access token. - - comms:conf:create: Allows creating a new conference. - - comms:conf:admin: Allows administrating a conference, including actions - such as Invite, Kick, Send Message, Set Spatial Listener's Audio, and Update Permissions. - - comms:conf:destroy: Allows terminating a live conference. - - comms:monitor:delete: Allows deleting data from the Monitor API, for example, deleting recordings. - - comms:monitor:read: Allows reading data through the Monitor API. - - comms:monitor:download: Allows generating download URLs for data (e.g. recording) through the Monitor API. - - comms:stream:write: Allows starting and stopping RTMP or Real-Time streaming. - - comms:remix:write: Allows remixing recordings. - - comms:remix:read: Allows reading the remix status. - - comms:record:write: Allows starting and stopping recordings. - Incorrect values are omitted. If you want to give the token access to all Communications REST APIs, - you can use a wildcard, such as comms:* - - Returns: - An :class:`AccessToken` object. - - Raises: - HttpRequestError: If a client error one occurred. - HTTPError: If one occurred. - """ - - data = { - 'grant_type': 'client_credentials', - } - add_if_not_none(data, 'expires_in', expires_in) - if scope is not None: - data['scope'] = ' '.join(scope) - - async with CommunicationsHttpContext() as http_context: - json_response = await http_context.requests_post_basic_auth( - app_key=app_key, - app_secret=app_secret, - url=f'{get_api_url()}/auth/token', - data=data - ) - - return AccessToken(json_response) diff --git a/client/src/dolbyio_rest_apis/communications/__init__.py b/client/src/dolbyio_rest_apis/communications/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/client/src/dolbyio_rest_apis/communications/authentication.py b/client/src/dolbyio_rest_apis/communications/authentication.py deleted file mode 100644 index 84ae7a8..0000000 --- a/client/src/dolbyio_rest_apis/communications/authentication.py +++ /dev/null @@ -1,61 +0,0 @@ -""" -dolbyio_rest_apis.communications.authentication -~~~~~~~~~~~~~~~ - -This module contains the functions to work with the authentication API. -""" - -from dolbyio_rest_apis.communications.internal.http_context import CommunicationsHttpContext -from dolbyio_rest_apis.core.helpers import add_if_not_none -from dolbyio_rest_apis.core.urls import get_comms_url_v2 -from dolbyio_rest_apis.models import AccessToken -from typing import List - -async def get_client_access_token_v2( - access_token: str, - session_scope: List[str], - external_id: str=None, - expires_in: int=None, - ) -> AccessToken: - r""" - This API returns an access token that your backend can request on behalf of a client to initialize - the Dolby.io SDK in a secure way. - - See: https://docs.dolby.io/communications-apis/reference/get-client-access-token - - Args: - access_token: Access token to use for authentication. - session_scope: A list of case-sensitive strings allowing you to control - what scope of access the client access token should have. If not specified, - the token will possess unrestricted access to all resources and actions. - The API supports the following scopes: - - conf:create: Allows creating a new conference. - - notifications:set: Allows the client to subscribe to events. - - file:convert: Allows converting files. - - session:update: Allows updating the participant's name and avatar URL. - Incorrect values are omitted. If you want to give the token access to all scopes, you can use a wildcard, such as *. - external_id: (Optional) The unique identifier of the participant who requests the token. - expires_in: (Optional) Access token expiration time in seconds. - If no value is specified, the default is 3,600, indicating one hour. - The maximum value is 86,400, indicating 24 hours. - Returns: - An :class:`AccessToken` object. - - Raises: - HttpRequestError: If a client error one occurred. - HTTPError: If one occurred. - """ - payload = {} - add_if_not_none(payload, 'externalId', external_id) - add_if_not_none(payload, 'expires_in', expires_in) - if session_scope is not None: - payload['sessionScope'] = ' '.join(session_scope) - - async with CommunicationsHttpContext() as http_context: - json_response = await http_context.requests_post( - access_token=access_token, - url=f'{get_comms_url_v2()}/client-access-token', - payload=payload, - ) - - return AccessToken(json_response) diff --git a/client/src/dolbyio_rest_apis/communications/conference.py b/client/src/dolbyio_rest_apis/communications/conference.py deleted file mode 100644 index 04ef6a2..0000000 --- a/client/src/dolbyio_rest_apis/communications/conference.py +++ /dev/null @@ -1,387 +0,0 @@ -""" -dolbyio_rest_apis.communications.conference -~~~~~~~~~~~~~~~ - -This module contains the functions to work with the conference API. -""" - -from dataclasses import asdict -from dolbyio_rest_apis.communications.internal.http_context import CommunicationsHttpContext -from dolbyio_rest_apis.core.helpers import get_value, add_if_not_none -from dolbyio_rest_apis.core.urls import get_comms_url_v2 -from .models import UserToken, Conference, SpatialAudioEnvironment, SpatialAudioListener, SpatialAudioUser, RTCPMode, Participant, VideoCodec, GetParticipantsResponse -from typing import List - -async def create_conference( - access_token: str, - owner_external_id: str, - alias: str=None, - pincode: str=None, - dolby_voice: bool=True, - live_recording: bool=False, - rtcp_mode: RTCPMode=RTCPMode.AVERAGE, - ttl: int=None, - video_codec: VideoCodec=None, - audio_only: bool=False, - participants: List[Participant]=None, - recording_formats: List[str]=None, - region: str=None, - ) -> Conference: - r""" - Creates a conference. - - See: https://docs.dolby.io/communications-apis/reference/create-conference - - Args: - access_token: Access token to use for authentication. - owner_external_id: External ID of the conference owner. - alias: (Optional) Name of the conference. - pincode: (Optional) The PIN code of the conference. - This applies to conferences using PSTN (telephony network). - dolby_voice: (Optional) Indicates if Dolby Voice is enabled for the conference. - The `True` value creates the conference with Dolby Voice enabled. - live_recording: (Optional) Indicates if live recording is enabled for the conference. - rtcp_mode: (Optional) Specifies the bitrate adaptation mode for the video transmission. - ttl: (Optional) Specifies the time to live that enables customizing the waiting time - (in seconds) and terminating empty conferences. - audio_only: (Optional) If `True`, the conference does not allow participants to enable video. - video_codec: (Optional) Specifies the video codec (VP8 or H264) for the conference. - participants: List of the :class:`Participant` object to update the permissions. - recording_formats: If specified, the default RecordingConfiguration is overridden. - Specifies the recording format. Valid values are 'mp3' and 'mp4'. - region: Dolby.io region where you want the conference to be hosted. Can be one of: - - au: Australia - - ca: Canada - - eu: Europe - - in: India - - us: United States - - Returns: - A :class:`Conference` object. - - Raises: - HttpRequestError: If a client error one occurred. - HTTPError: If one occurred. - """ - - parameters = { - 'dolbyVoice': dolby_voice, - 'liveRecording': live_recording, - 'rtcpMode': rtcp_mode.value, - } - add_if_not_none(parameters, 'pincode', pincode) - add_if_not_none(parameters, 'ttl', ttl) - add_if_not_none(parameters, 'audioOnly', audio_only) - add_if_not_none(parameters, 'videoCodec', video_codec) - - if recording_formats is not None and len(recording_formats) > 0: - parameters['recording'] = { - 'format': recording_formats - } - - payload = { - 'ownerExternalId': owner_external_id, - 'parameters': parameters, - } - add_if_not_none(payload, 'alias', alias) - - if participants is not None and len(participants) > 0: - obj_participants = { } - - for participant in participants: - if isinstance(participant, Participant): - permissions = [] - for p in participant.permissions: - permissions.append(get_value(p)) - - obj_participants[participant.external_id] = { - 'permissions': permissions, - 'notification': participant.notify, - } - - payload['participants'] = obj_participants - - async with CommunicationsHttpContext() as http_context: - json_response = await http_context.requests_post( - access_token=access_token, - url=f'{get_comms_url_v2(region)}/conferences/create', - payload=payload - ) - - return Conference(json_response) - -async def invite( - access_token: str, - conference_id: str, - participants: List[Participant], - ) -> List[UserToken]: - r""" - Invites participants to an ongoing conference. This API can also be used to generate new conference access tokens - for an ongoing conference. If the invite request includes participants that are already in the conference, a new - conference access token is not generated and an invitation is not sent. - - See: https://docs.dolby.io/communications-apis/reference/invite-to-conference - - Args: - access_token: Access token to use for authentication. - conference_id: Identifier of the conference. - participants: List of the :class:`Participant` object to invite to the conference. - - Returns: - A list of :class:`UserToken` objects. - - Raises: - HttpRequestError: If a client error one occurred. - HTTPError: If one occurred. - """ - - obj_participants = { } - for participant in participants: - if isinstance(participant, Participant): - permissions = [] - for p in participant.permissions: - permissions.append(get_value(p)) - - obj_participants[participant.external_id] = { - 'permissions': permissions, - 'notification': participant.notify, - } - - payload = { - 'participants': obj_participants, - } - - async with CommunicationsHttpContext() as http_context: - json_response = await http_context.requests_post( - access_token=access_token, - url=f'{get_comms_url_v2()}/conferences/{conference_id}/invite', - payload=payload - ) - - user_tokens = [] - for key in json_response.keys(): - user_token = UserToken(key, json_response[key]) - user_tokens.append(user_token) - - return user_tokens - -async def kick( - access_token: str, - conference_id: str, - external_ids: List[str], - ) -> None: - r""" - Kicks participants from an ongoing conference. - - See: https://docs.dolby.io/communications-apis/reference/kick-from-conference - - Args: - access_token: Access token to use for authentication. - conference_id: Identifier of the conference. - external_ids: List external IDs of the participants to kick out of the conference. - - Raises: - HttpRequestError: If a client error one occurred. - HTTPError: If one occurred. - """ - - payload = { - 'externalIds': external_ids, - } - - async with CommunicationsHttpContext() as http_context: - await http_context.requests_post( - access_token=access_token, - url=f'{get_comms_url_v2()}/conferences/{conference_id}/kick', - payload=payload - ) - -async def send_message( - access_token: str, - conference_id: str, - from_external_id: str, - to_external_ids: List[str], - message: str - ) -> None: - r""" - Sends a message to some or all participants in a conference. - - See: https://docs.dolby.io/communications-apis/reference/send-message - - Args: - access_token: Access token to use for authentication. - conference_id: Identifier of the conference. - from_external_id: The external ID of the author of the message. - to_external_ids: A list of external IDs that will receive the message. - If empty, the message will be broadcasted to all participants in the conference. - message: The message to send. - - Raises: - HttpRequestError: If a client error one occurred. - HTTPError: If one occurred. - """ - - payload = { - 'from': from_external_id, - 'message': message, - } - - if to_external_ids is not None and len(to_external_ids) > 0: - payload['to'] = to_external_ids - - async with CommunicationsHttpContext() as http_context: - await http_context.requests_post( - access_token=access_token, - url=f'{get_comms_url_v2()}/conferences/{conference_id}/message', - payload=payload - ) - -async def set_spatial_listeners_audio( - access_token: str, - conference_id: str, - environment: SpatialAudioEnvironment, - listener: SpatialAudioListener, - users: List[SpatialAudioUser], - ) -> None: - r""" - Sets the spatial audio scene for all listeners in an ongoing conference. - This sets the spatial audio environment, the position and direction for all listeners with the spatialAudio flag enabled. - The calls are not cumulative, and each call sets all the spatial listener values. - Participants who do not have a position set are muted. - - See: https://docs.dolby.io/communications-apis/reference/set-spatial-listeners-audio - - Args: - access_token: Access token to use for authentication. - conference_id: Identifier of the conference. - environment: The spatial environment of an application, - so the audio renderer understands which directions the application considers - forward, up, and right and which units it uses for distance. - listener: The listener's audio position and direction, defined using Cartesian coordinates. - users: The users' audio positions, defined using Cartesian coordinates. - - Raises: - HttpRequestError: If a client error one occurred. - HTTPError: If one occurred. - """ - - obj_users = { } - for user in users: - obj_users[user.external_id] = asdict(user.position) - - payload = { - 'environment': asdict(environment), - 'listener': asdict(listener), - 'users': obj_users, - } - - async with CommunicationsHttpContext() as http_context: - await http_context.requests_put( - access_token=access_token, - url=f'{get_comms_url_v2()}/conferences/{conference_id}/spatial-listeners-audio', - payload=payload - ) - -async def update_permissions( - access_token: str, - conference_id: str, - participants: List[Participant], - ) -> List[UserToken]: - r""" - Update permissions for participants in a conference. When a participant's permissions are updated, the new token - is sent directly to the SDK. The SDK automatically receives, stores, and manages the new token - and a permissionsUpdated event is sent. - - See: https://docs.dolby.io/communications-apis/reference/update-permissions - - Args: - access_token: Access token to use for authentication. - conference_id: Identifier of the conference. - participants: List of the :class:`Participant` object to update the permissions. - - Returns: - A list of :class:`UserToken` objects. - - Raises: - HttpRequestError: If a client error one occurred. - HTTPError: If one occurred. - """ - - obj_participants = { } - for participant in participants: - if isinstance(participant, Participant): - permissions = [] - for p in participant.permissions: - permissions.append(get_value(p)) - - obj_participants[participant.external_id] = { - 'permissions': permissions, - } - - payload = { - 'participants': obj_participants, - } - - async with CommunicationsHttpContext() as http_context: - json_response = await http_context.requests_post( - access_token=access_token, - url=f'{get_comms_url_v2()}/conferences/{conference_id}/permissions', - payload=payload - ) - - user_tokens = [] - for key in json_response.keys(): - user_token = UserToken(key, json_response[key]) - user_tokens.append(user_token) - - return user_tokens - -async def list_participants( - access_token: str, - conference_id: str, - ) -> GetParticipantsResponse: - r""" - Returns the current participant list. - - See: https://docs.dolby.io/communications-apis/reference/return-participant-list - - Args: - access_token: Access token to use for authentication. - conference_id: Identifier of the conference. - - Raises: - HttpRequestError: If a client error one occurred. - HTTPError: If one occurred. - """ - - async with CommunicationsHttpContext() as http_context: - json_response = await http_context.requests_get( - access_token=access_token, - url=f'{get_comms_url_v2()}/conferences/{conference_id}/participants', - ) - - return GetParticipantsResponse(json_response) - -async def terminate( - access_token: str, - conference_id: str, - ) -> None: - r""" - Terminates an ongoing conference and removes all remaining participants from the conference. - - See: https://docs.dolby.io/communications-apis/reference/terminate-conference - - Args: - access_token: Access token to use for authentication. - conference_id: Identifier of the conference. - - Raises: - HttpRequestError: If a client error one occurred. - HTTPError: If one occurred. - """ - - async with CommunicationsHttpContext() as http_context: - await http_context.requests_delete( - access_token=access_token, - url=f'{get_comms_url_v2()}/conferences/{conference_id}', - ) diff --git a/client/src/dolbyio_rest_apis/communications/internal/__init__.py b/client/src/dolbyio_rest_apis/communications/internal/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/client/src/dolbyio_rest_apis/communications/internal/http_context.py b/client/src/dolbyio_rest_apis/communications/internal/http_context.py deleted file mode 100644 index 8f61ccb..0000000 --- a/client/src/dolbyio_rest_apis/communications/internal/http_context.py +++ /dev/null @@ -1,390 +0,0 @@ -""" -dolbyio_rest_apis.communications.internal.helpers -~~~~~~~~~~~~~~~ - -This module contains internal helpers. -""" - -from aiohttp import BasicAuth, ClientResponse, ContentTypeError -from dolbyio_rest_apis.core.http_context import HttpContext -from dolbyio_rest_apis.core.helpers import get_value_or_default -from dolbyio_rest_apis.core.http_request_error import HttpRequestError -import json -import logging -from typing import Any, Dict, List - -class CommunicationsHttpContext(HttpContext): - """HTTP Context class for Communications APIs""" - - def __init__(self): - super().__init__() - - self._logger = logging.getLogger(CommunicationsHttpContext.__name__) - - async def _requests_post_put( - self, - access_token: str, - url: str, - method: str, - payload: Any=None, - extra_headers: Dict[str, str]=None, - ) -> Any or None: - r""" - Sends a POST or PUT request. - - Args: - access_token: The Access Token to use for authentication. - url: Where to send the request to. - method: HTTP method, POST or PUT. - payload: (Optional) Content of the request. - extra_headers: (Optional) Add extra HTTP headers in the request. - - Returns: - The JSON response if any or None. - - Raises: - HttpRequestError: If a client error one occurred. - HTTPError: If one occurred. - """ - - headers = { - 'Accept': 'application/json', - 'Content-Type': 'application/json', - 'Authorization': f'Bearer {access_token}', - } - - if extra_headers is not None: - headers.update(extra_headers) - - if payload is None: - payload = '{}' # The REST APIs don't support an empty payload - else: - payload = json.dumps(payload, indent=4) - - return await self._send_request( - method=method, - url=url, - headers=headers, - data=payload, - ) - - async def requests_put( - self, - access_token: str, - url: str, - payload: Any=None, - ) -> Any or None: - r""" - Sends a PUT request. - - Args: - access_token: The Access Token to use for authentication. - url: Where to send the request to. - payload: (Optional) Content of the request. - - Returns: - The JSON response if any or None. - - Raises: - HttpRequestError: If a client error one occurred. - HTTPError: If one occurred. - """ - - return await self._requests_post_put( - access_token=access_token, - url=url, - method='PUT', - payload=payload - ) - - async def requests_post( - self, - access_token: str, - url: str, - payload: Any=None, - extra_headers: Dict[str, str]=None, - ) -> Any or None: - r""" - Sends a POST request. - - Args: - access_token: The Access Token to use for authentication. - url: Where to send the request to. - payload: (Optional) Content of the request. - extra_headers: (Optional) Add extra HTTP headers in the request. - - Returns: - The JSON response if any or None. - - Raises: - HttpRequestError: If a client error one occurred. - HTTPError: If one occurred. - """ - - return await self._requests_post_put( - access_token=access_token, - url=url, - method='POST', - payload=payload, - extra_headers=extra_headers, - ) - - async def requests_post_basic_auth( - self, - app_key: str, - app_secret: str, - url: str, - json_payload: str=None, - data: Dict[str, Any]=None, - ) -> Any or None: - r""" - Sends a POST request with Basic authentication. - - Args: - app_key: The Dolby.io App Key. - app_secret: The Dolby.io App Secret. - url: Where to send the request to. - json_payload: (Optional) Content of the request as JSON payload. - data: (Optional) Content of the request. - - Returns: - The JSON response if any or None. - - Raises: - HttpRequestError: If a client error one occurred. - HTTPError: If one occurred. - """ - if data is None: - content_type = 'application/json' - if json_payload is None: - payload = '{}' # The REST APIs don't support an empty payload - else: - payload = json.dumps(json_payload, indent=4) - else: - content_type = 'application/x-www-form-urlencoded' - payload = data - - headers = { - 'Accept': 'application/json', - 'Content-Type': content_type, - } - - return await self._send_request( - method='POST', - url=url, - headers=headers, - auth=BasicAuth(app_key, app_secret), - data=payload, - ) - - async def requests_delete( - self, - access_token: str, - url: str, - ) -> None: - r""" - Sends a DELETE request. - - Args: - access_token: The Access Token to use for authentication. - url: Where to send the request to. - - Raises: - HttpRequestError: If a client error one occurred. - HTTPError: If one occurred. - """ - - headers = { - 'Accept': 'application/json', - 'Authorization': f'Bearer {access_token}', - } - - return await self._send_request( - method='DELETE', - url=url, - headers=headers, - ) - - async def requests_get( - self, - access_token: str, - url: str, - params: Dict[str, Any]=None, - ) -> Any or None: - r""" - Sends a GET request. - - Args: - access_token: The Access Token to use for authentication. - url: Where to send the request to. - params: (Optional) URL query parameters. - - Returns: - The JSON response if any or None. - - Raises: - HttpRequestError: If a client error one occurred. - HTTPError: If one occurred. - """ - - headers = { - 'Accept': 'application/json', - 'Authorization': f'Bearer {access_token}', - } - - return await self._send_request( - method='GET', - url=url, - params=params, - headers=headers, - ) - - async def requests_get_all( - self, - access_token: str, - url: str, - property_name: str, - params: Dict[str, Any]=None, - page_size: int=100, - ) -> List[Any]: - r""" - Sends a GET request and returns all elements from all pages. - - Args: - access_token: The Access Token to use for authentication. - url: Where to send the request to. - property_name: Name of the property that contains the elements of the page - params: (Optional) URL query parameters. - page_size: (Optional) number of elements per page. - - Returns: - The list of elements from all pages. - - Raises: - HttpRequestError: If a client error one occurred. - HTTPError: If one occurred. - """ - if params is None: - params = {} - - elements: List[Any] = [] - - while True: - json_response = await self.requests_get( - access_token=access_token, - url=url, - params=params, - ) - - if property_name in json_response: - sub_result = json_response[property_name] - for element in sub_result: - elements.append(element) - - if len(sub_result) < page_size: - break - - if not 'next' in json_response: - break - - params['start'] = json_response['next'] - if params['start'] is None or params['start'] == '': - break - - return elements - - async def requests_get_basic_auth( - self, - app_key: str, - app_secret: str, - url: str, - ) -> Any or None: - r""" - Sends a GET request with Basic authentication. - - Args: - app_key: The Dolby.io App Key. - app_secret: The Dolby.io App Secret. - url: Where to send the request to. - - Returns: - The JSON response if any or None. - - Raises: - HttpRequestError: If a client error one occurred. - HTTPError: If one occurred. - """ - - headers = { - 'Accept': 'application/json', - 'Content-Type': 'application/json', - } - - return await self._send_request( - method='GET', - url=url, - headers=headers, - auth=BasicAuth(app_key, app_secret), - ) - - async def download( - self, - access_token: str, - url: str, - accept: str, - file_path: str, - ) -> None: - r""" - Downloads a file. - - Args: - access_token: The Access Token to use for authentication. - url: Where to send the request to. - accept: Accept HTTP header. - file_path: Where to save the file. - - Raises: - HttpRequestError: If a client error one occurred. - HTTPError: If one occurred. - """ - headers = { - 'Accept': accept, - 'Authorization': f'Bearer {access_token}', - } - - await self._download_file( - url=url, - headers=headers, - file_path=file_path - ) - - async def _raise_for_status(self, http_response: ClientResponse): - r"""Raises :class:`HttpRequestError` or :class:`ClientResponseError`, if one occurred.""" - - if 400 <= http_response.status < 500: - if 400 < http_response.status < 404: - self._logger.error('Unauthorized to get data from the url %s - Response code %i', http_response.url, http_response.status) - elif http_response.status == 404: - self._logger.error('Unable to get data from the url %s - Not found (404)', http_response.url) - else: - self._logger.error('Did not find data at the url %s - Response code %i', http_response.url, http_response.status) - - try: - json_response = await http_response.json() - - error_type = get_value_or_default(json_response, 'type', None) - error_code = get_value_or_default(json_response, 'error_code', 0) - if error_code == 0: - error_code = get_value_or_default(json_response, 'status', 0) - error_reason = get_value_or_default(json_response, 'error_reason', None) - if error_reason is None: - error_reason = get_value_or_default(json_response, 'error', None) - error_description = get_value_or_default(json_response, 'error_description', None) - if error_description is None: - error_description = get_value_or_default(json_response, 'message', None) - - raise HttpRequestError(http_response, error_type, error_code, error_reason, error_description) - except (ValueError, ContentTypeError): # If the response body does not contain valid json. - pass - - http_response.raise_for_status() diff --git a/client/src/dolbyio_rest_apis/communications/models.py b/client/src/dolbyio_rest_apis/communications/models.py deleted file mode 100644 index 223895a..0000000 --- a/client/src/dolbyio_rest_apis/communications/models.py +++ /dev/null @@ -1,178 +0,0 @@ -""" -dolbyio_rest_apis.communications.models -~~~~~~~~~~~~~~~ - -This module contains the models used by the Dolby.io APIs. -""" - -from enum import Enum -from dataclasses import dataclass -from typing import List -from dolbyio_rest_apis.core.helpers import get_value_or_default, in_and_not_none - -class Participant: - """The :class:`Participant` object, which represents a participant's permissions.""" - - def __init__(self, external_id: str, permissions, notify: bool): - self.external_id = external_id - self.permissions = permissions - self.notify = notify - -class RTCPMode(str, Enum): - """The :class:`RTCPMode` enumeration, which represents the possible RTCP modes.""" - - WORST = 'worst' - '''Adjusts the transmission bitrate to the receiver who has the worst network conditions.''' - - AVERAGE = 'average' - '''Averages the available bandwidth of all the receivers and adjusts the transmission bitrate to this value.''' - - MAX = 'max' - '''Does not adjust the transmission bitrate to the receiver’s bandwidth.''' - -class VideoCodec(str, Enum): - """The :class:`VideoCodec` enumeration, which represents the possible video codecs.""" - - VP8 = 'VP8' - H264 = 'H264' - -class Permission(str, Enum): - """The :class:`Permission` enumeration, which represents the possible participant's permissions.""" - - INVITE = 'INVITE' - '''Allows a participant to invite participants to a conference.''' - - JOIN = 'JOIN' - '''Allows a participant to join a conference.''' - - SEND_AUDIO = 'SEND_AUDIO' - '''Allows a participant to send an audio stream during a conference.''' - - SEND_VIDEO = 'SEND_VIDEO' - '''Allows a participant to send a video stream during a conference.''' - - SHARE_SCREEN = 'SHARE_SCREEN' - '''Allows a participant to share their screen during a conference.''' - - SHARE_VIDEO = 'SHARE_VIDEO' - '''Allows a participant to share a video during a conference.''' - - SHARE_FILE = 'SHARE_FILE' - '''Allows a participant to share a file during a conference.''' - - SEND_MESSAGE = 'SEND_MESSAGE' - '''Allows a participant to send a message to other participants during a conference.''' - - RECORD = 'RECORD' - '''Allows a participant to record a conference.''' - - STREAM = 'STREAM' - '''Allows a participant to stream a conference.''' - - KICK = 'KICK' - '''Allows a participant to kick other participants from a conference.''' - - UPDATE_PERMISSIONS = 'UPDATE_PERMISSIONS' - '''Allows a participant to update other participants' permissions.''' - -@dataclass -class UserToken: - """Representation of a User access token.""" - external_id: str - token: str - -class Conference(dict): - """Representation of a newly created Conference.""" - - def __init__(self, dictionary: dict): - dict.__init__(self, dictionary) - - self.conference_id = get_value_or_default(self, 'conferenceId', None) - self.conference_alias = get_value_or_default(self, 'conferenceAlias', None) - self.conference_pincode = get_value_or_default(self, 'conferencePincode', None) - self.is_protected = get_value_or_default(self, 'isProtected', False) - self.owner_token = get_value_or_default(self, 'ownerToken', None) - - self.user_tokens: List[UserToken] = [] - if in_and_not_none(self, 'usersTokens'): - for key in self['usersTokens'].keys(): - user_token = UserToken(key, self['usersTokens'][key]) - self.user_tokens.append(user_token) - -class ConferenceParticipant(dict): - """Representation of a participant.""" - - def __init__(self, dictionary: dict): - dict.__init__(self, dictionary) - - self.user_id = get_value_or_default(self, 'userId', None) - self.external_id = get_value_or_default(self, 'externalId', None) - self.name = get_value_or_default(self, 'name', None) - self.avatar_url = get_value_or_default(self, 'avatarUrl', None) - self.ip_address = get_value_or_default(self, 'ipAddress', None) - self.user_agent = get_value_or_default(self, 'userAgent', None) - self.last_join_timestamp = get_value_or_default(self, 'lastJoinTimestamp', 0) - self.nb_session = get_value_or_default(self, 'nbSession', 0) - -class GetParticipantsResponse(dict): - """Representation of a Participants response.""" - - def __init__(self, dictionary: dict): - dict.__init__(self, dictionary) - - self.participants: List[ConferenceParticipant] = [] - if in_and_not_none(self, 'participants'): - for participant in self['participants']: - self.participants.append(ConferenceParticipant(participant)) - -class RemixStatus(dict): - """Representation of a Remix Status.""" - - def __init__(self, dictionary: dict): - dict.__init__(self, dictionary) - - self.status = get_value_or_default(self, 'status', None) - self.region = get_value_or_default(self, 'region', None) - self.alias = get_value_or_default(self, 'alias', None) - -class RtsStream(dict): - """Representation of an RTS Stream start response.""" - - def __init__(self, dictionary: dict): - dict.__init__(self, dictionary) - - self.stream_name = get_value_or_default(self, 'streamName', None) - self.subscribe_token = get_value_or_default(self, 'subscribeToken', None) - self.stream_account_id = get_value_or_default(self, 'streamAccountID', None) - self.viewer_url = get_value_or_default(self, 'viewerURL', None) - -@dataclass -class Coordinates: - """Representation of a Coordinate object.""" - x: int - y: int - z: int - -@dataclass -class SpatialAudioEnvironment: - """ - The spatial environment of an application, - so the audio renderer understands which directions the application considers - forward, up, and right and which units it uses for distance. - """ - scale: Coordinates - forward: Coordinates - up: Coordinates - right: Coordinates - -@dataclass -class SpatialAudioListener: - """Representation of the listener's audio position and direction, defined using Cartesian coordinates.""" - position: Coordinates - direction: Coordinates - -@dataclass -class SpatialAudioUser: - """Representation of the user's position, defined using Cartesian coordinates.""" - external_id: str - position: Coordinates diff --git a/client/src/dolbyio_rest_apis/communications/monitor/__init__.py b/client/src/dolbyio_rest_apis/communications/monitor/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/client/src/dolbyio_rest_apis/communications/monitor/conferences.py b/client/src/dolbyio_rest_apis/communications/monitor/conferences.py deleted file mode 100644 index 35eb5ef..0000000 --- a/client/src/dolbyio_rest_apis/communications/monitor/conferences.py +++ /dev/null @@ -1,438 +0,0 @@ -""" -dolbyio_rest_apis.communications.monitor.conferences -~~~~~~~~~~~~~~~ - -This module contains the functions to work with the monitor API related to conferences. -""" - -from dolbyio_rest_apis.communications.internal.http_context import CommunicationsHttpContext -from dolbyio_rest_apis.communications.monitor.models import GetConferencesResponse, ConferenceSummary, ConferenceStatistics, ConferenceParticipants, ConferenceParticipant -from dolbyio_rest_apis.core.urls import get_comms_monitor_url_v1 -from typing import Any, Dict, List - -async def get_conferences( - access_token: str, - tr_from: int=0, - tr_to: int=9999999999999, - maximum: int=100, - start: str=None, - filter_alias: str=None, - active: bool=False, - external_id: str=None, - live_stats: bool=False, - ) -> GetConferencesResponse: - r""" - Get a list of conferences. - - Get a list of conferences that were started in a specific time range, including ongoing conferences. - - Note: Only terminated conferences include a complete summary. - The summary of ongoing conferences includes the following fields in the response: - `confId`, `alias`, `region`, `dolbyVoice`, `start`, `live`, `owner`. - - See: https://docs.dolby.io/communications-apis/reference/get-conferences - - Args: - access_token: Access token to use for authentication. - tr_from: (Optional) The beginning of the time range (in milliseconds that have elapsed since epoch). - tr_to: (Optional) The end of the time range (in milliseconds that have elapsed since epoch). - maximum: (Optional) The maximum number of displayed results. - We recommend setting the proper value of this parameter to shorten the response time. - start: (Optional) When the results span multiple pages, use this option to navigate through pages. - By default, only the max number of results is displayed. To see the next results, - set the start parameter to the value of the next key returned in the previous response. - filter_alias: (Optional) Search conferences using Alias. Use regular expression to search for conferences with similar aliases. - For example: - - Use `foobar` to get all conferences with alias foobar. - - Use `.*foobar` to get all conferences with alias ending with foobar. - - Use `foobar.*` to get all conferences with alias starting with foobar - - Use `.*foobar.*` to get all conferences with alias containing foobar. - - Use `.*2019.*|.*2020.*` to get all conferences with alias containing either 2019 or 2020. - active: (Optional) Search for ongoing references (`true`) or all conferences (`false`). - external_id: (Optional) The external ID of the participant who created the conference. - live_stats: (Optional) For live conferences, the number of `user`, `listener`, and `pstn` participants. - - Returns: - A :class:`GetConferencesResponse` object. - - Raises: - HttpRequestError: If a client error one occurred. - HTTPError: If one occurred. - """ - url = f'{get_comms_monitor_url_v1()}/conferences' - - params = { - 'from': tr_from, - 'to': tr_to, - 'max': maximum, - 'active': str(active), - 'livestats': str(live_stats), - } - - if not start is None: - params['start'] = start - if not filter_alias is None: - params['alias'] = filter_alias - if not external_id is None: - params['exid'] = external_id - - async with CommunicationsHttpContext() as http_context: - json_response = await http_context.requests_get( - access_token=access_token, - url=url, - params=params, - ) - - return GetConferencesResponse(json_response) - -async def get_all_conferences( - access_token: str, - tr_from: int=0, - tr_to: int=9999999999999, - page_size: int=100, - filter_alias: str=None, - active: bool=False, - external_id: str=None, - live_stats: bool=False, - ) -> List[ConferenceSummary]: - r""" - Get a list of all conferences. - - Get a list of all conferences that were started in a specific time range, including ongoing conferences. - - Note: Only terminated conferences include a complete summary. - The summary of ongoing conferences includes the following fields in the response: - `confId`, `alias`, `region`, `dolbyVoice`, `start`, `live`, `owner`. - - See: https://docs.dolby.io/communications-apis/reference/get-conferences - - Args: - access_token: Access token to use for authentication. - tr_from: (Optional) The beginning of the time range (in milliseconds that have elapsed since epoch). - tr_to: (Optional) The end of the time range (in milliseconds that have elapsed since epoch). - page_size: (Optional) Number of elements to return per page. - filter_alias: (Optional) Search conferences using Alias. Use regular expression to search for conferences with similar aliases. - For example: - - Use `foobar` to get all conferences with alias foobar. - - Use `.*foobar` to get all conferences with alias ending with foobar. - - Use `foobar.*` to get all conferences with alias starting with foobar - - Use `.*foobar.*` to get all conferences with alias containing foobar. - - Use `.*2019.*|.*2020.*` to get all conferences with alias containing either 2019 or 2020. - active: (Optional) Search for ongoing references (`true`) or all conferences (`false`). - external_id: (Optional) The external ID of the participant who created the conference. - live_stats: (Optional) For live conferences, the number of `user`, `listener`, and `pstn` participants. - - Returns: - A list of :class:`Conference` objects. - - Raises: - HttpRequestError: If a client error one occurred. - HTTPError: If one occurred. - """ - url = f'{get_comms_monitor_url_v1()}/conferences' - - params = { - 'from': tr_from, - 'to': tr_to, - 'max': page_size, - 'active': str(active), - 'livestats': str(live_stats), - } - - if not filter_alias is None: - params['alias'] = filter_alias - if not external_id is None: - params['exid'] = external_id - - async with CommunicationsHttpContext() as http_context: - elements: List[Any] = await http_context.requests_get_all( - access_token=access_token, - url=url, - params=params, - property_name='conferences', - page_size=page_size - ) - - conferences: List[ConferenceSummary] = [] - for element in elements: - conference = ConferenceSummary(element) - conferences.append(conference) - - return conferences - -async def get_conference( - access_token: str, - conference_id: str, - live_stats: bool=False, - ) -> ConferenceSummary: - r""" - Get a summary of a conference. - - Note: Only terminated conferences include a complete summary. - The summary of ongoing conferences includes the following fields in the response: - `confId`, `alias`, `region`, `dolbyVoice`, `start`, `live`, `owner`. - - See: https://docs.dolby.io/communications-apis/reference/get-conference-summary - - Args: - access_token: Access token to use for authentication. - conference_id: Identifier of the conference. - live_stats: (Optional) For live conferences, the number of `user`, `listener`, and `pstn` participants. - - Returns: - A :class:`ConferenceSummary` object. - - Raises: - HttpRequestError: If a client error one occurred. - HTTPError: If one occurred. - """ - - url = f'{get_comms_monitor_url_v1()}/conferences/{conference_id}' - - params = { - 'livestats': str(live_stats), - } - - async with CommunicationsHttpContext() as http_context: - json_response = await http_context.requests_get( - access_token=access_token, - url=url, - params=params, - ) - - return ConferenceSummary(json_response) - -async def get_conference_statistics( - access_token: str, - conference_id: str, - ) -> ConferenceStatistics: - r""" - Get a conference statistics. - - Get statistics of a terminated conference. - The statistics include the maximum number of participants present during a conference - and the maximum number of the transmitted and received packets, bytes, and streams. - - Note: The statistics are available only for terminated conferences. - - See: https://docs.dolby.io/communications-apis/reference/get-conference-statistics - - Args: - access_token: Access token to use for authentication. - conference_id: Identifier of the conference. - live_stats: For live conferences, the number of `user`, `listener`, and `pstn` participants. - - Returns: - A :class:`ConferenceStatistics` object. - - Raises: - HttpRequestError: If a client error one occurred. - HTTPError: If one occurred. - """ - - url = f'{get_comms_monitor_url_v1()}/conferences/{conference_id}/statistics' - - async with CommunicationsHttpContext() as http_context: - json_response = await http_context.requests_get( - access_token=access_token, - url=url, - ) - - return ConferenceStatistics(json_response) - -async def get_conference_participants( - access_token: str, - conference_id: str, - participant_type: str=None, - tr_from: int=0, - tr_to: int=9999999999999, - maximum: int=100, - start: str=None, - ) -> ConferenceParticipants: - r""" - Get information about conference participants. - - Get statistics and connection details of all participants in a conference. - Optionally limit the search result with a specific time range. - - See: https://docs.dolby.io/communications-apis/reference/get-info-conference-participants - - Args: - access_token: Access token to use for authentication. - conference_id: Identifier of the conference. - participant_type: (Optional) The conference participant type. - - `user` - a participant who can send and receive video/audio stream to/from the conference. - - `listener` - a participant who can only receive video/audio stream from the conference. - - `pstn` - a participant who connected to the conference using PSTN (telephony network). - - `mixer` - an internal type indicating a mixer connection to the conference. - tr_from: (Optional) The beginning of the time range (in milliseconds that have elapsed since epoch). - tr_to: (Optional) The end of the time range (in milliseconds that have elapsed since epoch). - maximum: (Optional) The maximum number of displayed results. - We recommend setting the proper value of this parameter to shorten the response time. - start: (Optional) When the results span multiple pages, use this option to navigate through pages. - By default, only the max number of results is displayed. To see the next results, - set the start parameter to the value of the next key returned in the previous response. - - Returns: - A :class:`ConferenceParticipants` object. - - Raises: - HttpRequestError: If a client error one occurred. - HTTPError: If one occurred. - """ - url = f'{get_comms_monitor_url_v1()}/conferences/{conference_id}/participants' - - params = { - 'from': tr_from, - 'to': tr_to, - 'max': maximum, - } - if not participant_type is None: - params['type'] = participant_type - if not start is None: - params['start'] = start - - async with CommunicationsHttpContext() as http_context: - json_response = await http_context.requests_get( - access_token=access_token, - url=url, - params=params, - ) - - return ConferenceParticipants(json_response) - -async def get_all_conference_participants( - access_token: str, - conference_id: str, - participant_type: str=None, - tr_from: int=0, - tr_to: int=9999999999999, - page_size: int=100, - ) -> Dict[str, ConferenceParticipant]: - r""" - Get information about all conference participants. - - Get statistics and connection details of all participants in a conference. - Optionally limit the search result with a specific time range. - - See: https://docs.dolby.io/communications-apis/reference/get-info-conference-participants - - Args: - access_token: Access token to use for authentication. - conference_id: Identifier of the conference. - participant_type: (Optional) The conference participant type. - - `user` - a participant who can send and receive video/audio stream to/from the conference. - - `listener` - a participant who can only receive video/audio stream from the conference. - - `pstn` - a participant who connected to the conference using PSTN (telephony network). - - `mixer` - an internal type indicating a mixer connection to the conference. - tr_from: (Optional) The beginning of the time range (in milliseconds that have elapsed since epoch). - tr_to: (Optional) The end of the time range (in milliseconds that have elapsed since epoch). - page_size: (Optional) Number of elements to return per page. - - Returns: - A list of :class:`ConferenceParticipant` objects. - - Raises: - HttpRequestError: If a client error one occurred. - HTTPError: If one occurred. - """ - url = f'{get_comms_monitor_url_v1()}/conferences/{conference_id}/participants' - - params = { - 'from': tr_from, - 'to': tr_to, - 'max': page_size, - } - if not participant_type is None: - params['type'] = participant_type - - participants: Dict[str, ConferenceParticipant] = {} - - async with CommunicationsHttpContext() as http_context: - while True: - json_response = await http_context.requests_get( - access_token=access_token, - url=url, - params=params, - ) - - if 'participants' in json_response: - sub_result = json_response['participants'] - for participant_id in sub_result: - participant = ConferenceParticipant(participant_id, sub_result[participant_id]) - participants[participant.user_id] = participant - - if len(sub_result) < page_size: - break - - if not 'next' in json_response: - break - - params['start'] = json_response['next'] - if params['start'] is None or params['start'] == '': - break - - return participants - -async def get_conference_participant( - access_token: str, - conference_id: str, - participant_id: str, - participant_type: str=None, - tr_from: int=0, - tr_to: int=9999999999999, - maximum: int=100, - start: str=None, - ) -> ConferenceParticipant: - r""" - Get information about a specific conference participant. - - Gets the statistics and connection details of a conference participant, during a specific time range. - - See: https://docs.dolby.io/communications-apis/reference/get-info-conference-participant - - Args: - access_token: Access token to use for authentication. - conference_id: Identifier of the conference. - participant_id: Identifier of the participant. - participant_type: (Optional) The conference participant type. - - `user` - a participant who can send and receive video/audio stream to/from the conference. - - `listener` - a participant who can only receive video/audio stream from the conference. - - `pstn` - a participant who connected to the conference using PSTN (telephony network). - - `mixer` - an internal type indicating a mixer connection to the conference. - tr_from: (Optional) The beginning of the time range (in milliseconds that have elapsed since epoch). - tr_to: (Optional) The end of the time range (in milliseconds that have elapsed since epoch). - maximum: (Optional) The maximum number of displayed results. - We recommend setting the proper value of this parameter to shorten the response time. - start: (Optional) When the results span multiple pages, use this option to navigate through pages. - By default, only the max number of results is displayed. To see the next results, - set the start parameter to the value of the next key returned in the previous response. - - Returns: - A :class:`ConferenceParticipant` object. - - Raises: - HttpRequestError: If a client error one occurred. - HTTPError: If one occurred. - """ - url = f'{get_comms_monitor_url_v1()}/conferences/{conference_id}/participants/{participant_id}' - - params = { - 'from': tr_from, - 'to': tr_to, - 'max': maximum, - } - if not participant_type is None: - params['type'] = participant_type - if not start is None: - params['start'] = start - - async with CommunicationsHttpContext() as http_context: - json_response = await http_context.requests_get( - access_token=access_token, - url=url, - params=params, - ) - - participants = ConferenceParticipants(json_response) - return participants[participant_id] diff --git a/client/src/dolbyio_rest_apis/communications/monitor/models.py b/client/src/dolbyio_rest_apis/communications/monitor/models.py deleted file mode 100644 index a8f088b..0000000 --- a/client/src/dolbyio_rest_apis/communications/monitor/models.py +++ /dev/null @@ -1,301 +0,0 @@ -""" -dolbyio_rest_apis.communications.monitor.models -~~~~~~~~~~~~~~~ - -This module contains the models used by the Dolby.io APIs. -""" - -from dolbyio_rest_apis.core.helpers import get_value_or_default, in_and_not_none -from typing import List - -class PagedResponse(dict): - """Representation of a paged response.""" - - def __init__(self, dictionary: dict): - dict.__init__(self, dictionary) - - self.first = get_value_or_default(self, 'first', None) - self.next = get_value_or_default(self, 'next', None) - -class ConferenceOwner(dict): - """Representation of a Conference Owner.""" - - def __init__(self, dictionary: dict): - dict.__init__(self, dictionary) - - self.user_id = get_value_or_default(self, 'userId', None) - - if in_and_not_none(self, 'metadata'): - self.metadata = UserMetadata(self['metadata']) - -class ConferenceStatisticsMaxParticipants(dict): - """Representation of a Conference Statistics Max Participants.""" - - def __init__(self, dictionary: dict): - dict.__init__(self, dictionary) - - self.user = get_value_or_default(self, 'USER', 0) - self.listener = get_value_or_default(self, 'LISTENER', 0) - self.mixer = get_value_or_default(self, 'MIXER', 0) - self.pstn = get_value_or_default(self, 'PSTN', 0) - -class ConferenceParticipant(dict): - """Representation of a Conference Participant.""" - - def __init__(self, user_id, dictionary: dict): - self.user_id = user_id - dict.__init__(self, dictionary) - - #if in_and_not_none(self, 'connections'): - # self.connections = ConferenceOwner(self['connections']) - - #if in_and_not_none(self, 'stats'): - # self.stats = ConferenceStatistics(self['stats']) - -class ConferenceParticipants(PagedResponse): - """Representation of a Conference participants.""" - - def __init__(self, dictionary: dict): - PagedResponse.__init__(self, dictionary) - - self.participants: List[ConferenceParticipant] = [] - if in_and_not_none(self, 'participants'): - for key in self['participants'].keys(): - participant = ConferenceParticipant(key, self['participants'][key]) - self.participants.append(participant) - -class ConferenceStatisticsMaxRate(dict): - """Representation of a Conference Statistics Max Rate.""" - - def __init__(self, dictionary: dict): - dict.__init__(self, dictionary) - - self.dtls = get_value_or_default(self, 'DTLS', 0) - self.rtcp = get_value_or_default(self, 'RTCP', 0) - self.rtp = get_value_or_default(self, 'RTP', 0) - self.stun = get_value_or_default(self, 'STUN', 0) - -class ConferenceStatisticsMaxStreams(dict): - """Representation of a Conference Statistics Max Streams.""" - - def __init__(self, dictionary: dict): - dict.__init__(self, dictionary) - - self.audio = get_value_or_default(self, 'AUDIO', 0) - self.video = get_value_or_default(self, 'VIDEO', 0) - self.screenshare = get_value_or_default(self, 'SCREENSHARE', 0) - -class ConferenceStatisticsNetwork(dict): - """Representation of a Conference Statistics Network.""" - - def __init__(self, dictionary: dict): - dict.__init__(self, dictionary) - - if in_and_not_none(self, 'maxRxBytesRate'): - self.max_rx_bytes_rate = ConferenceStatisticsMaxRate(self['maxRxBytesRate']) - if in_and_not_none(self, 'maxRxPacketsRate'): - self.max_rx_packets_rate = ConferenceStatisticsMaxRate(self['maxRxPacketsRate']) - if in_and_not_none(self, 'maxRxStreams'): - self.max_rx_streams = ConferenceStatisticsMaxStreams(self['maxRxStreams']) - if in_and_not_none(self, 'maxTxBytesRate'): - self.max_tx_bytes_rate = ConferenceStatisticsMaxRate(self['maxTxBytesRate']) - if in_and_not_none(self, 'maxTxPacketsRate'): - self.max_tx_packets_rate = ConferenceStatisticsMaxRate(self['maxTxPacketsRate']) - if in_and_not_none(self, 'maxTxStreams'): - self.max_tx_streams = ConferenceStatisticsMaxStreams(self['maxTxStreams']) - -class ConferenceStatistics(dict): - """Representation of a Conference Statistics.""" - - def __init__(self, dictionary: dict): - dict.__init__(self, dictionary) - - if in_and_not_none(self, 'maxParticipants'): - self.max_participants = ConferenceStatisticsMaxParticipants(self['maxParticipants']) - - if in_and_not_none(self, 'network'): - self.network = ConferenceStatisticsNetwork(self['network']) - -class StreamingAPIUsage(dict): - """Representation of the Streaming API usage.""" - - def __init__(self, dictionary: dict): - dict.__init__(self, dictionary) - - self.bytes_in = get_value_or_default(self, 'bytesIn', None) - self.bytes_out = get_value_or_default(self, 'bytesOut', None) - self.publish_duration_sec = get_value_or_default(self, 'publishDurationSec', None) - self.publishes = get_value_or_default(self, 'publishes', None) - self.view_duration_sec = get_value_or_default(self, 'viewDurationSec', None) - self.views = get_value_or_default(self, 'views', None) - -class ConferenceSummary(dict): - """Representation of a Conference Summary.""" - - def __init__(self, dictionary: dict): - dict.__init__(self, dictionary) - - self.conf_id = get_value_or_default(self, 'confId', None) - self.alias = get_value_or_default(self, 'alias', None) - self.region = get_value_or_default(self, 'region', None) - self.start = get_value_or_default(self, 'start', 0) - self.live = get_value_or_default(self, 'live', False) - self.end = get_value_or_default(self, 'end', 0) - self.duration = get_value_or_default(self, 'duration', 0) - self.type = get_value_or_default(self, 'type', None) - self.presence_duration = get_value_or_default(self, 'presenceDuration', 0) - self.recording_duration = get_value_or_default(self, 'recordingDuration', 0) - self.mixer_live_recording = get_value_or_default(self, 'mixerLiveRecording', 0) - self.mixer_hls_streaming = get_value_or_default(self, 'mixerHlsStreaming', 0) - self.mixer_rtmp_streaming = get_value_or_default(self, 'mixerRtmpStreaming', 0) - self.nb_users = get_value_or_default(self, 'nbUsers', 0) - self.nb_listeners = get_value_or_default(self, 'nbListeners', 0) - self.nb_pstn = get_value_or_default(self, 'nbPstn', 0) - - if in_and_not_none(self, 'owner'): - self.mix = ConferenceOwner(self['owner']) - - if in_and_not_none(self, 'statistics'): - self.statistics = ConferenceStatistics(self['statistics']) - - if in_and_not_none(self, 'streamingAPIUsage'): - self.streaming_api_usage = StreamingAPIUsage(self['streamingAPIUsage']) - -class GetConferencesResponse(PagedResponse): - """Representation of a Conferences response.""" - - def __init__(self, dictionary: dict): - PagedResponse.__init__(self, dictionary) - - self.conferences: List[ConferenceSummary] = [] - if in_and_not_none(self, 'conferences'): - for conference in self['conferences']: - self.conferences.append(ConferenceSummary(conference)) - -class RecordingMix(dict): - """Representation of a Recording Mix.""" - - def __init__(self, dictionary: dict): - dict.__init__(self, dictionary) - - self.mp4 = get_value_or_default(self, 'mp4', 0) - self.mp3 = get_value_or_default(self, 'mp3', 0) - self.region = get_value_or_default(self, 'region', None) - -class UserMetadata(dict): - """Representation of a User Metadata.""" - - def __init__(self, dictionary: dict): - dict.__init__(self, dictionary) - - self.user_id = get_value_or_default(self, 'userID', None) - self.external_name = get_value_or_default(self, 'externalName', None) - self.external_id = get_value_or_default(self, 'externalId', None) - self.external_photo_url = get_value_or_default(self, 'externalPhotoUrl', None) - self.ip_address = get_value_or_default(self, 'ipAddress', None) - -class ConferenceRecordingMix(dict): - """Representation of a Conference Recording Mix.""" - - def __init__(self, dictionary: dict): - dict.__init__(self, dictionary) - - self.mix_id = get_value_or_default(self, 'mixId', None) - self.width = get_value_or_default(self, 'width', -1) - self.height = get_value_or_default(self, 'height', -1) - self.layout_url = get_value_or_default(self, 'layoutUrl', None) - -class Conference(dict): - """Representation of a Conference.""" - - def __init__(self, dictionary: dict): - dict.__init__(self, dictionary) - - self.conf_id = get_value_or_default(self, 'confId', None) - self.conf_alias = get_value_or_default(self, 'confAlias', None) - -class ConferenceRecording(dict): - """Representation of a Conference Recording.""" - - def __init__(self, dictionary: dict): - dict.__init__(self, dictionary) - - if in_and_not_none(self, 'conference'): - self.conference = Conference(self['conference']) - - self.region = get_value_or_default(self, 'region', None) - self.url = get_value_or_default(self, 'url', None) - self.created_at = get_value_or_default(self, 'createdAt', None) - self.recording_type = get_value_or_default(self, 'recordingType', None) - self.duration = get_value_or_default(self, 'duration', -1) - self.filename = get_value_or_default(self, 'filename', None) - self.size = get_value_or_default(self, 'size', -1) - self.start_time = get_value_or_default(self, 'startTime', 0) - self.media_type = get_value_or_default(self, 'mediaType', None) - self.region = get_value_or_default(self, 'region', None) - - if in_and_not_none(self, 'mix'): - self.mix = ConferenceRecordingMix(self['mix']) - -class GetRecordingsResponse(PagedResponse): - """Representation of a Recordings response.""" - - def __init__(self, dictionary: dict): - PagedResponse.__init__(self, dictionary) - - self.recordings = [] - if in_and_not_none(self, 'recordings'): - for recording in self['recordings']: - self.recordings.append(ConferenceRecording(recording)) - -class GetConferenceRecordingsResponse(GetRecordingsResponse): - """Representation of a Conference Recordings response.""" - - def __init__(self, dictionary: dict): - GetRecordingsResponse.__init__(self, dictionary) - - if in_and_not_none(self, 'conference'): - self.conference = Conference(self['conference']) - - self.live_conference = get_value_or_default(self, 'liveConference', False) - - self.recordings = [] - if in_and_not_none(self, 'recordings'): - for recording in self['recordings']: - self.recordings.append(ConferenceRecording(recording)) - -class WebHookResponse(dict): - """Representation of a WebHook event response.""" - - def __init__(self, dictionary: dict): - dict.__init__(self, dictionary) - - self.status = get_value_or_default(self, 'status', None) - self.headers = get_value_or_default(self, 'headers', None) - -class WebHook(dict): - """Representation of a WebHook event.""" - - def __init__(self, dictionary: dict): - dict.__init__(self, dictionary) - - self.id = get_value_or_default(self, 'id', None) - self.webhook = get_value_or_default(self, 'webhook', None) - self.url = get_value_or_default(self, 'url', None) - self.conf_id = get_value_or_default(self, 'confId', None) - self.third_party_id = get_value_or_default(self, 'thirdPartyId', None) - self.ts = get_value_or_default(self, 'ts', None) - - if in_and_not_none(self, 'response'): - self.response = WebHookResponse(self['response']) - -class GetWebHookResponse(PagedResponse): - """Representation of a WebHook response.""" - - def __init__(self, dictionary: dict): - PagedResponse.__init__(self, dictionary) - - self.webhooks = [] - if in_and_not_none(self, 'webhooks'): - for wbk in self['webhooks']: - self.webhooks.append(WebHook(wbk)) diff --git a/client/src/dolbyio_rest_apis/communications/monitor/recordings.py b/client/src/dolbyio_rest_apis/communications/monitor/recordings.py deleted file mode 100644 index b873423..0000000 --- a/client/src/dolbyio_rest_apis/communications/monitor/recordings.py +++ /dev/null @@ -1,301 +0,0 @@ -""" -dolbyio_rest_apis.communications.monitor.recordings -~~~~~~~~~~~~~~~ - -This module contains the functions to work with the monitor API related to recordings. -""" - -from dolbyio_rest_apis.communications.internal.http_context import CommunicationsHttpContext -from dolbyio_rest_apis.communications.monitor.models import GetConferenceRecordingsResponse -from dolbyio_rest_apis.communications.monitor.models import GetRecordingsResponse, ConferenceRecording -from dolbyio_rest_apis.core.urls import get_comms_monitor_url_v2 -from typing import Any, List - -async def get_recordings( - access_token: str, - region: str=None, - media_type: str=None, - recording_type: str=None, - tr_from: int=0, - tr_to: int=9999999999999, - page_size: int=100, - start: str=None, - ) -> GetRecordingsResponse: - r""" - Gets a list of the recorded conference metadata, such as duration or size of the recording. - This API checks only the recordings that have ended during a specific time range. - Recordings are indexed based on the ending time. - - This API returns presigned URLs for secure download of the recording files. - The URL returned in the response is an AWS S3 presigned URL with a validity of ten minutes. - - See: https://docs.dolby.io/communications-apis/reference/get-recordings - - Args: - access_token: Access token to use for authentication. - region: (Optional) The region code in which the mix recording took place. - media_type: (Optional) The media type of the recordings to return, 'audio/mpeg' or 'video/mp4'. - recording_type: (Optional) The type of the recordings to return, 'mix' or 'participant'. - tr_from: (Optional) The beginning of the time range (in milliseconds that have elapsed since epoch). - tr_to: (Optional) The end of the time range (in milliseconds that have elapsed since epoch). - page_size: (Optional) Number of elements to return per page. - start: (Optional) When the results span multiple pages, use this option to navigate through pages. - By default, only the max number of results is displayed. To see the next results, - set the start parameter to the value of the next key returned in the previous response. - - Returns: - A :class:`GetRecordingsV2Response` object. - - Raises: - HttpRequestError: If a client error one occurred. - HTTPError: If one occurred. - """ - url = f'{get_comms_monitor_url_v2()}/recordings' - - params = { - 'from': tr_from, - 'to': tr_to, - 'max': page_size, - } - - if not region is None: - params['region'] = region - if not media_type is None: - params['mediaType'] = media_type - if not recording_type is None: - params['type'] = recording_type - if not start is None: - params['start'] = start - - async with CommunicationsHttpContext() as http_context: - json_response = await http_context.requests_get( - access_token=access_token, - url=url, - params=params, - ) - - return GetRecordingsResponse(json_response) - -async def get_all_recordings( - access_token: str, - region: str=None, - media_type: str=None, - recording_type: str=None, - tr_from: int=0, - tr_to: int=9999999999999, - page_size: int=100, - ) -> List[ConferenceRecording]: - r""" - Gets a list of all the recorded conference metadata, such as duration or size of the recording. - This API checks only the recordings that have ended during a specific time range. - Recordings are indexed based on the ending time. - - This API returns presigned URLs for secure download of the recording files. - The URL returned in the response is an AWS S3 presigned URL with a validity of ten minutes. - - See: https://docs.dolby.io/communications-apis/reference/get-recordings - - Args: - access_token: Access token to use for authentication. - region: (Optional) The region code in which the mix recording took place. - media_type: (Optional) The media type of the recordings to return, 'audio/mpeg' or 'video/mp4'. - recording_type: (Optional) The type of the recordings to return, 'mix' or 'participant'. - tr_from: (Optional) The beginning of the time range (in milliseconds that have elapsed since epoch). - tr_to: (Optional) The end of the time range (in milliseconds that have elapsed since epoch). - page_size: (Optional) Number of elements to return per page. - - Returns: - A list of :class:`ConferenceRecording` objects. - - Raises: - HttpRequestError: If a client error one occurred. - HTTPError: If one occurred. - """ - url = f'{get_comms_monitor_url_v2()}/recordings' - - params = { - 'from': tr_from, - 'to': tr_to, - 'max': page_size, - } - - if not region is None: - params['region'] = region - if not media_type is None: - params['mediaType'] = media_type - if not recording_type is None: - params['type'] = recording_type - - recordings = [] - - async with CommunicationsHttpContext() as http_context: - elements: List[Any] = await http_context.requests_get_all( - access_token=access_token, - url=url, - params=params, - property_name='recordings', - page_size=page_size - ) - - recordings: List[ConferenceRecording] = [] - for element in elements: - recording = ConferenceRecording(element) - recordings.append(recording) - - return recordings - -async def get_conference_recordings( - access_token: str, - conference_id: str, - media_type: str=None, - recording_type: str=None, - tr_from: int=0, - tr_to: int=9999999999999, - page_size: int=100, - start: str=None, - ) -> GetConferenceRecordingsResponse: - r""" - Gets a list of the recorded conference metadata, such as duration or size of the recording. - - This API checks only the recordings that have ended during a specific time range. - Recordings are indexed based on the ending time. - - This API returns presigned URLs for secure download of the recording files. - The URL returned in the response is an AWS S3 presigned URL with a validity of ten minutes. - - See: https://docs.dolby.io/communications-apis/reference/get-conference-recordings - - Args: - access_token: Access token to use for authentication. - conference_id: Identifier of the conference. - media_type: (Optional) The media type of the recordings to return, 'audio/mpeg' or 'video/mp4'. - recording_type: (Optional) The type of the recordings to return, 'mix' or 'participant'. - tr_from:(Optional) The beginning of the time range (in milliseconds that have elapsed since epoch). - tr_to: (Optional) The end of the time range (in milliseconds that have elapsed since epoch). - page_size: (Optional) Number of elements to return per page. - - Returns: - A :class:`GetConferenceRecordingsResponse` object. - - Raises: - HttpRequestError: If a client error one occurred. - HTTPError: If one occurred. - """ - url = f'{get_comms_monitor_url_v2()}/conferences/{conference_id}/recordings' - - params = { - 'from': tr_from, - 'to': tr_to, - 'max': page_size, - } - - if not media_type is None: - params['mediaType'] = media_type - if not recording_type is None: - params['type'] = recording_type - if not start is None: - params['start'] = start - - async with CommunicationsHttpContext() as http_context: - json_response = await http_context.requests_get( - access_token=access_token, - url=url, - params=params, - ) - - return GetConferenceRecordingsResponse(json_response) - -async def get_all_conference_recordings( - access_token: str, - conference_id: str, - media_type: str=None, - recording_type: str=None, - tr_from: int=0, - tr_to: int=9999999999999, - page_size: int=100, - ) -> List[ConferenceRecording]: - r""" - Gets a list of all the recorded conference metadata, such as duration or size of the recording. - - This API checks only the recordings that have ended during a specific time range. - Recordings are indexed based on the ending time. - - This API returns presigned URLs for secure download of the recording files. - The URL returned in the response is an AWS S3 presigned URL with a validity of ten minutes. - - See: https://docs.dolby.io/communications-apis/reference/get-conference-recordings - - Args: - access_token: Access token to use for authentication. - conference_id: Identifier of the conference. - media_type: (Optional) The media type of the recordings to return, 'audio/mpeg' or 'video/mp4'. - recording_type: (Optional) The type of the recordings to return, 'mix' or 'participant'. - tr_from:(Optional) The beginning of the time range (in milliseconds that have elapsed since epoch). - tr_to: (Optional) The end of the time range (in milliseconds that have elapsed since epoch). - page_size: (Optional) Number of elements to return per page. - - Returns: - A list of :class:`ConferenceRecording` objects. - - Raises: - HttpRequestError: If a client error one occurred. - HTTPError: If one occurred. - """ - url = f'{get_comms_monitor_url_v2()}/conferences/{conference_id}/recordings' - - params = { - 'from': tr_from, - 'to': tr_to, - } - - if not media_type is None: - params['mediaType'] = media_type - if not recording_type is None: - params['type'] = recording_type - - recordings = [] - - async with CommunicationsHttpContext() as http_context: - elements: List[Any] = await http_context.requests_get_all( - access_token=access_token, - url=url, - params=params, - property_name='recordings', - page_size=page_size - ) - - recordings: List[ConferenceRecording] = [] - for element in elements: - recording = ConferenceRecording(element) - recordings.append(recording) - - return recordings - -async def delete_recording( - access_token: str, - conference_id: str, - ) -> None: - r""" - Delete recordings - - Delete all recording data related to a specific conference. - - Warning: After deleting the recording, it is not possible to restore the recording data. - - See: https://docs.dolby.io/communications-apis/reference/delete-conference-recordings - - Args: - access_token: Access token to use for authentication. - conference_id: Identifier of the conference. - - Raises: - HttpRequestError: If a client error one occurred. - HTTPError: If one occurred. - """ - url = f'{get_comms_monitor_url_v2()}/conferences/{conference_id}/recordings' - - async with CommunicationsHttpContext() as http_context: - await http_context.requests_delete( - access_token=access_token, - url=url, - ) diff --git a/client/src/dolbyio_rest_apis/communications/monitor/webhooks.py b/client/src/dolbyio_rest_apis/communications/monitor/webhooks.py deleted file mode 100644 index 28ac322..0000000 --- a/client/src/dolbyio_rest_apis/communications/monitor/webhooks.py +++ /dev/null @@ -1,131 +0,0 @@ -""" -dolbyio_rest_apis.communications.monitor.webhooks -~~~~~~~~~~~~~~~ - -This module contains the functions to work with the monitor API related to webhooks. -""" - -from dolbyio_rest_apis.communications.internal.http_context import CommunicationsHttpContext -from dolbyio_rest_apis.communications.monitor.models import GetWebHookResponse, WebHook -from dolbyio_rest_apis.core.urls import get_comms_monitor_url_v1 -from typing import Any, List - -async def get_events( - access_token: str, - conference_id: str=None, - tr_from: int=0, - tr_to: int=9999999999999, - maximum: int=100, - start: str=None, - filter_type: str=None, - ) -> GetWebHookResponse: - r""" - Get a list of Webhook events sent, during a specific time range. - The list includes associated endpoint response codes and headers. - - See: https://docs.dolby.io/communications-apis/reference/get-webhooks - - Args: - access_token: Access token to use for authentication. - conference_id: (Optional) Identifier of the conference. - tr_from: (Optional) The beginning of the time range (in milliseconds that have elapsed since epoch). - tr_to: (Optional) The end of the time range (in milliseconds that have elapsed since epoch). - maximum: (Optional) The maximum number of displayed results. - We recommend setting the proper value of this parameter to shorten the response time. - start: (Optional) When the results span multiple pages, use this option to navigate through pages. - By default, only the max number of results is displayed. To see the next results, - set the start parameter to the value of the next key returned in the previous response. - filter_type: (Optional) The Webhook event type or an expression of its type (for example `Recording.Live.InProgress` or `Rec.*`). - The default value of the type parameter returns all types of Webhooks. - - Returns: - A :class:`GetWebHookResponse` object. - - Raises: - HttpRequestError: If a client error one occurred. - HTTPError: If one occurred. - """ - url = f'{get_comms_monitor_url_v1()}/' - if not conference_id is None: - url += f'conferences/{conference_id}/' - url += 'webhooks' - - params = { - 'from': tr_from, - 'to': tr_to, - 'max': maximum, - } - - if not filter_type is None: - params['type'] = filter_type - if not start is None: - params['start'] = start - - async with CommunicationsHttpContext() as http_context: - json_response = await http_context.requests_get( - access_token=access_token, - url=url, - params=params, - ) - - return GetWebHookResponse(json_response) - -async def get_all_events( - access_token: str, - conference_id: str=None, - tr_from: int=0, - tr_to: int=9999999999999, - page_size: int=100, - filter_type: str=None, - ) -> List[WebHook]: - r""" - Get a list of all Webhook events sent, during a specific time range. - The list includes associated endpoint response codes and headers. - - See: https://docs.dolby.io/communications-apis/reference/get-webhooks - - Args: - access_token: Access token to use for authentication. - conference_id: (Optional) Identifier of the conference. - tr_from: (Optional) The beginning of the time range (in milliseconds that have elapsed since epoch). - tr_to: (Optional) The end of the time range (in milliseconds that have elapsed since epoch). - page_size: (Optional) Number of elements to return per page. - filter_type: (Optional) The Webhook event type or an expression of its type (for example `Recording.Live.InProgress` or `Rec.*`). - The default value of the type parameter returns all types of Webhooks. - - Returns: - A list of :class:`WebHook` objects. - - Raises: - HttpRequestError: If a client error one occurred. - HTTPError: If one occurred. - """ - url = f'{get_comms_monitor_url_v1()}/' - if not conference_id is None: - url += f'conferences/{conference_id}/' - url += 'webhooks' - - params = { - 'from': tr_from, - 'to': tr_to, - 'max': page_size, - } - - if not filter_type is None: - params['type'] = filter_type - - async with CommunicationsHttpContext() as http_context: - elements: List[Any] = await http_context.requests_get_all( - access_token=access_token, - url=url, - params=params, - property_name='webhooks', - page_size=page_size - ) - - webhooks: List[WebHook] = [] - for element in elements: - webhook = WebHook(element) - webhooks.append(webhook) - - return webhooks diff --git a/client/src/dolbyio_rest_apis/communications/recording.py b/client/src/dolbyio_rest_apis/communications/recording.py deleted file mode 100644 index 82b3f53..0000000 --- a/client/src/dolbyio_rest_apis/communications/recording.py +++ /dev/null @@ -1,96 +0,0 @@ -""" -dolbyio_rest_apis.communications.recording -~~~~~~~~~~~~~~~ - -This module contains the functions to work with the recording API. -""" - -from dolbyio_rest_apis.communications.internal.http_context import CommunicationsHttpContext -from dolbyio_rest_apis.core.helpers import add_if_not_none -from dolbyio_rest_apis.core.urls import get_comms_url_v2 - -async def start( - access_token: str, - conference_id: str, - layout_url: str=None, - width: int=-1, - height: int=-1, - mix_id: str=None, - ) -> None: - r""" - Starts recording for the specified conference. - - You can specify a custom layout URL per a recording request. - The `layout_url` parameter overrides the layout URL configured in the dashboard. - - You can also specify the resolution of the recording. - The default mixer layout application supports both 1920x1080 (16:9 aspect ratio) and 1080x1920 (9:16 aspect ratio). - If the `width` and `height` parameters are not specified, then the system defaults to 1920x1080. - - Using the `mix_id` parameter you can uniquely identify individual mixed recordings. - For example, `landscape-stage` and `portrait-audience` as mixId can help you identify the purpose of the recording - when you receive the webhook notification or use the Monitor API to retrieve the recordings. - You may start only one recording per mixId. - - See: https://docs.dolby.io/communications-apis/reference/api-recording-start - - Args: - access_token: Access token to use for authentication. - conference_id: Identifier of the conference. - layout_url: (Optional) Overwrites the layout URL configuration. - This field is ignored if it is not relevant regarding recording configuration, - for example if live_recording set to false or if the recording is MP3 only. - - `null`: uses the layout URL configured in the dashboard (if no URL is set in the dashboard, then uses the Dolby.io default); - - `default`: uses the Dolby.io default layout; - - URL string: uses this layout URL - width: (Optional) The frame width can range between 390 and 1920 pixels and is set to 1920 by default. - height: (Optional) The frame height can range between 390 and 1920 pixels and is set to 1080 by default. - mix_id: (Optional) A unique identifier for you to identify individual mixes. - You may only start one streaming per mixId. - Not providing its value results in setting the `default` value. - - Raises: - HttpRequestError: If a client error one occurred. - HTTPError: If one occurred. - """ - url = f'{get_comms_url_v2()}/conferences/mix/{conference_id}/recording/start' - - payload = {} - add_if_not_none(payload, 'layoutUrl', layout_url) - if width > 0: - payload['width'] = width - if height > 0: - payload['height'] = height - add_if_not_none(payload, 'mixId', mix_id) - - async with CommunicationsHttpContext() as http_context: - await http_context.requests_post( - access_token=access_token, - url=url, - payload=payload, - ) - -async def stop( - access_token: str, - conference_id: str, - ) -> None: - r""" - Stops the recording of the specified conference. - - See: https://docs.dolby.io/communications-apis/reference/api-recording-stop - - Args: - access_token: Access token to use for authentication. - conference_id: Identifier of the conference. - - Raises: - HttpRequestError: If a client error one occurred. - HTTPError: If one occurred. - """ - url = f'{get_comms_url_v2()}/conferences/mix/{conference_id}/recording/stop' - - async with CommunicationsHttpContext() as http_context: - await http_context.requests_post( - access_token=access_token, - url=url, - ) diff --git a/client/src/dolbyio_rest_apis/communications/remix.py b/client/src/dolbyio_rest_apis/communications/remix.py deleted file mode 100644 index 755e592..0000000 --- a/client/src/dolbyio_rest_apis/communications/remix.py +++ /dev/null @@ -1,106 +0,0 @@ -""" -dolbyio_rest_apis.communications.remix -~~~~~~~~~~~~~~~ - -This module contains the functions to work with the remix API. -""" - -from dolbyio_rest_apis.communications.internal.http_context import CommunicationsHttpContext -from dolbyio_rest_apis.core.helpers import add_if_not_none -from dolbyio_rest_apis.core.urls import get_comms_url_v2 -from .models import RemixStatus - -async def start( - access_token: str, - conference_id: str, - layout_url: str=None, - width: int=-1, - height: int=-1, - mix_id: str=None, - ) -> RemixStatus: - r""" - Remix a conference. - - Triggers a remix and regenerates a recording of a previously recorded conference - using a [mixer layout](https://docs.dolby.io/communications-apis/docs/guides-mixer-layout). - You can remix only one conference at a time. - The `Recordings.Available` event is sent if the customer has configured the webhook in the developer portal. - For more information, see the [Recording Conferences](https://docs.dolby.io/communications-apis/docs/guides-recording-conferences) - and [Multiple Layout Mixes](https://docs.dolby.io/communications-apis/docs/guides-multiple-layout-mixes) documents. - - You can also specify the resolution of the remix. - The default mixer layout application supports both 1920x1080 (16:9 aspect ratio) and 1080x1920 (9:16 aspect ratio). - If the `width` and `height` parameters are not specified, then the system defaults to 1920x1080. - - See: https://docs.dolby.io/communications-apis/reference/start-conference-remix - - Args: - access_token: Access token to use for authentication. - conference_id: Identifier of the conference. - layout_url: (Optional) Overwrites the layout URL configuration: - null: uses the layout URL configured in the dashboard - (if no URL is set in the dashboard, then uses the Dolby.io default) - default: uses the Dolby.io default layout - URL string: uses this layout URL - width: (Optional) The frame width can range between 390 and 1920 pixels and is set to 1920 by default. - height: (Optional) The frame height can range between 390 and 1920 pixels and is set to 1080 by default. - mix_id: (Optional) A unique identifier for you to identify individual mixes. - You may only start one streaming per mixId. - Not providing its value results in setting the `default` value. - - Returns: - A :class:`RemixStatus` object that represents the status of the remix. - - Raises: - HttpRequestError: If a client error one occurred. - HTTPError: If one occurred. - """ - url = f'{get_comms_url_v2()}/conferences/mix/{conference_id}/remix/start' - - payload = {} - add_if_not_none(payload, 'layoutUrl', layout_url) - if width > 0: - payload['width'] = width - if height > 0: - payload['height'] = height - add_if_not_none(payload, 'mixId', mix_id) - - async with CommunicationsHttpContext() as http_context: - json_response = await http_context.requests_post( - access_token=access_token, - url=url, - payload=payload, - ) - - return RemixStatus(json_response) - -async def get_status( - access_token: str, - conference_id: str, - ) -> RemixStatus: - r""" - Get the status of a current mixing job. You must use this API if the conference is protected - using enhanced conference access control. - - See: https://docs.dolby.io/communications-apis/reference/get-conference-remix-status - - Args: - access_token: Access token to use for authentication. - conference_id: Identifier of the conference. - - Returns: - A :class:`RemixStatus` object that represents the status of the remix. - - Raises: - HttpRequestError: If a client error one occurred. - HTTPError: If one occurred. - """ - url = f'{get_comms_url_v2()}/conferences/mix/{conference_id}/remix/status' - - async with CommunicationsHttpContext() as http_context: - json_response = await http_context.requests_get( - access_token=access_token, - url=url, - ) - - return RemixStatus(json_response) diff --git a/client/src/dolbyio_rest_apis/communications/streaming.py b/client/src/dolbyio_rest_apis/communications/streaming.py deleted file mode 100644 index 5e97ea7..0000000 --- a/client/src/dolbyio_rest_apis/communications/streaming.py +++ /dev/null @@ -1,172 +0,0 @@ -""" -dolbyio_rest_apis.communications.streaming -~~~~~~~~~~~~~~~ - -This module contains the functions to work with the streaming API. -""" - -from dolbyio_rest_apis.communications.internal.http_context import CommunicationsHttpContext -from dolbyio_rest_apis.core.helpers import add_if_not_none -from dolbyio_rest_apis.core.urls import get_comms_url_v2, get_comms_url_v3 -from .models import RtsStream - -async def start_rtmp( - access_token: str, - conference_id: str, - rtmp_url: str, - layout_url: str=None, - width: int=-1, - height: int=-1, - mix_id: str=None, - ) -> None: - r""" - Starts the RTMP live stream for the specified conference. Once the Dolby.io Communications APIs service starts - streaming to the target url, a `Stream.Rtmp.InProgress` Webhook event will be sent. - - You can also specify the resolution of the RTMP stream. - The default mixer layout application supports both 1920x1080 (16:9 aspect ratio) and 1080x1920 (9:16 aspect ratio). - If the `width` and `height` parameters are not specified, then the system defaults to 1920x1080. - - See: https://docs.dolby.io/communications-apis/reference/start-rtmp - - Args: - access_token: Access token to use for authentication. - conference_id: Identifier of the conference. - rtmp_url: The destination URI provided by the RTMP service. - layout_url: (Optional) Overwrites the layout URL configuration. - This field is ignored if it is not relevant regarding recording configuration, - for example if live_recording set to false or if the recording is MP3 only. - - `null`: uses the layout URL configured in the dashboard (if no URL is set in the dashboard, then uses the Dolby.io default); - - `default`: uses the Dolby.io default layout; - - URL string: uses this layout URL - width: (Optional) The frame width can range between 390 and 1920 pixels and is set to 1920 by default. - height: (Optional) The frame height can range between 390 and 1920 pixels and is set to 1080 by default. - mix_id: (Optional) A unique identifier for you to identify individual mixes. - You may only start one streaming per mixId. - Not providing its value results in setting the `default` value. - - Raises: - HttpRequestError: If a client error one occurred. - HTTPError: If one occurred. - """ - - payload = { - 'uri': rtmp_url, - } - add_if_not_none(payload, 'layoutUrl', layout_url) - if width > 0: - payload['width'] = width - if height > 0: - payload['height'] = height - add_if_not_none(payload, 'mixId', mix_id) - - async with CommunicationsHttpContext() as http_context: - await http_context.requests_post( - access_token=access_token, - url=f'{get_comms_url_v2()}/conferences/mix/{conference_id}/rtmp/start', - payload=payload - ) - -async def stop_rtmp( - access_token: str, - conference_id: str, - ) -> None: - r""" - Stops the RTMP stream of the specified conference. - - See: https://docs.dolby.io/communications-apis/reference/stop-rtmp - - Args: - access_token: Access token to use for authentication. - conference_id: Identifier of the conference. - - Raises: - HttpRequestError: If a client error one occurred. - HTTPError: If one occurred. - """ - - async with CommunicationsHttpContext() as http_context: - await http_context.requests_post( - access_token=access_token, - url=f'{get_comms_url_v2()}/conferences/mix/{conference_id}/rtmp/stop' - ) - -async def start_rts( - access_token: str, - conference_id: str, - layout_url: str=None, - width: int=-1, - height: int=-1, - mix_id: str=None, - ) -> RtsStream: - r""" - Starts real-time streaming using Dolby.io Real-time Streaming services (formerly Millicast). - - You can also specify the resolution of the RTS stream. - The default mixer layout application supports both 1920x1080 (16:9 aspect ratio) and 1080x1920 (9:16 aspect ratio). - If the `width` and `height` parameters are not specified, then the system defaults to 1920x1080. - - See: https://docs.dolby.io/communications-apis/reference/start-rts - - Args: - access_token: Access token to use for authentication. - conference_id: Identifier of the conference. - stream_name: The Dolby.io Real-time Streaming stream name to which the conference is broadcasted. - publishing_token: The publishing token used to identify the broadcaster. - layout_url: (Optional) Overwrites the layout URL configuration: - null: uses the layout URL configured in the dashboard - (if no URL is set in the dashboard, then uses the Dolby.io default) - default: uses the Dolby.io default layout - URL string: uses this layout URL - width: (Optional) The frame width can range between 390 and 1920 pixels and is set to 1920 by default. - height: (Optional) The frame height can range between 390 and 1920 pixels and is set to 1080 by default. - mix_id: (Optional) A unique identifier for you to identify individual mixes. - You may only start one streaming per mixId. - Not providing its value results in setting the `default` value. - - Returns: - A :class:`RtsStream` object that represents the status of the remix. - - Raises: - HttpRequestError: If a client error one occurred. - HTTPError: If one occurred. - """ - - payload = {} - add_if_not_none(payload, 'layoutUrl', layout_url) - if width > 0: - payload['width'] = width - if height > 0: - payload['height'] = height - add_if_not_none(payload, 'mixId', mix_id) - - async with CommunicationsHttpContext() as http_context: - await http_context.requests_post( - access_token=access_token, - url=f'{get_comms_url_v3()}/conferences/mix/{conference_id}/rts/start', - payload=payload, - ) - -async def stop_rts( - access_token: str, - conference_id: str, - ) -> None: - r""" - Stops real-time streaming to Dolby.io Real-time Streaming services. - - See: https://docs.dolby.io/communications-apis/reference/stop-rts - - Args: - access_token: Access token to use for authentication. - conference_id: Identifier of the conference. - - Raises: - HttpRequestError: If a client error one occurred. - HTTPError: If one occurred. - """ - - async with CommunicationsHttpContext() as http_context: - await http_context.requests_post( - access_token=access_token, - url=f'{get_comms_url_v3()}/conferences/mix/{conference_id}/rts/stop' - ) diff --git a/client/src/dolbyio_rest_apis/core/http_context.py b/client/src/dolbyio_rest_apis/core/http_context.py index b0d5948..17ebff5 100644 --- a/client/src/dolbyio_rest_apis/core/http_context.py +++ b/client/src/dolbyio_rest_apis/core/http_context.py @@ -146,7 +146,7 @@ async def _send_request( params: Mapping[str, str]=None, auth: BasicAuth=None, data: Any=None, - ) -> Any or None: + ) -> Any | None: if params is None: self._logger.debug('%s %s', method, url) else: diff --git a/client/src/dolbyio_rest_apis/core/urls.py b/client/src/dolbyio_rest_apis/core/urls.py index 69a818e..1fcd28a 100644 --- a/client/src/dolbyio_rest_apis/core/urls.py +++ b/client/src/dolbyio_rest_apis/core/urls.py @@ -6,30 +6,12 @@ """ API_URL = 'api.dolby.io' -COMMS_URL = 'comms.api.dolby.io' SAPI_URL = 'api.millicast.com' MAPI_URL = 'api.dolby.com' def get_api_url() -> str: return f'https://{API_URL}/v1' -def get_comms_url_v1() -> str: - return f'https://{COMMS_URL}/v1' - -def get_comms_url_v2(region: str = None) -> str: - if region is None: - return f'https://{COMMS_URL}/v2' - return f'https://{region}.{COMMS_URL}/v2' - -def get_comms_url_v3() -> str: - return f'https://{COMMS_URL}/v3' - -def get_comms_monitor_url_v1() -> str: - return f'{get_comms_url_v1()}/monitor' - -def get_comms_monitor_url_v2() -> str: - return f'{get_comms_url_v2()}/monitor' - def get_rts_url() -> str: return f'https://{SAPI_URL}' diff --git a/client/src/dolbyio_rest_apis/media/analyze.py b/client/src/dolbyio_rest_apis/media/analyze.py index 14f8212..38d400b 100644 --- a/client/src/dolbyio_rest_apis/media/analyze.py +++ b/client/src/dolbyio_rest_apis/media/analyze.py @@ -26,12 +26,6 @@ async def start( See: https://docs.dolby.io/media-apis/reference/media-analyze-post - Beta API - - This API is being made available as an early preview. - If you have feedback on how you'd like to use the API please reach out to share your feedback with our team. - https://dolby.io/contact - Content Length Media content with duration less than 2 seconds will not be processed. The API will return an ERROR in this case. @@ -48,11 +42,6 @@ async def start( HttpRequestError: If a client error one occurred. HTTPError: If one occurred. """ - print('''Beta API - This API is being made available as an early preview. - If you have feedback on how you\'d like to use the API please reach out to share your feedback with our team. - https://dolby.io/contact''') - async with MediaHttpContext() as http_context: json_response = await http_context.requests_post( access_token=access_token, diff --git a/client/src/dolbyio_rest_apis/media/analyze_music.py b/client/src/dolbyio_rest_apis/media/analyze_music.py index ded02da..6c0d2f0 100644 --- a/client/src/dolbyio_rest_apis/media/analyze_music.py +++ b/client/src/dolbyio_rest_apis/media/analyze_music.py @@ -22,11 +22,6 @@ async def start( See: https://docs.dolby.io/media-apis/reference/media-analyze-music-post - Beta API - This API is being made available as an early preview. - If you have feedback on how you'd like to use the API please reach out to share your feedback with our team. - https://dolby.io/contact - Args: access_token: Access token to use for authentication. job_content: Content of the job description as a JSON payload. @@ -39,11 +34,6 @@ async def start( HttpRequestError: If a client error one occurred. HTTPError: If one occurred. """ - print('''Beta API - This API is being made available as an early preview. - If you have feedback on how you\'d like to use the API please reach out to share your feedback with our team. - https://dolby.io/contact''') - async with MediaHttpContext() as http_context: json_response = await http_context.requests_post( access_token=access_token, diff --git a/client/src/dolbyio_rest_apis/media/analyze_speech.py b/client/src/dolbyio_rest_apis/media/analyze_speech.py index 001e5e9..86811c5 100644 --- a/client/src/dolbyio_rest_apis/media/analyze_speech.py +++ b/client/src/dolbyio_rest_apis/media/analyze_speech.py @@ -22,11 +22,6 @@ async def start( See: https://docs.dolby.io/media-apis/reference/media-analyze-speech-post - Beta API - This API is being made available as an early preview. - If you have feedback on how you'd like to use the API please reach out to share your feedback with our team. - https://dolby.io/contact - Args: access_token: Access token to use for authentication. job_content: Content of the job description as a JSON payload. @@ -39,11 +34,6 @@ async def start( HttpRequestError: If a client error one occurred. HTTPError: If one occurred. """ - print('''Beta API - This API is being made available as an early preview. - If you have feedback on how you\'d like to use the API please reach out to share your feedback with our team. - https://dolby.io/contact''') - async with MediaHttpContext() as http_context: json_response = await http_context.requests_post( access_token=access_token, diff --git a/client/src/dolbyio_rest_apis/media/authentication.py b/client/src/dolbyio_rest_apis/media/authentication.py new file mode 100644 index 0000000..6216864 --- /dev/null +++ b/client/src/dolbyio_rest_apis/media/authentication.py @@ -0,0 +1,51 @@ +""" +dolbyio_rest_apis.media.authentication +~~~~~~~~~~~~~~~ + +This module contains the functions to work with the authentication API. +""" + +from dolbyio_rest_apis.core.helpers import add_if_not_none +from dolbyio_rest_apis.core.urls import get_api_url +from dolbyio_rest_apis.media.models.access_token import AccessToken +from dolbyio_rest_apis.media.internal.http_context import MediaHttpContext + +async def get_api_token( + app_key: str, + app_secret: str, + expires_in: int=None, + ) -> AccessToken: + r""" + To make any API call, you must acquire a JWT (JSON Web Token) format API token. + + See: https://docs.dolby.io/media-apis/reference/get-api-token + + Args: + app_key: Your Dolby.io App Key. + app_secret: Your Dolby.io App Secret. + expires_in: (Optional) API token expiration time in seconds. + If no value is specified, the default is 1800, indicating 30 minutes. + The maximum value is 86,400, indicating 24 hours. + + Returns: + An :class:`AccessToken` object. + + Raises: + HttpRequestError: If a client error one occurred. + HTTPError: If one occurred. + """ + + data = { + 'grant_type': 'client_credentials', + } + add_if_not_none(data, 'expires_in', expires_in) + + async with MediaHttpContext() as http_context: + json_response = await http_context.requests_post_basic_auth( + app_key=app_key, + app_secret=app_secret, + url=f'{get_api_url()}/auth/token', + data=data + ) + + return AccessToken(json_response) diff --git a/client/src/dolbyio_rest_apis/media/diagnose.py b/client/src/dolbyio_rest_apis/media/diagnose.py index e8e8283..9704802 100644 --- a/client/src/dolbyio_rest_apis/media/diagnose.py +++ b/client/src/dolbyio_rest_apis/media/diagnose.py @@ -20,11 +20,6 @@ async def start( See: https://docs.dolby.io/media-apis/reference/media-diagnose-post - Beta API - This API is being made available as an early preview. - If you have feedback on how you'd like to use the API please reach out to share your feedback with our team. - https://dolby.io/contact - Args: access_token: Access token to use for authentication. job_content: Content of the job description as a JSON payload. @@ -37,11 +32,6 @@ async def start( HttpRequestError: If a client error one occurred. HTTPError: If one occurred. """ - print('''Beta API - This API is being made available as an early preview. - If you have feedback on how you\'d like to use the API please reach out to share your feedback with our team. - https://dolby.io/contact''') - async with MediaHttpContext() as http_context: json_response = await http_context.requests_post( access_token=access_token, diff --git a/client/src/dolbyio_rest_apis/models.py b/client/src/dolbyio_rest_apis/media/models/access_token.py similarity index 78% rename from client/src/dolbyio_rest_apis/models.py rename to client/src/dolbyio_rest_apis/media/models/access_token.py index 765c058..72f3d9e 100644 --- a/client/src/dolbyio_rest_apis/models.py +++ b/client/src/dolbyio_rest_apis/media/models/access_token.py @@ -1,8 +1,8 @@ """ -dolbyio_rest_apis.communications.models +dolbyio_rest_apis.models ~~~~~~~~~~~~~~~ -This module contains the models used by the Dolby.io APIs. +This module contains the models used by the Dolby.io MEDIA APIs. """ from dolbyio_rest_apis.core.helpers import get_value_or_default @@ -17,4 +17,3 @@ def __init__(self, dictionary: dict): self.access_token = get_value_or_default(self, 'access_token', None) self.refresh_token = get_value_or_default(self, 'refresh_token', None) self.expires_in_val = get_value_or_default(self, 'expires_in', 0) - self.scope = get_value_or_default(self, 'scope', None) diff --git a/client/src/dolbyio_rest_apis/streaming/account.py b/client/src/dolbyio_rest_apis/streaming/account.py new file mode 100644 index 0000000..66c6b28 --- /dev/null +++ b/client/src/dolbyio_rest_apis/streaming/account.py @@ -0,0 +1,68 @@ +""" +dolbyio_rest_apis.streaming.account +~~~~~~~~~~~~~~~ + +This module contains the functions to work with the Account APIs. +""" + +from dolbyio_rest_apis.core.helpers import add_if_not_none +from dolbyio_rest_apis.core.urls import get_rts_url +from dolbyio_rest_apis.streaming.internal.http_context import StreamingHttpContext +from dolbyio_rest_apis.streaming.models.account import AccountGeoCascade, AccountGeoRestrictions + +async def read_geo_cascade( + api_secret: str, + ) -> AccountGeoCascade: + async with StreamingHttpContext() as http_context: + dict_data = await http_context.requests_get( + api_secret=api_secret, + url=f'{get_rts_url()}/api/account/geo_cascade', + ) + + return AccountGeoCascade.from_dict(dict_data) + +async def update_geo_cascade( + api_secret: str, + geo_cascade: AccountGeoCascade, + ) -> AccountGeoCascade: + payload = { + 'isEnabled': geo_cascade.is_enabled, + } + add_if_not_none(payload, 'clusters', geo_cascade.clusters) + + async with StreamingHttpContext() as http_context: + dict_data = await http_context.requests_put( + api_secret=api_secret, + url=f'{get_rts_url()}/api/account/geo_cascade', + payload=payload, + ) + + return AccountGeoCascade.from_dict(dict_data) + +async def read_geo_restrictions( + api_secret: str, + ) -> AccountGeoRestrictions: + async with StreamingHttpContext() as http_context: + dict_data = await http_context.requests_get( + api_secret=api_secret, + url=f'{get_rts_url()}/api/geo/account', + ) + + return AccountGeoRestrictions.from_dict(dict_data) + +async def update_geo_restrictions( + api_secret: str, + geo_restrictions: AccountGeoRestrictions, + ) -> AccountGeoRestrictions: + payload = {} + add_if_not_none(payload, 'allowedCountries', geo_restrictions.allowed_countries) + add_if_not_none(payload, 'deniedCountries', geo_restrictions.denied_countries) + + async with StreamingHttpContext() as http_context: + dict_data = await http_context.requests_post( + api_secret=api_secret, + url=f'{get_rts_url()}/api/geo/account', + payload=payload, + ) + + return AccountGeoRestrictions.from_dict(dict_data) diff --git a/client/src/dolbyio_rest_apis/streaming/cluster.py b/client/src/dolbyio_rest_apis/streaming/cluster.py index 1f855ca..78c6ceb 100644 --- a/client/src/dolbyio_rest_apis/streaming/cluster.py +++ b/client/src/dolbyio_rest_apis/streaming/cluster.py @@ -13,12 +13,12 @@ async def read( api_secret: str, ) -> ClusterResponse: async with StreamingHttpContext() as http_context: - json_response = await http_context.requests_get( + dict_data = await http_context.requests_get( api_secret=api_secret, url=f'{get_rts_url()}/api/cluster', ) - return ClusterResponse(json_response) + return ClusterResponse.from_dict(dict_data) async def update( api_secret: str, @@ -29,10 +29,10 @@ async def update( } async with StreamingHttpContext() as http_context: - json_response = await http_context.requests_put( + dict_data = await http_context.requests_put( api_secret=api_secret, url=f'{get_rts_url()}/api/cluster', payload=payload, ) - return ClusterResponse(json_response) + return ClusterResponse.from_dict(dict_data) diff --git a/client/src/dolbyio_rest_apis/streaming/internal/http_context.py b/client/src/dolbyio_rest_apis/streaming/internal/http_context.py index 62bab54..edf5389 100644 --- a/client/src/dolbyio_rest_apis/streaming/internal/http_context.py +++ b/client/src/dolbyio_rest_apis/streaming/internal/http_context.py @@ -14,14 +14,14 @@ from typing import Any, Mapping class StreamingHttpContext(HttpContext): - """HTTP Context class for Real-time Streaming APIs""" + """HTTP Context class for Dolby Millicast APIs""" def __init__(self): super().__init__() self._logger = logging.getLogger(StreamingHttpContext.__name__) - async def _requests_post_put( + async def _requests_with_payload( self, api_secret: str, url: str, @@ -30,12 +30,12 @@ async def _requests_post_put( params: Mapping[str, str]=None, ) -> dict: r""" - Sends a POST or PUT request. + Sends a request with a payload. Args: api_secret: API secret to use for authentication. url: Where to send the request to. - method: HTTP method, POST or PUT. + method: HTTP method, POST, PUT or PATCH. payload: (Optional) Content of the request. params: (Optional) URL query parameters. @@ -64,8 +64,7 @@ async def _requests_post_put( data=payload, ) - base = BaseResponse(json_response) - return base.data + return BaseResponse.from_dict(json_response).data async def requests_post( self, @@ -91,7 +90,7 @@ async def requests_post( HTTPError: If one occurred. """ - return await self._requests_post_put( + return await self._requests_with_payload( api_secret=api_secret, url=url, method='POST', @@ -123,7 +122,7 @@ async def requests_put( HTTPError: If one occurred. """ - return await self._requests_post_put( + return await self._requests_with_payload( api_secret=api_secret, url=url, method='PUT', @@ -131,6 +130,38 @@ async def requests_put( params=params, ) + async def requests_patch( + self, + api_secret: str, + url: str, + payload: Any=None, + params: Mapping[str, str]=None, + ) -> dict: + r""" + Sends a PATCH request. + + Args: + api_secret: API secret to use for authentication. + url: Where to send the request to. + payload: (Optional) Content of the request. + params: (Optional) URL query parameters. + + Returns: + The JSON response. + + Raises: + HttpRequestError: If a client error one occurred. + HTTPError: If one occurred. + """ + + return await self._requests_with_payload( + api_secret=api_secret, + url=url, + method='PATCH', + payload=payload, + params=params, + ) + async def requests_get( self, api_secret: str, @@ -165,15 +196,14 @@ async def requests_get( headers=headers, ) - base = BaseResponse(json_response) - return base.data + return BaseResponse.from_dict(json_response).data async def requests_delete( self, - access_token: str, + api_secret: str, url: str, params: Mapping[str, str]=None, - ) -> None: + ) -> dict: r""" Sends a DELETE request. @@ -189,16 +219,18 @@ async def requests_delete( headers = { 'Accept': 'application/json', - 'Authorization': f'Bearer {access_token}', + 'Authorization': f'Bearer {api_secret}', } - return await self._send_request( + json_response = await self._send_request( method='DELETE', url=url, params=params, headers=headers, ) + return BaseResponse.from_dict(json_response).data + async def _raise_for_status(self, http_response: ClientResponse): r"""Raises :class:`HttpRequestError` or :class:`ClientResponseError`, if one occurred.""" @@ -212,8 +244,8 @@ async def _raise_for_status(self, http_response: ClientResponse): try: json_response = await http_response.json() - base_response = BaseResponse(json_response) - err = Error(base_response.data) + base_response = BaseResponse.from_dict(json_response) + err = Error.from_dict(base_response.data) raise HttpRequestError(http_response, '', http_response.status, base_response.status, err.message) except (ValueError, ContentTypeError): # If the response body does not contain valid json. diff --git a/client/src/dolbyio_rest_apis/streaming/models/account.py b/client/src/dolbyio_rest_apis/streaming/models/account.py new file mode 100644 index 0000000..058b375 --- /dev/null +++ b/client/src/dolbyio_rest_apis/streaming/models/account.py @@ -0,0 +1,25 @@ +""" +dolbyio_rest_apis.streaming.models.account +~~~~~~~~~~~~~~~ + +This module contains the models used by the Account module. +""" + +from dataclasses import dataclass, field +from dataclasses_json import LetterCase, dataclass_json + +@dataclass_json(letter_case=LetterCase.CAMEL) +@dataclass +class AccountGeoCascade: + """The :class:`AccountGeoCascade` object, the definition of the geo cascading rules for the account.""" + + is_enabled: bool = False + clusters: list[str] = field(default_factory=lambda: []) + +@dataclass_json(letter_case=LetterCase.CAMEL) +@dataclass +class AccountGeoRestrictions: + """The :class:`AccountGeoRestrictions` object, the definition of the geo restrictions rules for the account.""" + + allowed_countries: list[str] = field(default_factory=lambda: []) + denied_countries: list[str] = field(default_factory=lambda: []) diff --git a/client/src/dolbyio_rest_apis/streaming/models/cluster.py b/client/src/dolbyio_rest_apis/streaming/models/cluster.py index e04847b..6571469 100644 --- a/client/src/dolbyio_rest_apis/streaming/models/cluster.py +++ b/client/src/dolbyio_rest_apis/streaming/models/cluster.py @@ -2,28 +2,41 @@ dolbyio_rest_apis.streaming.models.cluster ~~~~~~~~~~~~~~~ -This module contains the models used by the Cluster model. +This module contains the models used by the Cluster module. """ -from dolbyio_rest_apis.core.helpers import get_value_or_default +from dataclasses import dataclass +from dataclasses_json import LetterCase, dataclass_json -class Cluster(dict): - """The :class:`Cluster` object, which represents a cluster.""" +@dataclass +class ClusterLocation: + """The :class:`ClusterLocation` object, which represents the location of a cluster.""" - def __init__(self, dictionary: dict): - dict.__init__(self, dictionary) + city: str | None = None + region: str | None = None + country: str | None = None - self.id = get_value_or_default(self, 'id', None) - self.name = get_value_or_default(self, 'name', None) - self.rtmp = get_value_or_default(self, 'rtmp', None) +@dataclass +class ClusterFeatures: + """The :class:`ClusterFeatures` object, which represents the available features of a cluster.""" -class ClusterResponse(dict): - """The :class:`ClusterResponse` object, which represents a cluster response.""" + transcoding: bool = False + +@dataclass +class Cluster: + """The :class:`Cluster` object, which represents a cluster.""" + + id: str + name: str + rtmp: str + srt: str + location: ClusterLocation + features: ClusterFeatures - def __init__(self, dictionary: dict): - dict.__init__(self, dictionary) +@dataclass_json(letter_case=LetterCase.CAMEL) +@dataclass +class ClusterResponse: + """The :class:`ClusterResponse` object, which represents a cluster response.""" - self.default_cluster = get_value_or_default(self, 'defaultCluster', None) - self.available_clusters = [] - for cluster in self['availableClusters']: - self.available_clusters.append(Cluster(cluster)) + default_cluster: str + available_clusters: list[Cluster] diff --git a/client/src/dolbyio_rest_apis/streaming/models/core.py b/client/src/dolbyio_rest_apis/streaming/models/core.py index a33c140..1743a42 100644 --- a/client/src/dolbyio_rest_apis/streaming/models/core.py +++ b/client/src/dolbyio_rest_apis/streaming/models/core.py @@ -3,17 +3,16 @@ ~~~~~~~~~~~~~~~ """ -from dolbyio_rest_apis.core.helpers import get_value_or_default +from dataclasses import dataclass +from dataclasses_json import dataclass_json -class BaseResponse(dict): - def __init__(self, dictionary: dict): - dict.__init__(self, dictionary) +@dataclass_json +@dataclass +class BaseResponse(): + status: str + data: dict | list[dict] - self.status = get_value_or_default(self, 'status', None) - self.data = get_value_or_default(self, 'data', None) - -class Error(dict): - def __init__(self, dictionary: dict): - dict.__init__(self, dictionary) - - self.message = get_value_or_default(self, 'message', None) +@dataclass_json +@dataclass +class Error: + message: str diff --git a/client/src/dolbyio_rest_apis/streaming/models/publish_token.py b/client/src/dolbyio_rest_apis/streaming/models/publish_token.py index 242b959..75c51d8 100644 --- a/client/src/dolbyio_rest_apis/streaming/models/publish_token.py +++ b/client/src/dolbyio_rest_apis/streaming/models/publish_token.py @@ -2,115 +2,143 @@ dolbyio_rest_apis.streaming.models.publish_token ~~~~~~~~~~~~~~~ -This module contains the models used by the Publish Token models. +This module contains the models used by the Publish Token module. """ -from typing import List -from dolbyio_rest_apis.core.helpers import get_value_or_default - -class PublishTokenStream(dict): - """The :class:`PublishTokenStream` object, which represents a Publish Token Stream.""" - - def __init__(self, dictionary: dict): - dict.__init__(self, dictionary) - - self.stream_name = get_value_or_default(self, 'streamName', None) - self.is_regex = get_value_or_default(self, 'isRegex', False) - -class PublishToken(dict): - """The :class:`PublishToken` object, which represents a Publish Token.""" - - def __init__(self, dictionary: dict): - dict.__init__(self, dictionary) - - self.id = get_value_or_default(self, 'id', None) - self.label = get_value_or_default(self, 'label', None) - self.token = get_value_or_default(self, 'token', None) - self.added_on = get_value_or_default(self, 'addedOn', None) - self.expires_on = get_value_or_default(self, 'expiresOn', None) - self.is_active = get_value_or_default(self, 'isActive', None) - self.streams = [] - for stream in self['streams']: - self.streams.append(PublishTokenStream(stream)) - self.allowed_origins = get_value_or_default(self, 'allowedOrigins', None) - self.allowed_ip_addresses = get_value_or_default(self, 'allowedIpAddresses', None) - self.bind_ips_on_usage = get_value_or_default(self, 'bindIpsOnUsage', None) - self.allowed_countries = get_value_or_default(self, 'allowedCountries', None) - self.denied_countries = get_value_or_default(self, 'deniedCountries', None) - self.origin_cluster = get_value_or_default(self, 'originCluster', None) - self.subscribe_requires_auth = get_value_or_default(self, 'subscribeRequiresAuth', False) - self.record = get_value_or_default(self, 'record', False) - self.multisource = get_value_or_default(self, 'multisource', False) - -class CreateUpdatePublishTokenStream(): - """The :class:`CreateUpdatePublishTokenStream` object, which represents a Publish Token Stream.""" - - def __init__(self, stream_name: str, is_regex: bool): - self.stream_name = stream_name - self.is_regex = is_regex - -class UpdatePublishToken(): +from dataclasses import dataclass, field +from dataclasses_json import LetterCase, dataclass_json + +@dataclass_json(letter_case=LetterCase.CAMEL) +@dataclass +class TokenGeoCascade: + """The :class:`TokenGeoCascade` object, the definition of the geo cascading rules for a publish token.""" + + is_enabled: bool = False + clusters: list[str] = field(default_factory=lambda: []) + +@dataclass_json(letter_case=LetterCase.CAMEL) +@dataclass +class TokenStreamName: + """The :class:`TokenStreamName` object, which represents a token stream name.""" + + stream_name: str + is_regex: bool = False + +@dataclass_json(letter_case=LetterCase.CAMEL) +@dataclass +class PublishTokenRestream: + """The :class:`PublishTokenRestream` object, the definition of a restream endpoint.""" + + url: str + key: str + +@dataclass_json(letter_case=LetterCase.CAMEL) +@dataclass +class TokenEffectiveSettings: + """The :class:`TokenEffectiveSettings` object, which represents the effective settings for a publish token.""" + + origin_cluster: str | None = None + allowed_countries: list[str] = field(default_factory=lambda: []) + denied_countries: list[str] = field(default_factory=lambda: []) + geo_cascade: TokenGeoCascade | None = None + +@dataclass_json(letter_case=LetterCase.CAMEL) +@dataclass +class PublishToken: + """The :class:`PublishToken` object, which represents a publish token.""" + + id: int | None = None + label: str | None = None + token: str | None = None + added_on: str | None = None + expires_on: str | None = None + is_active: bool | None = None + streams: list[TokenStreamName] = field(default_factory=lambda: []) + allowed_origins: list[str] = field(default_factory=lambda: []) + allowed_ip_addresses: list[str] = field(default_factory=lambda: []) + bind_ips_on_usage: int | None = None + allowed_countries: list[str] = field(default_factory=lambda: []) + denied_countries: list[str] = field(default_factory=lambda: []) + origin_cluster: str | None = None + subscribe_requires_auth: bool = False + record: bool = False + clip: bool = False + multisource: bool = False + low_latency_rtmp: bool = False + enable_thumbnails: bool = False + display_srt_passphrase: bool = False + srt_passphrase: bool = False + geo_cascade: TokenGeoCascade | None = None + restream: list[PublishTokenRestream] = field(default_factory=lambda: []) + effective_settings: TokenEffectiveSettings | None = None + +class UpdatePublishToken: """The :class:`UpdatePublishToken` object, which represents an Update Publish Token request.""" def __init__(self): self.label: str | None = None self.refresh_token: bool | None = None self.is_active: bool | None = None - self.add_token_streams: List[CreateUpdatePublishTokenStream] | None = None - self.remove_token_streams: List[CreateUpdatePublishTokenStream] | None = None - self.update_allowed_origins: List[str] | None = None - self.update_allowed_ip_addresses: List[str] | None = None + self.add_token_streams: list[TokenStreamName] | None = None + self.remove_token_streams: list[TokenStreamName] | None = None + self.update_allowed_origins: list[str] | None = None + self.update_allowed_ip_addresses: list[str] | None = None self.update_bind_ips_on_usage: int | None = None - self.update_allowed_countries: List[str] | None = None - self.update_denied_countries: List[str] | None = None + self.update_allowed_countries: list[str] | None = None + self.update_denied_countries: list[str] | None = None self.update_origin_cluster: str | None = None self.subscribe_requires_auth: bool | None = None self.record: bool | None = None self.multisource: bool | None = None - -class CreatePublishToken(): + self.enable_thumbnails: bool | None = None + self.display_srt_passphrase: bool | None = None + self.low_latency_rtmp: bool | None = None + self.clip: bool = False + self.update_geo_cascade: TokenGeoCascade | None = None + self.update_restream: list[PublishTokenRestream] | None = None + +class CreatePublishToken: """The :class:`CreatePublishToken` object, which represents a Create Publish Token request.""" def __init__(self, label: str): self.label = label self.expires_on: str | None = None - self.streams: List[CreateUpdatePublishTokenStream] = [] - self.allowed_origins: List[str] | None = None - self.allowed_ip_addresses: List[str] | None = None + self.streams: list[TokenStreamName] = [] + self.allowed_origins: list[str] | None = None + self.allowed_ip_addresses: list[str] | None = None self.bind_ips_on_usage: int | None = None - self.allowed_countries: List[str] | None = None - self.denied_countries: List[str] | None = None + self.allowed_countries: list[str] | None = None + self.denied_countries: list[str] | None = None self.origin_cluster: str | None = None self.subscribe_requires_auth: bool = False self.record: bool = False self.multisource: bool = False - -class ActivePublishToken(dict): + self.enable_thumbnails: bool = False + self.display_srt_passphrase: bool = False + self.low_latency_rtmp: bool = True + self.geo_cascade: TokenGeoCascade | None = None + self.clip: bool = False + self.restream: list[PublishTokenRestream] = [] + +@dataclass_json(letter_case=LetterCase.CAMEL) +@dataclass +class ActivePublishToken: """The :class:`ActivePublishToken` object.""" - def __init__(self, dictionary: dict): - dict.__init__(self, dictionary) - - self.token_ids: List[int] = [] - for tid in self['tokenIds']: - self.token_ids.append(tid) + token_ids: list[int] -class FailedToken(dict): +@dataclass_json(letter_case=LetterCase.CAMEL) +@dataclass +class FailedToken: """The :class:`FailedToken` object, which represents a Failed Token.""" - def __init__(self, dictionary: dict): - dict.__init__(self, dictionary) + token_id: str | None = None + error_message: str | None = None - self.token_id = get_value_or_default(self, 'tokenId', None) - self.error_message = get_value_or_default(self, 'errorMessage', None) - -class DisablePublishTokenResponse(dict): +@dataclass_json(letter_case=LetterCase.CAMEL) +@dataclass +class DisablePublishTokenResponse: """The :class:`DisablePublishTokenResponse` object, which represents a the response to the disable publish token.""" - def __init__(self, dictionary: dict): - dict.__init__(self, dictionary) - - self.successful_tokens = get_value_or_default(self, 'successfulTokens', None) - self.failed_tokens: List[int] = [] - for ft in self['failedTokens']: - self.failed_tokens.append(FailedToken(ft)) + successful_tokens: list[int] + failed_tokens: list[FailedToken] diff --git a/client/src/dolbyio_rest_apis/streaming/models/stream.py b/client/src/dolbyio_rest_apis/streaming/models/stream.py new file mode 100644 index 0000000..9bd2bab --- /dev/null +++ b/client/src/dolbyio_rest_apis/streaming/models/stream.py @@ -0,0 +1,16 @@ +""" +dolbyio_rest_apis.streaming.models.stream +~~~~~~~~~~~~~~~ + +This module contains the models used by the Stream module. +""" + +from dataclasses import dataclass +from dataclasses_json import LetterCase, dataclass_json + +@dataclass_json(letter_case=LetterCase.CAMEL) +@dataclass +class StreamStoppingLevel: + """The :class:`StreamStoppingLevel` object, which represents the response to stopping a stream.""" + + stopping_level: str diff --git a/client/src/dolbyio_rest_apis/streaming/models/subscribe_token.py b/client/src/dolbyio_rest_apis/streaming/models/subscribe_token.py index d494433..7c87d67 100644 --- a/client/src/dolbyio_rest_apis/streaming/models/subscribe_token.py +++ b/client/src/dolbyio_rest_apis/streaming/models/subscribe_token.py @@ -2,64 +2,55 @@ dolbyio_rest_apis.streaming.models.subscribe_token ~~~~~~~~~~~~~~~ -This module contains the models used by the Subscribe Token models. +This module contains the models used by the Subscribe Token module. """ -from typing import List -from dolbyio_rest_apis.core.helpers import get_value_or_default +from dataclasses import dataclass, field +from dataclasses_json import LetterCase, dataclass_json +from dolbyio_rest_apis.streaming.models.publish_token import TokenEffectiveSettings, TokenStreamName -class SubscribeTokenStream(dict): - """The :class:`SubscribeTokenStream` object, which represents a Subscribe Token Stream.""" +@dataclass_json(letter_case=LetterCase.CAMEL) +@dataclass +class SubscribeTokenTracking: + """The :class:`SubscribeTokenTracking` object, the definition of the tracking info of a subscribe token.""" - def __init__(self, dictionary: dict): - dict.__init__(self, dictionary) + tracking_id: bool = False - self.stream_name = get_value_or_default(self, 'streamName', None) - self.is_regex = get_value_or_default(self, 'isRegex', False) - -class SubscribeToken(dict): +@dataclass_json(letter_case=LetterCase.CAMEL) +@dataclass +class SubscribeToken: """The :class:`SubscribeToken` object, which represents a Subscribe Token.""" - def __init__(self, dictionary: dict): - dict.__init__(self, dictionary) - - self.id = get_value_or_default(self, 'id', None) - self.label = get_value_or_default(self, 'label', None) - self.token = get_value_or_default(self, 'token', None) - self.added_on = get_value_or_default(self, 'addedOn', None) - self.expires_on = get_value_or_default(self, 'expiresOn', None) - self.is_active = get_value_or_default(self, 'isActive', None) - self.streams = [] - for stream in self['streams']: - self.streams.append(SubscribeTokenStream(stream)) - self.allowed_origins = get_value_or_default(self, 'allowedOrigins', None) - self.allowed_ip_addresses = get_value_or_default(self, 'allowedIpAddresses', None) - self.bind_ips_on_usage = get_value_or_default(self, 'bindIpsOnUsage', None) - self.allowed_countries = get_value_or_default(self, 'allowedCountries', None) - self.denied_countries = get_value_or_default(self, 'deniedCountries', None) - self.origin_cluster = get_value_or_default(self, 'originCluster', None) - -class CreateUpdateSubscribeTokenStream(): - """The :class:`UpdateSubscribeTokenStream` object, which represents an update Subscribe Token Stream.""" - - def __init__(self, stream_name: str, is_regex: bool): - self.stream_name = stream_name - self.is_regex = is_regex + id: int | None = None + label: str | None = None + token: str | None = None + added_on: str | None = None + expires_on: str | None = None + is_active: bool | None = None + streams: list[TokenStreamName] = field(default_factory=lambda: []) + allowed_origins: list[str] = field(default_factory=lambda: []) + allowed_ip_addresses: list[str] = field(default_factory=lambda: []) + bind_ips_on_usage: int | None = None + allowed_countries: list[str] = field(default_factory=lambda: []) + denied_countries: list[str] = field(default_factory=lambda: []) + origin_cluster: str | None = None + effective_settings: TokenEffectiveSettings | None = None + tracking: SubscribeTokenTracking | None = None -class UpdateSubscribeToken(): +class UpdateSubscribeToken: """The :class:`UpdateSubscribeToken` object, which represents an Update Subscribe Token request.""" def __init__(self): self.label: str | None = None self.refresh_token: bool | None = None self.is_active: bool | None = None - self.add_token_streams: List[CreateUpdateSubscribeTokenStream] | None = None - self.remove_token_streams: List[CreateUpdateSubscribeTokenStream] | None = None - self.update_allowed_origins: List[str] | None = None - self.update_allowed_ip_addresses: List[str] | None = None + self.add_token_streams: list[TokenStreamName] | None = None + self.remove_token_streams: list[TokenStreamName] | None = None + self.update_allowed_origins: list[str] | None = None + self.update_allowed_ip_addresses: list[str] | None = None self.update_bind_ips_on_usage: int | None = None - self.update_allowed_countries: List[str] | None = None - self.update_denied_countries: List[str] | None = None + self.update_allowed_countries: list[str] | None = None + self.update_denied_countries: list[str] | None = None self.update_origin_cluster: str | None = None class CreateSubscribeToken(): @@ -68,10 +59,11 @@ class CreateSubscribeToken(): def __init__(self, label: str): self.label = label self.expires_on: str | None = None - self.streams: List[CreateUpdateSubscribeTokenStream] = [] - self.allowed_origins: List[str] | None = None - self.allowed_ip_addresses: List[str] | None = None + self.streams: list[TokenStreamName] = [] + self.allowed_origins: list[str] | None = None + self.allowed_ip_addresses: list[str] | None = None self.bind_ips_on_usage: int | None = None - self.allowed_countries: List[str] | None = None - self.denied_countries: List[str] | None = None + self.allowed_countries: list[str] | None = None + self.denied_countries: list[str] | None = None self.origin_cluster: str | None = None + self.tracking_id: str | None = None diff --git a/client/src/dolbyio_rest_apis/streaming/models/webhooks.py b/client/src/dolbyio_rest_apis/streaming/models/webhooks.py new file mode 100644 index 0000000..ab9c443 --- /dev/null +++ b/client/src/dolbyio_rest_apis/streaming/models/webhooks.py @@ -0,0 +1,46 @@ +""" +dolbyio_rest_apis.streaming.models.webhooks +~~~~~~~~~~~~~~~ + +This module contains the models used by the Webhooks module. +""" + +from dataclasses import dataclass +from dataclasses_json import LetterCase, dataclass_json + +@dataclass_json(letter_case=LetterCase.CAMEL) +@dataclass +class Webhook: + """The :class:`Webhook` object, which represents a webhook.""" + + id: int | None = None + url: str | None = None + secret: str | None = None + is_feed_hooks: bool | None = None + is_recording_hooks: bool | None = None + is_thumbnail_hooks: bool | None = None + is_transcoder_hooks: bool | None = None + is_clip_hooks: bool | None = None + +class UpdateWebhook: + """The :class:`UpdateWebhook` object, which represents an Update webhook request.""" + + def __init__(self): + self.url: str | None = None + self.refresh_secret: bool | None = None + self.is_feed_hooks: bool | None = None + self.is_recording_hooks: bool | None = None + self.is_thumbnail_hooks: bool | None = None + self.is_transcoder_hooks: bool | None = None + self.is_clip_hooks: bool | None = None + +class CreateWebhook(): + """The :class:`CreateWebhook` object, which represents a Create webhook request.""" + + def __init__(self, url: str, is_feed_hooks: bool, is_recording_hooks: bool): + self.url = url + self.is_feed_hooks = is_feed_hooks + self.is_recording_hooks = is_recording_hooks + self.is_thumbnail_hooks = False + self.is_transcoder_hooks = False + self.is_clip_hooks = False diff --git a/client/src/dolbyio_rest_apis/streaming/publish_token.py b/client/src/dolbyio_rest_apis/streaming/publish_token.py index b31acb6..40f3f07 100644 --- a/client/src/dolbyio_rest_apis/streaming/publish_token.py +++ b/client/src/dolbyio_rest_apis/streaming/publish_token.py @@ -5,7 +5,6 @@ This module contains the functions to work with the Publish Token APIs. """ -from typing import List from dolbyio_rest_apis.core.helpers import add_if_not_none from dolbyio_rest_apis.core.urls import get_rts_url from dolbyio_rest_apis.streaming.internal.http_context import StreamingHttpContext @@ -16,12 +15,12 @@ async def read( token_id: int, ) -> PublishToken: async with StreamingHttpContext() as http_context: - json_response = await http_context.requests_get( + dict_data = await http_context.requests_get( api_secret=api_secret, url=f'{get_rts_url()}/api/publish_token/{token_id}', ) - return PublishToken(json_response) + return PublishToken.from_dict(dict_data) async def delete( api_secret: str, @@ -67,23 +66,40 @@ async def update( add_if_not_none(payload, 'subscribeRequiresAuth', token.subscribe_requires_auth) add_if_not_none(payload, 'record', token.record) add_if_not_none(payload, 'multisource', token.multisource) + add_if_not_none(payload, 'enableThumbnails', token.enable_thumbnails) + add_if_not_none(payload, 'displaySrtPassphrase', token.display_srt_passphrase) + add_if_not_none(payload, 'lowLatencyRtmp', token.low_latency_rtmp) + add_if_not_none(payload, 'clip', token.clip) + if token.update_geo_cascade is not None: + payload['updateGeoCascade'] = { + 'isEnabled': token.update_geo_cascade.is_enabled, + 'clusters': token.update_geo_cascade.clusters, + } + if token.update_restream is not None: + payload['updateRestream'] = [] + for restream in token.update_restream: + restream_obj = { + 'url': restream.url, + 'key': restream.key, + } + payload['updateRestream'].append(restream_obj) async with StreamingHttpContext() as http_context: - json_response = await http_context.requests_put( + dict_data = await http_context.requests_put( api_secret=api_secret, url=f'{get_rts_url()}/api/publish_token/{token_id}', payload=payload, ) - return PublishToken(json_response) + return PublishToken.from_dict(dict_data) async def list_tokens( api_secret: str, sort_by: str, page: int, items_on_page: int, - is_descending: bool, - ) -> List[PublishToken]: + is_descending: bool = False, + ) -> list[PublishToken]: params = { 'sortBy': sort_by, 'page': str(page), @@ -92,16 +108,16 @@ async def list_tokens( } async with StreamingHttpContext() as http_context: - json_response = await http_context.requests_get( + dict_data = await http_context.requests_get( api_secret=api_secret, url=f'{get_rts_url()}/api/publish_token/list', params=params, ) - publish_tokens = [] - for token in json_response: - publish_tokens.append(PublishToken(token)) - return publish_tokens + tokens = [] + for token in dict_data: + tokens.append(PublishToken.from_dict(token)) + return tokens async def create( api_secret: str, @@ -113,6 +129,11 @@ async def create( 'subscribeRequiresAuth': token.subscribe_requires_auth, 'record': token.record, 'multisource': token.multisource, + 'enableThumbnails': token.enable_thumbnails, + 'displaySrtPassphrase': token.display_srt_passphrase, + 'lowLatencyRtmp': token.low_latency_rtmp, + 'clip': token.clip, + 'restream': [], } add_if_not_none(payload, 'expiresOn', token.expires_on) for stream in token.streams: @@ -127,56 +148,65 @@ async def create( add_if_not_none(payload, 'allowedCountries', token.allowed_countries) add_if_not_none(payload, 'deniedCountries', token.denied_countries) add_if_not_none(payload, 'originCluster', token.origin_cluster) + if not token.geo_cascade is None: + payload['geoCascade'] = { + 'isEnabled': token.geo_cascade.is_enabled, + 'clusters': token.geo_cascade.clusters, + } + for restream in token.restream: + payload['restream'] = { + 'url': restream.url, + 'key': restream.key, + } async with StreamingHttpContext() as http_context: - json_response = await http_context.requests_post( + dict_data = await http_context.requests_post( api_secret=api_secret, url=f'{get_rts_url()}/api/publish_token', payload=payload, ) - return PublishToken(json_response) + return PublishToken.from_dict(dict_data) async def get_active_publish_token_id( api_secret: str, - account_id: str, - stream_name: str, + stream_id: str, ) -> ActivePublishToken: params = { - 'streamId': f'{account_id}/{stream_name}', + 'streamId': stream_id, } async with StreamingHttpContext() as http_context: - json_response = await http_context.requests_get( + dict_data = await http_context.requests_get( api_secret=api_secret, url=f'{get_rts_url()}/api/publish_token/active', params=params, ) - return ActivePublishToken(json_response) + return ActivePublishToken.from_dict(dict_data) async def get_all_active_publish_token_id( api_secret: str, ) -> ActivePublishToken: async with StreamingHttpContext() as http_context: - json_response = await http_context.requests_get( + dict_data = await http_context.requests_get( api_secret=api_secret, url=f'{get_rts_url()}/api/publish_token/active/all', ) - return ActivePublishToken(json_response) + return ActivePublishToken.from_dict(dict_data) async def disable( api_secret: str, - token_ids: List[int], + token_ids: list[int], ) -> DisablePublishTokenResponse: payload = { 'tokenIds': token_ids, } async with StreamingHttpContext() as http_context: - json_response = await http_context.requests_patch( + dict_data = await http_context.requests_patch( api_secret=api_secret, url=f'{get_rts_url()}/api/publish_token/disable', payload=payload, ) - return DisablePublishTokenResponse(json_response) + return DisablePublishTokenResponse.from_dict(dict_data) diff --git a/client/src/dolbyio_rest_apis/streaming/stream.py b/client/src/dolbyio_rest_apis/streaming/stream.py index 6ba6061..075da4d 100644 --- a/client/src/dolbyio_rest_apis/streaming/stream.py +++ b/client/src/dolbyio_rest_apis/streaming/stream.py @@ -7,28 +7,32 @@ from dolbyio_rest_apis.core.urls import get_rts_url from dolbyio_rest_apis.streaming.internal.http_context import StreamingHttpContext +from dolbyio_rest_apis.streaming.models.stream import StreamStoppingLevel async def stop( api_secret: str, - account_id: str, - stream_name: str, + stream_id: str, ) -> None: payload = { - 'streamId': f'{account_id}/{stream_name}', + 'streamId': stream_id, } async with StreamingHttpContext() as http_context: - await http_context.requests_post( + dict_data = await http_context.requests_post( api_secret=api_secret, url=f'{get_rts_url()}/api/stream/stop', payload=payload, ) + return StreamStoppingLevel.from_dict(dict_data) + async def stop_all( api_secret: str, ) -> None: async with StreamingHttpContext() as http_context: - await http_context.requests_post( + dict_data = await http_context.requests_post( api_secret=api_secret, url=f'{get_rts_url()}/api/stream/stop/all', ) + + return StreamStoppingLevel.from_dict(dict_data) diff --git a/client/src/dolbyio_rest_apis/streaming/subscribe_token.py b/client/src/dolbyio_rest_apis/streaming/subscribe_token.py index 55bb4c1..dd51e17 100644 --- a/client/src/dolbyio_rest_apis/streaming/subscribe_token.py +++ b/client/src/dolbyio_rest_apis/streaming/subscribe_token.py @@ -5,7 +5,6 @@ This module contains the functions to work with the Subscribe Token APIs. """ -from typing import List from dolbyio_rest_apis.core.helpers import add_if_not_none from dolbyio_rest_apis.core.urls import get_rts_url from dolbyio_rest_apis.streaming.internal.http_context import StreamingHttpContext @@ -16,12 +15,12 @@ async def read( token_id: int, ) -> SubscribeToken: async with StreamingHttpContext() as http_context: - json_response = await http_context.requests_get( + dict_data = await http_context.requests_get( api_secret=api_secret, url=f'{get_rts_url()}/api/subscribe_token/{token_id}', ) - return SubscribeToken(json_response) + return SubscribeToken.from_dict(dict_data) async def delete( api_secret: str, @@ -64,22 +63,23 @@ async def update( add_if_not_none(payload, 'updateAllowedCountries', token.update_allowed_countries) add_if_not_none(payload, 'updateDeniedCountries', token.update_denied_countries) add_if_not_none(payload, 'updateOriginCluster', token.update_origin_cluster) + async with StreamingHttpContext() as http_context: - json_response = await http_context.requests_put( + dict_data = await http_context.requests_put( api_secret=api_secret, url=f'{get_rts_url()}/api/subscribe_token/{token_id}', payload=payload, ) - return SubscribeToken(json_response) + return SubscribeToken.from_dict(dict_data) async def list_tokens( api_secret: str, sort_by: str, page: int, items_on_page: int, - is_descending: bool, - ) -> List[SubscribeToken]: + is_descending: bool = False, + ) -> list[SubscribeToken]: params = { 'sortBy': sort_by, 'page': str(page), @@ -88,16 +88,16 @@ async def list_tokens( } async with StreamingHttpContext() as http_context: - json_response = await http_context.requests_get( + dict_data = await http_context.requests_get( api_secret=api_secret, url=f'{get_rts_url()}/api/subscribe_token/list', params=params, ) - publish_tokens = [] - for token in json_response: - publish_tokens.append(SubscribeToken(token)) - return publish_tokens + tokens = [] + for token in dict_data: + tokens.append(SubscribeToken.from_dict(token)) + return tokens async def create( api_secret: str, @@ -120,12 +120,16 @@ async def create( add_if_not_none(payload, 'allowedCountries', token.allowed_countries) add_if_not_none(payload, 'deniedCountries', token.denied_countries) add_if_not_none(payload, 'originCluster', token.origin_cluster) + if not token.tracking_id is None: + payload['tracking'] = { + 'trackingId': token.tracking_id, + } async with StreamingHttpContext() as http_context: - json_response = await http_context.requests_post( + dict_data = await http_context.requests_post( api_secret=api_secret, url=f'{get_rts_url()}/api/subscribe_token', - payload=token, + payload=payload, ) - return SubscribeToken(json_response) + return SubscribeToken.from_dict(dict_data) diff --git a/client/src/dolbyio_rest_apis/streaming/webhooks.py b/client/src/dolbyio_rest_apis/streaming/webhooks.py new file mode 100644 index 0000000..3bc811d --- /dev/null +++ b/client/src/dolbyio_rest_apis/streaming/webhooks.py @@ -0,0 +1,102 @@ +""" +dolbyio_rest_apis.streaming.webhooks +~~~~~~~~~~~~~~~ + +This module contains the functions to work with the Webhooks APIs. +""" + +from dolbyio_rest_apis.core.helpers import add_if_not_none +from dolbyio_rest_apis.core.urls import get_rts_url +from dolbyio_rest_apis.streaming.internal.http_context import StreamingHttpContext +from dolbyio_rest_apis.streaming.models.webhooks import CreateWebhook, UpdateWebhook, Webhook + +async def read( + api_secret: str, + webhook_id: int, + ) -> Webhook: + async with StreamingHttpContext() as http_context: + dict_data = await http_context.requests_get( + api_secret=api_secret, + url=f'{get_rts_url()}/api/webhooks/{webhook_id}', + ) + + return Webhook.from_dict(dict_data) + +async def delete( + api_secret: str, + webhook_id: int, + ) -> None: + async with StreamingHttpContext() as http_context: + await http_context.requests_delete( + api_secret=api_secret, + url=f'{get_rts_url()}/api/webhooks/{webhook_id}', + ) + +async def update( + api_secret: str, + webhook_id: int, + webhook: UpdateWebhook, + ) -> Webhook: + payload = {} + add_if_not_none(payload, 'url', webhook.url) + add_if_not_none(payload, 'refreshSecret', webhook.refresh_secret) + add_if_not_none(payload, 'isFeedHooks', webhook.is_feed_hooks) + add_if_not_none(payload, 'isRecordingHooks', webhook.is_recording_hooks) + add_if_not_none(payload, 'isThumbnailHooks', webhook.is_thumbnail_hooks) + add_if_not_none(payload, 'isTranscoderHooks', webhook.is_transcoder_hooks) + add_if_not_none(payload, 'isClipHooks', webhook.is_clip_hooks) + + async with StreamingHttpContext() as http_context: + dict_data = await http_context.requests_put( + api_secret=api_secret, + url=f'{get_rts_url()}/api/webhooks/{webhook_id}', + payload=payload, + ) + + return Webhook.from_dict(dict_data) + +async def list_webhooks( + api_secret: str, + starting_id: int, + item_count: int = 10, + is_descending: bool = False, + ) -> list[Webhook]: + params = { + 'startingId': str(starting_id), + 'item_count': str(item_count), + 'isDescending': str(is_descending), + } + + async with StreamingHttpContext() as http_context: + dict_data = await http_context.requests_get( + api_secret=api_secret, + url=f'{get_rts_url()}/api/webhooks/list', + params=params, + ) + + webhooks = [] + for webhook in dict_data: + webhooks.append(Webhook.from_dict(webhook)) + return webhooks + +async def create( + api_secret: str, + webhook: CreateWebhook, + ) -> Webhook: + payload = { + 'url': webhook.url, + 'isFeedHooks': webhook.is_feed_hooks, + 'isRecordingHooks': webhook.is_recording_hooks, + } + add_if_not_none(payload, 'isThumbnailHooks', webhook.is_thumbnail_hooks) + add_if_not_none(payload, 'isTranscoderHooks', webhook.is_transcoder_hooks) + add_if_not_none(payload, 'isClipHooks', webhook.is_clip_hooks) + + async with StreamingHttpContext() as http_context: + dict_data = await http_context.requests_post( + api_secret=api_secret, + url=f'{get_rts_url()}/api/webhooks', + payload=payload, + ) + + return Webhook.from_dict(dict_data) diff --git a/requirements.txt b/requirements.txt index 8e93d90..584ef02 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,5 +4,5 @@ build>=0.5.1 twine devpi-client sphinx -pylint>=2.17.0 +pylint==2.17.7 pylint-quotes>=0.2.3