py-tgcalls 2.1.1__py3-none-any.whl → 2.1.2b2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. {py_tgcalls-2.1.1.dist-info → py_tgcalls-2.1.2b2.dist-info}/METADATA +2 -2
  2. {py_tgcalls-2.1.1.dist-info → py_tgcalls-2.1.2b2.dist-info}/RECORD +38 -24
  3. {py_tgcalls-2.1.1.dist-info → py_tgcalls-2.1.2b2.dist-info}/WHEEL +1 -1
  4. pytgcalls/__version__.py +1 -1
  5. pytgcalls/exceptions.py +0 -7
  6. pytgcalls/methods/__init__.py +2 -0
  7. pytgcalls/methods/internal/__init__.py +35 -0
  8. pytgcalls/methods/internal/clear_cache.py +8 -0
  9. pytgcalls/methods/internal/clear_call.py +20 -0
  10. pytgcalls/methods/internal/connect_call.py +133 -0
  11. pytgcalls/methods/internal/emit_sig_data.py +9 -0
  12. pytgcalls/methods/internal/handle_connection_changed.py +27 -0
  13. pytgcalls/methods/internal/handle_mtproto_updates.py +175 -0
  14. pytgcalls/methods/internal/handle_stream_ended.py +23 -0
  15. pytgcalls/methods/internal/handle_stream_frame.py +41 -0
  16. pytgcalls/methods/internal/join_presentation.py +58 -0
  17. pytgcalls/methods/internal/request_broadcast_part.py +42 -0
  18. pytgcalls/methods/internal/request_broadcast_timestamp.py +25 -0
  19. pytgcalls/methods/internal/switch_connection.py +35 -0
  20. pytgcalls/methods/internal/update_status.py +22 -0
  21. pytgcalls/methods/stream/play.py +6 -107
  22. pytgcalls/methods/stream/record.py +0 -6
  23. pytgcalls/methods/utilities/__init__.py +0 -6
  24. pytgcalls/methods/utilities/start.py +23 -296
  25. pytgcalls/mtproto/bridged_client.py +28 -7
  26. pytgcalls/mtproto/client_cache.py +0 -1
  27. pytgcalls/mtproto/hydrogram_client.py +57 -0
  28. pytgcalls/mtproto/mtproto_client.py +33 -2
  29. pytgcalls/mtproto/pyrogram_client.py +61 -4
  30. pytgcalls/mtproto/telethon_client.py +57 -0
  31. pytgcalls/scaffold.py +79 -0
  32. pytgcalls/types/__init__.py +2 -0
  33. pytgcalls/types/calls/__init__.py +2 -0
  34. pytgcalls/types/calls/pending_connection.py +17 -0
  35. pytgcalls/methods/utilities/join_presentation.py +0 -50
  36. {py_tgcalls-2.1.1.dist-info → py_tgcalls-2.1.2b2.dist-info}/licenses/LICENSE +0 -0
  37. {py_tgcalls-2.1.1.dist-info → py_tgcalls-2.1.2b2.dist-info}/top_level.txt +0 -0
  38. /pytgcalls/methods/{utilities → internal}/log_retries.py +0 -0
  39. /pytgcalls/methods/{utilities → internal}/update_sources.py +0 -0
@@ -5,9 +5,11 @@ from typing import Optional
5
5
  from typing import Union
6
6
 
7
7
  import pyrogram
8
+ from ntgcalls import MediaSegmentQuality
8
9
  from ntgcalls import Protocol
9
10
  from pyrogram import Client
10
11
  from pyrogram import ContinuePropagation
12
+ from pyrogram.errors import FloodWait
11
13
  from pyrogram.raw.base import InputPeer
12
14
  from pyrogram.raw.base import InputUser
13
15
  from pyrogram.raw.functions.channels import GetFullChannel
@@ -19,6 +21,7 @@ from pyrogram.raw.functions.phone import CreateGroupCall
19
21
  from pyrogram.raw.functions.phone import DiscardCall
20
22
  from pyrogram.raw.functions.phone import EditGroupCallParticipant
21
23
  from pyrogram.raw.functions.phone import GetGroupCall
24
+ from pyrogram.raw.functions.phone import GetGroupCallStreamChannels
22
25
  from pyrogram.raw.functions.phone import GetGroupParticipants
23
26
  from pyrogram.raw.functions.phone import JoinGroupCall
24
27
  from pyrogram.raw.functions.phone import JoinGroupCallPresentation
@@ -26,6 +29,7 @@ from pyrogram.raw.functions.phone import LeaveGroupCall
26
29
  from pyrogram.raw.functions.phone import LeaveGroupCallPresentation
27
30
  from pyrogram.raw.functions.phone import RequestCall
28
31
  from pyrogram.raw.functions.phone import SendSignalingData
32
+ from pyrogram.raw.functions.upload import GetFile
29
33
  from pyrogram.raw.types import Channel
30
34
  from pyrogram.raw.types import ChannelForbidden
31
35
  from pyrogram.raw.types import Chat
@@ -35,6 +39,7 @@ from pyrogram.raw.types import GroupCall
35
39
  from pyrogram.raw.types import GroupCallDiscarded
36
40
  from pyrogram.raw.types import InputChannel
37
41
  from pyrogram.raw.types import InputGroupCall
42
+ from pyrogram.raw.types import InputGroupCallStream
38
43
  from pyrogram.raw.types import InputPeerChannel
39
44
  from pyrogram.raw.types import InputPhoneCall
40
45
  from pyrogram.raw.types import MessageActionChatDeleteUser
@@ -539,8 +544,8 @@ class PyrogramClient(BridgedClient):
539
544
  )
540
545
 
541
546
  async def create_group_call(
542
- self,
543
- chat_id: int,
547
+ self,
548
+ chat_id: int,
544
549
  ):
545
550
  result: Updates = await self._app.send(
546
551
  CreateGroupCall(
@@ -567,8 +572,8 @@ class PyrogramClient(BridgedClient):
567
572
  )
568
573
 
569
574
  async def leave_group_call(
570
- self,
571
- chat_id: int,
575
+ self,
576
+ chat_id: int,
572
577
  ):
573
578
  chat_call = await self._cache.get_full_chat(chat_id)
574
579
  if chat_call is not None:
@@ -620,6 +625,58 @@ class PyrogramClient(BridgedClient):
620
625
  ),
621
626
  )
622
627
 
628
+ async def download_stream(
629
+ self,
630
+ chat_id: int,
631
+ timestamp: int,
632
+ limit: int,
633
+ video_channel: Optional[int],
634
+ video_quality: MediaSegmentQuality,
635
+ ):
636
+ chat_call = await self._cache.get_full_chat(chat_id)
637
+ if chat_call is not None:
638
+ try:
639
+ return (
640
+ await self._app.send(
641
+ GetFile(
642
+ location=InputGroupCallStream(
643
+ call=chat_call,
644
+ time_ms=timestamp,
645
+ scale=0,
646
+ video_channel=video_channel,
647
+ video_quality=BridgedClient.parse_quality(
648
+ video_quality,
649
+ ),
650
+ ),
651
+ offset=0,
652
+ limit=limit,
653
+ ),
654
+ sleep_threshold=0,
655
+ )
656
+ ).bytes
657
+ except FloodWait:
658
+ pass
659
+ return None
660
+
661
+ async def get_stream_timestamp(
662
+ self,
663
+ chat_id: int,
664
+ ):
665
+ chat_call = await self._cache.get_full_chat(chat_id)
666
+ if chat_call is not None:
667
+ # noinspection PyBroadException
668
+ channels = (
669
+ await self._app.send(
670
+ GetGroupCallStreamChannels(
671
+ call=chat_call,
672
+ ),
673
+ )
674
+ ).channels
675
+ if len(channels) > 0:
676
+ return channels[0].last_timestamp_ms
677
+
678
+ return 0
679
+
623
680
  async def set_call_status(
624
681
  self,
625
682
  chat_id: int,
@@ -3,9 +3,11 @@ from typing import List
3
3
  from typing import Optional
4
4
  from typing import Union
5
5
 
6
+ from ntgcalls import MediaSegmentQuality
6
7
  from ntgcalls import Protocol
7
8
  from telethon import TelegramClient
8
9
  from telethon.errors import ChannelPrivateError
10
+ from telethon.errors import FloodWaitError
9
11
  from telethon.events import Raw
10
12
  from telethon.tl.functions.channels import GetFullChannelRequest
11
13
  from telethon.tl.functions.messages import GetDhConfigRequest
@@ -16,6 +18,7 @@ from telethon.tl.functions.phone import CreateGroupCallRequest
16
18
  from telethon.tl.functions.phone import DiscardCallRequest
17
19
  from telethon.tl.functions.phone import EditGroupCallParticipantRequest
18
20
  from telethon.tl.functions.phone import GetGroupCallRequest
21
+ from telethon.tl.functions.phone import GetGroupCallStreamChannelsRequest
19
22
  from telethon.tl.functions.phone import GetGroupParticipantsRequest
20
23
  from telethon.tl.functions.phone import JoinGroupCallPresentationRequest
21
24
  from telethon.tl.functions.phone import JoinGroupCallRequest
@@ -23,12 +26,14 @@ from telethon.tl.functions.phone import LeaveGroupCallPresentationRequest
23
26
  from telethon.tl.functions.phone import LeaveGroupCallRequest
24
27
  from telethon.tl.functions.phone import RequestCallRequest
25
28
  from telethon.tl.functions.phone import SendSignalingDataRequest
29
+ from telethon.tl.functions.upload import GetFileRequest
26
30
  from telethon.tl.types import ChatForbidden
27
31
  from telethon.tl.types import DataJSON
28
32
  from telethon.tl.types import GroupCall
29
33
  from telethon.tl.types import GroupCallDiscarded
30
34
  from telethon.tl.types import InputChannel
31
35
  from telethon.tl.types import InputGroupCall
36
+ from telethon.tl.types import InputGroupCallStream
32
37
  from telethon.tl.types import InputPeerChannel
33
38
  from telethon.tl.types import InputPhoneCall
34
39
  from telethon.tl.types import MessageActionChatDeleteUser
@@ -604,6 +609,58 @@ class TelethonClient(BridgedClient):
604
609
  ),
605
610
  )
606
611
 
612
+ async def download_stream(
613
+ self,
614
+ chat_id: int,
615
+ timestamp: int,
616
+ limit: int,
617
+ video_channel: Optional[int],
618
+ video_quality: MediaSegmentQuality,
619
+ ):
620
+ chat_call = await self._cache.get_full_chat(chat_id)
621
+ if chat_call is not None:
622
+ try:
623
+ return (
624
+ await self._app(
625
+ GetFileRequest(
626
+ location=InputGroupCallStream(
627
+ call=chat_call,
628
+ time_ms=timestamp,
629
+ scale=0,
630
+ video_channel=video_channel,
631
+ video_quality=BridgedClient.parse_quality(
632
+ video_quality,
633
+ ),
634
+ ),
635
+ offset=0,
636
+ limit=limit,
637
+ ),
638
+ flood_sleep_threshold=0,
639
+ )
640
+ ).bytes
641
+ except FloodWaitError:
642
+ pass
643
+ return None
644
+
645
+ async def get_stream_timestamp(
646
+ self,
647
+ chat_id: int,
648
+ ):
649
+ chat_call = await self._cache.get_full_chat(chat_id)
650
+ if chat_call is not None:
651
+ # noinspection PyBroadException
652
+ channels = (
653
+ await self._app(
654
+ GetGroupCallStreamChannelsRequest(
655
+ call=chat_call,
656
+ ),
657
+ )
658
+ ).channels
659
+ if len(channels) > 0:
660
+ return channels[0].last_timestamp_ms
661
+
662
+ return 0
663
+
607
664
  async def set_call_status(
608
665
  self,
609
666
  chat_id: int,
pytgcalls/scaffold.py CHANGED
@@ -1,6 +1,20 @@
1
+ from typing import List
2
+ from typing import Optional
1
3
  from typing import Union
2
4
 
5
+ from ntgcalls import Frame as RawFrame
6
+ from ntgcalls import MediaDescription
7
+ from ntgcalls import MediaState
8
+ from ntgcalls import NetworkInfo
9
+ from ntgcalls import SegmentPartRequest
10
+ from ntgcalls import StreamDevice
11
+ from ntgcalls import StreamMode
12
+ from ntgcalls import StreamType
13
+
3
14
  from .handlers import HandlersHolder
15
+ from .types import CallConfig
16
+ from .types import GroupCallConfig
17
+ from .types import Update
4
18
 
5
19
 
6
20
  class Scaffold(HandlersHolder):
@@ -25,6 +39,7 @@ class Scaffold(HandlersHolder):
25
39
  self._call_sources = dict()
26
40
  self._wait_connect = dict()
27
41
  self._presentations = set()
42
+ self._pending_connections = dict()
28
43
 
29
44
  def _handle_mtproto(self):
30
45
  pass
@@ -47,9 +62,73 @@ class Scaffold(HandlersHolder):
47
62
  async def _join_presentation(self, chat_id: Union[int, str], join: bool):
48
63
  pass
49
64
 
65
+ async def _clear_call(self, chat_id: int):
66
+ pass
67
+
68
+ async def _update_status(self, chat_id: int, state: MediaState):
69
+ pass
70
+
71
+ async def _switch_connection(self, chat_id: int):
72
+ pass
73
+
74
+ async def _handle_stream_ended(
75
+ self,
76
+ chat_id: int,
77
+ stream_type: StreamType,
78
+ device: StreamDevice,
79
+ ):
80
+ pass
81
+
82
+ async def _emit_sig_data(self, chat_id: int, data: bytes):
83
+ pass
84
+
85
+ async def _request_broadcast_timestamp(
86
+ self,
87
+ chat_id: int,
88
+ ):
89
+ pass
90
+
91
+ async def _request_broadcast_part(
92
+ self,
93
+ chat_id: int,
94
+ part_request: SegmentPartRequest,
95
+ ):
96
+ pass
97
+
98
+ async def _handle_stream_frame(
99
+ self,
100
+ chat_id: int,
101
+ mode: StreamMode,
102
+ device: StreamDevice,
103
+ frames: List[RawFrame],
104
+ ):
105
+ pass
106
+
107
+ async def _handle_connection_changed(
108
+ self,
109
+ chat_id: int,
110
+ net_state: NetworkInfo,
111
+ ):
112
+ pass
113
+
114
+ async def _handle_mtproto_updates(self, update: Update):
115
+ pass
116
+
117
+ async def _connect_call(
118
+ self,
119
+ chat_id: int,
120
+ media_description: MediaDescription,
121
+ config: Union[CallConfig, GroupCallConfig],
122
+ payload: Optional[str],
123
+ ):
124
+ pass
125
+
50
126
  @staticmethod
51
127
  def _log_retries(r: int):
52
128
  pass
53
129
 
130
+ def _clear_cache(self, chat_id: int):
131
+ pass
132
+
54
133
  def on_update(self, filters=None):
55
134
  pass
@@ -5,6 +5,7 @@ from .calls import CallConfig
5
5
  from .calls import CallData
6
6
  from .calls import CallProtocol
7
7
  from .calls import GroupCallConfig
8
+ from .calls import PendingConnection
8
9
  from .calls import RawCallUpdate
9
10
  from .chats import ChatUpdate
10
11
  from .chats import GroupCallParticipant
@@ -35,6 +36,7 @@ __all__ = (
35
36
  'CallProtocol',
36
37
  'CallData',
37
38
  'RawCallUpdate',
39
+ 'PendingConnection',
38
40
  'GroupCallConfig',
39
41
  'GroupCallParticipant',
40
42
  'RecordStream',
@@ -4,6 +4,7 @@ from .call_data import CallData
4
4
  from .call_protocol import CallProtocol
5
5
  from .call_sources import CallSources
6
6
  from .group_call_config import GroupCallConfig
7
+ from .pending_connection import PendingConnection
7
8
  from .raw_call_update import RawCallUpdate
8
9
 
9
10
  __all__ = (
@@ -13,5 +14,6 @@ __all__ = (
13
14
  'CallProtocol',
14
15
  'CallSources',
15
16
  'GroupCallConfig',
17
+ 'PendingConnection',
16
18
  'RawCallUpdate',
17
19
  )
@@ -0,0 +1,17 @@
1
+ from ntgcalls import MediaDescription
2
+
3
+ from .group_call_config import GroupCallConfig
4
+
5
+
6
+ class PendingConnection:
7
+ def __init__(
8
+ self,
9
+ media_description: MediaDescription,
10
+ config: GroupCallConfig,
11
+ payload: str,
12
+ presentation: bool,
13
+ ):
14
+ self.media_description = media_description
15
+ self.config = config
16
+ self.payload = payload
17
+ self.presentation = presentation
@@ -1,50 +0,0 @@
1
- from typing import Union
2
-
3
- from ntgcalls import ConnectionError
4
- from ntgcalls import TelegramServerError
5
-
6
- from ...scaffold import Scaffold
7
-
8
-
9
- class JoinPresentation(Scaffold):
10
- async def _join_presentation(
11
- self,
12
- chat_id: Union[int, str],
13
- join: bool,
14
- ):
15
- if join:
16
- if chat_id in self._presentations:
17
- return
18
- for retries in range(4):
19
- try:
20
- self._wait_connect[
21
- chat_id
22
- ] = self.loop.create_future()
23
- payload = await self._binding.init_presentation(
24
- chat_id,
25
- )
26
- result_params = await self._app.join_presentation(
27
- chat_id,
28
- payload,
29
- )
30
- await self._binding.connect(
31
- chat_id,
32
- result_params,
33
- True,
34
- )
35
- await self._wait_connect[chat_id]
36
- self._presentations.add(chat_id)
37
- break
38
- except TelegramServerError:
39
- if retries == 3:
40
- raise
41
- self._log_retries(retries)
42
- finally:
43
- self._wait_connect.pop(chat_id, None)
44
- elif chat_id in self._presentations:
45
- try:
46
- await self._binding.stop_presentation(chat_id)
47
- await self._app.leave_presentation(chat_id)
48
- except ConnectionError:
49
- pass
50
- self._presentations.discard(chat_id)