Skip to content

Commit 43bef00

Browse files
authored
Adding recording apis and download apis (#20749)
This commit adds Recording and Download Async APIs.
1 parent d279fc6 commit 43bef00

File tree

34 files changed

+2844
-264
lines changed

34 files changed

+2844
-264
lines changed

sdk/communication/azure-communication-callingserver/azure/communication/callingserver/_callingserver_client.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ class CallingServerClient(object):
6464
:language: python
6565
:dedent: 8
6666
"""
67+
6768
def __init__(
6869
self,
6970
endpoint, # type: str
@@ -500,20 +501,19 @@ def download(
500501
if not CallingServerUtils.is_valid_url(content_url):
501502
raise ValueError("content_url is invalid")
502503

504+
# pylint:disable=protected-access
503505
content_downloader = ContentDownloader(
504-
self._callingserver_service_client._client, # pylint:disable=protected-access
505-
self._callingserver_service_client._serialize, # pylint:disable=protected-access
506-
self._callingserver_service_client._deserialize, # pylint:disable=protected-access
507-
self._callingserver_service_client._config) # pylint:disable=protected-access
508-
stream_downloader = ContentStreamDownloader(
506+
self._callingserver_service_client._client,
507+
self._callingserver_service_client._serialize,
508+
self._callingserver_service_client._deserialize,
509+
self._callingserver_service_client._config)
510+
511+
return ContentStreamDownloader(
509512
content_downloader,
510-
self._callingserver_service_client._config, # pylint:disable=protected-access
513+
self._callingserver_service_client._config,
511514
start_range,
512515
end_range,
513516
endpoint=content_url,
514517
parallel_download_options=parallel_download_options,
515518
**kwargs
516519
)
517-
stream_downloader._setup() # pylint:disable=protected-access
518-
519-
return stream_downloader

sdk/communication/azure-communication-callingserver/azure/communication/callingserver/_communication_identifier_serializer.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ def serialize_identifier(identifier):
3030
request_model[identifier.kind] = dict(identifier.properties)
3131
return request_model
3232
except AttributeError:
33-
raise TypeError("Unsupported identifier type " + identifier.__class__.__name__)
33+
raise TypeError("Unsupported identifier type " +
34+
identifier.__class__.__name__)
3435

3536

3637
def deserialize_identifier(identifier_model):

sdk/communication/azure-communication-callingserver/azure/communication/callingserver/_download.py

Lines changed: 4 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -11,35 +11,7 @@
1111
from io import BytesIO
1212
from azure.core.exceptions import HttpResponseError
1313
from azure.core.tracing.common import with_current_context
14-
15-
16-
def parse_length_from_content_range(content_range):
17-
'''
18-
Parses the content length from the content range header: bytes 1-3/65537
19-
'''
20-
if content_range is None:
21-
return None
22-
23-
# First, split in space and take the second half: '1-3/65537'
24-
# Next, split on slash and take the second half: '65537'
25-
# Finally, convert to an int: 65537
26-
return int(content_range.split(' ', 1)[1].split('/', 1)[1])
27-
28-
29-
def validate_and_format_range_headers(start_range, end_range):
30-
# If end range is provided, start range must be provided
31-
if (end_range is not None) and start_range is None:
32-
raise ValueError("/ value cannot be None.")
33-
34-
# Format based on whether end_range is present
35-
range_header = None
36-
if end_range is not None:
37-
range_header = 'bytes={0}-{1}'.format(start_range, end_range)
38-
elif start_range is not None:
39-
range_header = "bytes={0}-".format(start_range)
40-
41-
return range_header
42-
14+
from .utils._utils import CallingServerUtils
4315

4416
class _ChunkDownloader(object): # pylint: disable=too-many-instance-attributes
4517
def __init__(
@@ -120,7 +92,7 @@ def _write_to_stream(self, chunk_data, chunk_start):
12092
self.stream.write(chunk_data)
12193

12294
def _download_chunk(self, chunk_start, chunk_end):
123-
range_header = validate_and_format_range_headers(
95+
range_header = CallingServerUtils.validate_and_format_range_headers(
12496
chunk_start,
12597
chunk_end
12698
)
@@ -236,16 +208,14 @@ def __init__(
236208
initial_request_end = initial_request_start + self._block_size - 1
237209

238210
self._initial_range = (initial_request_start, initial_request_end)
239-
240-
def _setup(self):
241211
self._response = self._initial_request()
242212
if self.size == 0:
243213
self._current_content = b""
244214
else:
245215
self._current_content = self._response.response.internal_response.content # pylint: disable=protected-access
246216

247217
def _initial_request(self):
248-
range_header = validate_and_format_range_headers(
218+
range_header = CallingServerUtils.validate_and_format_range_headers(
249219
self._initial_range[0],
250220
self._initial_range[1])
251221
try:
@@ -255,7 +225,7 @@ def _initial_request(self):
255225
**self._request_options)
256226
# Parse the total file size and adjust the download size if ranges
257227
# were specified
258-
self._file_size = parse_length_from_content_range(
228+
self._file_size = CallingServerUtils.parse_length_from_content_range(
259229
response.response.headers["Content-Range"])
260230

261231
if self._end_range is not None:

sdk/communication/azure-communication-callingserver/azure/communication/callingserver/_shared/models.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ class CommunicationIdentifier(Protocol):
5151
)
5252

5353

54-
class CommunicationUserIdentifier(object):
54+
class CommunicationUserIdentifier(CommunicationIdentifier):
5555
"""Represents a user in Azure Communication Service.
5656
5757
:ivar str raw_id: Optional raw ID of the identifier.
@@ -77,7 +77,7 @@ def __init__(self, id, **kwargs):
7777
)
7878

7979

80-
class PhoneNumberIdentifier(object):
80+
class PhoneNumberIdentifier(CommunicationIdentifier):
8181
"""Represents a phone number.
8282
8383
:ivar str raw_id: Optional raw ID of the identifier.
@@ -125,7 +125,7 @@ def __init__(self, identifier):
125125
)
126126

127127

128-
class MicrosoftTeamsUserIdentifier(object):
128+
class MicrosoftTeamsUserIdentifier(CommunicationIdentifier):
129129
"""Represents an identifier for a Microsoft Teams user.
130130
131131
:ivar str raw_id: Optional raw ID of the identifier.

sdk/communication/azure-communication-callingserver/azure/communication/callingserver/aio/_callingserver_client_async.py

Lines changed: 129 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
from typing import TYPE_CHECKING, Any, List, Optional # pylint: disable=unused-import
1212

1313
from azure.core.tracing.decorator_async import distributed_trace_async
14+
from azure.core.pipeline.transport import HttpResponse
15+
1416
from ..utils._utils import CallingServerUtils
1517
from .._communication_identifier_serializer import serialize_identifier
1618
from .._communication_call_locator_serializer import serialize_call_locator
@@ -20,10 +22,16 @@
2022
CreateCallRequest,
2123
PhoneNumberIdentifierModel,
2224
PlayAudioResult,
23-
AddParticipantResult
25+
AddParticipantResult,
26+
StartCallRecordingResult,
27+
CallRecordingProperties,
28+
StartCallRecordingWithCallLocatorRequest,
29+
StartCallRecordingRequest
2430
)
2531
from .._shared.models import CommunicationIdentifier
26-
from .._models import CallLocator
32+
from ._content_downloader_async import ContentDownloader
33+
from ._download_async import ContentStreamDownloader
34+
from .._models import CallLocator, ParallelDownloadOptions
2735
from ._call_connection_async import CallConnection
2836
from .._converters import (
2937
JoinCallRequestConverter,
@@ -66,8 +74,8 @@ def __init__(
6674
try:
6775
if not endpoint.lower().startswith('http'):
6876
endpoint = "https://" + endpoint
69-
except AttributeError:
70-
raise ValueError("Account URL must be a string.")
77+
except AttributeError as ex:
78+
raise ValueError("Account URL must be a string.") from ex
7179

7280
if not credential:
7381
raise ValueError(
@@ -388,6 +396,123 @@ async def cancel_participant_media_operation(
388396
**kwargs
389397
)
390398

399+
@distributed_trace_async()
400+
async def start_recording(
401+
self,
402+
call_locator: CallLocator,
403+
recording_state_callback_uri: str,
404+
**kwargs: Any
405+
) -> StartCallRecordingResult:
406+
407+
if not call_locator:
408+
raise ValueError("call_locator cannot be None")
409+
if not CallingServerUtils.is_valid_url(recording_state_callback_uri):
410+
raise ValueError("recording_state_callback_uri is invalid")
411+
412+
start_call_recording_request = StartCallRecordingRequest(
413+
recording_state_callback_uri=recording_state_callback_uri,
414+
**kwargs
415+
)
416+
417+
start_call_recording_with_calllocator_request = StartCallRecordingWithCallLocatorRequest(
418+
call_locator=serialize_call_locator(call_locator),
419+
start_call_recording_request=start_call_recording_request
420+
)
421+
422+
return await self._server_call_client.start_recording(
423+
start_call_recording_with_calllocator_request,
424+
**kwargs
425+
)
426+
427+
@distributed_trace_async()
428+
async def pause_recording(
429+
self,
430+
recording_id: str,
431+
**kwargs: Any
432+
) -> HttpResponse:
433+
434+
if not recording_id:
435+
raise ValueError("recording_id cannot be None")
436+
437+
return await self._server_call_client.pause_recording(
438+
recording_id=recording_id,
439+
**kwargs
440+
)
441+
442+
@distributed_trace_async()
443+
async def resume_recording(
444+
self,
445+
recording_id: str,
446+
**kwargs: Any
447+
) -> HttpResponse:
448+
449+
if not recording_id:
450+
raise ValueError("recording_id cannot be None")
451+
452+
return await self._server_call_client.resume_recording(
453+
recording_id=recording_id,
454+
**kwargs
455+
)
456+
457+
@distributed_trace_async()
458+
async def stop_recording(
459+
self,
460+
recording_id: str,
461+
**kwargs: Any
462+
) -> HttpResponse:
463+
464+
if not recording_id:
465+
raise ValueError("recording_id cannot be None")
466+
467+
return await self._server_call_client.stop_recording(
468+
recording_id=recording_id,
469+
**kwargs
470+
)
471+
472+
@distributed_trace_async()
473+
async def get_recording_properities(
474+
self,
475+
recording_id: str,
476+
**kwargs: Any
477+
) -> CallRecordingProperties:
478+
479+
if not recording_id:
480+
raise ValueError("recording_id cannot be None")
481+
482+
return await self._server_call_client.get_recording_properties(
483+
recording_id=recording_id,
484+
**kwargs
485+
)
486+
487+
@distributed_trace_async
488+
async def download(
489+
self,
490+
content_url: str,
491+
start_range: int = None,
492+
end_range: int = None,
493+
parallel_download_options: ParallelDownloadOptions = None,
494+
**kwargs: Any
495+
) -> ContentStreamDownloader:
496+
497+
#pylint: disable=protected-access
498+
content_downloader = ContentDownloader(
499+
self._callingserver_service_client._client,
500+
self._callingserver_service_client._config,
501+
self._callingserver_service_client._serialize,
502+
self._callingserver_service_client._deserialize
503+
)
504+
stream_downloader = ContentStreamDownloader(
505+
content_downloader,
506+
self._callingserver_service_client._config,
507+
start_range,
508+
end_range,
509+
endpoint=content_url,
510+
parallel_download_options=parallel_download_options,
511+
**kwargs
512+
)
513+
await stream_downloader._setup()
514+
return stream_downloader
515+
391516
async def close(self) -> None:
392517
"""Close the :class:
393518
`~azure.communication.callingserver.aio.CallingServerClient` session.

0 commit comments

Comments
 (0)