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.
- {py_tgcalls-2.1.1.dist-info → py_tgcalls-2.1.2b2.dist-info}/METADATA +2 -2
- {py_tgcalls-2.1.1.dist-info → py_tgcalls-2.1.2b2.dist-info}/RECORD +38 -24
- {py_tgcalls-2.1.1.dist-info → py_tgcalls-2.1.2b2.dist-info}/WHEEL +1 -1
- pytgcalls/__version__.py +1 -1
- pytgcalls/exceptions.py +0 -7
- pytgcalls/methods/__init__.py +2 -0
- pytgcalls/methods/internal/__init__.py +35 -0
- pytgcalls/methods/internal/clear_cache.py +8 -0
- pytgcalls/methods/internal/clear_call.py +20 -0
- pytgcalls/methods/internal/connect_call.py +133 -0
- pytgcalls/methods/internal/emit_sig_data.py +9 -0
- pytgcalls/methods/internal/handle_connection_changed.py +27 -0
- pytgcalls/methods/internal/handle_mtproto_updates.py +175 -0
- pytgcalls/methods/internal/handle_stream_ended.py +23 -0
- pytgcalls/methods/internal/handle_stream_frame.py +41 -0
- pytgcalls/methods/internal/join_presentation.py +58 -0
- pytgcalls/methods/internal/request_broadcast_part.py +42 -0
- pytgcalls/methods/internal/request_broadcast_timestamp.py +25 -0
- pytgcalls/methods/internal/switch_connection.py +35 -0
- pytgcalls/methods/internal/update_status.py +22 -0
- pytgcalls/methods/stream/play.py +6 -107
- pytgcalls/methods/stream/record.py +0 -6
- pytgcalls/methods/utilities/__init__.py +0 -6
- pytgcalls/methods/utilities/start.py +23 -296
- pytgcalls/mtproto/bridged_client.py +28 -7
- pytgcalls/mtproto/client_cache.py +0 -1
- pytgcalls/mtproto/hydrogram_client.py +57 -0
- pytgcalls/mtproto/mtproto_client.py +33 -2
- pytgcalls/mtproto/pyrogram_client.py +61 -4
- pytgcalls/mtproto/telethon_client.py +57 -0
- pytgcalls/scaffold.py +79 -0
- pytgcalls/types/__init__.py +2 -0
- pytgcalls/types/calls/__init__.py +2 -0
- pytgcalls/types/calls/pending_connection.py +17 -0
- pytgcalls/methods/utilities/join_presentation.py +0 -50
- {py_tgcalls-2.1.1.dist-info → py_tgcalls-2.1.2b2.dist-info}/licenses/LICENSE +0 -0
- {py_tgcalls-2.1.1.dist-info → py_tgcalls-2.1.2b2.dist-info}/top_level.txt +0 -0
- /pytgcalls/methods/{utilities → internal}/log_retries.py +0 -0
- /pytgcalls/methods/{utilities → internal}/update_sources.py +0 -0
|
@@ -1,309 +1,19 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import logging
|
|
3
|
-
from typing import List
|
|
4
3
|
|
|
5
|
-
from ntgcalls import ConnectionError
|
|
6
|
-
from ntgcalls import ConnectionNotFound
|
|
7
|
-
from ntgcalls import ConnectionState
|
|
8
|
-
from ntgcalls import Frame as RawFrame
|
|
9
|
-
from ntgcalls import MediaState
|
|
10
|
-
from ntgcalls import NetworkInfo
|
|
11
|
-
from ntgcalls import StreamDevice
|
|
12
|
-
from ntgcalls import StreamMode
|
|
13
|
-
from ntgcalls import StreamType
|
|
14
|
-
from ntgcalls import TelegramServerError
|
|
15
|
-
|
|
16
|
-
from ...exceptions import CallBusy
|
|
17
|
-
from ...exceptions import CallDeclined
|
|
18
|
-
from ...exceptions import CallDiscarded
|
|
19
4
|
from ...exceptions import PyTgCallsAlreadyRunning
|
|
20
|
-
from ...mtproto import BridgedClient
|
|
21
5
|
from ...pytgcalls_session import PyTgCallsSession
|
|
22
6
|
from ...scaffold import Scaffold
|
|
23
|
-
from ...types import CallData
|
|
24
|
-
from ...types import ChatUpdate
|
|
25
|
-
from ...types import Device
|
|
26
|
-
from ...types import Direction
|
|
27
|
-
from ...types import Frame
|
|
28
|
-
from ...types import GroupCallParticipant
|
|
29
|
-
from ...types import RawCallUpdate
|
|
30
|
-
from ...types import StreamEnded
|
|
31
|
-
from ...types import StreamFrames
|
|
32
|
-
from ...types import Update
|
|
33
|
-
from ...types import UpdatedGroupCallParticipant
|
|
34
7
|
|
|
35
8
|
py_logger = logging.getLogger('pytgcalls')
|
|
36
9
|
|
|
37
10
|
|
|
38
11
|
class Start(Scaffold):
|
|
39
12
|
async def start(self):
|
|
40
|
-
|
|
41
|
-
@self._app.on_update()
|
|
42
|
-
async def update_handler(update: Update):
|
|
43
|
-
chat_id = update.chat_id
|
|
44
|
-
if update.chat_id in self._p2p_configs:
|
|
45
|
-
p2p_config = self._p2p_configs[chat_id]
|
|
46
|
-
if not p2p_config.wait_data.done():
|
|
47
|
-
if isinstance(update, RawCallUpdate):
|
|
48
|
-
if update.status & RawCallUpdate.Type.UPDATED_CALL:
|
|
49
|
-
p2p_config.wait_data.set_result(
|
|
50
|
-
update,
|
|
51
|
-
)
|
|
52
|
-
if isinstance(update, ChatUpdate) and \
|
|
53
|
-
p2p_config.outgoing:
|
|
54
|
-
if update.status & ChatUpdate.Status.DISCARDED_CALL:
|
|
55
|
-
self._wait_connect.pop(chat_id, None)
|
|
56
|
-
p2p_config.wait_data.set_exception(
|
|
57
|
-
CallBusy(
|
|
58
|
-
chat_id,
|
|
59
|
-
) if update.status &
|
|
60
|
-
ChatUpdate.Status.BUSY_CALL else
|
|
61
|
-
CallDeclined(
|
|
62
|
-
chat_id,
|
|
63
|
-
),
|
|
64
|
-
)
|
|
65
|
-
if chat_id in self._wait_connect and \
|
|
66
|
-
not self._wait_connect[chat_id].done():
|
|
67
|
-
if isinstance(update, ChatUpdate):
|
|
68
|
-
if update.status & ChatUpdate.Status.DISCARDED_CALL:
|
|
69
|
-
self._wait_connect[chat_id].set_exception(
|
|
70
|
-
CallDiscarded(
|
|
71
|
-
chat_id,
|
|
72
|
-
),
|
|
73
|
-
)
|
|
74
|
-
if isinstance(update, RawCallUpdate):
|
|
75
|
-
if update.status & RawCallUpdate.Type.REQUESTED:
|
|
76
|
-
self._p2p_configs[chat_id] = CallData(
|
|
77
|
-
await self._app.get_dhc(),
|
|
78
|
-
self.loop,
|
|
79
|
-
update.g_a_or_b,
|
|
80
|
-
)
|
|
81
|
-
update = ChatUpdate(
|
|
82
|
-
chat_id,
|
|
83
|
-
ChatUpdate.Status.INCOMING_CALL,
|
|
84
|
-
)
|
|
85
|
-
if isinstance(update, RawCallUpdate):
|
|
86
|
-
if update.status & RawCallUpdate.Type.SIGNALING_DATA:
|
|
87
|
-
try:
|
|
88
|
-
await self._binding.send_signaling(
|
|
89
|
-
update.chat_id,
|
|
90
|
-
update.signaling_data,
|
|
91
|
-
)
|
|
92
|
-
except (ConnectionNotFound, ConnectionError):
|
|
93
|
-
pass
|
|
94
|
-
if isinstance(update, ChatUpdate):
|
|
95
|
-
if update.status & ChatUpdate.Status.LEFT_CALL:
|
|
96
|
-
await clear_call(chat_id)
|
|
97
|
-
if isinstance(update, UpdatedGroupCallParticipant):
|
|
98
|
-
participant = update.participant
|
|
99
|
-
action = participant.action
|
|
100
|
-
chat_peer = self._cache_user_peer.get(chat_id)
|
|
101
|
-
user_id = participant.user_id
|
|
102
|
-
if chat_id in self._call_sources:
|
|
103
|
-
call_sources = self._call_sources[chat_id]
|
|
104
|
-
was_camera = user_id in call_sources.camera
|
|
105
|
-
was_screen = user_id in call_sources.presentation
|
|
106
|
-
|
|
107
|
-
if was_camera != participant.video_camera:
|
|
108
|
-
if participant.video_info:
|
|
109
|
-
self._call_sources[chat_id].camera[
|
|
110
|
-
user_id
|
|
111
|
-
] = participant.video_info.endpoint
|
|
112
|
-
try:
|
|
113
|
-
await self._binding.add_incoming_video(
|
|
114
|
-
chat_id,
|
|
115
|
-
participant.video_info.endpoint,
|
|
116
|
-
participant.video_info.sources,
|
|
117
|
-
)
|
|
118
|
-
except (ConnectionNotFound, ConnectionError):
|
|
119
|
-
pass
|
|
120
|
-
elif user_id in self._call_sources[chat_id].camera:
|
|
121
|
-
try:
|
|
122
|
-
await self._binding.remove_incoming_video(
|
|
123
|
-
chat_id,
|
|
124
|
-
self._call_sources[
|
|
125
|
-
chat_id
|
|
126
|
-
].camera[user_id],
|
|
127
|
-
)
|
|
128
|
-
except (ConnectionNotFound, ConnectionError):
|
|
129
|
-
pass
|
|
130
|
-
self._call_sources[chat_id].camera.pop(
|
|
131
|
-
user_id, None,
|
|
132
|
-
)
|
|
133
|
-
|
|
134
|
-
if was_screen != participant.screen_sharing:
|
|
135
|
-
if participant.presentation_info:
|
|
136
|
-
self._call_sources[chat_id].presentation[
|
|
137
|
-
user_id
|
|
138
|
-
] = participant.presentation_info.endpoint
|
|
139
|
-
try:
|
|
140
|
-
await self._binding.add_incoming_video(
|
|
141
|
-
chat_id,
|
|
142
|
-
participant.presentation_info.endpoint,
|
|
143
|
-
participant.presentation_info.sources,
|
|
144
|
-
)
|
|
145
|
-
except (ConnectionNotFound, ConnectionError):
|
|
146
|
-
pass
|
|
147
|
-
elif user_id in self._call_sources[
|
|
148
|
-
chat_id
|
|
149
|
-
].presentation:
|
|
150
|
-
try:
|
|
151
|
-
await self._binding.remove_incoming_video(
|
|
152
|
-
chat_id,
|
|
153
|
-
self._call_sources[
|
|
154
|
-
chat_id
|
|
155
|
-
].presentation[user_id],
|
|
156
|
-
)
|
|
157
|
-
except (ConnectionNotFound, ConnectionError):
|
|
158
|
-
pass
|
|
159
|
-
self._call_sources[chat_id].presentation.pop(
|
|
160
|
-
user_id, None,
|
|
161
|
-
)
|
|
162
|
-
|
|
163
|
-
if chat_peer:
|
|
164
|
-
is_self = BridgedClient.chat_id(
|
|
165
|
-
chat_peer,
|
|
166
|
-
) == participant.user_id if chat_peer else False
|
|
167
|
-
if is_self:
|
|
168
|
-
if action == GroupCallParticipant.Action.LEFT:
|
|
169
|
-
if await clear_call(chat_id):
|
|
170
|
-
await self._propagate(
|
|
171
|
-
ChatUpdate(
|
|
172
|
-
chat_id,
|
|
173
|
-
ChatUpdate.Status.KICKED,
|
|
174
|
-
),
|
|
175
|
-
self,
|
|
176
|
-
)
|
|
177
|
-
if (
|
|
178
|
-
chat_id in self._need_unmute and
|
|
179
|
-
action == GroupCallParticipant.Action.UPDATED and
|
|
180
|
-
not participant.muted_by_admin
|
|
181
|
-
):
|
|
182
|
-
try:
|
|
183
|
-
await update_status(
|
|
184
|
-
chat_id,
|
|
185
|
-
await self._binding.get_state(chat_id),
|
|
186
|
-
)
|
|
187
|
-
except ConnectionNotFound:
|
|
188
|
-
pass
|
|
189
|
-
|
|
190
|
-
if (
|
|
191
|
-
participant.muted_by_admin and
|
|
192
|
-
action != GroupCallParticipant.Action.LEFT
|
|
193
|
-
):
|
|
194
|
-
self._need_unmute.add(chat_id)
|
|
195
|
-
else:
|
|
196
|
-
self._need_unmute.discard(chat_id)
|
|
197
|
-
if not isinstance(update, RawCallUpdate):
|
|
198
|
-
await self._propagate(
|
|
199
|
-
update,
|
|
200
|
-
self,
|
|
201
|
-
)
|
|
202
|
-
|
|
203
|
-
async def clear_call(chat_id) -> bool:
|
|
204
|
-
res = False
|
|
205
|
-
if chat_id in self._wait_connect:
|
|
206
|
-
self._wait_connect[chat_id].set_exception(
|
|
207
|
-
TelegramServerError(),
|
|
208
|
-
)
|
|
209
|
-
try:
|
|
210
|
-
await self._binding.stop(chat_id)
|
|
211
|
-
res = True
|
|
212
|
-
except ConnectionNotFound:
|
|
213
|
-
pass
|
|
214
|
-
await clear_cache(chat_id)
|
|
215
|
-
return res
|
|
216
|
-
|
|
217
|
-
async def update_status(chat_id: int, state: MediaState):
|
|
218
|
-
try:
|
|
219
|
-
await self._app.set_call_status(
|
|
220
|
-
chat_id,
|
|
221
|
-
state.muted,
|
|
222
|
-
state.video_paused,
|
|
223
|
-
state.video_stopped,
|
|
224
|
-
state.presentation_paused,
|
|
225
|
-
self._cache_user_peer.get(chat_id),
|
|
226
|
-
)
|
|
227
|
-
except Exception as e:
|
|
228
|
-
py_logger.debug(f'SetVideoCallStatus: {e}')
|
|
229
|
-
|
|
230
|
-
async def stream_ended(
|
|
231
|
-
chat_id: int,
|
|
232
|
-
stream_type: StreamType,
|
|
233
|
-
device: StreamDevice,
|
|
234
|
-
):
|
|
235
|
-
await self._propagate(
|
|
236
|
-
StreamEnded(
|
|
237
|
-
chat_id,
|
|
238
|
-
StreamEnded.Type.from_raw(stream_type),
|
|
239
|
-
Device.from_raw(device),
|
|
240
|
-
),
|
|
241
|
-
self,
|
|
242
|
-
)
|
|
243
|
-
|
|
244
|
-
async def emit_sig_data(chat_id: int, data: bytes):
|
|
245
|
-
try:
|
|
246
|
-
await self._app.send_signaling(
|
|
247
|
-
chat_id,
|
|
248
|
-
data,
|
|
249
|
-
)
|
|
250
|
-
except (ConnectionError, ConnectionNotFound):
|
|
251
|
-
pass
|
|
252
|
-
|
|
253
|
-
async def stream_frame(
|
|
254
|
-
chat_id: int,
|
|
255
|
-
mode: StreamMode,
|
|
256
|
-
device: StreamDevice,
|
|
257
|
-
frames: List[RawFrame],
|
|
258
|
-
):
|
|
259
|
-
await self._propagate(
|
|
260
|
-
StreamFrames(
|
|
261
|
-
chat_id,
|
|
262
|
-
Direction.from_raw(mode),
|
|
263
|
-
Device.from_raw(device),
|
|
264
|
-
[
|
|
265
|
-
Frame(
|
|
266
|
-
x.ssrc,
|
|
267
|
-
x.data,
|
|
268
|
-
Frame.Info(
|
|
269
|
-
x.frame_data.absolute_capture_timestamp_ms,
|
|
270
|
-
x.frame_data.width,
|
|
271
|
-
x.frame_data.height,
|
|
272
|
-
x.frame_data.rotation,
|
|
273
|
-
),
|
|
274
|
-
) for x in frames
|
|
275
|
-
],
|
|
276
|
-
),
|
|
277
|
-
self,
|
|
278
|
-
)
|
|
279
|
-
|
|
280
|
-
async def connection_changed(
|
|
281
|
-
chat_id: int,
|
|
282
|
-
net_state: NetworkInfo,
|
|
283
|
-
):
|
|
284
|
-
state = net_state.state
|
|
285
|
-
if state == ConnectionState.CONNECTING:
|
|
286
|
-
return
|
|
287
|
-
if chat_id in self._wait_connect:
|
|
288
|
-
if state == ConnectionState.CONNECTED:
|
|
289
|
-
self._wait_connect[chat_id].set_result(None)
|
|
290
|
-
else:
|
|
291
|
-
self._wait_connect[chat_id].set_exception(
|
|
292
|
-
TelegramServerError(),
|
|
293
|
-
)
|
|
294
|
-
elif state != ConnectionState.CONNECTED:
|
|
295
|
-
if chat_id > 0:
|
|
296
|
-
await self._app.discard_call(chat_id)
|
|
297
|
-
await clear_cache(chat_id)
|
|
298
|
-
|
|
299
|
-
async def clear_cache(chat_id: int):
|
|
300
|
-
self._p2p_configs.pop(chat_id, None)
|
|
301
|
-
self._cache_user_peer.pop(chat_id)
|
|
302
|
-
self._need_unmute.discard(chat_id)
|
|
303
|
-
|
|
304
13
|
if not self._is_running:
|
|
305
14
|
self._is_running = True
|
|
306
15
|
self._env_checker.check_environment()
|
|
16
|
+
self._app.add_handler(self._handle_mtproto_updates)
|
|
307
17
|
if not self._app.is_connected:
|
|
308
18
|
await self._app.start()
|
|
309
19
|
|
|
@@ -324,33 +34,33 @@ class Start(Scaffold):
|
|
|
324
34
|
self._binding.on_stream_end(
|
|
325
35
|
lambda chat_id, stream_type, device:
|
|
326
36
|
asyncio.run_coroutine_threadsafe(
|
|
327
|
-
|
|
37
|
+
self._handle_stream_ended(chat_id, stream_type, device),
|
|
328
38
|
self.loop,
|
|
329
39
|
),
|
|
330
40
|
)
|
|
331
41
|
self._binding.on_upgrade(
|
|
332
42
|
lambda chat_id, state:
|
|
333
43
|
asyncio.run_coroutine_threadsafe(
|
|
334
|
-
|
|
44
|
+
self._update_status(chat_id, state),
|
|
335
45
|
self.loop,
|
|
336
46
|
),
|
|
337
47
|
)
|
|
338
48
|
self._binding.on_connection_change(
|
|
339
49
|
lambda chat_id, net_state: asyncio.run_coroutine_threadsafe(
|
|
340
|
-
|
|
50
|
+
self._handle_connection_changed(chat_id, net_state),
|
|
341
51
|
self.loop,
|
|
342
52
|
),
|
|
343
53
|
)
|
|
344
54
|
self._binding.on_signaling(
|
|
345
55
|
lambda chat_id, data: asyncio.run_coroutine_threadsafe(
|
|
346
|
-
|
|
56
|
+
self._emit_sig_data(chat_id, data),
|
|
347
57
|
self.loop,
|
|
348
58
|
),
|
|
349
59
|
)
|
|
350
60
|
self._binding.on_frames(
|
|
351
61
|
lambda chat_id, mode, device, frames:
|
|
352
62
|
asyncio.run_coroutine_threadsafe(
|
|
353
|
-
|
|
63
|
+
self._handle_stream_frame(
|
|
354
64
|
chat_id,
|
|
355
65
|
mode,
|
|
356
66
|
device,
|
|
@@ -359,6 +69,23 @@ class Start(Scaffold):
|
|
|
359
69
|
self.loop,
|
|
360
70
|
),
|
|
361
71
|
)
|
|
72
|
+
self._binding.on_request_broadcast_part(
|
|
73
|
+
lambda chat_id, part_request:
|
|
74
|
+
asyncio.run_coroutine_threadsafe(
|
|
75
|
+
self._request_broadcast_part(
|
|
76
|
+
chat_id,
|
|
77
|
+
part_request,
|
|
78
|
+
),
|
|
79
|
+
self.loop,
|
|
80
|
+
),
|
|
81
|
+
)
|
|
82
|
+
self._binding.on_request_broadcast_timestamp(
|
|
83
|
+
lambda chat_id:
|
|
84
|
+
asyncio.run_coroutine_threadsafe(
|
|
85
|
+
self._request_broadcast_timestamp(chat_id),
|
|
86
|
+
self.loop,
|
|
87
|
+
),
|
|
88
|
+
)
|
|
362
89
|
await PyTgCallsSession().start()
|
|
363
90
|
else:
|
|
364
91
|
raise PyTgCallsAlreadyRunning()
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import random
|
|
2
2
|
from typing import Any
|
|
3
|
-
from typing import Callable
|
|
4
3
|
from typing import List
|
|
5
4
|
from typing import Optional
|
|
6
5
|
|
|
6
|
+
from ntgcalls import MediaSegmentQuality
|
|
7
7
|
from ntgcalls import Protocol
|
|
8
8
|
from ntgcalls import RTCServer
|
|
9
9
|
from ntgcalls import SsrcGroup
|
|
@@ -109,6 +109,22 @@ class BridgedClient(HandlersHolder):
|
|
|
109
109
|
):
|
|
110
110
|
pass
|
|
111
111
|
|
|
112
|
+
async def download_stream(
|
|
113
|
+
self,
|
|
114
|
+
chat_id: int,
|
|
115
|
+
timestamp: int,
|
|
116
|
+
limit: int,
|
|
117
|
+
video_channel: Optional[int],
|
|
118
|
+
video_quality: MediaSegmentQuality,
|
|
119
|
+
):
|
|
120
|
+
pass
|
|
121
|
+
|
|
122
|
+
async def get_stream_timestamp(
|
|
123
|
+
self,
|
|
124
|
+
chat_id: int,
|
|
125
|
+
):
|
|
126
|
+
pass
|
|
127
|
+
|
|
112
128
|
async def set_call_status(
|
|
113
129
|
self,
|
|
114
130
|
chat_id: int,
|
|
@@ -233,6 +249,17 @@ class BridgedClient(HandlersHolder):
|
|
|
233
249
|
for server in servers
|
|
234
250
|
]
|
|
235
251
|
|
|
252
|
+
@staticmethod
|
|
253
|
+
def parse_quality(quality: MediaSegmentQuality) -> Optional[int]:
|
|
254
|
+
if quality == MediaSegmentQuality.THUMBNAIL:
|
|
255
|
+
return 0
|
|
256
|
+
elif quality == MediaSegmentQuality.MEDIUM:
|
|
257
|
+
return 1
|
|
258
|
+
elif quality == MediaSegmentQuality.FULL:
|
|
259
|
+
return 2
|
|
260
|
+
else:
|
|
261
|
+
return None
|
|
262
|
+
|
|
236
263
|
@staticmethod
|
|
237
264
|
def rnd_id() -> int:
|
|
238
265
|
return random.randint(0, 0x7FFFFFFF - 1)
|
|
@@ -240,12 +267,6 @@ class BridgedClient(HandlersHolder):
|
|
|
240
267
|
async def get_dhc(self):
|
|
241
268
|
pass
|
|
242
269
|
|
|
243
|
-
def on_update(self) -> Callable:
|
|
244
|
-
def decorator(func: Callable) -> Callable:
|
|
245
|
-
return self.add_handler(func)
|
|
246
|
-
|
|
247
|
-
return decorator
|
|
248
|
-
|
|
249
270
|
async def get_id(self):
|
|
250
271
|
pass
|
|
251
272
|
|
|
@@ -6,6 +6,7 @@ from typing import Union
|
|
|
6
6
|
|
|
7
7
|
from hydrogram import Client
|
|
8
8
|
from hydrogram import ContinuePropagation
|
|
9
|
+
from hydrogram.errors import FloodWait
|
|
9
10
|
from hydrogram.raw.base import InputPeer
|
|
10
11
|
from hydrogram.raw.base import InputUser
|
|
11
12
|
from hydrogram.raw.functions.channels import GetFullChannel
|
|
@@ -17,6 +18,7 @@ from hydrogram.raw.functions.phone import CreateGroupCall
|
|
|
17
18
|
from hydrogram.raw.functions.phone import DiscardCall
|
|
18
19
|
from hydrogram.raw.functions.phone import EditGroupCallParticipant
|
|
19
20
|
from hydrogram.raw.functions.phone import GetGroupCall
|
|
21
|
+
from hydrogram.raw.functions.phone import GetGroupCallStreamChannels
|
|
20
22
|
from hydrogram.raw.functions.phone import GetGroupParticipants
|
|
21
23
|
from hydrogram.raw.functions.phone import JoinGroupCall
|
|
22
24
|
from hydrogram.raw.functions.phone import JoinGroupCallPresentation
|
|
@@ -24,6 +26,7 @@ from hydrogram.raw.functions.phone import LeaveGroupCall
|
|
|
24
26
|
from hydrogram.raw.functions.phone import LeaveGroupCallPresentation
|
|
25
27
|
from hydrogram.raw.functions.phone import RequestCall
|
|
26
28
|
from hydrogram.raw.functions.phone import SendSignalingData
|
|
29
|
+
from hydrogram.raw.functions.upload import GetFile
|
|
27
30
|
from hydrogram.raw.types import Channel
|
|
28
31
|
from hydrogram.raw.types import ChannelForbidden
|
|
29
32
|
from hydrogram.raw.types import Chat
|
|
@@ -33,6 +36,7 @@ from hydrogram.raw.types import GroupCall
|
|
|
33
36
|
from hydrogram.raw.types import GroupCallDiscarded
|
|
34
37
|
from hydrogram.raw.types import InputChannel
|
|
35
38
|
from hydrogram.raw.types import InputGroupCall
|
|
39
|
+
from hydrogram.raw.types import InputGroupCallStream
|
|
36
40
|
from hydrogram.raw.types import InputPeerChannel
|
|
37
41
|
from hydrogram.raw.types import InputPhoneCall
|
|
38
42
|
from hydrogram.raw.types import MessageActionChatDeleteUser
|
|
@@ -58,6 +62,7 @@ from hydrogram.raw.types import UpdatePhoneCall
|
|
|
58
62
|
from hydrogram.raw.types import UpdatePhoneCallSignalingData
|
|
59
63
|
from hydrogram.raw.types import Updates
|
|
60
64
|
from hydrogram.raw.types.messages import DhConfig
|
|
65
|
+
from ntgcalls import MediaSegmentQuality
|
|
61
66
|
from ntgcalls import Protocol
|
|
62
67
|
|
|
63
68
|
from ..types import CallProtocol
|
|
@@ -612,6 +617,58 @@ class HydrogramClient(BridgedClient):
|
|
|
612
617
|
),
|
|
613
618
|
)
|
|
614
619
|
|
|
620
|
+
async def download_stream(
|
|
621
|
+
self,
|
|
622
|
+
chat_id: int,
|
|
623
|
+
timestamp: int,
|
|
624
|
+
limit: int,
|
|
625
|
+
video_channel: Optional[int],
|
|
626
|
+
video_quality: MediaSegmentQuality,
|
|
627
|
+
):
|
|
628
|
+
chat_call = await self._cache.get_full_chat(chat_id)
|
|
629
|
+
if chat_call is not None:
|
|
630
|
+
try:
|
|
631
|
+
return (
|
|
632
|
+
await self._app.invoke(
|
|
633
|
+
GetFile(
|
|
634
|
+
location=InputGroupCallStream(
|
|
635
|
+
call=chat_call,
|
|
636
|
+
time_ms=timestamp,
|
|
637
|
+
scale=0,
|
|
638
|
+
video_channel=video_channel,
|
|
639
|
+
video_quality=BridgedClient.parse_quality(
|
|
640
|
+
video_quality,
|
|
641
|
+
),
|
|
642
|
+
),
|
|
643
|
+
offset=0,
|
|
644
|
+
limit=limit,
|
|
645
|
+
),
|
|
646
|
+
sleep_threshold=0,
|
|
647
|
+
)
|
|
648
|
+
).bytes
|
|
649
|
+
except FloodWait:
|
|
650
|
+
pass
|
|
651
|
+
return None
|
|
652
|
+
|
|
653
|
+
async def get_stream_timestamp(
|
|
654
|
+
self,
|
|
655
|
+
chat_id: int,
|
|
656
|
+
):
|
|
657
|
+
chat_call = await self._cache.get_full_chat(chat_id)
|
|
658
|
+
if chat_call is not None:
|
|
659
|
+
# noinspection PyBroadException
|
|
660
|
+
channels = (
|
|
661
|
+
await self._app.invoke(
|
|
662
|
+
GetGroupCallStreamChannels(
|
|
663
|
+
call=chat_call,
|
|
664
|
+
),
|
|
665
|
+
)
|
|
666
|
+
).channels
|
|
667
|
+
if len(channels) > 0:
|
|
668
|
+
return channels[0].last_timestamp_ms
|
|
669
|
+
|
|
670
|
+
return 0
|
|
671
|
+
|
|
615
672
|
async def set_call_status(
|
|
616
673
|
self,
|
|
617
674
|
chat_id: int,
|
|
@@ -3,6 +3,7 @@ from typing import Callable
|
|
|
3
3
|
from typing import List
|
|
4
4
|
from typing import Optional
|
|
5
5
|
|
|
6
|
+
from ntgcalls import MediaSegmentQuality
|
|
6
7
|
from ntgcalls import Protocol
|
|
7
8
|
|
|
8
9
|
from ..exceptions import InvalidMTProtoClient
|
|
@@ -211,6 +212,36 @@ class MtProtoClient:
|
|
|
211
212
|
else:
|
|
212
213
|
raise InvalidMTProtoClient()
|
|
213
214
|
|
|
215
|
+
async def download_stream(
|
|
216
|
+
self,
|
|
217
|
+
chat_id: int,
|
|
218
|
+
timestamp: int,
|
|
219
|
+
limit: int,
|
|
220
|
+
video_channel: Optional[int],
|
|
221
|
+
video_quality: MediaSegmentQuality,
|
|
222
|
+
):
|
|
223
|
+
if self._bind_client is not None:
|
|
224
|
+
return await self._bind_client.download_stream(
|
|
225
|
+
chat_id,
|
|
226
|
+
timestamp,
|
|
227
|
+
limit,
|
|
228
|
+
video_channel,
|
|
229
|
+
video_quality,
|
|
230
|
+
)
|
|
231
|
+
else:
|
|
232
|
+
raise InvalidMTProtoClient()
|
|
233
|
+
|
|
234
|
+
async def get_stream_timestamp(
|
|
235
|
+
self,
|
|
236
|
+
chat_id: int,
|
|
237
|
+
):
|
|
238
|
+
if self._bind_client is not None:
|
|
239
|
+
return await self._bind_client.get_stream_timestamp(
|
|
240
|
+
chat_id,
|
|
241
|
+
)
|
|
242
|
+
else:
|
|
243
|
+
raise InvalidMTProtoClient()
|
|
244
|
+
|
|
214
245
|
async def set_call_status(
|
|
215
246
|
self,
|
|
216
247
|
chat_id: int,
|
|
@@ -281,7 +312,7 @@ class MtProtoClient:
|
|
|
281
312
|
else:
|
|
282
313
|
raise InvalidMTProtoClient()
|
|
283
314
|
|
|
284
|
-
def
|
|
315
|
+
def add_handler(self, func: Callable):
|
|
285
316
|
if self._bind_client is not None:
|
|
286
|
-
return self._bind_client.
|
|
317
|
+
return self._bind_client.add_handler(func)
|
|
287
318
|
raise InvalidMTProtoClient()
|