Module pytgcalls.implementation.group_call_native

Expand source code
#  tgcalls - a Python binding for C++ library by Telegram
#  pytgcalls - a library connecting the Python binding with MTProto
#  Copyright (C) 2020-2021 Il`ya (Marshal) <https://github.com/MarshalX>
#
#  This file is part of tgcalls and pytgcalls.
#
#  tgcalls and pytgcalls is free software: you can redistribute it and/or modify
#  it under the terms of the GNU Lesser General Public License as published
#  by the Free Software Foundation, either version 3 of the License, or
#  (at your option) any later version.
#
#  tgcalls and pytgcalls is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU Lesser General Public License for more details.
#
#  You should have received a copy of the GNU Lesser General Public License v3
#  along with tgcalls. If not, see <http://www.gnu.org/licenses/>.

import logging
import warnings
from typing import Optional, List

import tgcalls
from pytgcalls.exceptions import CallBeforeStartError

logger = logging.getLogger(__name__)


def if_native_instance_created(func):
    def wrapper(self, *args, **kwargs):
        if self.is_group_call_native_created():
            return func(self, *args, **kwargs)
        else:
            raise CallBeforeStartError("You can't use this method before calling .start()")

    return wrapper


class GroupCallNative:
    def __init__(
        self,
        emit_join_payload_callback,
        network_state_updated_callback,
        enable_logs_to_console: bool,
        path_to_log_file: str,
        outgoing_audio_bitrate_kbit: int,
    ):
        """Create NativeInstance of tgcalls C++ part.

        Args:
            enable_logs_to_console (`bool`): Is enable logs to stderr from tgcalls.
            path_to_log_file (`str`, optional): Path to log file for logs of tgcalls.
        """

        # bypass None value
        if not path_to_log_file:
            path_to_log_file = ''

        logger.debug('Create a new native instance...')
        self.__native_instance = tgcalls.NativeInstance(enable_logs_to_console, path_to_log_file)

        self.__native_instance.setupGroupCall(
            emit_join_payload_callback,
            network_state_updated_callback,
            outgoing_audio_bitrate_kbit,
        )

        logger.debug('Native instance created.')

    def is_group_call_native_created(self):
        return self.__native_instance.isGroupCallNativeCreated()

    def _setup_and_start_group_call(self):
        raise NotImplementedError()

    @if_native_instance_created
    def _set_connection_mode(self, mode: tgcalls.GroupConnectionMode, keep_broadcast_if_was_enabled=False):
        logger.debug(f'Set native connection mode {mode}.')
        self.__native_instance.setConnectionMode(mode, keep_broadcast_if_was_enabled)

    @if_native_instance_created
    def _emit_join_payload(self, callback):
        logger.debug(f'Trigger native emit join payload.')
        self.__native_instance.emitJoinPayload(callback)

    def _start_native_group_call(self, *args):
        logger.debug('Start native group call...')
        self.__native_instance.startGroupCall(*args)

    @if_native_instance_created
    def _emit_join_payload(self, callback):
        logger.debug('Emit native join payload.')
        self.__native_instance.emitJoinPayload(callback)

    @if_native_instance_created
    def _set_join_response_payload(self, payload):
        logger.debug('Set native join response payload...')
        self.__native_instance.setJoinResponsePayload(payload)

    @if_native_instance_created
    def _set_is_mute(self, is_muted: bool):
        logger.debug(f'Set is muted on native instance side. New value: {is_muted}.')
        self.__native_instance.setIsMuted(is_muted)

    @if_native_instance_created
    def _set_volume(self, ssrc, volume):
        logger.debug(f'Set native volume for {ssrc} to {volume}.')
        self.__native_instance.setVolume(ssrc, volume)

    @if_native_instance_created
    def _stop_audio_device_module(self):
        logger.debug(f'Stop audio device module.')
        self.__native_instance.stopAudioDeviceModule()

    @if_native_instance_created
    def _start_audio_device_module(self):
        logger.debug(f'Start audio device module.')
        self.__native_instance.startAudioDeviceModule()

    @if_native_instance_created
    def get_playout_devices(self) -> List['tgcalls.AudioDevice']:
        """Get available playout audio devices in the system.

        Note:
            `tgcalls.AudioDevice` have 2 attributes: name, guid.
        """

        logger.debug('Get native playout devices.')
        return self.__native_instance.getPlayoutDevices()

    @if_native_instance_created
    def get_recording_devices(self) -> List['tgcalls.AudioDevice']:
        """Get available recording audio devices in the system.

        Note:
            `tgcalls.AudioDevice` have 2 attributes: name, guid.
        """

        logger.debug('Get native recording devices.')
        return self.__native_instance.getRecordingDevices()

    @if_native_instance_created
    def set_audio_input_device(self, name: Optional[str] = None):
        """Set audio input device.

        Note:
            If `name` is `None`, will use default system device.
            And this is works only at first device initialization time!

        Args:
            name (`str`): Name or GUID of device.
        """

        logger.debug(f'Set native audio input device to {name}.')
        self.__native_instance.setAudioInputDevice(name or '')

    @if_native_instance_created
    def set_audio_output_device(self, name: Optional[str] = None):
        """Set audio output device.

        Note:
            If `name` is `None`, will use default system device.
            And this is works only at first device initialization time!

        Args:
            name (`str`): Name or GUID of device.
        """

        logger.debug(f'Set native audio output device to {name}.')
        self.__native_instance.setAudioOutputDevice(name or '')

    @if_native_instance_created
    def restart_playout(self):
        """Start play current input file from start or just reload file audio device.

        Note:
            Device restart needed to apply new filename in tgcalls.
        """

        logger.debug(f'Restart native audio input device.')
        self.__native_instance.restartAudioInputDevice()

    @if_native_instance_created
    def restart_recording(self):
        """Start recording to output file from begin or just restart recording device.

        Note:
            Device restart needed to apply new filename in tgcalls.
        """

        logger.debug(f'Restart native audio output device.')
        self.__native_instance.restartAudioOutputDevice()

    # legacy below

    def print_available_playout_devices(self):
        """Print name and guid of available playout audio devices in system. Just helper method

        Note:
            You should use this method after calling .start()!
        """

        warnings.warn("It's a deprecated method. Use .get_recording_devices() instead", DeprecationWarning, 2)

        for device in self.get_playout_devices():
            print(f'Playout device \n name: {device.name} \n guid: {device.guid}')

    def print_available_recording_devices(self):
        """Print name and guid of available recording audio devices in system. Just helper method

        Note:
            You should use this method after calling .start()!
        """

        warnings.warn("It's a deprecated method. Use .get_playout_devices() instead", DeprecationWarning, 2)

        for device in self.get_recording_devices():
            print(f'Recording device \n name: {device.name} \n guid: {device.guid}')

Functions

def if_native_instance_created(func)
Expand source code
def if_native_instance_created(func):
    def wrapper(self, *args, **kwargs):
        if self.is_group_call_native_created():
            return func(self, *args, **kwargs)
        else:
            raise CallBeforeStartError("You can't use this method before calling .start()")

    return wrapper

Classes

class GroupCallNative (emit_join_payload_callback, network_state_updated_callback, enable_logs_to_console: bool, path_to_log_file: str, outgoing_audio_bitrate_kbit: int)

Create NativeInstance of tgcalls C++ part.

Args

enable_logs_to_console (bool): Is enable logs to stderr from tgcalls. path_to_log_file (str, optional): Path to log file for logs of tgcalls.

Expand source code
class GroupCallNative:
    def __init__(
        self,
        emit_join_payload_callback,
        network_state_updated_callback,
        enable_logs_to_console: bool,
        path_to_log_file: str,
        outgoing_audio_bitrate_kbit: int,
    ):
        """Create NativeInstance of tgcalls C++ part.

        Args:
            enable_logs_to_console (`bool`): Is enable logs to stderr from tgcalls.
            path_to_log_file (`str`, optional): Path to log file for logs of tgcalls.
        """

        # bypass None value
        if not path_to_log_file:
            path_to_log_file = ''

        logger.debug('Create a new native instance...')
        self.__native_instance = tgcalls.NativeInstance(enable_logs_to_console, path_to_log_file)

        self.__native_instance.setupGroupCall(
            emit_join_payload_callback,
            network_state_updated_callback,
            outgoing_audio_bitrate_kbit,
        )

        logger.debug('Native instance created.')

    def is_group_call_native_created(self):
        return self.__native_instance.isGroupCallNativeCreated()

    def _setup_and_start_group_call(self):
        raise NotImplementedError()

    @if_native_instance_created
    def _set_connection_mode(self, mode: tgcalls.GroupConnectionMode, keep_broadcast_if_was_enabled=False):
        logger.debug(f'Set native connection mode {mode}.')
        self.__native_instance.setConnectionMode(mode, keep_broadcast_if_was_enabled)

    @if_native_instance_created
    def _emit_join_payload(self, callback):
        logger.debug(f'Trigger native emit join payload.')
        self.__native_instance.emitJoinPayload(callback)

    def _start_native_group_call(self, *args):
        logger.debug('Start native group call...')
        self.__native_instance.startGroupCall(*args)

    @if_native_instance_created
    def _emit_join_payload(self, callback):
        logger.debug('Emit native join payload.')
        self.__native_instance.emitJoinPayload(callback)

    @if_native_instance_created
    def _set_join_response_payload(self, payload):
        logger.debug('Set native join response payload...')
        self.__native_instance.setJoinResponsePayload(payload)

    @if_native_instance_created
    def _set_is_mute(self, is_muted: bool):
        logger.debug(f'Set is muted on native instance side. New value: {is_muted}.')
        self.__native_instance.setIsMuted(is_muted)

    @if_native_instance_created
    def _set_volume(self, ssrc, volume):
        logger.debug(f'Set native volume for {ssrc} to {volume}.')
        self.__native_instance.setVolume(ssrc, volume)

    @if_native_instance_created
    def _stop_audio_device_module(self):
        logger.debug(f'Stop audio device module.')
        self.__native_instance.stopAudioDeviceModule()

    @if_native_instance_created
    def _start_audio_device_module(self):
        logger.debug(f'Start audio device module.')
        self.__native_instance.startAudioDeviceModule()

    @if_native_instance_created
    def get_playout_devices(self) -> List['tgcalls.AudioDevice']:
        """Get available playout audio devices in the system.

        Note:
            `tgcalls.AudioDevice` have 2 attributes: name, guid.
        """

        logger.debug('Get native playout devices.')
        return self.__native_instance.getPlayoutDevices()

    @if_native_instance_created
    def get_recording_devices(self) -> List['tgcalls.AudioDevice']:
        """Get available recording audio devices in the system.

        Note:
            `tgcalls.AudioDevice` have 2 attributes: name, guid.
        """

        logger.debug('Get native recording devices.')
        return self.__native_instance.getRecordingDevices()

    @if_native_instance_created
    def set_audio_input_device(self, name: Optional[str] = None):
        """Set audio input device.

        Note:
            If `name` is `None`, will use default system device.
            And this is works only at first device initialization time!

        Args:
            name (`str`): Name or GUID of device.
        """

        logger.debug(f'Set native audio input device to {name}.')
        self.__native_instance.setAudioInputDevice(name or '')

    @if_native_instance_created
    def set_audio_output_device(self, name: Optional[str] = None):
        """Set audio output device.

        Note:
            If `name` is `None`, will use default system device.
            And this is works only at first device initialization time!

        Args:
            name (`str`): Name or GUID of device.
        """

        logger.debug(f'Set native audio output device to {name}.')
        self.__native_instance.setAudioOutputDevice(name or '')

    @if_native_instance_created
    def restart_playout(self):
        """Start play current input file from start or just reload file audio device.

        Note:
            Device restart needed to apply new filename in tgcalls.
        """

        logger.debug(f'Restart native audio input device.')
        self.__native_instance.restartAudioInputDevice()

    @if_native_instance_created
    def restart_recording(self):
        """Start recording to output file from begin or just restart recording device.

        Note:
            Device restart needed to apply new filename in tgcalls.
        """

        logger.debug(f'Restart native audio output device.')
        self.__native_instance.restartAudioOutputDevice()

    # legacy below

    def print_available_playout_devices(self):
        """Print name and guid of available playout audio devices in system. Just helper method

        Note:
            You should use this method after calling .start()!
        """

        warnings.warn("It's a deprecated method. Use .get_recording_devices() instead", DeprecationWarning, 2)

        for device in self.get_playout_devices():
            print(f'Playout device \n name: {device.name} \n guid: {device.guid}')

    def print_available_recording_devices(self):
        """Print name and guid of available recording audio devices in system. Just helper method

        Note:
            You should use this method after calling .start()!
        """

        warnings.warn("It's a deprecated method. Use .get_playout_devices() instead", DeprecationWarning, 2)

        for device in self.get_recording_devices():
            print(f'Recording device \n name: {device.name} \n guid: {device.guid}')

Subclasses

Methods

def get_playout_devices(self, *args, **kwargs)
Expand source code
def wrapper(self, *args, **kwargs):
    if self.is_group_call_native_created():
        return func(self, *args, **kwargs)
    else:
        raise CallBeforeStartError("You can't use this method before calling .start()")
def get_recording_devices(self, *args, **kwargs)
Expand source code
def wrapper(self, *args, **kwargs):
    if self.is_group_call_native_created():
        return func(self, *args, **kwargs)
    else:
        raise CallBeforeStartError("You can't use this method before calling .start()")
def is_group_call_native_created(self)
Expand source code
def is_group_call_native_created(self):
    return self.__native_instance.isGroupCallNativeCreated()
def print_available_playout_devices(self)

Print name and guid of available playout audio devices in system. Just helper method

Note

You should use this method after calling .start()!

Expand source code
def print_available_playout_devices(self):
    """Print name and guid of available playout audio devices in system. Just helper method

    Note:
        You should use this method after calling .start()!
    """

    warnings.warn("It's a deprecated method. Use .get_recording_devices() instead", DeprecationWarning, 2)

    for device in self.get_playout_devices():
        print(f'Playout device \n name: {device.name} \n guid: {device.guid}')
def print_available_recording_devices(self)

Print name and guid of available recording audio devices in system. Just helper method

Note

You should use this method after calling .start()!

Expand source code
def print_available_recording_devices(self):
    """Print name and guid of available recording audio devices in system. Just helper method

    Note:
        You should use this method after calling .start()!
    """

    warnings.warn("It's a deprecated method. Use .get_playout_devices() instead", DeprecationWarning, 2)

    for device in self.get_recording_devices():
        print(f'Recording device \n name: {device.name} \n guid: {device.guid}')
def restart_playout(self, *args, **kwargs)
Expand source code
def wrapper(self, *args, **kwargs):
    if self.is_group_call_native_created():
        return func(self, *args, **kwargs)
    else:
        raise CallBeforeStartError("You can't use this method before calling .start()")
def restart_recording(self, *args, **kwargs)
Expand source code
def wrapper(self, *args, **kwargs):
    if self.is_group_call_native_created():
        return func(self, *args, **kwargs)
    else:
        raise CallBeforeStartError("You can't use this method before calling .start()")
def set_audio_input_device(self, *args, **kwargs)
Expand source code
def wrapper(self, *args, **kwargs):
    if self.is_group_call_native_created():
        return func(self, *args, **kwargs)
    else:
        raise CallBeforeStartError("You can't use this method before calling .start()")
def set_audio_output_device(self, *args, **kwargs)
Expand source code
def wrapper(self, *args, **kwargs):
    if self.is_group_call_native_created():
        return func(self, *args, **kwargs)
    else:
        raise CallBeforeStartError("You can't use this method before calling .start()")